Passed
Push — master ( e95921...ddbd1b )
by Dimas
13:28 queued 05:15
created

assets/mdb-dashboard/js/modules/chart.js   F

Complexity

Total Complexity 2177
Complexity/F 2.39

Size

Lines of Code 14447
Function Count 909

Duplication

Duplicated Lines 1764
Ratio 12.21 %

Importance

Changes 0
Metric Value
eloc 8227
dl 1764
loc 14447
rs 0.8
c 0
b 0
f 0
wmc 2177
mnd 1268
bc 1268
fnc 909
bpm 1.3949
cpm 2.3948
noi 83

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like assets/mdb-dashboard/js/modules/chart.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*!
2
 * Chart.js
3
 * http://chartjs.org/
4
 * Version: 2.7.3
5
 *
6
 * Copyright 2018 Chart.js Contributors
7
 * Released under the MIT license
8
 * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
9
 */
10
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
11
12
},{}],2:[function(require,module,exports){
13
/* MIT license */
14
var colorNames = require(6);
15
16
module.exports = {
17
   getRgba: getRgba,
18
   getHsla: getHsla,
19
   getRgb: getRgb,
20
   getHsl: getHsl,
21
   getHwb: getHwb,
22
   getAlpha: getAlpha,
23
24
   hexString: hexString,
25
   rgbString: rgbString,
26
   rgbaString: rgbaString,
27
   percentString: percentString,
28
   percentaString: percentaString,
29
   hslString: hslString,
30
   hslaString: hslaString,
31
   hwbString: hwbString,
32
   keyword: keyword
33
}
34
35
function getRgba(string) {
36
   if (!string) {
37
      return;
38
   }
39
   var abbr =  /^#([a-fA-F0-9]{3})$/i,
40
       hex =  /^#([a-fA-F0-9]{6})$/i,
41
       rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
42
       per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
43
       keyword = /(\w+)/;
44
45
   var rgb = [0, 0, 0],
46
       a = 1,
47
       match = string.match(abbr);
48
   if (match) {
49
      match = match[1];
50
      for (var i = 0; i < rgb.length; i++) {
51
         rgb[i] = parseInt(match[i] + match[i], 16);
52
      }
53
   }
54
   else if (match = string.match(hex)) {
55
      match = match[1];
56
      for (var i = 0; i < rgb.length; i++) {
57
         rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
58
      }
59
   }
60
   else if (match = string.match(rgba)) {
61
      for (var i = 0; i < rgb.length; i++) {
62
         rgb[i] = parseInt(match[i + 1]);
63
      }
64
      a = parseFloat(match[4]);
65
   }
66
   else if (match = string.match(per)) {
67
      for (var i = 0; i < rgb.length; i++) {
68
         rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
69
      }
70
      a = parseFloat(match[4]);
71
   }
72
   else if (match = string.match(keyword)) {
73
      if (match[1] == "transparent") {
74
         return [0, 0, 0, 0];
75
      }
76
      rgb = colorNames[match[1]];
77
      if (!rgb) {
78
         return;
79
      }
80
   }
81
82
   for (var i = 0; i < rgb.length; i++) {
83
      rgb[i] = scale(rgb[i], 0, 255);
84
   }
85
   if (!a && a != 0) {
86
      a = 1;
87
   }
88
   else {
89
      a = scale(a, 0, 1);
90
   }
91
   rgb[3] = a;
92
   return rgb;
93
}
94
95
function getHsla(string) {
96
   if (!string) {
97
      return;
98
   }
99
   var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
100
   var match = string.match(hsl);
101
   if (match) {
102
      var alpha = parseFloat(match[4]);
103
      var h = scale(parseInt(match[1]), 0, 360),
104
          s = scale(parseFloat(match[2]), 0, 100),
105
          l = scale(parseFloat(match[3]), 0, 100),
106
          a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
107
      return [h, s, l, a];
108
   }
109
}
110
111
function getHwb(string) {
112
   if (!string) {
113
      return;
114
   }
115
   var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
116
   var match = string.match(hwb);
117
   if (match) {
118
    var alpha = parseFloat(match[4]);
119
      var h = scale(parseInt(match[1]), 0, 360),
120
          w = scale(parseFloat(match[2]), 0, 100),
121
          b = scale(parseFloat(match[3]), 0, 100),
122
          a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
123
      return [h, w, b, a];
124
   }
125
}
126
127
function getRgb(string) {
128
   var rgba = getRgba(string);
129
   return rgba && rgba.slice(0, 3);
130
}
131
132
function getHsl(string) {
133
  var hsla = getHsla(string);
134
  return hsla && hsla.slice(0, 3);
135
}
136
137
function getAlpha(string) {
138
   var vals = getRgba(string);
139
   if (vals) {
140
      return vals[3];
141
   }
142
   else if (vals = getHsla(string)) {
143
      return vals[3];
144
   }
145
   else if (vals = getHwb(string)) {
146
      return vals[3];
147
   }
148
}
149
150
// generators
151
function hexString(rgb) {
152
   return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1])
153
              + hexDouble(rgb[2]);
154
}
155
156
function rgbString(rgba, alpha) {
157
   if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
158
      return rgbaString(rgba, alpha);
159
   }
160
   return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
161
}
162
163
function rgbaString(rgba, alpha) {
164
   if (alpha === undefined) {
165
      alpha = (rgba[3] !== undefined ? rgba[3] : 1);
166
   }
167
   return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
168
           + ", " + alpha + ")";
169
}
170
171
function percentString(rgba, alpha) {
172
   if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
173
      return percentaString(rgba, alpha);
174
   }
175
   var r = Math.round(rgba[0]/255 * 100),
176
       g = Math.round(rgba[1]/255 * 100),
177
       b = Math.round(rgba[2]/255 * 100);
178
179
   return "rgb(" + r + "%, " + g + "%, " + b + "%)";
180
}
181
182
function percentaString(rgba, alpha) {
183
   var r = Math.round(rgba[0]/255 * 100),
184
       g = Math.round(rgba[1]/255 * 100),
185
       b = Math.round(rgba[2]/255 * 100);
186
   return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
187
}
188
189
function hslString(hsla, alpha) {
190
   if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
191
      return hslaString(hsla, alpha);
192
   }
193
   return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
194
}
195
196
function hslaString(hsla, alpha) {
197
   if (alpha === undefined) {
198
      alpha = (hsla[3] !== undefined ? hsla[3] : 1);
199
   }
200
   return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
201
           + alpha + ")";
202
}
203
204
// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
205
// (hwb have alpha optional & 1 is default value)
206
function hwbString(hwb, alpha) {
207
   if (alpha === undefined) {
208
      alpha = (hwb[3] !== undefined ? hwb[3] : 1);
209
   }
210
   return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
211
           + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
212
}
213
214
function keyword(rgb) {
215
  return reverseNames[rgb.slice(0, 3)];
216
}
217
218
// helpers
219
function scale(num, min, max) {
220
   return Math.min(Math.max(min, num), max);
221
}
222
223
function hexDouble(num) {
224
  var str = num.toString(16).toUpperCase();
225
  return (str.length < 2) ? "0" + str : str;
226
}
227
228
229
//create a list of reverse color names
230
var reverseNames = {};
231
for (var name in colorNames) {
232
   reverseNames[colorNames[name]] = name;
233
}
234
235
},{"6":6}],3:[function(require,module,exports){
236
/* MIT license */
237
var convert = require(5);
238
var string = require(2);
239
240
var Color = function (obj) {
241
	if (obj instanceof Color) {
242
		return obj;
243
	}
244
	if (!(this instanceof Color)) {
245
		return new Color(obj);
246
	}
247
248
	this.valid = false;
249
	this.values = {
250
		rgb: [0, 0, 0],
251
		hsl: [0, 0, 0],
252
		hsv: [0, 0, 0],
253
		hwb: [0, 0, 0],
254
		cmyk: [0, 0, 0, 0],
255
		alpha: 1
256
	};
257
258
	// parse Color() argument
259
	var vals;
260
	if (typeof obj === 'string') {
261
		vals = string.getRgba(obj);
262
		if (vals) {
263
			this.setValues('rgb', vals);
264
		} else if (vals = string.getHsla(obj)) {
265
			this.setValues('hsl', vals);
266
		} else if (vals = string.getHwb(obj)) {
267
			this.setValues('hwb', vals);
268
		}
269
	} else if (typeof obj === 'object') {
270
		vals = obj;
271
		if (vals.r !== undefined || vals.red !== undefined) {
272
			this.setValues('rgb', vals);
273
		} else if (vals.l !== undefined || vals.lightness !== undefined) {
274
			this.setValues('hsl', vals);
275
		} else if (vals.v !== undefined || vals.value !== undefined) {
276
			this.setValues('hsv', vals);
277
		} else if (vals.w !== undefined || vals.whiteness !== undefined) {
278
			this.setValues('hwb', vals);
279
		} else if (vals.c !== undefined || vals.cyan !== undefined) {
280
			this.setValues('cmyk', vals);
281
		}
282
	}
283
};
284
285
Color.prototype = {
286
	isValid: function () {
287
		return this.valid;
288
	},
289
	rgb: function () {
290
		return this.setSpace('rgb', arguments);
291
	},
292
	hsl: function () {
293
		return this.setSpace('hsl', arguments);
294
	},
295
	hsv: function () {
296
		return this.setSpace('hsv', arguments);
297
	},
298
	hwb: function () {
299
		return this.setSpace('hwb', arguments);
300
	},
301
	cmyk: function () {
302
		return this.setSpace('cmyk', arguments);
303
	},
304
305
	rgbArray: function () {
306
		return this.values.rgb;
307
	},
308
	hslArray: function () {
309
		return this.values.hsl;
310
	},
311
	hsvArray: function () {
312
		return this.values.hsv;
313
	},
314
	hwbArray: function () {
315
		var values = this.values;
316
		if (values.alpha !== 1) {
317
			return values.hwb.concat([values.alpha]);
318
		}
319
		return values.hwb;
320
	},
321
	cmykArray: function () {
322
		return this.values.cmyk;
323
	},
324
	rgbaArray: function () {
325
		var values = this.values;
326
		return values.rgb.concat([values.alpha]);
327
	},
328
	hslaArray: function () {
329
		var values = this.values;
330
		return values.hsl.concat([values.alpha]);
331
	},
332
	alpha: function (val) {
333
		if (val === undefined) {
334
			return this.values.alpha;
335
		}
336
		this.setValues('alpha', val);
337
		return this;
338
	},
339
340
	red: function (val) {
341
		return this.setChannel('rgb', 0, val);
342
	},
343
	green: function (val) {
344
		return this.setChannel('rgb', 1, val);
345
	},
346
	blue: function (val) {
347
		return this.setChannel('rgb', 2, val);
348
	},
349
	hue: function (val) {
350
		if (val) {
351
			val %= 360;
352
			val = val < 0 ? 360 + val : val;
353
		}
354
		return this.setChannel('hsl', 0, val);
355
	},
356
	saturation: function (val) {
357
		return this.setChannel('hsl', 1, val);
358
	},
359
	lightness: function (val) {
360
		return this.setChannel('hsl', 2, val);
361
	},
362
	saturationv: function (val) {
363
		return this.setChannel('hsv', 1, val);
364
	},
365
	whiteness: function (val) {
366
		return this.setChannel('hwb', 1, val);
367
	},
368
	blackness: function (val) {
369
		return this.setChannel('hwb', 2, val);
370
	},
371
	value: function (val) {
372
		return this.setChannel('hsv', 2, val);
373
	},
374
	cyan: function (val) {
375
		return this.setChannel('cmyk', 0, val);
376
	},
377
	magenta: function (val) {
378
		return this.setChannel('cmyk', 1, val);
379
	},
380
	yellow: function (val) {
381
		return this.setChannel('cmyk', 2, val);
382
	},
383
	black: function (val) {
384
		return this.setChannel('cmyk', 3, val);
385
	},
386
387
	hexString: function () {
388
		return string.hexString(this.values.rgb);
389
	},
390
	rgbString: function () {
391
		return string.rgbString(this.values.rgb, this.values.alpha);
392
	},
393
	rgbaString: function () {
394
		return string.rgbaString(this.values.rgb, this.values.alpha);
395
	},
396
	percentString: function () {
397
		return string.percentString(this.values.rgb, this.values.alpha);
398
	},
399
	hslString: function () {
400
		return string.hslString(this.values.hsl, this.values.alpha);
401
	},
402
	hslaString: function () {
403
		return string.hslaString(this.values.hsl, this.values.alpha);
404
	},
405
	hwbString: function () {
406
		return string.hwbString(this.values.hwb, this.values.alpha);
407
	},
408
	keyword: function () {
409
		return string.keyword(this.values.rgb, this.values.alpha);
410
	},
411
412
	rgbNumber: function () {
413
		var rgb = this.values.rgb;
414
		return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
415
	},
416
417
	luminosity: function () {
418
		// http://www.w3.org/TR/WCAG20/#relativeluminancedef
419
		var rgb = this.values.rgb;
420
		var lum = [];
421
		for (var i = 0; i < rgb.length; i++) {
422
			var chan = rgb[i] / 255;
423
			lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
424
		}
425
		return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
426
	},
427
428
	contrast: function (color2) {
429
		// http://www.w3.org/TR/WCAG20/#contrast-ratiodef
430
		var lum1 = this.luminosity();
431
		var lum2 = color2.luminosity();
432
		if (lum1 > lum2) {
433
			return (lum1 + 0.05) / (lum2 + 0.05);
434
		}
435
		return (lum2 + 0.05) / (lum1 + 0.05);
436
	},
437
438
	level: function (color2) {
439
		var contrastRatio = this.contrast(color2);
440
		if (contrastRatio >= 7.1) {
441
			return 'AAA';
442
		}
443
444
		return (contrastRatio >= 4.5) ? 'AA' : '';
445
	},
446
447
	dark: function () {
448
		// YIQ equation from http://24ways.org/2010/calculating-color-contrast
449
		var rgb = this.values.rgb;
450
		var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
451
		return yiq < 128;
452
	},
453
454
	light: function () {
455
		return !this.dark();
456
	},
457
458
	negate: function () {
459
		var rgb = [];
460
		for (var i = 0; i < 3; i++) {
461
			rgb[i] = 255 - this.values.rgb[i];
462
		}
463
		this.setValues('rgb', rgb);
464
		return this;
465
	},
466
467
	lighten: function (ratio) {
468
		var hsl = this.values.hsl;
469
		hsl[2] += hsl[2] * ratio;
470
		this.setValues('hsl', hsl);
471
		return this;
472
	},
473
474
	darken: function (ratio) {
475
		var hsl = this.values.hsl;
476
		hsl[2] -= hsl[2] * ratio;
477
		this.setValues('hsl', hsl);
478
		return this;
479
	},
480
481
	saturate: function (ratio) {
482
		var hsl = this.values.hsl;
483
		hsl[1] += hsl[1] * ratio;
484
		this.setValues('hsl', hsl);
485
		return this;
486
	},
487
488
	desaturate: function (ratio) {
489
		var hsl = this.values.hsl;
490
		hsl[1] -= hsl[1] * ratio;
491
		this.setValues('hsl', hsl);
492
		return this;
493
	},
494
495
	whiten: function (ratio) {
496
		var hwb = this.values.hwb;
497
		hwb[1] += hwb[1] * ratio;
498
		this.setValues('hwb', hwb);
499
		return this;
500
	},
501
502
	blacken: function (ratio) {
503
		var hwb = this.values.hwb;
504
		hwb[2] += hwb[2] * ratio;
505
		this.setValues('hwb', hwb);
506
		return this;
507
	},
508
509
	greyscale: function () {
510
		var rgb = this.values.rgb;
511
		// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
512
		var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
513
		this.setValues('rgb', [val, val, val]);
514
		return this;
515
	},
516
517
	clearer: function (ratio) {
518
		var alpha = this.values.alpha;
519
		this.setValues('alpha', alpha - (alpha * ratio));
520
		return this;
521
	},
522
523
	opaquer: function (ratio) {
524
		var alpha = this.values.alpha;
525
		this.setValues('alpha', alpha + (alpha * ratio));
526
		return this;
527
	},
528
529
	rotate: function (degrees) {
530
		var hsl = this.values.hsl;
531
		var hue = (hsl[0] + degrees) % 360;
532
		hsl[0] = hue < 0 ? 360 + hue : hue;
533
		this.setValues('hsl', hsl);
534
		return this;
535
	},
536
537
	/**
538
	 * Ported from sass implementation in C
539
	 * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
540
	 */
541
	mix: function (mixinColor, weight) {
542
		var color1 = this;
543
		var color2 = mixinColor;
544
		var p = weight === undefined ? 0.5 : weight;
545
546
		var w = 2 * p - 1;
547
		var a = color1.alpha() - color2.alpha();
548
549
		var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
550
		var w2 = 1 - w1;
551
552
		return this
553
			.rgb(
554
				w1 * color1.red() + w2 * color2.red(),
555
				w1 * color1.green() + w2 * color2.green(),
556
				w1 * color1.blue() + w2 * color2.blue()
557
			)
558
			.alpha(color1.alpha() * p + color2.alpha() * (1 - p));
559
	},
560
561
	toJSON: function () {
562
		return this.rgb();
563
	},
564
565
	clone: function () {
566
		// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
567
		// making the final build way to big to embed in Chart.js. So let's do it manually,
568
		// assuming that values to clone are 1 dimension arrays containing only numbers,
569
		// except 'alpha' which is a number.
570
		var result = new Color();
571
		var source = this.values;
572
		var target = result.values;
573
		var value, type;
574
575
		for (var prop in source) {
576
			if (source.hasOwnProperty(prop)) {
577
				value = source[prop];
578
				type = ({}).toString.call(value);
579
				if (type === '[object Array]') {
580
					target[prop] = value.slice(0);
581
				} else if (type === '[object Number]') {
582
					target[prop] = value;
583
				} else {
584
					console.error('unexpected color value:', value);
585
				}
586
			}
587
		}
588
589
		return result;
590
	}
591
};
592
593
Color.prototype.spaces = {
594
	rgb: ['red', 'green', 'blue'],
595
	hsl: ['hue', 'saturation', 'lightness'],
596
	hsv: ['hue', 'saturation', 'value'],
597
	hwb: ['hue', 'whiteness', 'blackness'],
598
	cmyk: ['cyan', 'magenta', 'yellow', 'black']
599
};
600
601
Color.prototype.maxes = {
602
	rgb: [255, 255, 255],
603
	hsl: [360, 100, 100],
604
	hsv: [360, 100, 100],
605
	hwb: [360, 100, 100],
606
	cmyk: [100, 100, 100, 100]
607
};
608
609
Color.prototype.getValues = function (space) {
610
	var values = this.values;
611
	var vals = {};
612
613
	for (var i = 0; i < space.length; i++) {
614
		vals[space.charAt(i)] = values[space][i];
615
	}
616
617
	if (values.alpha !== 1) {
618
		vals.a = values.alpha;
619
	}
620
621
	// {r: 255, g: 255, b: 255, a: 0.4}
622
	return vals;
623
};
624
625
Color.prototype.setValues = function (space, vals) {
626
	var values = this.values;
627
	var spaces = this.spaces;
628
	var maxes = this.maxes;
629
	var alpha = 1;
630
	var i;
631
632
	this.valid = true;
633
634
	if (space === 'alpha') {
635
		alpha = vals;
636
	} else if (vals.length) {
637
		// [10, 10, 10]
638
		values[space] = vals.slice(0, space.length);
639
		alpha = vals[space.length];
640
	} else if (vals[space.charAt(0)] !== undefined) {
641
		// {r: 10, g: 10, b: 10}
642
		for (i = 0; i < space.length; i++) {
643
			values[space][i] = vals[space.charAt(i)];
644
		}
645
646
		alpha = vals.a;
647
	} else if (vals[spaces[space][0]] !== undefined) {
648
		// {red: 10, green: 10, blue: 10}
649
		var chans = spaces[space];
650
651
		for (i = 0; i < space.length; i++) {
652
			values[space][i] = vals[chans[i]];
653
		}
654
655
		alpha = vals.alpha;
656
	}
657
658
	values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));
659
660
	if (space === 'alpha') {
661
		return false;
662
	}
663
664
	var capped;
665
666
	// cap values of the space prior converting all values
667
	for (i = 0; i < space.length; i++) {
668
		capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
669
		values[space][i] = Math.round(capped);
670
	}
671
672
	// convert to all the other color spaces
673
	for (var sname in spaces) {
674
		if (sname !== space) {
675
			values[sname] = convert[space][sname](values[space]);
676
		}
677
	}
678
679
	return true;
680
};
681
682
Color.prototype.setSpace = function (space, args) {
683
	var vals = args[0];
684
685
	if (vals === undefined) {
686
		// color.rgb()
687
		return this.getValues(space);
688
	}
689
690
	// color.rgb(10, 10, 10)
691
	if (typeof vals === 'number') {
692
		vals = Array.prototype.slice.call(args);
693
	}
694
695
	this.setValues(space, vals);
696
	return this;
697
};
698
699
Color.prototype.setChannel = function (space, index, val) {
700
	var svalues = this.values[space];
701
	if (val === undefined) {
702
		// color.red()
703
		return svalues[index];
704
	} else if (val === svalues[index]) {
705
		// color.red(color.red())
706
		return this;
707
	}
708
709
	// color.red(100)
710
	svalues[index] = val;
711
	this.setValues(space, svalues);
712
713
	return this;
714
};
715
716
if (typeof window !== 'undefined') {
717
	window.Color = Color;
718
}
719
720
module.exports = Color;
721
722
},{"2":2,"5":5}],4:[function(require,module,exports){
723
/* MIT license */
724
725
module.exports = {
726
  rgb2hsl: rgb2hsl,
727
  rgb2hsv: rgb2hsv,
728
  rgb2hwb: rgb2hwb,
729
  rgb2cmyk: rgb2cmyk,
730
  rgb2keyword: rgb2keyword,
731
  rgb2xyz: rgb2xyz,
732
  rgb2lab: rgb2lab,
733
  rgb2lch: rgb2lch,
734
735
  hsl2rgb: hsl2rgb,
736
  hsl2hsv: hsl2hsv,
737
  hsl2hwb: hsl2hwb,
738
  hsl2cmyk: hsl2cmyk,
739
  hsl2keyword: hsl2keyword,
740
741
  hsv2rgb: hsv2rgb,
742
  hsv2hsl: hsv2hsl,
743
  hsv2hwb: hsv2hwb,
744
  hsv2cmyk: hsv2cmyk,
745
  hsv2keyword: hsv2keyword,
746
747
  hwb2rgb: hwb2rgb,
748
  hwb2hsl: hwb2hsl,
749
  hwb2hsv: hwb2hsv,
750
  hwb2cmyk: hwb2cmyk,
751
  hwb2keyword: hwb2keyword,
752
753
  cmyk2rgb: cmyk2rgb,
754
  cmyk2hsl: cmyk2hsl,
755
  cmyk2hsv: cmyk2hsv,
756
  cmyk2hwb: cmyk2hwb,
757
  cmyk2keyword: cmyk2keyword,
758
759
  keyword2rgb: keyword2rgb,
760
  keyword2hsl: keyword2hsl,
761
  keyword2hsv: keyword2hsv,
762
  keyword2hwb: keyword2hwb,
763
  keyword2cmyk: keyword2cmyk,
764
  keyword2lab: keyword2lab,
765
  keyword2xyz: keyword2xyz,
766
767
  xyz2rgb: xyz2rgb,
768
  xyz2lab: xyz2lab,
769
  xyz2lch: xyz2lch,
770
771
  lab2xyz: lab2xyz,
772
  lab2rgb: lab2rgb,
773
  lab2lch: lab2lch,
774
775
  lch2lab: lch2lab,
776
  lch2xyz: lch2xyz,
777
  lch2rgb: lch2rgb
778
}
779
780
781
function rgb2hsl(rgb) {
782
  var r = rgb[0]/255,
783
      g = rgb[1]/255,
784
      b = rgb[2]/255,
785
      min = Math.min(r, g, b),
786
      max = Math.max(r, g, b),
787
      delta = max - min,
788
      h, s, l;
789
790
  if (max == min)
791
    h = 0;
792
  else if (r == max)
793
    h = (g - b) / delta;
794
  else if (g == max)
795
    h = 2 + (b - r) / delta;
796
  else if (b == max)
797
    h = 4 + (r - g)/ delta;
798
799
  h = Math.min(h * 60, 360);
800
801
  if (h < 0)
802
    h += 360;
803
804
  l = (min + max) / 2;
805
806
  if (max == min)
807
    s = 0;
808
  else if (l <= 0.5)
809
    s = delta / (max + min);
810
  else
811
    s = delta / (2 - max - min);
812
813
  return [h, s * 100, l * 100];
814
}
815
816
function rgb2hsv(rgb) {
817
  var r = rgb[0],
818
      g = rgb[1],
819
      b = rgb[2],
820
      min = Math.min(r, g, b),
821
      max = Math.max(r, g, b),
822
      delta = max - min,
823
      h, s, v;
824
825
  if (max == 0)
826
    s = 0;
827
  else
828
    s = (delta/max * 1000)/10;
829
830
  if (max == min)
831
    h = 0;
832
  else if (r == max)
833
    h = (g - b) / delta;
834
  else if (g == max)
835
    h = 2 + (b - r) / delta;
836
  else if (b == max)
837
    h = 4 + (r - g) / delta;
838
839
  h = Math.min(h * 60, 360);
840
841
  if (h < 0)
842
    h += 360;
843
844
  v = ((max / 255) * 1000) / 10;
845
846
  return [h, s, v];
847
}
848
849
function rgb2hwb(rgb) {
850
  var r = rgb[0],
851
      g = rgb[1],
852
      b = rgb[2],
853
      h = rgb2hsl(rgb)[0],
854
      w = 1/255 * Math.min(r, Math.min(g, b)),
855
      b = 1 - 1/255 * Math.max(r, Math.max(g, b));
856
857
  return [h, w * 100, b * 100];
858
}
859
860
function rgb2cmyk(rgb) {
861
  var r = rgb[0] / 255,
862
      g = rgb[1] / 255,
863
      b = rgb[2] / 255,
864
      c, m, y, k;
865
866
  k = Math.min(1 - r, 1 - g, 1 - b);
867
  c = (1 - r - k) / (1 - k) || 0;
868
  m = (1 - g - k) / (1 - k) || 0;
869
  y = (1 - b - k) / (1 - k) || 0;
870
  return [c * 100, m * 100, y * 100, k * 100];
871
}
872
873
function rgb2keyword(rgb) {
874
  return reverseKeywords[JSON.stringify(rgb)];
875
}
876
877
function rgb2xyz(rgb) {
878
  var r = rgb[0] / 255,
879
      g = rgb[1] / 255,
880
      b = rgb[2] / 255;
881
882
  // assume sRGB
883
  r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
884
  g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
885
  b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
886
887
  var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
888
  var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
889
  var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
890
891
  return [x * 100, y *100, z * 100];
892
}
893
894
function rgb2lab(rgb) {
895
  var xyz = rgb2xyz(rgb),
896
        x = xyz[0],
897
        y = xyz[1],
898
        z = xyz[2],
899
        l, a, b;
900
901
  x /= 95.047;
902
  y /= 100;
903
  z /= 108.883;
904
905
  x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
906
  y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
907
  z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
908
909
  l = (116 * y) - 16;
910
  a = 500 * (x - y);
911
  b = 200 * (y - z);
912
913
  return [l, a, b];
914
}
915
916
function rgb2lch(args) {
917
  return lab2lch(rgb2lab(args));
918
}
919
920
function hsl2rgb(hsl) {
921
  var h = hsl[0] / 360,
922
      s = hsl[1] / 100,
923
      l = hsl[2] / 100,
924
      t1, t2, t3, rgb, val;
925
926
  if (s == 0) {
927
    val = l * 255;
928
    return [val, val, val];
929
  }
930
931
  if (l < 0.5)
932
    t2 = l * (1 + s);
933
  else
934
    t2 = l + s - l * s;
935
  t1 = 2 * l - t2;
936
937
  rgb = [0, 0, 0];
938
  for (var i = 0; i < 3; i++) {
939
    t3 = h + 1 / 3 * - (i - 1);
940
    t3 < 0 && t3++;
941
    t3 > 1 && t3--;
942
943
    if (6 * t3 < 1)
944
      val = t1 + (t2 - t1) * 6 * t3;
945
    else if (2 * t3 < 1)
946
      val = t2;
947
    else if (3 * t3 < 2)
948
      val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
949
    else
950
      val = t1;
951
952
    rgb[i] = val * 255;
953
  }
954
955
  return rgb;
956
}
957
958
function hsl2hsv(hsl) {
959
  var h = hsl[0],
960
      s = hsl[1] / 100,
961
      l = hsl[2] / 100,
962
      sv, v;
963
964
  if(l === 0) {
965
      // no need to do calc on black
966
      // also avoids divide by 0 error
967
      return [0, 0, 0];
968
  }
969
970
  l *= 2;
971
  s *= (l <= 1) ? l : 2 - l;
972
  v = (l + s) / 2;
973
  sv = (2 * s) / (l + s);
974
  return [h, sv * 100, v * 100];
975
}
976
977
function hsl2hwb(args) {
978
  return rgb2hwb(hsl2rgb(args));
979
}
980
981
function hsl2cmyk(args) {
982
  return rgb2cmyk(hsl2rgb(args));
983
}
984
985
function hsl2keyword(args) {
986
  return rgb2keyword(hsl2rgb(args));
987
}
988
989
990
function hsv2rgb(hsv) {
991
  var h = hsv[0] / 60,
992
      s = hsv[1] / 100,
993
      v = hsv[2] / 100,
994
      hi = Math.floor(h) % 6;
995
996
  var f = h - Math.floor(h),
997
      p = 255 * v * (1 - s),
998
      q = 255 * v * (1 - (s * f)),
999
      t = 255 * v * (1 - (s * (1 - f))),
1000
      v = 255 * v;
1001
1002
  switch(hi) {
1003
    case 0:
1004
      return [v, t, p];
1005
    case 1:
1006
      return [q, v, p];
1007
    case 2:
1008
      return [p, v, t];
1009
    case 3:
1010
      return [p, q, v];
1011
    case 4:
1012
      return [t, p, v];
1013
    case 5:
1014
      return [v, p, q];
1015
  }
1016
}
1017
1018
function hsv2hsl(hsv) {
1019
  var h = hsv[0],
1020
      s = hsv[1] / 100,
1021
      v = hsv[2] / 100,
1022
      sl, l;
1023
1024
  l = (2 - s) * v;
1025
  sl = s * v;
1026
  sl /= (l <= 1) ? l : 2 - l;
1027
  sl = sl || 0;
1028
  l /= 2;
1029
  return [h, sl * 100, l * 100];
1030
}
1031
1032
function hsv2hwb(args) {
1033
  return rgb2hwb(hsv2rgb(args))
1034
}
1035
1036
function hsv2cmyk(args) {
1037
  return rgb2cmyk(hsv2rgb(args));
1038
}
1039
1040
function hsv2keyword(args) {
1041
  return rgb2keyword(hsv2rgb(args));
1042
}
1043
1044
// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
1045
function hwb2rgb(hwb) {
1046
  var h = hwb[0] / 360,
1047
      wh = hwb[1] / 100,
1048
      bl = hwb[2] / 100,
1049
      ratio = wh + bl,
1050
      i, v, f, n;
1051
1052
  // wh + bl cant be > 1
1053
  if (ratio > 1) {
1054
    wh /= ratio;
1055
    bl /= ratio;
1056
  }
1057
1058
  i = Math.floor(6 * h);
1059
  v = 1 - bl;
1060
  f = 6 * h - i;
1061
  if ((i & 0x01) != 0) {
1062
    f = 1 - f;
1063
  }
1064
  n = wh + f * (v - wh);  // linear interpolation
1065
1066
  switch (i) {
1067
    default:
1068
    case 6:
1069
    case 0: r = v; g = n; b = wh; break;
1070
    case 1: r = n; g = v; b = wh; break;
1071
    case 2: r = wh; g = v; b = n; break;
1072
    case 3: r = wh; g = n; b = v; break;
1073
    case 4: r = n; g = wh; b = v; break;
1074
    case 5: r = v; g = wh; b = n; break;
1075
  }
1076
1077
  return [r * 255, g * 255, b * 255];
1078
}
1079
1080
function hwb2hsl(args) {
1081
  return rgb2hsl(hwb2rgb(args));
1082
}
1083
1084
function hwb2hsv(args) {
1085
  return rgb2hsv(hwb2rgb(args));
1086
}
1087
1088
function hwb2cmyk(args) {
1089
  return rgb2cmyk(hwb2rgb(args));
1090
}
1091
1092
function hwb2keyword(args) {
1093
  return rgb2keyword(hwb2rgb(args));
1094
}
1095
1096
function cmyk2rgb(cmyk) {
1097
  var c = cmyk[0] / 100,
1098
      m = cmyk[1] / 100,
1099
      y = cmyk[2] / 100,
1100
      k = cmyk[3] / 100,
1101
      r, g, b;
1102
1103
  r = 1 - Math.min(1, c * (1 - k) + k);
1104
  g = 1 - Math.min(1, m * (1 - k) + k);
1105
  b = 1 - Math.min(1, y * (1 - k) + k);
1106
  return [r * 255, g * 255, b * 255];
1107
}
1108
1109
function cmyk2hsl(args) {
1110
  return rgb2hsl(cmyk2rgb(args));
1111
}
1112
1113
function cmyk2hsv(args) {
1114
  return rgb2hsv(cmyk2rgb(args));
1115
}
1116
1117
function cmyk2hwb(args) {
1118
  return rgb2hwb(cmyk2rgb(args));
1119
}
1120
1121
function cmyk2keyword(args) {
1122
  return rgb2keyword(cmyk2rgb(args));
1123
}
1124
1125
1126
function xyz2rgb(xyz) {
1127
  var x = xyz[0] / 100,
1128
      y = xyz[1] / 100,
1129
      z = xyz[2] / 100,
1130
      r, g, b;
1131
1132
  r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
1133
  g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
1134
  b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
1135
1136
  // assume sRGB
1137
  r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
1138
    : r = (r * 12.92);
1139
1140
  g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
1141
    : g = (g * 12.92);
1142
1143
  b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
1144
    : b = (b * 12.92);
1145
1146
  r = Math.min(Math.max(0, r), 1);
1147
  g = Math.min(Math.max(0, g), 1);
1148
  b = Math.min(Math.max(0, b), 1);
1149
1150
  return [r * 255, g * 255, b * 255];
1151
}
1152
1153
function xyz2lab(xyz) {
1154
  var x = xyz[0],
1155
      y = xyz[1],
1156
      z = xyz[2],
1157
      l, a, b;
1158
1159
  x /= 95.047;
1160
  y /= 100;
1161
  z /= 108.883;
1162
1163
  x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
1164
  y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
1165
  z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
1166
1167
  l = (116 * y) - 16;
1168
  a = 500 * (x - y);
1169
  b = 200 * (y - z);
1170
1171
  return [l, a, b];
1172
}
1173
1174
function xyz2lch(args) {
1175
  return lab2lch(xyz2lab(args));
1176
}
1177
1178
function lab2xyz(lab) {
1179
  var l = lab[0],
1180
      a = lab[1],
1181
      b = lab[2],
1182
      x, y, z, y2;
1183
1184
  if (l <= 8) {
1185
    y = (l * 100) / 903.3;
1186
    y2 = (7.787 * (y / 100)) + (16 / 116);
1187
  } else {
1188
    y = 100 * Math.pow((l + 16) / 116, 3);
1189
    y2 = Math.pow(y / 100, 1/3);
1190
  }
1191
1192
  x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);
1193
1194
  z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);
1195
1196
  return [x, y, z];
1197
}
1198
1199
function lab2lch(lab) {
1200
  var l = lab[0],
1201
      a = lab[1],
1202
      b = lab[2],
1203
      hr, h, c;
1204
1205
  hr = Math.atan2(b, a);
1206
  h = hr * 360 / 2 / Math.PI;
1207
  if (h < 0) {
1208
    h += 360;
1209
  }
1210
  c = Math.sqrt(a * a + b * b);
1211
  return [l, c, h];
1212
}
1213
1214
function lab2rgb(args) {
1215
  return xyz2rgb(lab2xyz(args));
1216
}
1217
1218
function lch2lab(lch) {
1219
  var l = lch[0],
1220
      c = lch[1],
1221
      h = lch[2],
1222
      a, b, hr;
1223
1224
  hr = h / 360 * 2 * Math.PI;
1225
  a = c * Math.cos(hr);
1226
  b = c * Math.sin(hr);
1227
  return [l, a, b];
1228
}
1229
1230
function lch2xyz(args) {
1231
  return lab2xyz(lch2lab(args));
1232
}
1233
1234
function lch2rgb(args) {
1235
  return lab2rgb(lch2lab(args));
1236
}
1237
1238
function keyword2rgb(keyword) {
1239
  return cssKeywords[keyword];
1240
}
1241
1242
function keyword2hsl(args) {
1243
  return rgb2hsl(keyword2rgb(args));
1244
}
1245
1246
function keyword2hsv(args) {
1247
  return rgb2hsv(keyword2rgb(args));
1248
}
1249
1250
function keyword2hwb(args) {
1251
  return rgb2hwb(keyword2rgb(args));
1252
}
1253
1254
function keyword2cmyk(args) {
1255
  return rgb2cmyk(keyword2rgb(args));
1256
}
1257
1258
function keyword2lab(args) {
1259
  return rgb2lab(keyword2rgb(args));
1260
}
1261
1262
function keyword2xyz(args) {
1263
  return rgb2xyz(keyword2rgb(args));
1264
}
1265
1266
var cssKeywords = {
1267
  aliceblue:  [240,248,255],
1268
  antiquewhite: [250,235,215],
1269
  aqua: [0,255,255],
1270
  aquamarine: [127,255,212],
1271
  azure:  [240,255,255],
1272
  beige:  [245,245,220],
1273
  bisque: [255,228,196],
1274
  black:  [0,0,0],
1275
  blanchedalmond: [255,235,205],
1276
  blue: [0,0,255],
1277
  blueviolet: [138,43,226],
1278
  brown:  [165,42,42],
1279
  burlywood:  [222,184,135],
1280
  cadetblue:  [95,158,160],
1281
  chartreuse: [127,255,0],
1282
  chocolate:  [210,105,30],
1283
  coral:  [255,127,80],
1284
  cornflowerblue: [100,149,237],
1285
  cornsilk: [255,248,220],
1286
  crimson:  [220,20,60],
1287
  cyan: [0,255,255],
1288
  darkblue: [0,0,139],
1289
  darkcyan: [0,139,139],
1290
  darkgoldenrod:  [184,134,11],
1291
  darkgray: [169,169,169],
1292
  darkgreen:  [0,100,0],
1293
  darkgrey: [169,169,169],
1294
  darkkhaki:  [189,183,107],
1295
  darkmagenta:  [139,0,139],
1296
  darkolivegreen: [85,107,47],
1297
  darkorange: [255,140,0],
1298
  darkorchid: [153,50,204],
1299
  darkred:  [139,0,0],
1300
  darksalmon: [233,150,122],
1301
  darkseagreen: [143,188,143],
1302
  darkslateblue:  [72,61,139],
1303
  darkslategray:  [47,79,79],
1304
  darkslategrey:  [47,79,79],
1305
  darkturquoise:  [0,206,209],
1306
  darkviolet: [148,0,211],
1307
  deeppink: [255,20,147],
1308
  deepskyblue:  [0,191,255],
1309
  dimgray:  [105,105,105],
1310
  dimgrey:  [105,105,105],
1311
  dodgerblue: [30,144,255],
1312
  firebrick:  [178,34,34],
1313
  floralwhite:  [255,250,240],
1314
  forestgreen:  [34,139,34],
1315
  fuchsia:  [255,0,255],
1316
  gainsboro:  [220,220,220],
1317
  ghostwhite: [248,248,255],
1318
  gold: [255,215,0],
1319
  goldenrod:  [218,165,32],
1320
  gray: [128,128,128],
1321
  green:  [0,128,0],
1322
  greenyellow:  [173,255,47],
1323
  grey: [128,128,128],
1324
  honeydew: [240,255,240],
1325
  hotpink:  [255,105,180],
1326
  indianred:  [205,92,92],
1327
  indigo: [75,0,130],
1328
  ivory:  [255,255,240],
1329
  khaki:  [240,230,140],
1330
  lavender: [230,230,250],
1331
  lavenderblush:  [255,240,245],
1332
  lawngreen:  [124,252,0],
1333
  lemonchiffon: [255,250,205],
1334
  lightblue:  [173,216,230],
1335
  lightcoral: [240,128,128],
1336
  lightcyan:  [224,255,255],
1337
  lightgoldenrodyellow: [250,250,210],
1338
  lightgray:  [211,211,211],
1339
  lightgreen: [144,238,144],
1340
  lightgrey:  [211,211,211],
1341
  lightpink:  [255,182,193],
1342
  lightsalmon:  [255,160,122],
1343
  lightseagreen:  [32,178,170],
1344
  lightskyblue: [135,206,250],
1345
  lightslategray: [119,136,153],
1346
  lightslategrey: [119,136,153],
1347
  lightsteelblue: [176,196,222],
1348
  lightyellow:  [255,255,224],
1349
  lime: [0,255,0],
1350
  limegreen:  [50,205,50],
1351
  linen:  [250,240,230],
1352
  magenta:  [255,0,255],
1353
  maroon: [128,0,0],
1354
  mediumaquamarine: [102,205,170],
1355
  mediumblue: [0,0,205],
1356
  mediumorchid: [186,85,211],
1357
  mediumpurple: [147,112,219],
1358
  mediumseagreen: [60,179,113],
1359
  mediumslateblue:  [123,104,238],
1360
  mediumspringgreen:  [0,250,154],
1361
  mediumturquoise:  [72,209,204],
1362
  mediumvioletred:  [199,21,133],
1363
  midnightblue: [25,25,112],
1364
  mintcream:  [245,255,250],
1365
  mistyrose:  [255,228,225],
1366
  moccasin: [255,228,181],
1367
  navajowhite:  [255,222,173],
1368
  navy: [0,0,128],
1369
  oldlace:  [253,245,230],
1370
  olive:  [128,128,0],
1371
  olivedrab:  [107,142,35],
1372
  orange: [255,165,0],
1373
  orangered:  [255,69,0],
1374
  orchid: [218,112,214],
1375
  palegoldenrod:  [238,232,170],
1376
  palegreen:  [152,251,152],
1377
  paleturquoise:  [175,238,238],
1378
  palevioletred:  [219,112,147],
1379
  papayawhip: [255,239,213],
1380
  peachpuff:  [255,218,185],
1381
  peru: [205,133,63],
1382
  pink: [255,192,203],
1383
  plum: [221,160,221],
1384
  powderblue: [176,224,230],
1385
  purple: [128,0,128],
1386
  rebeccapurple: [102, 51, 153],
1387
  red:  [255,0,0],
1388
  rosybrown:  [188,143,143],
1389
  royalblue:  [65,105,225],
1390
  saddlebrown:  [139,69,19],
1391
  salmon: [250,128,114],
1392
  sandybrown: [244,164,96],
1393
  seagreen: [46,139,87],
1394
  seashell: [255,245,238],
1395
  sienna: [160,82,45],
1396
  silver: [192,192,192],
1397
  skyblue:  [135,206,235],
1398
  slateblue:  [106,90,205],
1399
  slategray:  [112,128,144],
1400
  slategrey:  [112,128,144],
1401
  snow: [255,250,250],
1402
  springgreen:  [0,255,127],
1403
  steelblue:  [70,130,180],
1404
  tan:  [210,180,140],
1405
  teal: [0,128,128],
1406
  thistle:  [216,191,216],
1407
  tomato: [255,99,71],
1408
  turquoise:  [64,224,208],
1409
  violet: [238,130,238],
1410
  wheat:  [245,222,179],
1411
  white:  [255,255,255],
1412
  whitesmoke: [245,245,245],
1413
  yellow: [255,255,0],
1414
  yellowgreen:  [154,205,50]
1415
};
1416
1417
var reverseKeywords = {};
1418
for (var key in cssKeywords) {
1419
  reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
1420
}
1421
1422
},{}],5:[function(require,module,exports){
1423
var conversions = require(4);
1424
1425
var convert = function() {
1426
   return new Converter();
1427
}
1428
1429
for (var func in conversions) {
1430
  // export Raw versions
1431
  convert[func + "Raw"] =  (function(func) {
1432
    // accept array or plain args
1433
    return function(arg) {
1434
      if (typeof arg == "number")
1435
        arg = Array.prototype.slice.call(arguments);
1436
      return conversions[func](arg);
1437
    }
1438
  })(func);
1439
1440
  var pair = /(\w+)2(\w+)/.exec(func),
1441
      from = pair[1],
1442
      to = pair[2];
1443
1444
  // export rgb2hsl and ["rgb"]["hsl"]
1445
  convert[from] = convert[from] || {};
1446
1447
  convert[from][to] = convert[func] = (function(func) {
1448
    return function(arg) {
1449
      if (typeof arg == "number")
1450
        arg = Array.prototype.slice.call(arguments);
1451
1452
      var val = conversions[func](arg);
1453
      if (typeof val == "string" || val === undefined)
1454
        return val; // keyword
1455
1456
      for (var i = 0; i < val.length; i++)
1457
        val[i] = Math.round(val[i]);
1458
      return val;
1459
    }
1460
  })(func);
1461
}
1462
1463
1464
/* Converter does lazy conversion and caching */
1465
var Converter = function() {
1466
   this.convs = {};
1467
};
1468
1469
/* Either get the values for a space or
1470
  set the values for a space, depending on args */
1471
Converter.prototype.routeSpace = function(space, args) {
1472
   var values = args[0];
1473
   if (values === undefined) {
1474
      // color.rgb()
1475
      return this.getValues(space);
1476
   }
1477
   // color.rgb(10, 10, 10)
1478
   if (typeof values == "number") {
1479
      values = Array.prototype.slice.call(args);
1480
   }
1481
1482
   return this.setValues(space, values);
1483
};
1484
1485
/* Set the values for a space, invalidating cache */
1486
Converter.prototype.setValues = function(space, values) {
1487
   this.space = space;
1488
   this.convs = {};
1489
   this.convs[space] = values;
1490
   return this;
1491
};
1492
1493
/* Get the values for a space. If there's already
1494
  a conversion for the space, fetch it, otherwise
1495
  compute it */
1496
Converter.prototype.getValues = function(space) {
1497
   var vals = this.convs[space];
1498
   if (!vals) {
1499
      var fspace = this.space,
1500
          from = this.convs[fspace];
1501
      vals = convert[fspace][space](from);
1502
1503
      this.convs[space] = vals;
1504
   }
1505
  return vals;
1506
};
1507
1508
["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) {
1509
   Converter.prototype[space] = function(vals) {
1510
      return this.routeSpace(space, arguments);
1511
   }
1512
});
1513
1514
module.exports = convert;
1515
},{"4":4}],6:[function(require,module,exports){
1516
'use strict'
1517
1518
module.exports = {
1519
	"aliceblue": [240, 248, 255],
1520
	"antiquewhite": [250, 235, 215],
1521
	"aqua": [0, 255, 255],
1522
	"aquamarine": [127, 255, 212],
1523
	"azure": [240, 255, 255],
1524
	"beige": [245, 245, 220],
1525
	"bisque": [255, 228, 196],
1526
	"black": [0, 0, 0],
1527
	"blanchedalmond": [255, 235, 205],
1528
	"blue": [0, 0, 255],
1529
	"blueviolet": [138, 43, 226],
1530
	"brown": [165, 42, 42],
1531
	"burlywood": [222, 184, 135],
1532
	"cadetblue": [95, 158, 160],
1533
	"chartreuse": [127, 255, 0],
1534
	"chocolate": [210, 105, 30],
1535
	"coral": [255, 127, 80],
1536
	"cornflowerblue": [100, 149, 237],
1537
	"cornsilk": [255, 248, 220],
1538
	"crimson": [220, 20, 60],
1539
	"cyan": [0, 255, 255],
1540
	"darkblue": [0, 0, 139],
1541
	"darkcyan": [0, 139, 139],
1542
	"darkgoldenrod": [184, 134, 11],
1543
	"darkgray": [169, 169, 169],
1544
	"darkgreen": [0, 100, 0],
1545
	"darkgrey": [169, 169, 169],
1546
	"darkkhaki": [189, 183, 107],
1547
	"darkmagenta": [139, 0, 139],
1548
	"darkolivegreen": [85, 107, 47],
1549
	"darkorange": [255, 140, 0],
1550
	"darkorchid": [153, 50, 204],
1551
	"darkred": [139, 0, 0],
1552
	"darksalmon": [233, 150, 122],
1553
	"darkseagreen": [143, 188, 143],
1554
	"darkslateblue": [72, 61, 139],
1555
	"darkslategray": [47, 79, 79],
1556
	"darkslategrey": [47, 79, 79],
1557
	"darkturquoise": [0, 206, 209],
1558
	"darkviolet": [148, 0, 211],
1559
	"deeppink": [255, 20, 147],
1560
	"deepskyblue": [0, 191, 255],
1561
	"dimgray": [105, 105, 105],
1562
	"dimgrey": [105, 105, 105],
1563
	"dodgerblue": [30, 144, 255],
1564
	"firebrick": [178, 34, 34],
1565
	"floralwhite": [255, 250, 240],
1566
	"forestgreen": [34, 139, 34],
1567
	"fuchsia": [255, 0, 255],
1568
	"gainsboro": [220, 220, 220],
1569
	"ghostwhite": [248, 248, 255],
1570
	"gold": [255, 215, 0],
1571
	"goldenrod": [218, 165, 32],
1572
	"gray": [128, 128, 128],
1573
	"green": [0, 128, 0],
1574
	"greenyellow": [173, 255, 47],
1575
	"grey": [128, 128, 128],
1576
	"honeydew": [240, 255, 240],
1577
	"hotpink": [255, 105, 180],
1578
	"indianred": [205, 92, 92],
1579
	"indigo": [75, 0, 130],
1580
	"ivory": [255, 255, 240],
1581
	"khaki": [240, 230, 140],
1582
	"lavender": [230, 230, 250],
1583
	"lavenderblush": [255, 240, 245],
1584
	"lawngreen": [124, 252, 0],
1585
	"lemonchiffon": [255, 250, 205],
1586
	"lightblue": [173, 216, 230],
1587
	"lightcoral": [240, 128, 128],
1588
	"lightcyan": [224, 255, 255],
1589
	"lightgoldenrodyellow": [250, 250, 210],
1590
	"lightgray": [211, 211, 211],
1591
	"lightgreen": [144, 238, 144],
1592
	"lightgrey": [211, 211, 211],
1593
	"lightpink": [255, 182, 193],
1594
	"lightsalmon": [255, 160, 122],
1595
	"lightseagreen": [32, 178, 170],
1596
	"lightskyblue": [135, 206, 250],
1597
	"lightslategray": [119, 136, 153],
1598
	"lightslategrey": [119, 136, 153],
1599
	"lightsteelblue": [176, 196, 222],
1600
	"lightyellow": [255, 255, 224],
1601
	"lime": [0, 255, 0],
1602
	"limegreen": [50, 205, 50],
1603
	"linen": [250, 240, 230],
1604
	"magenta": [255, 0, 255],
1605
	"maroon": [128, 0, 0],
1606
	"mediumaquamarine": [102, 205, 170],
1607
	"mediumblue": [0, 0, 205],
1608
	"mediumorchid": [186, 85, 211],
1609
	"mediumpurple": [147, 112, 219],
1610
	"mediumseagreen": [60, 179, 113],
1611
	"mediumslateblue": [123, 104, 238],
1612
	"mediumspringgreen": [0, 250, 154],
1613
	"mediumturquoise": [72, 209, 204],
1614
	"mediumvioletred": [199, 21, 133],
1615
	"midnightblue": [25, 25, 112],
1616
	"mintcream": [245, 255, 250],
1617
	"mistyrose": [255, 228, 225],
1618
	"moccasin": [255, 228, 181],
1619
	"navajowhite": [255, 222, 173],
1620
	"navy": [0, 0, 128],
1621
	"oldlace": [253, 245, 230],
1622
	"olive": [128, 128, 0],
1623
	"olivedrab": [107, 142, 35],
1624
	"orange": [255, 165, 0],
1625
	"orangered": [255, 69, 0],
1626
	"orchid": [218, 112, 214],
1627
	"palegoldenrod": [238, 232, 170],
1628
	"palegreen": [152, 251, 152],
1629
	"paleturquoise": [175, 238, 238],
1630
	"palevioletred": [219, 112, 147],
1631
	"papayawhip": [255, 239, 213],
1632
	"peachpuff": [255, 218, 185],
1633
	"peru": [205, 133, 63],
1634
	"pink": [255, 192, 203],
1635
	"plum": [221, 160, 221],
1636
	"powderblue": [176, 224, 230],
1637
	"purple": [128, 0, 128],
1638
	"rebeccapurple": [102, 51, 153],
1639
	"red": [255, 0, 0],
1640
	"rosybrown": [188, 143, 143],
1641
	"royalblue": [65, 105, 225],
1642
	"saddlebrown": [139, 69, 19],
1643
	"salmon": [250, 128, 114],
1644
	"sandybrown": [244, 164, 96],
1645
	"seagreen": [46, 139, 87],
1646
	"seashell": [255, 245, 238],
1647
	"sienna": [160, 82, 45],
1648
	"silver": [192, 192, 192],
1649
	"skyblue": [135, 206, 235],
1650
	"slateblue": [106, 90, 205],
1651
	"slategray": [112, 128, 144],
1652
	"slategrey": [112, 128, 144],
1653
	"snow": [255, 250, 250],
1654
	"springgreen": [0, 255, 127],
1655
	"steelblue": [70, 130, 180],
1656
	"tan": [210, 180, 140],
1657
	"teal": [0, 128, 128],
1658
	"thistle": [216, 191, 216],
1659
	"tomato": [255, 99, 71],
1660
	"turquoise": [64, 224, 208],
1661
	"violet": [238, 130, 238],
1662
	"wheat": [245, 222, 179],
1663
	"white": [255, 255, 255],
1664
	"whitesmoke": [245, 245, 245],
1665
	"yellow": [255, 255, 0],
1666
	"yellowgreen": [154, 205, 50]
1667
};
1668
1669
},{}],7:[function(require,module,exports){
1670
/**
1671
 * @namespace Chart
1672
 */
1673
var Chart = require(30)();
1674
1675
Chart.helpers = require(46);
1676
1677
// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
1678
require(28)(Chart);
1679
1680
Chart.Animation = require(22);
1681
Chart.animationService = require(23);
1682
Chart.defaults = require(26);
1683
Chart.Element = require(27);
1684
Chart.elements = require(41);
1685
Chart.Interaction = require(29);
1686
Chart.layouts = require(31);
1687
Chart.platform = require(49);
1688
Chart.plugins = require(32);
1689
Chart.Scale = require(33);
1690
Chart.scaleService = require(34);
1691
Chart.Ticks = require(35);
1692
Chart.Tooltip = require(36);
1693
1694
require(24)(Chart);
1695
require(25)(Chart);
1696
1697
require(56)(Chart);
1698
require(54)(Chart);
1699
require(55)(Chart);
1700
require(57)(Chart);
1701
require(58)(Chart);
1702
require(59)(Chart);
1703
1704
// Controllers must be loaded after elements
1705
// See Chart.core.datasetController.dataElementType
1706
require(15)(Chart);
1707
require(16)(Chart);
1708
require(17)(Chart);
1709
require(18)(Chart);
1710
require(19)(Chart);
1711
require(20)(Chart);
1712
require(21)(Chart);
1713
1714
require(8)(Chart);
1715
require(9)(Chart);
1716
require(10)(Chart);
1717
require(11)(Chart);
1718
require(12)(Chart);
1719
require(13)(Chart);
1720
require(14)(Chart);
1721
1722
// Loading built-in plugins
1723
var plugins = require(50);
1724
for (var k in plugins) {
1725
	if (plugins.hasOwnProperty(k)) {
1726
		Chart.plugins.register(plugins[k]);
1727
	}
1728
}
1729
1730
Chart.platform.initialize();
1731
1732
module.exports = Chart;
1733
if (typeof window !== 'undefined') {
1734
	window.Chart = Chart;
1735
}
1736
1737
// DEPRECATIONS
1738
1739
/**
1740
 * Provided for backward compatibility, not available anymore
1741
 * @namespace Chart.Legend
1742
 * @deprecated since version 2.1.5
1743
 * @todo remove at version 3
1744
 * @private
1745
 */
1746
Chart.Legend = plugins.legend._element;
1747
1748
/**
1749
 * Provided for backward compatibility, not available anymore
1750
 * @namespace Chart.Title
1751
 * @deprecated since version 2.1.5
1752
 * @todo remove at version 3
1753
 * @private
1754
 */
1755
Chart.Title = plugins.title._element;
1756
1757
/**
1758
 * Provided for backward compatibility, use Chart.plugins instead
1759
 * @namespace Chart.pluginService
1760
 * @deprecated since version 2.1.5
1761
 * @todo remove at version 3
1762
 * @private
1763
 */
1764
Chart.pluginService = Chart.plugins;
1765
1766
/**
1767
 * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
1768
 * effect, instead simply create/register plugins via plain JavaScript objects.
1769
 * @interface Chart.PluginBase
1770
 * @deprecated since version 2.5.0
1771
 * @todo remove at version 3
1772
 * @private
1773
 */
1774
Chart.PluginBase = Chart.Element.extend({});
1775
1776
/**
1777
 * Provided for backward compatibility, use Chart.helpers.canvas instead.
1778
 * @namespace Chart.canvasHelpers
1779
 * @deprecated since version 2.6.0
1780
 * @todo remove at version 3
1781
 * @private
1782
 */
1783
Chart.canvasHelpers = Chart.helpers.canvas;
1784
1785
/**
1786
 * Provided for backward compatibility, use Chart.layouts instead.
1787
 * @namespace Chart.layoutService
1788
 * @deprecated since version 2.8.0
1789
 * @todo remove at version 3
1790
 * @private
1791
 */
1792
Chart.layoutService = Chart.layouts;
1793
1794
},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"41":41,"46":46,"49":49,"50":50,"54":54,"55":55,"56":56,"57":57,"58":58,"59":59,"8":8,"9":9}],8:[function(require,module,exports){
1795
'use strict';
1796
1797
module.exports = function(Chart) {
1798
1799
	Chart.Bar = function(context, config) {
1800
		config.type = 'bar';
1801
1802
		return new Chart(context, config);
1803
	};
1804
1805
};
1806
1807
},{}],9:[function(require,module,exports){
1808
'use strict';
1809
1810
module.exports = function(Chart) {
1811
1812
	Chart.Bubble = function(context, config) {
1813
		config.type = 'bubble';
1814
		return new Chart(context, config);
1815
	};
1816
1817
};
1818
1819
},{}],10:[function(require,module,exports){
1820
'use strict';
1821
1822
module.exports = function(Chart) {
1823
1824
	Chart.Doughnut = function(context, config) {
1825
		config.type = 'doughnut';
1826
1827
		return new Chart(context, config);
1828
	};
1829
1830
};
1831
1832
},{}],11:[function(require,module,exports){
1833
'use strict';
1834
1835
module.exports = function(Chart) {
1836
1837
	Chart.Line = function(context, config) {
1838
		config.type = 'line';
1839
1840
		return new Chart(context, config);
1841
	};
1842
1843
};
1844
1845
},{}],12:[function(require,module,exports){
1846
'use strict';
1847
1848
module.exports = function(Chart) {
1849
1850
	Chart.PolarArea = function(context, config) {
1851
		config.type = 'polarArea';
1852
1853
		return new Chart(context, config);
1854
	};
1855
1856
};
1857
1858
},{}],13:[function(require,module,exports){
1859
'use strict';
1860
1861
module.exports = function(Chart) {
1862
1863
	Chart.Radar = function(context, config) {
1864
		config.type = 'radar';
1865
1866
		return new Chart(context, config);
1867
	};
1868
1869
};
1870
1871
},{}],14:[function(require,module,exports){
1872
'use strict';
1873
1874
module.exports = function(Chart) {
1875
	Chart.Scatter = function(context, config) {
1876
		config.type = 'scatter';
1877
		return new Chart(context, config);
1878
	};
1879
};
1880
1881
},{}],15:[function(require,module,exports){
1882
'use strict';
1883
1884
var defaults = require(26);
1885
var elements = require(41);
1886
var helpers = require(46);
1887
1888
defaults._set('bar', {
1889
	hover: {
1890
		mode: 'label'
1891
	},
1892
1893
	scales: {
1894
		xAxes: [{
1895
			type: 'category',
1896
1897
			// Specific to Bar Controller
1898
			categoryPercentage: 0.8,
1899
			barPercentage: 0.9,
1900
1901
			// offset settings
1902
			offset: true,
1903
1904
			// grid line settings
1905
			gridLines: {
1906
				offsetGridLines: true
1907
			}
1908
		}],
1909
1910
		yAxes: [{
1911
			type: 'linear'
1912
		}]
1913
	}
1914
});
1915
1916
defaults._set('horizontalBar', {
1917
	hover: {
1918
		mode: 'index',
1919
		axis: 'y'
1920
	},
1921
1922
	scales: {
1923
		xAxes: [{
1924
			type: 'linear',
1925
			position: 'bottom'
1926
		}],
1927
1928
		yAxes: [{
1929
			position: 'left',
1930
			type: 'category',
1931
1932
			// Specific to Horizontal Bar Controller
1933
			categoryPercentage: 0.8,
1934
			barPercentage: 0.9,
1935
1936
			// offset settings
1937
			offset: true,
1938
1939
			// grid line settings
1940
			gridLines: {
1941
				offsetGridLines: true
1942
			}
1943
		}]
1944
	},
1945
1946
	elements: {
1947
		rectangle: {
1948
			borderSkipped: 'left'
1949
		}
1950
	},
1951
1952
	tooltips: {
1953
		callbacks: {
1954
			title: function(item, data) {
1955
				// Pick first xLabel for now
1956
				var title = '';
1957
1958
				if (item.length > 0) {
1959
					if (item[0].yLabel) {
1960
						title = item[0].yLabel;
1961
					} else if (data.labels.length > 0 && item[0].index < data.labels.length) {
1962
						title = data.labels[item[0].index];
1963
					}
1964
				}
1965
1966
				return title;
1967
			},
1968
1969
			label: function(item, data) {
1970
				var datasetLabel = data.datasets[item.datasetIndex].label || '';
1971
				return datasetLabel + ': ' + item.xLabel;
1972
			}
1973
		},
1974
		mode: 'index',
1975
		axis: 'y'
1976
	}
1977
});
1978
1979
/**
1980
 * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
1981
 * @private
1982
 */
1983
function computeMinSampleSize(scale, pixels) {
1984
	var min = scale.isHorizontal() ? scale.width : scale.height;
1985
	var ticks = scale.getTicks();
1986
	var prev, curr, i, ilen;
1987
1988
	for (i = 1, ilen = pixels.length; i < ilen; ++i) {
1989
		min = Math.min(min, pixels[i] - pixels[i - 1]);
1990
	}
1991
1992
	for (i = 0, ilen = ticks.length; i < ilen; ++i) {
1993
		curr = scale.getPixelForTick(i);
1994
		min = i > 0 ? Math.min(min, curr - prev) : min;
1995
		prev = curr;
1996
	}
1997
1998
	return min;
1999
}
2000
2001
/**
2002
 * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null,
2003
 * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This
2004
 * mode currently always generates bars equally sized (until we introduce scriptable options?).
2005
 * @private
2006
 */
2007
function computeFitCategoryTraits(index, ruler, options) {
2008
	var thickness = options.barThickness;
2009
	var count = ruler.stackCount;
2010
	var curr = ruler.pixels[index];
2011
	var size, ratio;
2012
2013
	if (helpers.isNullOrUndef(thickness)) {
2014
		size = ruler.min * options.categoryPercentage;
2015
		ratio = options.barPercentage;
2016
	} else {
2017
		// When bar thickness is enforced, category and bar percentages are ignored.
2018
		// Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%')
2019
		// and deprecate barPercentage since this value is ignored when thickness is absolute.
2020
		size = thickness * count;
2021
		ratio = 1;
2022
	}
2023
2024
	return {
2025
		chunk: size / count,
2026
		ratio: ratio,
2027
		start: curr - (size / 2)
2028
	};
2029
}
2030
2031
/**
2032
 * Computes an "optimal" category that globally arranges bars side by side (no gap when
2033
 * percentage options are 1), based on the previous and following categories. This mode
2034
 * generates bars with different widths when data are not evenly spaced.
2035
 * @private
2036
 */
2037
function computeFlexCategoryTraits(index, ruler, options) {
2038
	var pixels = ruler.pixels;
2039
	var curr = pixels[index];
2040
	var prev = index > 0 ? pixels[index - 1] : null;
2041
	var next = index < pixels.length - 1 ? pixels[index + 1] : null;
2042
	var percent = options.categoryPercentage;
2043
	var start, size;
2044
2045
	if (prev === null) {
2046
		// first data: its size is double based on the next point or,
2047
		// if it's also the last data, we use the scale end extremity.
2048
		prev = curr - (next === null ? ruler.end - curr : next - curr);
2049
	}
2050
2051
	if (next === null) {
2052
		// last data: its size is also double based on the previous point.
2053
		next = curr + curr - prev;
2054
	}
2055
2056
	start = curr - ((curr - prev) / 2) * percent;
2057
	size = ((next - prev) / 2) * percent;
2058
2059
	return {
2060
		chunk: size / ruler.stackCount,
2061
		ratio: options.barPercentage,
2062
		start: start
2063
	};
2064
}
2065
2066
module.exports = function(Chart) {
2067
2068
	Chart.controllers.bar = Chart.DatasetController.extend({
2069
2070
		dataElementType: elements.Rectangle,
2071
2072
		initialize: function() {
2073
			var me = this;
2074
			var meta;
2075
2076
			Chart.DatasetController.prototype.initialize.apply(me, arguments);
2077
2078
			meta = me.getMeta();
2079
			meta.stack = me.getDataset().stack;
2080
			meta.bar = true;
2081
		},
2082
2083
		update: function(reset) {
2084
			var me = this;
2085
			var rects = me.getMeta().data;
2086
			var i, ilen;
2087
2088
			me._ruler = me.getRuler();
2089
2090
			for (i = 0, ilen = rects.length; i < ilen; ++i) {
2091
				me.updateElement(rects[i], i, reset);
2092
			}
2093
		},
2094
2095
		updateElement: function(rectangle, index, reset) {
2096
			var me = this;
2097
			var chart = me.chart;
2098
			var meta = me.getMeta();
2099
			var dataset = me.getDataset();
2100
			var custom = rectangle.custom || {};
2101
			var rectangleOptions = chart.options.elements.rectangle;
2102
2103
			rectangle._xScale = me.getScaleForId(meta.xAxisID);
2104
			rectangle._yScale = me.getScaleForId(meta.yAxisID);
2105
			rectangle._datasetIndex = me.index;
2106
			rectangle._index = index;
2107
2108
			rectangle._model = {
2109
				datasetLabel: dataset.label,
2110
				label: chart.data.labels[index],
2111
				borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped,
2112
				backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor),
2113
				borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor),
2114
				borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth)
2115
			};
2116
2117
			me.updateElementGeometry(rectangle, index, reset);
2118
2119
			rectangle.pivot();
2120
		},
2121
2122
		/**
2123
		 * @private
2124
		 */
2125
		updateElementGeometry: function(rectangle, index, reset) {
2126
			var me = this;
2127
			var model = rectangle._model;
2128
			var vscale = me.getValueScale();
2129
			var base = vscale.getBasePixel();
2130
			var horizontal = vscale.isHorizontal();
2131
			var ruler = me._ruler || me.getRuler();
2132
			var vpixels = me.calculateBarValuePixels(me.index, index);
2133
			var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);
2134
2135
			model.horizontal = horizontal;
2136
			model.base = reset ? base : vpixels.base;
2137
			model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
2138
			model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
2139
			model.height = horizontal ? ipixels.size : undefined;
2140
			model.width = horizontal ? undefined : ipixels.size;
2141
		},
2142
2143
		/**
2144
		 * @private
2145
		 */
2146
		getValueScaleId: function() {
2147
			return this.getMeta().yAxisID;
2148
		},
2149
2150
		/**
2151
		 * @private
2152
		 */
2153
		getIndexScaleId: function() {
2154
			return this.getMeta().xAxisID;
2155
		},
2156
2157
		/**
2158
		 * @private
2159
		 */
2160
		getValueScale: function() {
2161
			return this.getScaleForId(this.getValueScaleId());
2162
		},
2163
2164
		/**
2165
		 * @private
2166
		 */
2167
		getIndexScale: function() {
2168
			return this.getScaleForId(this.getIndexScaleId());
2169
		},
2170
2171
		/**
2172
		 * Returns the stacks based on groups and bar visibility.
2173
		 * @param {Number} [last] - The dataset index
2174
		 * @returns {Array} The stack list
2175
		 * @private
2176
		 */
2177
		_getStacks: function(last) {
2178
			var me = this;
2179
			var chart = me.chart;
2180
			var scale = me.getIndexScale();
2181
			var stacked = scale.options.stacked;
2182
			var ilen = last === undefined ? chart.data.datasets.length : last + 1;
2183
			var stacks = [];
2184
			var i, meta;
2185
2186
			for (i = 0; i < ilen; ++i) {
2187
				meta = chart.getDatasetMeta(i);
2188
				if (meta.bar && chart.isDatasetVisible(i) &&
2189
					(stacked === false ||
2190
					(stacked === true && stacks.indexOf(meta.stack) === -1) ||
2191
					(stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {
2192
					stacks.push(meta.stack);
2193
				}
2194
			}
2195
2196
			return stacks;
2197
		},
2198
2199
		/**
2200
		 * Returns the effective number of stacks based on groups and bar visibility.
2201
		 * @private
2202
		 */
2203
		getStackCount: function() {
2204
			return this._getStacks().length;
2205
		},
2206
2207
		/**
2208
		 * Returns the stack index for the given dataset based on groups and bar visibility.
2209
		 * @param {Number} [datasetIndex] - The dataset index
2210
		 * @param {String} [name] - The stack name to find
2211
		 * @returns {Number} The stack index
2212
		 * @private
2213
		 */
2214
		getStackIndex: function(datasetIndex, name) {
2215
			var stacks = this._getStacks(datasetIndex);
2216
			var index = (name !== undefined)
2217
				? stacks.indexOf(name)
2218
				: -1; // indexOf returns -1 if element is not present
2219
2220
			return (index === -1)
2221
				? stacks.length - 1
2222
				: index;
2223
		},
2224
2225
		/**
2226
		 * @private
2227
		 */
2228
		getRuler: function() {
2229
			var me = this;
2230
			var scale = me.getIndexScale();
2231
			var stackCount = me.getStackCount();
2232
			var datasetIndex = me.index;
2233
			var isHorizontal = scale.isHorizontal();
2234
			var start = isHorizontal ? scale.left : scale.top;
2235
			var end = start + (isHorizontal ? scale.width : scale.height);
2236
			var pixels = [];
2237
			var i, ilen, min;
2238
2239
			for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
2240
				pixels.push(scale.getPixelForValue(null, i, datasetIndex));
2241
			}
2242
2243
			min = helpers.isNullOrUndef(scale.options.barThickness)
2244
				? computeMinSampleSize(scale, pixels)
2245
				: -1;
2246
2247
			return {
2248
				min: min,
2249
				pixels: pixels,
2250
				start: start,
2251
				end: end,
2252
				stackCount: stackCount,
2253
				scale: scale
2254
			};
2255
		},
2256
2257
		/**
2258
		 * Note: pixel values are not clamped to the scale area.
2259
		 * @private
2260
		 */
2261
		calculateBarValuePixels: function(datasetIndex, index) {
2262
			var me = this;
2263
			var chart = me.chart;
2264
			var meta = me.getMeta();
2265
			var scale = me.getValueScale();
2266
			var datasets = chart.data.datasets;
2267
			var value = scale.getRightValue(datasets[datasetIndex].data[index]);
2268
			var stacked = scale.options.stacked;
2269
			var stack = meta.stack;
2270
			var start = 0;
2271
			var i, imeta, ivalue, base, head, size;
2272
2273
			if (stacked || (stacked === undefined && stack !== undefined)) {
2274
				for (i = 0; i < datasetIndex; ++i) {
2275
					imeta = chart.getDatasetMeta(i);
2276
2277
					if (imeta.bar &&
2278
						imeta.stack === stack &&
2279
						imeta.controller.getValueScaleId() === scale.id &&
2280
						chart.isDatasetVisible(i)) {
2281
2282
						ivalue = scale.getRightValue(datasets[i].data[index]);
2283
						if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
2284
							start += ivalue;
2285
						}
2286
					}
2287
				}
2288
			}
2289
2290
			base = scale.getPixelForValue(start);
2291
			head = scale.getPixelForValue(start + value);
2292
			size = (head - base) / 2;
2293
2294
			return {
2295
				size: size,
2296
				base: base,
2297
				head: head,
2298
				center: head + size / 2
2299
			};
2300
		},
2301
2302
		/**
2303
		 * @private
2304
		 */
2305
		calculateBarIndexPixels: function(datasetIndex, index, ruler) {
2306
			var me = this;
2307
			var options = ruler.scale.options;
2308
			var range = options.barThickness === 'flex'
2309
				? computeFlexCategoryTraits(index, ruler, options)
2310
				: computeFitCategoryTraits(index, ruler, options);
2311
2312
			var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);
2313
			var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
2314
			var size = Math.min(
2315
				helpers.valueOrDefault(options.maxBarThickness, Infinity),
2316
				range.chunk * range.ratio);
2317
2318
			return {
2319
				base: center - size / 2,
2320
				head: center + size / 2,
2321
				center: center,
2322
				size: size
2323
			};
2324
		},
2325
2326
		draw: function() {
2327
			var me = this;
2328
			var chart = me.chart;
2329
			var scale = me.getValueScale();
2330
			var rects = me.getMeta().data;
2331
			var dataset = me.getDataset();
2332
			var ilen = rects.length;
2333
			var i = 0;
2334
2335
			helpers.canvas.clipArea(chart.ctx, chart.chartArea);
2336
2337
			for (; i < ilen; ++i) {
2338
				if (!isNaN(scale.getRightValue(dataset.data[i]))) {
2339
					rects[i].draw();
2340
				}
2341
			}
2342
2343
			helpers.canvas.unclipArea(chart.ctx);
2344
		},
2345
	});
2346
2347
	Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
2348
		/**
2349
		 * @private
2350
		 */
2351
		getValueScaleId: function() {
2352
			return this.getMeta().xAxisID;
2353
		},
2354
2355
		/**
2356
		 * @private
2357
		 */
2358
		getIndexScaleId: function() {
2359
			return this.getMeta().yAxisID;
2360
		}
2361
	});
2362
};
2363
2364
},{"26":26,"41":41,"46":46}],16:[function(require,module,exports){
2365
'use strict';
2366
2367
var defaults = require(26);
2368
var elements = require(41);
2369
var helpers = require(46);
2370
2371
defaults._set('bubble', {
2372
	hover: {
2373
		mode: 'single'
2374
	},
2375
2376
	scales: {
2377
		xAxes: [{
2378
			type: 'linear', // bubble should probably use a linear scale by default
2379
			position: 'bottom',
2380
			id: 'x-axis-0' // need an ID so datasets can reference the scale
2381
		}],
2382
		yAxes: [{
2383
			type: 'linear',
2384
			position: 'left',
2385
			id: 'y-axis-0'
2386
		}]
2387
	},
2388
2389
	tooltips: {
2390
		callbacks: {
2391
			title: function() {
2392
				// Title doesn't make sense for scatter since we format the data as a point
2393
				return '';
2394
			},
2395
			label: function(item, data) {
2396
				var datasetLabel = data.datasets[item.datasetIndex].label || '';
2397
				var dataPoint = data.datasets[item.datasetIndex].data[item.index];
2398
				return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';
2399
			}
2400
		}
2401
	}
2402
});
2403
2404
2405
module.exports = function(Chart) {
2406
2407
	Chart.controllers.bubble = Chart.DatasetController.extend({
2408
		/**
2409
		 * @protected
2410
		 */
2411
		dataElementType: elements.Point,
2412
2413
		/**
2414
		 * @protected
2415
		 */
2416
		update: function(reset) {
2417
			var me = this;
2418
			var meta = me.getMeta();
2419
			var points = meta.data;
2420
2421
			// Update Points
2422
			helpers.each(points, function(point, index) {
2423
				me.updateElement(point, index, reset);
2424
			});
2425
		},
2426
2427
		/**
2428
		 * @protected
2429
		 */
2430
		updateElement: function(point, index, reset) {
2431
			var me = this;
2432
			var meta = me.getMeta();
2433
			var custom = point.custom || {};
2434
			var xScale = me.getScaleForId(meta.xAxisID);
2435
			var yScale = me.getScaleForId(meta.yAxisID);
2436
			var options = me._resolveElementOptions(point, index);
2437
			var data = me.getDataset().data[index];
2438
			var dsIndex = me.index;
2439
2440
			var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
2441
			var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);
2442
2443
			point._xScale = xScale;
2444
			point._yScale = yScale;
2445
			point._options = options;
2446
			point._datasetIndex = dsIndex;
2447
			point._index = index;
2448
			point._model = {
2449
				backgroundColor: options.backgroundColor,
2450
				borderColor: options.borderColor,
2451
				borderWidth: options.borderWidth,
2452
				hitRadius: options.hitRadius,
2453
				pointStyle: options.pointStyle,
2454
				rotation: options.rotation,
2455
				radius: reset ? 0 : options.radius,
2456
				skip: custom.skip || isNaN(x) || isNaN(y),
2457
				x: x,
2458
				y: y,
2459
			};
2460
2461
			point.pivot();
2462
		},
2463
2464
		/**
2465
		 * @protected
2466
		 */
2467
		setHoverStyle: function(point) {
2468
			var model = point._model;
2469
			var options = point._options;
2470
			point.$previousStyle = {
2471
				backgroundColor: model.backgroundColor,
2472
				borderColor: model.borderColor,
2473
				borderWidth: model.borderWidth,
2474
				radius: model.radius
2475
			};
2476
			model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor));
2477
			model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor));
2478
			model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth);
2479
			model.radius = options.radius + options.hoverRadius;
2480
		},
2481
2482
		/**
2483
		 * @private
2484
		 */
2485
		_resolveElementOptions: function(point, index) {
2486
			var me = this;
2487
			var chart = me.chart;
2488
			var datasets = chart.data.datasets;
2489
			var dataset = datasets[me.index];
2490
			var custom = point.custom || {};
2491
			var options = chart.options.elements.point;
2492
			var resolve = helpers.options.resolve;
2493
			var data = dataset.data[index];
2494
			var values = {};
2495
			var i, ilen, key;
2496
2497
			// Scriptable options
2498
			var context = {
2499
				chart: chart,
2500
				dataIndex: index,
2501
				dataset: dataset,
2502
				datasetIndex: me.index
2503
			};
2504
2505
			var keys = [
2506
				'backgroundColor',
2507
				'borderColor',
2508
				'borderWidth',
2509
				'hoverBackgroundColor',
2510
				'hoverBorderColor',
2511
				'hoverBorderWidth',
2512
				'hoverRadius',
2513
				'hitRadius',
2514
				'pointStyle',
2515
				'rotation'
2516
			];
2517
2518
			for (i = 0, ilen = keys.length; i < ilen; ++i) {
2519
				key = keys[i];
2520
				values[key] = resolve([
2521
					custom[key],
2522
					dataset[key],
2523
					options[key]
2524
				], context, index);
2525
			}
2526
2527
			// Custom radius resolution
2528
			values.radius = resolve([
2529
				custom.radius,
2530
				data ? data.r : undefined,
2531
				dataset.radius,
2532
				options.radius
2533
			], context, index);
2534
			return values;
2535
		}
2536
	});
2537
};
2538
2539
},{"26":26,"41":41,"46":46}],17:[function(require,module,exports){
2540
'use strict';
2541
2542
var defaults = require(26);
2543
var elements = require(41);
2544
var helpers = require(46);
2545
2546
defaults._set('doughnut', {
2547
	animation: {
2548
		// Boolean - Whether we animate the rotation of the Doughnut
2549
		animateRotate: true,
2550
		// Boolean - Whether we animate scaling the Doughnut from the centre
2551
		animateScale: false
2552
	},
2553
	hover: {
2554
		mode: 'single'
2555
	},
2556
	legendCallback: function(chart) {
2557
		var text = [];
2558
		text.push('<ul class="' + chart.id + '-legend">');
2559
2560
		var data = chart.data;
2561
		var datasets = data.datasets;
2562
		var labels = data.labels;
2563
2564
		if (datasets.length) {
2565
			for (var i = 0; i < datasets[0].data.length; ++i) {
2566
				text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
2567
				if (labels[i]) {
2568
					text.push(labels[i]);
2569
				}
2570
				text.push('</li>');
2571
			}
2572
		}
2573
2574
		text.push('</ul>');
2575
		return text.join('');
2576
	},
2577
	legend: {
2578
		labels: {
2579
			generateLabels: function(chart) {
2580
				var data = chart.data;
2581
				if (data.labels.length && data.datasets.length) {
2582
					return data.labels.map(function(label, i) {
2583
						var meta = chart.getDatasetMeta(0);
2584
						var ds = data.datasets[0];
2585
						var arc = meta.data[i];
2586
						var custom = arc && arc.custom || {};
2587
						var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
2588
						var arcOpts = chart.options.elements.arc;
2589
						var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
2590
						var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
2591
						var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
2592
2593
						return {
2594
							text: label,
2595
							fillStyle: fill,
2596
							strokeStyle: stroke,
2597
							lineWidth: bw,
2598
							hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
2599
2600
							// Extra data used for toggling the correct item
2601
							index: i
2602
						};
2603
					});
2604
				}
2605
				return [];
2606
			}
2607
		},
2608
2609
		onClick: function(e, legendItem) {
2610
			var index = legendItem.index;
2611
			var chart = this.chart;
2612
			var i, ilen, meta;
2613
2614
			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
2615
				meta = chart.getDatasetMeta(i);
2616
				// toggle visibility of index if exists
2617
				if (meta.data[index]) {
2618
					meta.data[index].hidden = !meta.data[index].hidden;
2619
				}
2620
			}
2621
2622
			chart.update();
2623
		}
2624
	},
2625
2626
	// The percentage of the chart that we cut out of the middle.
2627
	cutoutPercentage: 50,
2628
2629
	// The rotation of the chart, where the first data arc begins.
2630
	rotation: Math.PI * -0.5,
2631
2632
	// The total circumference of the chart.
2633
	circumference: Math.PI * 2.0,
2634
2635
	// Need to override these to give a nice default
2636
	tooltips: {
2637
		callbacks: {
2638
			title: function() {
2639
				return '';
2640
			},
2641
			label: function(tooltipItem, data) {
2642
				var dataLabel = data.labels[tooltipItem.index];
2643
				var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
2644
2645
				if (helpers.isArray(dataLabel)) {
2646
					// show value on first line of multiline label
2647
					// need to clone because we are changing the value
2648
					dataLabel = dataLabel.slice();
2649
					dataLabel[0] += value;
2650
				} else {
2651
					dataLabel += value;
2652
				}
2653
2654
				return dataLabel;
2655
			}
2656
		}
2657
	}
2658
});
2659
2660
defaults._set('pie', helpers.clone(defaults.doughnut));
2661
defaults._set('pie', {
2662
	cutoutPercentage: 0
2663
});
2664
2665
module.exports = function(Chart) {
2666
2667
	Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
2668
2669
		dataElementType: elements.Arc,
2670
2671
		linkScales: helpers.noop,
2672
2673
		// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
2674
		getRingIndex: function(datasetIndex) {
2675
			var ringIndex = 0;
2676
2677
			for (var j = 0; j < datasetIndex; ++j) {
2678
				if (this.chart.isDatasetVisible(j)) {
2679
					++ringIndex;
2680
				}
2681
			}
2682
2683
			return ringIndex;
2684
		},
2685
2686
		update: function(reset) {
2687
			var me = this;
2688
			var chart = me.chart;
2689
			var chartArea = chart.chartArea;
2690
			var opts = chart.options;
2691
			var arcOpts = opts.elements.arc;
2692
			var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth;
2693
			var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;
2694
			var minSize = Math.min(availableWidth, availableHeight);
2695
			var offset = {x: 0, y: 0};
2696
			var meta = me.getMeta();
2697
			var cutoutPercentage = opts.cutoutPercentage;
2698
			var circumference = opts.circumference;
2699
2700
			// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
2701
			if (circumference < Math.PI * 2.0) {
2702
				var startAngle = opts.rotation % (Math.PI * 2.0);
2703
				startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
2704
				var endAngle = startAngle + circumference;
2705
				var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
2706
				var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
2707
				var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
2708
				var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
2709
				var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
2710
				var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
2711
				var cutout = cutoutPercentage / 100.0;
2712
				var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
2713
				var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
2714
				var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
2715
				minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
2716
				offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
2717
			}
2718
2719
			chart.borderWidth = me.getMaxBorderWidth(meta.data);
2720
			chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
2721
			chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
2722
			chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
2723
			chart.offsetX = offset.x * chart.outerRadius;
2724
			chart.offsetY = offset.y * chart.outerRadius;
2725
2726
			meta.total = me.calculateTotal();
2727
2728
			me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
2729
			me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);
2730
2731
			helpers.each(meta.data, function(arc, index) {
2732
				me.updateElement(arc, index, reset);
2733
			});
2734
		},
2735
2736
		updateElement: function(arc, index, reset) {
2737
			var me = this;
2738
			var chart = me.chart;
2739
			var chartArea = chart.chartArea;
2740
			var opts = chart.options;
2741
			var animationOpts = opts.animation;
2742
			var centerX = (chartArea.left + chartArea.right) / 2;
2743
			var centerY = (chartArea.top + chartArea.bottom) / 2;
2744
			var startAngle = opts.rotation; // non reset case handled later
2745
			var endAngle = opts.rotation; // non reset case handled later
2746
			var dataset = me.getDataset();
2747
			var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
2748
			var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
2749
			var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
2750
			var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
2751
2752
			helpers.extend(arc, {
2753
				// Utility
2754
				_datasetIndex: me.index,
2755
				_index: index,
2756
2757
				// Desired view properties
2758
				_model: {
2759
					x: centerX + chart.offsetX,
2760
					y: centerY + chart.offsetY,
2761
					startAngle: startAngle,
2762
					endAngle: endAngle,
2763
					circumference: circumference,
2764
					outerRadius: outerRadius,
2765
					innerRadius: innerRadius,
2766
					label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
2767
				}
2768
			});
2769
2770
			var model = arc._model;
2771
2772
			// Resets the visual styles
2773
			var custom = arc.custom || {};
2774
			var valueOrDefault = helpers.valueAtIndexOrDefault;
2775
			var elementOpts = this.chart.options.elements.arc;
2776
			model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
2777
			model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
2778
			model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
2779
2780
			// Set correct angles if not resetting
2781
			if (!reset || !animationOpts.animateRotate) {
2782
				if (index === 0) {
2783
					model.startAngle = opts.rotation;
2784
				} else {
2785
					model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
2786
				}
2787
2788
				model.endAngle = model.startAngle + model.circumference;
2789
			}
2790
2791
			arc.pivot();
2792
		},
2793
2794
		calculateTotal: function() {
2795
			var dataset = this.getDataset();
2796
			var meta = this.getMeta();
2797
			var total = 0;
2798
			var value;
2799
2800
			helpers.each(meta.data, function(element, index) {
2801
				value = dataset.data[index];
2802
				if (!isNaN(value) && !element.hidden) {
2803
					total += Math.abs(value);
2804
				}
2805
			});
2806
2807
			/* if (total === 0) {
2808
				total = NaN;
2809
			}*/
2810
2811
			return total;
2812
		},
2813
2814
		calculateCircumference: function(value) {
2815
			var total = this.getMeta().total;
2816
			if (total > 0 && !isNaN(value)) {
2817
				return (Math.PI * 2.0) * (Math.abs(value) / total);
2818
			}
2819
			return 0;
2820
		},
2821
2822
		// gets the max border or hover width to properly scale pie charts
2823
		getMaxBorderWidth: function(arcs) {
2824
			var max = 0;
2825
			var index = this.index;
2826
			var length = arcs.length;
2827
			var borderWidth;
2828
			var hoverWidth;
2829
2830
			for (var i = 0; i < length; i++) {
2831
				borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0;
2832
				hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
2833
2834
				max = borderWidth > max ? borderWidth : max;
2835
				max = hoverWidth > max ? hoverWidth : max;
2836
			}
2837
			return max;
2838
		}
2839
	});
2840
};
2841
2842
},{"26":26,"41":41,"46":46}],18:[function(require,module,exports){
2843
'use strict';
2844
2845
var defaults = require(26);
2846
var elements = require(41);
2847
var helpers = require(46);
2848
2849
defaults._set('line', {
2850
	showLines: true,
2851
	spanGaps: false,
2852
2853
	hover: {
2854
		mode: 'label'
2855
	},
2856
2857
	scales: {
2858
		xAxes: [{
2859
			type: 'category',
2860
			id: 'x-axis-0'
2861
		}],
2862
		yAxes: [{
2863
			type: 'linear',
2864
			id: 'y-axis-0'
2865
		}]
2866
	}
2867
});
2868
2869
module.exports = function(Chart) {
2870
2871
	function lineEnabled(dataset, options) {
2872
		return helpers.valueOrDefault(dataset.showLine, options.showLines);
2873
	}
2874
2875
	Chart.controllers.line = Chart.DatasetController.extend({
2876
2877
		datasetElementType: elements.Line,
2878
2879
		dataElementType: elements.Point,
2880
2881
		update: function(reset) {
2882
			var me = this;
2883
			var meta = me.getMeta();
2884
			var line = meta.dataset;
2885
			var points = meta.data || [];
2886
			var options = me.chart.options;
2887
			var lineElementOptions = options.elements.line;
2888
			var scale = me.getScaleForId(meta.yAxisID);
2889
			var i, ilen, custom;
2890
			var dataset = me.getDataset();
2891
			var showLine = lineEnabled(dataset, options);
2892
2893
			// Update Line
2894
			if (showLine) {
2895
				custom = line.custom || {};
2896
2897
				// Compatibility: If the properties are defined with only the old name, use those values
2898
				if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
2899
					dataset.lineTension = dataset.tension;
2900
				}
2901
2902
				// Utility
2903
				line._scale = scale;
2904
				line._datasetIndex = me.index;
2905
				// Data
2906
				line._children = points;
2907
				// Model
2908
				line._model = {
2909
					// Appearance
2910
					// The default behavior of lines is to break at null values, according
2911
					// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
2912
					// This option gives lines the ability to span gaps
2913
					spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,
2914
					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
2915
					backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
2916
					borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
2917
					borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
2918
					borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
2919
					borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
2920
					borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
2921
					borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
2922
					fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
2923
					steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped),
2924
					cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),
2925
				};
2926
2927
				line.pivot();
2928
			}
2929
2930
			// Update Points
2931
			for (i = 0, ilen = points.length; i < ilen; ++i) {
2932
				me.updateElement(points[i], i, reset);
2933
			}
2934
2935
			if (showLine && line._model.tension !== 0) {
2936
				me.updateBezierControlPoints();
2937
			}
2938
2939
			// Now pivot the point for animation
2940
			for (i = 0, ilen = points.length; i < ilen; ++i) {
2941
				points[i].pivot();
2942
			}
2943
		},
2944
2945
		getPointBackgroundColor: function(point, index) {
2946
			var backgroundColor = this.chart.options.elements.point.backgroundColor;
2947
			var dataset = this.getDataset();
2948
			var custom = point.custom || {};
2949
2950
			if (custom.backgroundColor) {
2951
				backgroundColor = custom.backgroundColor;
2952
			} else if (dataset.pointBackgroundColor) {
2953
				backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
2954
			} else if (dataset.backgroundColor) {
2955
				backgroundColor = dataset.backgroundColor;
2956
			}
2957
2958
			return backgroundColor;
2959
		},
2960
2961
		getPointBorderColor: function(point, index) {
2962
			var borderColor = this.chart.options.elements.point.borderColor;
2963
			var dataset = this.getDataset();
2964
			var custom = point.custom || {};
2965
2966
			if (custom.borderColor) {
2967
				borderColor = custom.borderColor;
2968
			} else if (dataset.pointBorderColor) {
2969
				borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
2970
			} else if (dataset.borderColor) {
2971
				borderColor = dataset.borderColor;
2972
			}
2973
2974
			return borderColor;
2975
		},
2976
2977
		getPointBorderWidth: function(point, index) {
2978
			var borderWidth = this.chart.options.elements.point.borderWidth;
2979
			var dataset = this.getDataset();
2980
			var custom = point.custom || {};
2981
2982
			if (!isNaN(custom.borderWidth)) {
2983
				borderWidth = custom.borderWidth;
2984
			} else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) {
2985
				borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
2986
			} else if (!isNaN(dataset.borderWidth)) {
2987
				borderWidth = dataset.borderWidth;
2988
			}
2989
2990
			return borderWidth;
2991
		},
2992
2993
		getPointRotation: function(point, index) {
2994
			var pointRotation = this.chart.options.elements.point.rotation;
2995
			var dataset = this.getDataset();
2996
			var custom = point.custom || {};
2997
2998
			if (!isNaN(custom.rotation)) {
2999
				pointRotation = custom.rotation;
3000
			} else if (!isNaN(dataset.pointRotation) || helpers.isArray(dataset.pointRotation)) {
3001
				pointRotation = helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointRotation);
3002
			}
3003
			return pointRotation;
3004
		},
3005
3006
		updateElement: function(point, index, reset) {
3007
			var me = this;
3008
			var meta = me.getMeta();
3009
			var custom = point.custom || {};
3010
			var dataset = me.getDataset();
3011
			var datasetIndex = me.index;
3012
			var value = dataset.data[index];
3013
			var yScale = me.getScaleForId(meta.yAxisID);
3014
			var xScale = me.getScaleForId(meta.xAxisID);
3015
			var pointOptions = me.chart.options.elements.point;
3016
			var x, y;
3017
3018
			// Compatibility: If the properties are defined with only the old name, use those values
3019
			if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
3020
				dataset.pointRadius = dataset.radius;
3021
			}
3022
			if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
3023
				dataset.pointHitRadius = dataset.hitRadius;
3024
			}
3025
3026
			x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
3027
			y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
3028
3029
			// Utility
3030
			point._xScale = xScale;
3031
			point._yScale = yScale;
3032
			point._datasetIndex = datasetIndex;
3033
			point._index = index;
3034
3035
			// Desired view properties
3036
			point._model = {
3037
				x: x,
3038
				y: y,
3039
				skip: custom.skip || isNaN(x) || isNaN(y),
3040
				// Appearance
3041
				radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
3042
				pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
3043
				rotation: me.getPointRotation(point, index),
3044
				backgroundColor: me.getPointBackgroundColor(point, index),
3045
				borderColor: me.getPointBorderColor(point, index),
3046
				borderWidth: me.getPointBorderWidth(point, index),
3047
				tension: meta.dataset._model ? meta.dataset._model.tension : 0,
3048
				steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,
3049
				// Tooltip
3050
				hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
3051
			};
3052
		},
3053
3054
		calculatePointY: function(value, index, datasetIndex) {
3055
			var me = this;
3056
			var chart = me.chart;
3057
			var meta = me.getMeta();
3058
			var yScale = me.getScaleForId(meta.yAxisID);
3059
			var sumPos = 0;
3060
			var sumNeg = 0;
3061
			var i, ds, dsMeta;
3062
3063
			if (yScale.options.stacked) {
3064
				for (i = 0; i < datasetIndex; i++) {
3065
					ds = chart.data.datasets[i];
3066
					dsMeta = chart.getDatasetMeta(i);
3067
					if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
3068
						var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
3069
						if (stackedRightValue < 0) {
3070
							sumNeg += stackedRightValue || 0;
3071
						} else {
3072
							sumPos += stackedRightValue || 0;
3073
						}
3074
					}
3075
				}
3076
3077
				var rightValue = Number(yScale.getRightValue(value));
3078
				if (rightValue < 0) {
3079
					return yScale.getPixelForValue(sumNeg + rightValue);
3080
				}
3081
				return yScale.getPixelForValue(sumPos + rightValue);
3082
			}
3083
3084
			return yScale.getPixelForValue(value);
3085
		},
3086
3087
		updateBezierControlPoints: function() {
3088
			var me = this;
3089
			var meta = me.getMeta();
3090
			var area = me.chart.chartArea;
3091
			var points = (meta.data || []);
3092
			var i, ilen, point, model, controlPoints;
3093
3094
			// Only consider points that are drawn in case the spanGaps option is used
3095
			if (meta.dataset._model.spanGaps) {
3096
				points = points.filter(function(pt) {
3097
					return !pt._model.skip;
3098
				});
3099
			}
3100
3101
			function capControlPoint(pt, min, max) {
3102
				return Math.max(Math.min(pt, max), min);
3103
			}
3104
3105
			if (meta.dataset._model.cubicInterpolationMode === 'monotone') {
3106
				helpers.splineCurveMonotone(points);
3107
			} else {
3108
				for (i = 0, ilen = points.length; i < ilen; ++i) {
3109
					point = points[i];
3110
					model = point._model;
3111
					controlPoints = helpers.splineCurve(
3112
						helpers.previousItem(points, i)._model,
3113
						model,
3114
						helpers.nextItem(points, i)._model,
3115
						meta.dataset._model.tension
3116
					);
3117
					model.controlPointPreviousX = controlPoints.previous.x;
3118
					model.controlPointPreviousY = controlPoints.previous.y;
3119
					model.controlPointNextX = controlPoints.next.x;
3120
					model.controlPointNextY = controlPoints.next.y;
3121
				}
3122
			}
3123
3124
			if (me.chart.options.elements.line.capBezierPoints) {
3125
				for (i = 0, ilen = points.length; i < ilen; ++i) {
3126
					model = points[i]._model;
3127
					model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
3128
					model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
3129
					model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
3130
					model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
3131
				}
3132
			}
3133
		},
3134
3135
		draw: function() {
3136
			var me = this;
3137
			var chart = me.chart;
3138
			var meta = me.getMeta();
3139
			var points = meta.data || [];
3140
			var area = chart.chartArea;
3141
			var ilen = points.length;
3142
			var halfBorderWidth;
3143
			var i = 0;
3144
3145
			if (lineEnabled(me.getDataset(), chart.options)) {
3146
				halfBorderWidth = (meta.dataset._model.borderWidth || 0) / 2;
3147
3148
				helpers.canvas.clipArea(chart.ctx, {
3149
					left: area.left,
3150
					right: area.right,
3151
					top: area.top - halfBorderWidth,
3152
					bottom: area.bottom + halfBorderWidth
3153
				});
3154
3155
				meta.dataset.draw();
3156
3157
				helpers.canvas.unclipArea(chart.ctx);
3158
			}
3159
3160
			// Draw the points
3161
			for (; i < ilen; ++i) {
3162
				points[i].draw(area);
3163
			}
3164
		},
3165
3166
		setHoverStyle: function(element) {
3167
			// Point
3168
			var dataset = this.chart.data.datasets[element._datasetIndex];
3169
			var index = element._index;
3170
			var custom = element.custom || {};
3171
			var model = element._model;
3172
3173
			element.$previousStyle = {
3174
				backgroundColor: model.backgroundColor,
3175
				borderColor: model.borderColor,
3176
				borderWidth: model.borderWidth,
3177
				radius: model.radius
3178
			};
3179
3180
			model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
3181
			model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
3182
			model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
3183
			model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
3184
		},
3185
	});
3186
};
3187
3188
},{"26":26,"41":41,"46":46}],19:[function(require,module,exports){
3189
'use strict';
3190
3191
var defaults = require(26);
3192
var elements = require(41);
3193
var helpers = require(46);
3194
3195
defaults._set('polarArea', {
3196
	scale: {
3197
		type: 'radialLinear',
3198
		angleLines: {
3199
			display: false
3200
		},
3201
		gridLines: {
3202
			circular: true
3203
		},
3204
		pointLabels: {
3205
			display: false
3206
		},
3207
		ticks: {
3208
			beginAtZero: true
3209
		}
3210
	},
3211
3212
	// Boolean - Whether to animate the rotation of the chart
3213
	animation: {
3214
		animateRotate: true,
3215
		animateScale: true
3216
	},
3217
3218
	startAngle: -0.5 * Math.PI,
3219
	legendCallback: function(chart) {
3220
		var text = [];
3221
		text.push('<ul class="' + chart.id + '-legend">');
3222
3223
		var data = chart.data;
3224
		var datasets = data.datasets;
3225
		var labels = data.labels;
3226
3227
		if (datasets.length) {
3228
			for (var i = 0; i < datasets[0].data.length; ++i) {
3229
				text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
3230
				if (labels[i]) {
3231
					text.push(labels[i]);
3232
				}
3233
				text.push('</li>');
3234
			}
3235
		}
3236
3237
		text.push('</ul>');
3238
		return text.join('');
3239
	},
3240
	legend: {
3241
		labels: {
3242
			generateLabels: function(chart) {
3243
				var data = chart.data;
3244
				if (data.labels.length && data.datasets.length) {
3245
					return data.labels.map(function(label, i) {
3246
						var meta = chart.getDatasetMeta(0);
3247
						var ds = data.datasets[0];
3248
						var arc = meta.data[i];
3249
						var custom = arc.custom || {};
3250
						var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
3251
						var arcOpts = chart.options.elements.arc;
3252
						var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
3253
						var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
3254
						var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
3255
3256
						return {
3257
							text: label,
3258
							fillStyle: fill,
3259
							strokeStyle: stroke,
3260
							lineWidth: bw,
3261
							hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
3262
3263
							// Extra data used for toggling the correct item
3264
							index: i
3265
						};
3266
					});
3267
				}
3268
				return [];
3269
			}
3270
		},
3271
3272
		onClick: function(e, legendItem) {
3273
			var index = legendItem.index;
3274
			var chart = this.chart;
3275
			var i, ilen, meta;
3276
3277
			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
3278
				meta = chart.getDatasetMeta(i);
3279
				meta.data[index].hidden = !meta.data[index].hidden;
3280
			}
3281
3282
			chart.update();
3283
		}
3284
	},
3285
3286
	// Need to override these to give a nice default
3287
	tooltips: {
3288
		callbacks: {
3289
			title: function() {
3290
				return '';
3291
			},
3292
			label: function(item, data) {
3293
				return data.labels[item.index] + ': ' + item.yLabel;
3294
			}
3295
		}
3296
	}
3297
});
3298
3299
module.exports = function(Chart) {
3300
3301
	Chart.controllers.polarArea = Chart.DatasetController.extend({
3302
3303
		dataElementType: elements.Arc,
3304
3305
		linkScales: helpers.noop,
3306
3307
		update: function(reset) {
3308
			var me = this;
3309
			var dataset = me.getDataset();
3310
			var meta = me.getMeta();
3311
			var start = me.chart.options.startAngle || 0;
3312
			var starts = me._starts = [];
3313
			var angles = me._angles = [];
3314
			var i, ilen, angle;
3315
3316
			me._updateRadius();
3317
3318
			meta.count = me.countVisibleElements();
3319
3320
			for (i = 0, ilen = dataset.data.length; i < ilen; i++) {
3321
				starts[i] = start;
3322
				angle = me._computeAngle(i);
3323
				angles[i] = angle;
3324
				start += angle;
3325
			}
3326
3327
			helpers.each(meta.data, function(arc, index) {
3328
				me.updateElement(arc, index, reset);
3329
			});
3330
		},
3331
3332
		/**
3333
		 * @private
3334
		 */
3335
		_updateRadius: function() {
3336
			var me = this;
3337
			var chart = me.chart;
3338
			var chartArea = chart.chartArea;
3339
			var opts = chart.options;
3340
			var arcOpts = opts.elements.arc;
3341
			var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
3342
3343
			chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
3344
			chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
3345
			chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
3346
3347
			me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
3348
			me.innerRadius = me.outerRadius - chart.radiusLength;
3349
		},
3350
3351
		updateElement: function(arc, index, reset) {
3352
			var me = this;
3353
			var chart = me.chart;
3354
			var dataset = me.getDataset();
3355
			var opts = chart.options;
3356
			var animationOpts = opts.animation;
3357
			var scale = chart.scale;
3358
			var labels = chart.data.labels;
3359
3360
			var centerX = scale.xCenter;
3361
			var centerY = scale.yCenter;
3362
3363
			// var negHalfPI = -0.5 * Math.PI;
3364
			var datasetStartAngle = opts.startAngle;
3365
			var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
3366
			var startAngle = me._starts[index];
3367
			var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]);
3368
3369
			var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
3370
3371
			helpers.extend(arc, {
3372
				// Utility
3373
				_datasetIndex: me.index,
3374
				_index: index,
3375
				_scale: scale,
3376
3377
				// Desired view properties
3378
				_model: {
3379
					x: centerX,
3380
					y: centerY,
3381
					innerRadius: 0,
3382
					outerRadius: reset ? resetRadius : distance,
3383
					startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
3384
					endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
3385
					label: helpers.valueAtIndexOrDefault(labels, index, labels[index])
3386
				}
3387
			});
3388
3389
			// Apply border and fill style
3390
			var elementOpts = this.chart.options.elements.arc;
3391
			var custom = arc.custom || {};
3392
			var valueOrDefault = helpers.valueAtIndexOrDefault;
3393
			var model = arc._model;
3394
3395
			model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
3396
			model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
3397
			model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
3398
3399
			arc.pivot();
3400
		},
3401
3402
		countVisibleElements: function() {
3403
			var dataset = this.getDataset();
3404
			var meta = this.getMeta();
3405
			var count = 0;
3406
3407
			helpers.each(meta.data, function(element, index) {
3408
				if (!isNaN(dataset.data[index]) && !element.hidden) {
3409
					count++;
3410
				}
3411
			});
3412
3413
			return count;
3414
		},
3415
3416
		/**
3417
		 * @private
3418
		 */
3419
		_computeAngle: function(index) {
3420
			var me = this;
3421
			var count = this.getMeta().count;
3422
			var dataset = me.getDataset();
3423
			var meta = me.getMeta();
3424
3425
			if (isNaN(dataset.data[index]) || meta.data[index].hidden) {
3426
				return 0;
3427
			}
3428
3429
			// Scriptable options
3430
			var context = {
3431
				chart: me.chart,
3432
				dataIndex: index,
3433
				dataset: dataset,
3434
				datasetIndex: me.index
3435
			};
3436
3437
			return helpers.options.resolve([
3438
				me.chart.options.elements.arc.angle,
3439
				(2 * Math.PI) / count
3440
			], context, index);
3441
		}
3442
	});
3443
};
3444
3445
},{"26":26,"41":41,"46":46}],20:[function(require,module,exports){
3446
'use strict';
3447
3448
var defaults = require(26);
3449
var elements = require(41);
3450
var helpers = require(46);
3451
3452
defaults._set('radar', {
3453
	scale: {
3454
		type: 'radialLinear'
3455
	},
3456
	elements: {
3457
		line: {
3458
			tension: 0 // no bezier in radar
3459
		}
3460
	}
3461
});
3462
3463
module.exports = function(Chart) {
3464
3465
	Chart.controllers.radar = Chart.DatasetController.extend({
3466
3467
		datasetElementType: elements.Line,
3468
3469
		dataElementType: elements.Point,
3470
3471
		linkScales: helpers.noop,
3472
3473
		update: function(reset) {
3474
			var me = this;
3475
			var meta = me.getMeta();
3476
			var line = meta.dataset;
3477
			var points = meta.data;
3478
			var custom = line.custom || {};
3479
			var dataset = me.getDataset();
3480
			var lineElementOptions = me.chart.options.elements.line;
3481
			var scale = me.chart.scale;
3482
3483
			// Compatibility: If the properties are defined with only the old name, use those values
3484
			if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
3485
				dataset.lineTension = dataset.tension;
3486
			}
3487
3488
			helpers.extend(meta.dataset, {
3489
				// Utility
3490
				_datasetIndex: me.index,
3491
				_scale: scale,
3492
				// Data
3493
				_children: points,
3494
				_loop: true,
3495
				// Model
3496
				_model: {
3497
					// Appearance
3498
					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
3499
					backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
3500
					borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
3501
					borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
3502
					fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
3503
					borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
3504
					borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
3505
					borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
3506
					borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
3507
				}
3508
			});
3509
3510
			meta.dataset.pivot();
3511
3512
			// Update Points
3513
			helpers.each(points, function(point, index) {
3514
				me.updateElement(point, index, reset);
3515
			}, me);
3516
3517
			// Update bezier control points
3518
			me.updateBezierControlPoints();
3519
		},
3520
		updateElement: function(point, index, reset) {
3521
			var me = this;
3522
			var custom = point.custom || {};
3523
			var dataset = me.getDataset();
3524
			var scale = me.chart.scale;
3525
			var pointElementOptions = me.chart.options.elements.point;
3526
			var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
3527
3528
			// Compatibility: If the properties are defined with only the old name, use those values
3529
			if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
3530
				dataset.pointRadius = dataset.radius;
3531
			}
3532
			if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
3533
				dataset.pointHitRadius = dataset.hitRadius;
3534
			}
3535
3536
			helpers.extend(point, {
3537
				// Utility
3538
				_datasetIndex: me.index,
3539
				_index: index,
3540
				_scale: scale,
3541
3542
				// Desired view properties
3543
				_model: {
3544
					x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
3545
					y: reset ? scale.yCenter : pointPosition.y,
3546
3547
					// Appearance
3548
					tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension),
3549
					radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
3550
					backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor),
3551
					borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
3552
					borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
3553
					pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),
3554
					rotation: custom.rotation ? custom.rotation : helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointElementOptions.rotation),
3555
3556
					// Tooltip
3557
					hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius)
3558
				}
3559
			});
3560
3561
			point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
3562
		},
3563
		updateBezierControlPoints: function() {
3564
			var chartArea = this.chart.chartArea;
3565
			var meta = this.getMeta();
3566
3567
			helpers.each(meta.data, function(point, index) {
3568
				var model = point._model;
3569
				var controlPoints = helpers.splineCurve(
3570
					helpers.previousItem(meta.data, index, true)._model,
3571
					model,
3572
					helpers.nextItem(meta.data, index, true)._model,
3573
					model.tension
3574
				);
3575
3576
				// Prevent the bezier going outside of the bounds of the graph
3577
				model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left);
3578
				model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top);
3579
3580
				model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left);
3581
				model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top);
3582
3583
				// Now pivot the point for animation
3584
				point.pivot();
3585
			});
3586
		},
3587
3588
		setHoverStyle: function(point) {
3589
			// Point
3590
			var dataset = this.chart.data.datasets[point._datasetIndex];
3591
			var custom = point.custom || {};
3592
			var index = point._index;
3593
			var model = point._model;
3594
3595
			point.$previousStyle = {
3596
				backgroundColor: model.backgroundColor,
3597
				borderColor: model.borderColor,
3598
				borderWidth: model.borderWidth,
3599
				radius: model.radius
3600
			};
3601
3602
			model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
3603
			model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
3604
			model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
3605
			model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
3606
		},
3607
	});
3608
};
3609
3610
},{"26":26,"41":41,"46":46}],21:[function(require,module,exports){
3611
'use strict';
3612
3613
var defaults = require(26);
3614
3615
defaults._set('scatter', {
3616
	hover: {
3617
		mode: 'single'
3618
	},
3619
3620
	scales: {
3621
		xAxes: [{
3622
			id: 'x-axis-1',    // need an ID so datasets can reference the scale
3623
			type: 'linear',    // scatter should not use a category axis
3624
			position: 'bottom'
3625
		}],
3626
		yAxes: [{
3627
			id: 'y-axis-1',
3628
			type: 'linear',
3629
			position: 'left'
3630
		}]
3631
	},
3632
3633
	showLines: false,
3634
3635
	tooltips: {
3636
		callbacks: {
3637
			title: function() {
3638
				return '';     // doesn't make sense for scatter since data are formatted as a point
3639
			},
3640
			label: function(item) {
3641
				return '(' + item.xLabel + ', ' + item.yLabel + ')';
3642
			}
3643
		}
3644
	}
3645
});
3646
3647
module.exports = function(Chart) {
3648
3649
	// Scatter charts use line controllers
3650
	Chart.controllers.scatter = Chart.controllers.line;
3651
3652
};
3653
3654
},{"26":26}],22:[function(require,module,exports){
3655
'use strict';
3656
3657
var Element = require(27);
3658
3659
var exports = module.exports = Element.extend({
3660
	chart: null, // the animation associated chart instance
3661
	currentStep: 0, // the current animation step
3662
	numSteps: 60, // default number of steps
3663
	easing: '', // the easing to use for this animation
3664
	render: null, // render function used by the animation service
3665
3666
	onAnimationProgress: null, // user specified callback to fire on each step of the animation
3667
	onAnimationComplete: null, // user specified callback to fire when the animation finishes
3668
});
3669
3670
// DEPRECATIONS
3671
3672
/**
3673
 * Provided for backward compatibility, use Chart.Animation instead
3674
 * @prop Chart.Animation#animationObject
3675
 * @deprecated since version 2.6.0
3676
 * @todo remove at version 3
3677
 */
3678
Object.defineProperty(exports.prototype, 'animationObject', {
3679
	get: function() {
3680
		return this;
3681
	}
3682
});
3683
3684
/**
3685
 * Provided for backward compatibility, use Chart.Animation#chart instead
3686
 * @prop Chart.Animation#chartInstance
3687
 * @deprecated since version 2.6.0
3688
 * @todo remove at version 3
3689
 */
3690
Object.defineProperty(exports.prototype, 'chartInstance', {
3691
	get: function() {
3692
		return this.chart;
3693
	},
3694
	set: function(value) {
3695
		this.chart = value;
3696
	}
3697
});
3698
3699
},{"27":27}],23:[function(require,module,exports){
3700
/* global window: false */
3701
'use strict';
3702
3703
var defaults = require(26);
3704
var helpers = require(46);
3705
3706
defaults._set('global', {
3707
	animation: {
3708
		duration: 1000,
3709
		easing: 'easeOutQuart',
3710
		onProgress: helpers.noop,
3711
		onComplete: helpers.noop
3712
	}
3713
});
3714
3715
module.exports = {
3716
	frameDuration: 17,
3717
	animations: [],
3718
	dropFrames: 0,
3719
	request: null,
3720
3721
	/**
3722
	 * @param {Chart} chart - The chart to animate.
3723
	 * @param {Chart.Animation} animation - The animation that we will animate.
3724
	 * @param {Number} duration - The animation duration in ms.
3725
	 * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
3726
	 */
3727
	addAnimation: function(chart, animation, duration, lazy) {
3728
		var animations = this.animations;
3729
		var i, ilen;
3730
3731
		animation.chart = chart;
3732
3733
		if (!lazy) {
3734
			chart.animating = true;
3735
		}
3736
3737
		for (i = 0, ilen = animations.length; i < ilen; ++i) {
3738
			if (animations[i].chart === chart) {
3739
				animations[i] = animation;
3740
				return;
3741
			}
3742
		}
3743
3744
		animations.push(animation);
3745
3746
		// If there are no animations queued, manually kickstart a digest, for lack of a better word
3747
		if (animations.length === 1) {
3748
			this.requestAnimationFrame();
3749
		}
3750
	},
3751
3752
	cancelAnimation: function(chart) {
3753
		var index = helpers.findIndex(this.animations, function(animation) {
3754
			return animation.chart === chart;
3755
		});
3756
3757
		if (index !== -1) {
3758
			this.animations.splice(index, 1);
3759
			chart.animating = false;
3760
		}
3761
	},
3762
3763
	requestAnimationFrame: function() {
3764
		var me = this;
3765
		if (me.request === null) {
3766
			// Skip animation frame requests until the active one is executed.
3767
			// This can happen when processing mouse events, e.g. 'mousemove'
3768
			// and 'mouseout' events will trigger multiple renders.
3769
			me.request = helpers.requestAnimFrame.call(window, function() {
3770
				me.request = null;
3771
				me.startDigest();
3772
			});
3773
		}
3774
	},
3775
3776
	/**
3777
	 * @private
3778
	 */
3779
	startDigest: function() {
3780
		var me = this;
3781
		var startTime = Date.now();
3782
		var framesToDrop = 0;
3783
3784
		if (me.dropFrames > 1) {
3785
			framesToDrop = Math.floor(me.dropFrames);
3786
			me.dropFrames = me.dropFrames % 1;
3787
		}
3788
3789
		me.advance(1 + framesToDrop);
3790
3791
		var endTime = Date.now();
3792
3793
		me.dropFrames += (endTime - startTime) / me.frameDuration;
3794
3795
		// Do we have more stuff to animate?
3796
		if (me.animations.length > 0) {
3797
			me.requestAnimationFrame();
3798
		}
3799
	},
3800
3801
	/**
3802
	 * @private
3803
	 */
3804
	advance: function(count) {
3805
		var animations = this.animations;
3806
		var animation, chart;
3807
		var i = 0;
3808
3809
		while (i < animations.length) {
3810
			animation = animations[i];
3811
			chart = animation.chart;
3812
3813
			animation.currentStep = (animation.currentStep || 0) + count;
3814
			animation.currentStep = Math.min(animation.currentStep, animation.numSteps);
3815
3816
			helpers.callback(animation.render, [chart, animation], chart);
3817
			helpers.callback(animation.onAnimationProgress, [animation], chart);
3818
3819
			if (animation.currentStep >= animation.numSteps) {
3820
				helpers.callback(animation.onAnimationComplete, [animation], chart);
3821
				chart.animating = false;
3822
				animations.splice(i, 1);
3823
			} else {
3824
				++i;
3825
			}
3826
		}
3827
	}
3828
};
3829
3830
},{"26":26,"46":46}],24:[function(require,module,exports){
3831
'use strict';
3832
3833
var Animation = require(22);
3834
var animations = require(23);
3835
var defaults = require(26);
3836
var helpers = require(46);
3837
var Interaction = require(29);
3838
var layouts = require(31);
3839
var platform = require(49);
3840
var plugins = require(32);
3841
var scaleService = require(34);
3842
var Tooltip = require(36);
3843
3844
module.exports = function(Chart) {
3845
3846
	// Create a dictionary of chart types, to allow for extension of existing types
3847
	Chart.types = {};
3848
3849
	// Store a reference to each instance - allowing us to globally resize chart instances on window resize.
3850
	// Destroy method on the chart will remove the instance of the chart from this reference.
3851
	Chart.instances = {};
3852
3853
	// Controllers available for dataset visualization eg. bar, line, slice, etc.
3854
	Chart.controllers = {};
3855
3856
	/**
3857
	 * Initializes the given config with global and chart default values.
3858
	 */
3859
	function initConfig(config) {
3860
		config = config || {};
3861
3862
		// Do NOT use configMerge() for the data object because this method merges arrays
3863
		// and so would change references to labels and datasets, preventing data updates.
3864
		var data = config.data = config.data || {};
3865
		data.datasets = data.datasets || [];
3866
		data.labels = data.labels || [];
3867
3868
		config.options = helpers.configMerge(
3869
			defaults.global,
3870
			defaults[config.type],
3871
			config.options || {});
3872
3873
		return config;
3874
	}
3875
3876
	/**
3877
	 * Updates the config of the chart
3878
	 * @param chart {Chart} chart to update the options for
3879
	 */
3880
	function updateConfig(chart) {
3881
		var newOptions = chart.options;
3882
3883
		helpers.each(chart.scales, function(scale) {
3884
			layouts.removeBox(chart, scale);
3885
		});
3886
3887
		newOptions = helpers.configMerge(
3888
			Chart.defaults.global,
3889
			Chart.defaults[chart.config.type],
3890
			newOptions);
3891
3892
		chart.options = chart.config.options = newOptions;
3893
		chart.ensureScalesHaveIDs();
3894
		chart.buildOrUpdateScales();
3895
		// Tooltip
3896
		chart.tooltip._options = newOptions.tooltips;
3897
		chart.tooltip.initialize();
3898
	}
3899
3900
	function positionIsHorizontal(position) {
3901
		return position === 'top' || position === 'bottom';
3902
	}
3903
3904
	helpers.extend(Chart.prototype, /** @lends Chart */ {
3905
		/**
3906
		 * @private
3907
		 */
3908
		construct: function(item, config) {
3909
			var me = this;
3910
3911
			config = initConfig(config);
3912
3913
			var context = platform.acquireContext(item, config);
3914
			var canvas = context && context.canvas;
3915
			var height = canvas && canvas.height;
3916
			var width = canvas && canvas.width;
3917
3918
			me.id = helpers.uid();
3919
			me.ctx = context;
3920
			me.canvas = canvas;
3921
			me.config = config;
3922
			me.width = width;
3923
			me.height = height;
3924
			me.aspectRatio = height ? width / height : null;
3925
			me.options = config.options;
3926
			me._bufferedRender = false;
3927
3928
			/**
3929
			 * Provided for backward compatibility, Chart and Chart.Controller have been merged,
3930
			 * the "instance" still need to be defined since it might be called from plugins.
3931
			 * @prop Chart#chart
3932
			 * @deprecated since version 2.6.0
3933
			 * @todo remove at version 3
3934
			 * @private
3935
			 */
3936
			me.chart = me;
3937
			me.controller = me; // chart.chart.controller #inception
3938
3939
			// Add the chart instance to the global namespace
3940
			Chart.instances[me.id] = me;
3941
3942
			// Define alias to the config data: `chart.data === chart.config.data`
3943
			Object.defineProperty(me, 'data', {
3944
				get: function() {
3945
					return me.config.data;
3946
				},
3947
				set: function(value) {
3948
					me.config.data = value;
3949
				}
3950
			});
3951
3952
			if (!context || !canvas) {
3953
				// The given item is not a compatible context2d element, let's return before finalizing
3954
				// the chart initialization but after setting basic chart / controller properties that
3955
				// can help to figure out that the chart is not valid (e.g chart.canvas !== null);
3956
				// https://github.com/chartjs/Chart.js/issues/2807
3957
				console.error("Failed to create chart: can't acquire context from the given item");
3958
				return;
3959
			}
3960
3961
			me.initialize();
3962
			me.update();
3963
		},
3964
3965
		/**
3966
		 * @private
3967
		 */
3968
		initialize: function() {
3969
			var me = this;
3970
3971
			// Before init plugin notification
3972
			plugins.notify(me, 'beforeInit');
3973
3974
			helpers.retinaScale(me, me.options.devicePixelRatio);
3975
3976
			me.bindEvents();
3977
3978
			if (me.options.responsive) {
3979
				// Initial resize before chart draws (must be silent to preserve initial animations).
3980
				me.resize(true);
3981
			}
3982
3983
			// Make sure scales have IDs and are built before we build any controllers.
3984
			me.ensureScalesHaveIDs();
3985
			me.buildOrUpdateScales();
3986
			me.initToolTip();
3987
3988
			// After init plugin notification
3989
			plugins.notify(me, 'afterInit');
3990
3991
			return me;
3992
		},
3993
3994
		clear: function() {
3995
			helpers.canvas.clear(this);
3996
			return this;
3997
		},
3998
3999
		stop: function() {
4000
			// Stops any current animation loop occurring
4001
			animations.cancelAnimation(this);
4002
			return this;
4003
		},
4004
4005
		resize: function(silent) {
4006
			var me = this;
4007
			var options = me.options;
4008
			var canvas = me.canvas;
4009
			var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;
4010
4011
			// the canvas render width and height will be casted to integers so make sure that
4012
			// the canvas display style uses the same integer values to avoid blurring effect.
4013
4014
			// Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed
4015
			var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas)));
4016
			var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)));
4017
4018
			if (me.width === newWidth && me.height === newHeight) {
4019
				return;
4020
			}
4021
4022
			canvas.width = me.width = newWidth;
4023
			canvas.height = me.height = newHeight;
4024
			canvas.style.width = newWidth + 'px';
4025
			canvas.style.height = newHeight + 'px';
4026
4027
			helpers.retinaScale(me, options.devicePixelRatio);
4028
4029
			if (!silent) {
4030
				// Notify any plugins about the resize
4031
				var newSize = {width: newWidth, height: newHeight};
4032
				plugins.notify(me, 'resize', [newSize]);
4033
4034
				// Notify of resize
4035
				if (me.options.onResize) {
4036
					me.options.onResize(me, newSize);
4037
				}
4038
4039
				me.stop();
4040
				me.update({
4041
					duration: me.options.responsiveAnimationDuration
4042
				});
4043
			}
4044
		},
4045
4046
		ensureScalesHaveIDs: function() {
4047
			var options = this.options;
4048
			var scalesOptions = options.scales || {};
4049
			var scaleOptions = options.scale;
4050
4051
			helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
4052
				xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
4053
			});
4054
4055
			helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
4056
				yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
4057
			});
4058
4059
			if (scaleOptions) {
4060
				scaleOptions.id = scaleOptions.id || 'scale';
4061
			}
4062
		},
4063
4064
		/**
4065
		 * Builds a map of scale ID to scale object for future lookup.
4066
		 */
4067
		buildOrUpdateScales: function() {
4068
			var me = this;
4069
			var options = me.options;
4070
			var scales = me.scales || {};
4071
			var items = [];
4072
			var updated = Object.keys(scales).reduce(function(obj, id) {
4073
				obj[id] = false;
4074
				return obj;
4075
			}, {});
4076
4077
			if (options.scales) {
4078
				items = items.concat(
4079
					(options.scales.xAxes || []).map(function(xAxisOptions) {
4080
						return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};
4081
					}),
4082
					(options.scales.yAxes || []).map(function(yAxisOptions) {
4083
						return {options: yAxisOptions, dtype: 'linear', dposition: 'left'};
4084
					})
4085
				);
4086
			}
4087
4088
			if (options.scale) {
4089
				items.push({
4090
					options: options.scale,
4091
					dtype: 'radialLinear',
4092
					isDefault: true,
4093
					dposition: 'chartArea'
4094
				});
4095
			}
4096
4097
			helpers.each(items, function(item) {
4098
				var scaleOptions = item.options;
4099
				var id = scaleOptions.id;
4100
				var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype);
4101
4102
				if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
4103
					scaleOptions.position = item.dposition;
4104
				}
4105
4106
				updated[id] = true;
4107
				var scale = null;
4108
				if (id in scales && scales[id].type === scaleType) {
4109
					scale = scales[id];
4110
					scale.options = scaleOptions;
4111
					scale.ctx = me.ctx;
4112
					scale.chart = me;
4113
				} else {
4114
					var scaleClass = scaleService.getScaleConstructor(scaleType);
4115
					if (!scaleClass) {
4116
						return;
4117
					}
4118
					scale = new scaleClass({
4119
						id: id,
4120
						type: scaleType,
4121
						options: scaleOptions,
4122
						ctx: me.ctx,
4123
						chart: me
4124
					});
4125
					scales[scale.id] = scale;
4126
				}
4127
4128
				scale.mergeTicksOptions();
4129
4130
				// TODO(SB): I think we should be able to remove this custom case (options.scale)
4131
				// and consider it as a regular scale part of the "scales"" map only! This would
4132
				// make the logic easier and remove some useless? custom code.
4133
				if (item.isDefault) {
4134
					me.scale = scale;
4135
				}
4136
			});
4137
			// clear up discarded scales
4138
			helpers.each(updated, function(hasUpdated, id) {
4139
				if (!hasUpdated) {
4140
					delete scales[id];
4141
				}
4142
			});
4143
4144
			me.scales = scales;
4145
4146
			scaleService.addScalesToLayout(this);
4147
		},
4148
4149
		buildOrUpdateControllers: function() {
4150
			var me = this;
4151
			var types = [];
4152
			var newControllers = [];
4153
4154
			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4155
				var meta = me.getDatasetMeta(datasetIndex);
4156
				var type = dataset.type || me.config.type;
4157
4158
				if (meta.type && meta.type !== type) {
4159
					me.destroyDatasetMeta(datasetIndex);
4160
					meta = me.getDatasetMeta(datasetIndex);
4161
				}
4162
				meta.type = type;
4163
4164
				types.push(meta.type);
4165
4166
				if (meta.controller) {
4167
					meta.controller.updateIndex(datasetIndex);
4168
					meta.controller.linkScales();
4169
				} else {
4170
					var ControllerClass = Chart.controllers[meta.type];
4171
					if (ControllerClass === undefined) {
4172
						throw new Error('"' + meta.type + '" is not a chart type.');
4173
					}
4174
4175
					meta.controller = new ControllerClass(me, datasetIndex);
4176
					newControllers.push(meta.controller);
4177
				}
4178
			}, me);
4179
4180
			return newControllers;
4181
		},
4182
4183
		/**
4184
		 * Reset the elements of all datasets
4185
		 * @private
4186
		 */
4187
		resetElements: function() {
4188
			var me = this;
4189
			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4190
				me.getDatasetMeta(datasetIndex).controller.reset();
4191
			}, me);
4192
		},
4193
4194
		/**
4195
		* Resets the chart back to it's state before the initial animation
4196
		*/
4197
		reset: function() {
4198
			this.resetElements();
4199
			this.tooltip.initialize();
4200
		},
4201
4202
		update: function(config) {
4203
			var me = this;
4204
4205
			if (!config || typeof config !== 'object') {
4206
				// backwards compatibility
4207
				config = {
4208
					duration: config,
4209
					lazy: arguments[1]
4210
				};
4211
			}
4212
4213
			updateConfig(me);
4214
4215
			// plugins options references might have change, let's invalidate the cache
4216
			// https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
4217
			plugins._invalidate(me);
4218
4219
			if (plugins.notify(me, 'beforeUpdate') === false) {
4220
				return;
4221
			}
4222
4223
			// In case the entire data object changed
4224
			me.tooltip._data = me.data;
4225
4226
			// Make sure dataset controllers are updated and new controllers are reset
4227
			var newControllers = me.buildOrUpdateControllers();
4228
4229
			// Make sure all dataset controllers have correct meta data counts
4230
			helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4231
				me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
4232
			}, me);
4233
4234
			me.updateLayout();
4235
4236
			// Can only reset the new controllers after the scales have been updated
4237
			if (me.options.animation && me.options.animation.duration) {
4238
				helpers.each(newControllers, function(controller) {
4239
					controller.reset();
4240
				});
4241
			}
4242
4243
			me.updateDatasets();
4244
4245
			// Need to reset tooltip in case it is displayed with elements that are removed
4246
			// after update.
4247
			me.tooltip.initialize();
4248
4249
			// Last active contains items that were previously in the tooltip.
4250
			// When we reset the tooltip, we need to clear it
4251
			me.lastActive = [];
4252
4253
			// Do this before render so that any plugins that need final scale updates can use it
4254
			plugins.notify(me, 'afterUpdate');
4255
4256
			if (me._bufferedRender) {
4257
				me._bufferedRequest = {
4258
					duration: config.duration,
4259
					easing: config.easing,
4260
					lazy: config.lazy
4261
				};
4262
			} else {
4263
				me.render(config);
4264
			}
4265
		},
4266
4267
		/**
4268
		 * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
4269
		 * hook, in which case, plugins will not be called on `afterLayout`.
4270
		 * @private
4271
		 */
4272
		updateLayout: function() {
4273
			var me = this;
4274
4275
			if (plugins.notify(me, 'beforeLayout') === false) {
4276
				return;
4277
			}
4278
4279
			layouts.update(this, this.width, this.height);
4280
4281
			/**
4282
			 * Provided for backward compatibility, use `afterLayout` instead.
4283
			 * @method IPlugin#afterScaleUpdate
4284
			 * @deprecated since version 2.5.0
4285
			 * @todo remove at version 3
4286
			 * @private
4287
			 */
4288
			plugins.notify(me, 'afterScaleUpdate');
4289
			plugins.notify(me, 'afterLayout');
4290
		},
4291
4292
		/**
4293
		 * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
4294
		 * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
4295
		 * @private
4296
		 */
4297
		updateDatasets: function() {
4298
			var me = this;
4299
4300
			if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
4301
				return;
4302
			}
4303
4304
			for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
4305
				me.updateDataset(i);
4306
			}
4307
4308
			plugins.notify(me, 'afterDatasetsUpdate');
4309
		},
4310
4311
		/**
4312
		 * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
4313
		 * hook, in which case, plugins will not be called on `afterDatasetUpdate`.
4314
		 * @private
4315
		 */
4316
		updateDataset: function(index) {
4317
			var me = this;
4318
			var meta = me.getDatasetMeta(index);
4319
			var args = {
4320
				meta: meta,
4321
				index: index
4322
			};
4323
4324
			if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
4325
				return;
4326
			}
4327
4328
			meta.controller.update();
4329
4330
			plugins.notify(me, 'afterDatasetUpdate', [args]);
4331
		},
4332
4333
		render: function(config) {
4334
			var me = this;
4335
4336
			if (!config || typeof config !== 'object') {
4337
				// backwards compatibility
4338
				config = {
4339
					duration: config,
4340
					lazy: arguments[1]
4341
				};
4342
			}
4343
4344
			var duration = config.duration;
4345
			var lazy = config.lazy;
4346
4347
			if (plugins.notify(me, 'beforeRender') === false) {
4348
				return;
4349
			}
4350
4351
			var animationOptions = me.options.animation;
4352
			var onComplete = function(animation) {
4353
				plugins.notify(me, 'afterRender');
4354
				helpers.callback(animationOptions && animationOptions.onComplete, [animation], me);
4355
			};
4356
4357
			if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {
4358
				var animation = new Animation({
4359
					numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps
4360
					easing: config.easing || animationOptions.easing,
4361
4362
					render: function(chart, animationObject) {
4363
						var easingFunction = helpers.easing.effects[animationObject.easing];
4364
						var currentStep = animationObject.currentStep;
4365
						var stepDecimal = currentStep / animationObject.numSteps;
4366
4367
						chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
4368
					},
4369
4370
					onAnimationProgress: animationOptions.onProgress,
4371
					onAnimationComplete: onComplete
4372
				});
4373
4374
				animations.addAnimation(me, animation, duration, lazy);
4375
			} else {
4376
				me.draw();
4377
4378
				// See https://github.com/chartjs/Chart.js/issues/3781
4379
				onComplete(new Animation({numSteps: 0, chart: me}));
4380
			}
4381
4382
			return me;
4383
		},
4384
4385
		draw: function(easingValue) {
4386
			var me = this;
4387
4388
			me.clear();
4389
4390
			if (helpers.isNullOrUndef(easingValue)) {
4391
				easingValue = 1;
4392
			}
4393
4394
			me.transition(easingValue);
4395
4396
			if (me.width <= 0 || me.height <= 0) {
4397
				return;
4398
			}
4399
4400
			if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
4401
				return;
4402
			}
4403
4404
			// Draw all the scales
4405
			helpers.each(me.boxes, function(box) {
4406
				box.draw(me.chartArea);
4407
			}, me);
4408
4409
			if (me.scale) {
4410
				me.scale.draw();
4411
			}
4412
4413
			me.drawDatasets(easingValue);
4414
			me._drawTooltip(easingValue);
4415
4416
			plugins.notify(me, 'afterDraw', [easingValue]);
4417
		},
4418
4419
		/**
4420
		 * @private
4421
		 */
4422
		transition: function(easingValue) {
4423
			var me = this;
4424
4425
			for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
4426
				if (me.isDatasetVisible(i)) {
4427
					me.getDatasetMeta(i).controller.transition(easingValue);
4428
				}
4429
			}
4430
4431
			me.tooltip.transition(easingValue);
4432
		},
4433
4434
		/**
4435
		 * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
4436
		 * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
4437
		 * @private
4438
		 */
4439
		drawDatasets: function(easingValue) {
4440
			var me = this;
4441
4442
			if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
4443
				return;
4444
			}
4445
4446
			// Draw datasets reversed to support proper line stacking
4447
			for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) {
4448
				if (me.isDatasetVisible(i)) {
4449
					me.drawDataset(i, easingValue);
4450
				}
4451
			}
4452
4453
			plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
4454
		},
4455
4456
		/**
4457
		 * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
4458
		 * hook, in which case, plugins will not be called on `afterDatasetDraw`.
4459
		 * @private
4460
		 */
4461
		drawDataset: function(index, easingValue) {
4462
			var me = this;
4463
			var meta = me.getDatasetMeta(index);
4464
			var args = {
4465
				meta: meta,
4466
				index: index,
4467
				easingValue: easingValue
4468
			};
4469
4470
			if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
4471
				return;
4472
			}
4473
4474
			meta.controller.draw(easingValue);
4475
4476
			plugins.notify(me, 'afterDatasetDraw', [args]);
4477
		},
4478
4479
		/**
4480
		 * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw`
4481
		 * hook, in which case, plugins will not be called on `afterTooltipDraw`.
4482
		 * @private
4483
		 */
4484
		_drawTooltip: function(easingValue) {
4485
			var me = this;
4486
			var tooltip = me.tooltip;
4487
			var args = {
4488
				tooltip: tooltip,
4489
				easingValue: easingValue
4490
			};
4491
4492
			if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) {
4493
				return;
4494
			}
4495
4496
			tooltip.draw();
4497
4498
			plugins.notify(me, 'afterTooltipDraw', [args]);
4499
		},
4500
4501
		// Get the single element that was clicked on
4502
		// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
4503
		getElementAtEvent: function(e) {
4504
			return Interaction.modes.single(this, e);
4505
		},
4506
4507
		getElementsAtEvent: function(e) {
4508
			return Interaction.modes.label(this, e, {intersect: true});
4509
		},
4510
4511
		getElementsAtXAxis: function(e) {
4512
			return Interaction.modes['x-axis'](this, e, {intersect: true});
4513
		},
4514
4515
		getElementsAtEventForMode: function(e, mode, options) {
4516
			var method = Interaction.modes[mode];
4517
			if (typeof method === 'function') {
4518
				return method(this, e, options);
4519
			}
4520
4521
			return [];
4522
		},
4523
4524
		getDatasetAtEvent: function(e) {
4525
			return Interaction.modes.dataset(this, e, {intersect: true});
4526
		},
4527
4528
		getDatasetMeta: function(datasetIndex) {
4529
			var me = this;
4530
			var dataset = me.data.datasets[datasetIndex];
4531
			if (!dataset._meta) {
4532
				dataset._meta = {};
4533
			}
4534
4535
			var meta = dataset._meta[me.id];
4536
			if (!meta) {
4537
				meta = dataset._meta[me.id] = {
4538
					type: null,
4539
					data: [],
4540
					dataset: null,
4541
					controller: null,
4542
					hidden: null,			// See isDatasetVisible() comment
4543
					xAxisID: null,
4544
					yAxisID: null
4545
				};
4546
			}
4547
4548
			return meta;
4549
		},
4550
4551
		getVisibleDatasetCount: function() {
4552
			var count = 0;
4553
			for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
4554
				if (this.isDatasetVisible(i)) {
4555
					count++;
4556
				}
4557
			}
4558
			return count;
4559
		},
4560
4561
		isDatasetVisible: function(datasetIndex) {
4562
			var meta = this.getDatasetMeta(datasetIndex);
4563
4564
			// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
4565
			// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
4566
			return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
4567
		},
4568
4569
		generateLegend: function() {
4570
			return this.options.legendCallback(this);
4571
		},
4572
4573
		/**
4574
		 * @private
4575
		 */
4576
		destroyDatasetMeta: function(datasetIndex) {
4577
			var id = this.id;
4578
			var dataset = this.data.datasets[datasetIndex];
4579
			var meta = dataset._meta && dataset._meta[id];
4580
4581
			if (meta) {
4582
				meta.controller.destroy();
4583
				delete dataset._meta[id];
4584
			}
4585
		},
4586
4587
		destroy: function() {
4588
			var me = this;
4589
			var canvas = me.canvas;
4590
			var i, ilen;
4591
4592
			me.stop();
4593
4594
			// dataset controllers need to cleanup associated data
4595
			for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
4596
				me.destroyDatasetMeta(i);
4597
			}
4598
4599
			if (canvas) {
4600
				me.unbindEvents();
4601
				helpers.canvas.clear(me);
4602
				platform.releaseContext(me.ctx);
4603
				me.canvas = null;
4604
				me.ctx = null;
4605
			}
4606
4607
			plugins.notify(me, 'destroy');
4608
4609
			delete Chart.instances[me.id];
4610
		},
4611
4612
		toBase64Image: function() {
4613
			return this.canvas.toDataURL.apply(this.canvas, arguments);
4614
		},
4615
4616
		initToolTip: function() {
4617
			var me = this;
4618
			me.tooltip = new Tooltip({
4619
				_chart: me,
4620
				_chartInstance: me, // deprecated, backward compatibility
4621
				_data: me.data,
4622
				_options: me.options.tooltips
4623
			}, me);
4624
		},
4625
4626
		/**
4627
		 * @private
4628
		 */
4629
		bindEvents: function() {
4630
			var me = this;
4631
			var listeners = me._listeners = {};
4632
			var listener = function() {
4633
				me.eventHandler.apply(me, arguments);
4634
			};
4635
4636
			helpers.each(me.options.events, function(type) {
4637
				platform.addEventListener(me, type, listener);
4638
				listeners[type] = listener;
4639
			});
4640
4641
			// Elements used to detect size change should not be injected for non responsive charts.
4642
			// See https://github.com/chartjs/Chart.js/issues/2210
4643
			if (me.options.responsive) {
4644
				listener = function() {
4645
					me.resize();
4646
				};
4647
4648
				platform.addEventListener(me, 'resize', listener);
4649
				listeners.resize = listener;
4650
			}
4651
		},
4652
4653
		/**
4654
		 * @private
4655
		 */
4656
		unbindEvents: function() {
4657
			var me = this;
4658
			var listeners = me._listeners;
4659
			if (!listeners) {
4660
				return;
4661
			}
4662
4663
			delete me._listeners;
4664
			helpers.each(listeners, function(listener, type) {
4665
				platform.removeEventListener(me, type, listener);
4666
			});
4667
		},
4668
4669
		updateHoverStyle: function(elements, mode, enabled) {
4670
			var method = enabled ? 'setHoverStyle' : 'removeHoverStyle';
4671
			var element, i, ilen;
4672
4673
			for (i = 0, ilen = elements.length; i < ilen; ++i) {
4674
				element = elements[i];
4675
				if (element) {
4676
					this.getDatasetMeta(element._datasetIndex).controller[method](element);
4677
				}
4678
			}
4679
		},
4680
4681
		/**
4682
		 * @private
4683
		 */
4684
		eventHandler: function(e) {
4685
			var me = this;
4686
			var tooltip = me.tooltip;
4687
4688
			if (plugins.notify(me, 'beforeEvent', [e]) === false) {
4689
				return;
4690
			}
4691
4692
			// Buffer any update calls so that renders do not occur
4693
			me._bufferedRender = true;
4694
			me._bufferedRequest = null;
4695
4696
			var changed = me.handleEvent(e);
4697
			// for smooth tooltip animations issue #4989
4698
			// the tooltip should be the source of change
4699
			// Animation check workaround:
4700
			// tooltip._start will be null when tooltip isn't animating
4701
			if (tooltip) {
4702
				changed = tooltip._start
4703
					? tooltip.handleEvent(e)
4704
					: changed | tooltip.handleEvent(e);
4705
			}
4706
4707
			plugins.notify(me, 'afterEvent', [e]);
4708
4709
			var bufferedRequest = me._bufferedRequest;
4710
			if (bufferedRequest) {
4711
				// If we have an update that was triggered, we need to do a normal render
4712
				me.render(bufferedRequest);
4713
			} else if (changed && !me.animating) {
4714
				// If entering, leaving, or changing elements, animate the change via pivot
4715
				me.stop();
4716
4717
				// We only need to render at this point. Updating will cause scales to be
4718
				// recomputed generating flicker & using more memory than necessary.
4719
				me.render({
4720
					duration: me.options.hover.animationDuration,
4721
					lazy: true
4722
				});
4723
			}
4724
4725
			me._bufferedRender = false;
4726
			me._bufferedRequest = null;
4727
4728
			return me;
4729
		},
4730
4731
		/**
4732
		 * Handle an event
4733
		 * @private
4734
		 * @param {IEvent} event the event to handle
4735
		 * @return {Boolean} true if the chart needs to re-render
4736
		 */
4737
		handleEvent: function(e) {
4738
			var me = this;
4739
			var options = me.options || {};
4740
			var hoverOptions = options.hover;
4741
			var changed = false;
4742
4743
			me.lastActive = me.lastActive || [];
4744
4745
			// Find Active Elements for hover and tooltips
4746
			if (e.type === 'mouseout') {
4747
				me.active = [];
4748
			} else {
4749
				me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
4750
			}
4751
4752
			// Invoke onHover hook
4753
			// Need to call with native event here to not break backwards compatibility
4754
			helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);
4755
4756
			if (e.type === 'mouseup' || e.type === 'click') {
4757
				if (options.onClick) {
4758
					// Use e.native here for backwards compatibility
4759
					options.onClick.call(me, e.native, me.active);
4760
				}
4761
			}
4762
4763
			// Remove styling for last active (even if it may still be active)
4764
			if (me.lastActive.length) {
4765
				me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
4766
			}
4767
4768
			// Built in hover styling
4769
			if (me.active.length && hoverOptions.mode) {
4770
				me.updateHoverStyle(me.active, hoverOptions.mode, true);
4771
			}
4772
4773
			changed = !helpers.arrayEquals(me.active, me.lastActive);
4774
4775
			// Remember Last Actives
4776
			me.lastActive = me.active;
4777
4778
			return changed;
4779
		}
4780
	});
4781
4782
	/**
4783
	 * Provided for backward compatibility, use Chart instead.
4784
	 * @class Chart.Controller
4785
	 * @deprecated since version 2.6.0
4786
	 * @todo remove at version 3
4787
	 * @private
4788
	 */
4789
	Chart.Controller = Chart;
4790
};
4791
4792
},{"22":22,"23":23,"26":26,"29":29,"31":31,"32":32,"34":34,"36":36,"46":46,"49":49}],25:[function(require,module,exports){
4793
'use strict';
4794
4795
var helpers = require(46);
4796
4797
module.exports = function(Chart) {
4798
4799
	var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
4800
4801
	/**
4802
	 * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
4803
	 * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
4804
	 * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
4805
	 */
4806
	function listenArrayEvents(array, listener) {
4807
		if (array._chartjs) {
4808
			array._chartjs.listeners.push(listener);
4809
			return;
4810
		}
4811
4812
		Object.defineProperty(array, '_chartjs', {
4813
			configurable: true,
4814
			enumerable: false,
4815
			value: {
4816
				listeners: [listener]
4817
			}
4818
		});
4819
4820
		arrayEvents.forEach(function(key) {
4821
			var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
4822
			var base = array[key];
4823
4824
			Object.defineProperty(array, key, {
4825
				configurable: true,
4826
				enumerable: false,
4827
				value: function() {
4828
					var args = Array.prototype.slice.call(arguments);
4829
					var res = base.apply(this, args);
4830
4831
					helpers.each(array._chartjs.listeners, function(object) {
4832
						if (typeof object[method] === 'function') {
4833
							object[method].apply(object, args);
4834
						}
4835
					});
4836
4837
					return res;
4838
				}
4839
			});
4840
		});
4841
	}
4842
4843
	/**
4844
	 * Removes the given array event listener and cleanup extra attached properties (such as
4845
	 * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
4846
	 */
4847
	function unlistenArrayEvents(array, listener) {
4848
		var stub = array._chartjs;
4849
		if (!stub) {
4850
			return;
4851
		}
4852
4853
		var listeners = stub.listeners;
4854
		var index = listeners.indexOf(listener);
4855
		if (index !== -1) {
4856
			listeners.splice(index, 1);
4857
		}
4858
4859
		if (listeners.length > 0) {
4860
			return;
4861
		}
4862
4863
		arrayEvents.forEach(function(key) {
4864
			delete array[key];
4865
		});
4866
4867
		delete array._chartjs;
4868
	}
4869
4870
	// Base class for all dataset controllers (line, bar, etc)
4871
	Chart.DatasetController = function(chart, datasetIndex) {
4872
		this.initialize(chart, datasetIndex);
4873
	};
4874
4875
	helpers.extend(Chart.DatasetController.prototype, {
4876
4877
		/**
4878
		 * Element type used to generate a meta dataset (e.g. Chart.element.Line).
4879
		 * @type {Chart.core.element}
4880
		 */
4881
		datasetElementType: null,
4882
4883
		/**
4884
		 * Element type used to generate a meta data (e.g. Chart.element.Point).
4885
		 * @type {Chart.core.element}
4886
		 */
4887
		dataElementType: null,
4888
4889
		initialize: function(chart, datasetIndex) {
4890
			var me = this;
4891
			me.chart = chart;
4892
			me.index = datasetIndex;
4893
			me.linkScales();
4894
			me.addElements();
4895
		},
4896
4897
		updateIndex: function(datasetIndex) {
4898
			this.index = datasetIndex;
4899
		},
4900
4901
		linkScales: function() {
4902
			var me = this;
4903
			var meta = me.getMeta();
4904
			var dataset = me.getDataset();
4905
4906
			if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) {
4907
				meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
4908
			}
4909
			if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) {
4910
				meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
4911
			}
4912
		},
4913
4914
		getDataset: function() {
4915
			return this.chart.data.datasets[this.index];
4916
		},
4917
4918
		getMeta: function() {
4919
			return this.chart.getDatasetMeta(this.index);
4920
		},
4921
4922
		getScaleForId: function(scaleID) {
4923
			return this.chart.scales[scaleID];
4924
		},
4925
4926
		reset: function() {
4927
			this.update(true);
4928
		},
4929
4930
		/**
4931
		 * @private
4932
		 */
4933
		destroy: function() {
4934
			if (this._data) {
4935
				unlistenArrayEvents(this._data, this);
4936
			}
4937
		},
4938
4939
		createMetaDataset: function() {
4940
			var me = this;
4941
			var type = me.datasetElementType;
4942
			return type && new type({
4943
				_chart: me.chart,
4944
				_datasetIndex: me.index
4945
			});
4946
		},
4947
4948
		createMetaData: function(index) {
4949
			var me = this;
4950
			var type = me.dataElementType;
4951
			return type && new type({
4952
				_chart: me.chart,
4953
				_datasetIndex: me.index,
4954
				_index: index
4955
			});
4956
		},
4957
4958
		addElements: function() {
4959
			var me = this;
4960
			var meta = me.getMeta();
4961
			var data = me.getDataset().data || [];
4962
			var metaData = meta.data;
4963
			var i, ilen;
4964
4965
			for (i = 0, ilen = data.length; i < ilen; ++i) {
4966
				metaData[i] = metaData[i] || me.createMetaData(i);
4967
			}
4968
4969
			meta.dataset = meta.dataset || me.createMetaDataset();
4970
		},
4971
4972
		addElementAndReset: function(index) {
4973
			var element = this.createMetaData(index);
4974
			this.getMeta().data.splice(index, 0, element);
4975
			this.updateElement(element, index, true);
4976
		},
4977
4978
		buildOrUpdateElements: function() {
4979
			var me = this;
4980
			var dataset = me.getDataset();
4981
			var data = dataset.data || (dataset.data = []);
4982
4983
			// In order to correctly handle data addition/deletion animation (an thus simulate
4984
			// real-time charts), we need to monitor these data modifications and synchronize
4985
			// the internal meta data accordingly.
4986
			if (me._data !== data) {
4987
				if (me._data) {
4988
					// This case happens when the user replaced the data array instance.
4989
					unlistenArrayEvents(me._data, me);
4990
				}
4991
4992
				listenArrayEvents(data, me);
4993
				me._data = data;
4994
			}
4995
4996
			// Re-sync meta data in case the user replaced the data array or if we missed
4997
			// any updates and so make sure that we handle number of datapoints changing.
4998
			me.resyncElements();
4999
		},
5000
5001
		update: helpers.noop,
5002
5003
		transition: function(easingValue) {
5004
			var meta = this.getMeta();
5005
			var elements = meta.data || [];
5006
			var ilen = elements.length;
5007
			var i = 0;
5008
5009
			for (; i < ilen; ++i) {
5010
				elements[i].transition(easingValue);
5011
			}
5012
5013
			if (meta.dataset) {
5014
				meta.dataset.transition(easingValue);
5015
			}
5016
		},
5017
5018
		draw: function() {
5019
			var meta = this.getMeta();
5020
			var elements = meta.data || [];
5021
			var ilen = elements.length;
5022
			var i = 0;
5023
5024
			if (meta.dataset) {
5025
				meta.dataset.draw();
5026
			}
5027
5028
			for (; i < ilen; ++i) {
5029
				elements[i].draw();
5030
			}
5031
		},
5032
5033
		removeHoverStyle: function(element) {
5034
			helpers.merge(element._model, element.$previousStyle || {});
5035
			delete element.$previousStyle;
5036
		},
5037
5038
		setHoverStyle: function(element) {
5039
			var dataset = this.chart.data.datasets[element._datasetIndex];
5040
			var index = element._index;
5041
			var custom = element.custom || {};
5042
			var valueOrDefault = helpers.valueAtIndexOrDefault;
5043
			var getHoverColor = helpers.getHoverColor;
5044
			var model = element._model;
5045
5046
			element.$previousStyle = {
5047
				backgroundColor: model.backgroundColor,
5048
				borderColor: model.borderColor,
5049
				borderWidth: model.borderWidth
5050
			};
5051
5052
			model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
5053
			model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
5054
			model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
5055
		},
5056
5057
		/**
5058
		 * @private
5059
		 */
5060
		resyncElements: function() {
5061
			var me = this;
5062
			var meta = me.getMeta();
5063
			var data = me.getDataset().data;
5064
			var numMeta = meta.data.length;
5065
			var numData = data.length;
5066
5067
			if (numData < numMeta) {
5068
				meta.data.splice(numData, numMeta - numData);
5069
			} else if (numData > numMeta) {
5070
				me.insertElements(numMeta, numData - numMeta);
5071
			}
5072
		},
5073
5074
		/**
5075
		 * @private
5076
		 */
5077
		insertElements: function(start, count) {
5078
			for (var i = 0; i < count; ++i) {
5079
				this.addElementAndReset(start + i);
5080
			}
5081
		},
5082
5083
		/**
5084
		 * @private
5085
		 */
5086
		onDataPush: function() {
5087
			this.insertElements(this.getDataset().data.length - 1, arguments.length);
5088
		},
5089
5090
		/**
5091
		 * @private
5092
		 */
5093
		onDataPop: function() {
5094
			this.getMeta().data.pop();
5095
		},
5096
5097
		/**
5098
		 * @private
5099
		 */
5100
		onDataShift: function() {
5101
			this.getMeta().data.shift();
5102
		},
5103
5104
		/**
5105
		 * @private
5106
		 */
5107
		onDataSplice: function(start, count) {
5108
			this.getMeta().data.splice(start, count);
5109
			this.insertElements(start, arguments.length - 2);
5110
		},
5111
5112
		/**
5113
		 * @private
5114
		 */
5115
		onDataUnshift: function() {
5116
			this.insertElements(0, arguments.length);
5117
		}
5118
	});
5119
5120
	Chart.DatasetController.extend = helpers.inherits;
5121
};
5122
5123
},{"46":46}],26:[function(require,module,exports){
5124
'use strict';
5125
5126
var helpers = require(46);
5127
5128
module.exports = {
5129
	/**
5130
	 * @private
5131
	 */
5132
	_set: function(scope, values) {
5133
		return helpers.merge(this[scope] || (this[scope] = {}), values);
5134
	}
5135
};
5136
5137
},{"46":46}],27:[function(require,module,exports){
5138
'use strict';
5139
5140
var color = require(3);
5141
var helpers = require(46);
5142
5143
function interpolate(start, view, model, ease) {
5144
	var keys = Object.keys(model);
5145
	var i, ilen, key, actual, origin, target, type, c0, c1;
5146
5147
	for (i = 0, ilen = keys.length; i < ilen; ++i) {
5148
		key = keys[i];
5149
5150
		target = model[key];
5151
5152
		// if a value is added to the model after pivot() has been called, the view
5153
		// doesn't contain it, so let's initialize the view to the target value.
5154
		if (!view.hasOwnProperty(key)) {
5155
			view[key] = target;
5156
		}
5157
5158
		actual = view[key];
5159
5160
		if (actual === target || key[0] === '_') {
5161
			continue;
5162
		}
5163
5164
		if (!start.hasOwnProperty(key)) {
5165
			start[key] = actual;
5166
		}
5167
5168
		origin = start[key];
5169
5170
		type = typeof target;
5171
5172
		if (type === typeof origin) {
5173
			if (type === 'string') {
5174
				c0 = color(origin);
5175
				if (c0.valid) {
5176
					c1 = color(target);
5177
					if (c1.valid) {
5178
						view[key] = c1.mix(c0, ease).rgbString();
5179
						continue;
5180
					}
5181
				}
5182
			} else if (type === 'number' && isFinite(origin) && isFinite(target)) {
5183
				view[key] = origin + (target - origin) * ease;
5184
				continue;
5185
			}
5186
		}
5187
5188
		view[key] = target;
5189
	}
5190
}
5191
5192
var Element = function(configuration) {
5193
	helpers.extend(this, configuration);
5194
	this.initialize.apply(this, arguments);
5195
};
5196
5197
helpers.extend(Element.prototype, {
5198
5199
	initialize: function() {
5200
		this.hidden = false;
5201
	},
5202
5203
	pivot: function() {
5204
		var me = this;
5205
		if (!me._view) {
5206
			me._view = helpers.clone(me._model);
5207
		}
5208
		me._start = {};
5209
		return me;
5210
	},
5211
5212
	transition: function(ease) {
5213
		var me = this;
5214
		var model = me._model;
5215
		var start = me._start;
5216
		var view = me._view;
5217
5218
		// No animation -> No Transition
5219
		if (!model || ease === 1) {
5220
			me._view = model;
5221
			me._start = null;
5222
			return me;
5223
		}
5224
5225
		if (!view) {
5226
			view = me._view = {};
5227
		}
5228
5229
		if (!start) {
5230
			start = me._start = {};
5231
		}
5232
5233
		interpolate(start, view, model, ease);
5234
5235
		return me;
5236
	},
5237
5238
	tooltipPosition: function() {
5239
		return {
5240
			x: this._model.x,
5241
			y: this._model.y
5242
		};
5243
	},
5244
5245
	hasValue: function() {
5246
		return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
5247
	}
5248
});
5249
5250
Element.extend = helpers.inherits;
5251
5252
module.exports = Element;
5253
5254
},{"3":3,"46":46}],28:[function(require,module,exports){
5255
/* global window: false */
5256
/* global document: false */
5257
'use strict';
5258
5259
var color = require(3);
5260
var defaults = require(26);
5261
var helpers = require(46);
5262
var scaleService = require(34);
5263
5264
module.exports = function() {
5265
5266
	// -- Basic js utility methods
5267
5268
	helpers.configMerge = function(/* objects ... */) {
5269
		return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
5270
			merger: function(key, target, source, options) {
5271
				var tval = target[key] || {};
5272
				var sval = source[key];
5273
5274
				if (key === 'scales') {
5275
					// scale config merging is complex. Add our own function here for that
5276
					target[key] = helpers.scaleMerge(tval, sval);
5277
				} else if (key === 'scale') {
5278
					// used in polar area & radar charts since there is only one scale
5279
					target[key] = helpers.merge(tval, [scaleService.getScaleDefaults(sval.type), sval]);
5280
				} else {
5281
					helpers._merger(key, target, source, options);
5282
				}
5283
			}
5284
		});
5285
	};
5286
5287
	helpers.scaleMerge = function(/* objects ... */) {
5288
		return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
5289
			merger: function(key, target, source, options) {
5290
				if (key === 'xAxes' || key === 'yAxes') {
5291
					var slen = source[key].length;
5292
					var i, type, scale;
5293
5294
					if (!target[key]) {
5295
						target[key] = [];
5296
					}
5297
5298
					for (i = 0; i < slen; ++i) {
5299
						scale = source[key][i];
5300
						type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear');
5301
5302
						if (i >= target[key].length) {
5303
							target[key].push({});
5304
						}
5305
5306
						if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) {
5307
							// new/untyped scale or type changed: let's apply the new defaults
5308
							// then merge source scale to correctly overwrite the defaults.
5309
							helpers.merge(target[key][i], [scaleService.getScaleDefaults(type), scale]);
5310
						} else {
5311
							// scales type are the same
5312
							helpers.merge(target[key][i], scale);
5313
						}
5314
					}
5315
				} else {
5316
					helpers._merger(key, target, source, options);
5317
				}
5318
			}
5319
		});
5320
	};
5321
5322
	helpers.where = function(collection, filterCallback) {
5323
		if (helpers.isArray(collection) && Array.prototype.filter) {
5324
			return collection.filter(filterCallback);
5325
		}
5326
		var filtered = [];
5327
5328
		helpers.each(collection, function(item) {
5329
			if (filterCallback(item)) {
5330
				filtered.push(item);
5331
			}
5332
		});
5333
5334
		return filtered;
5335
	};
5336
	helpers.findIndex = Array.prototype.findIndex ?
5337
		function(array, callback, scope) {
5338
			return array.findIndex(callback, scope);
5339
		} :
5340
		function(array, callback, scope) {
5341
			scope = scope === undefined ? array : scope;
5342
			for (var i = 0, ilen = array.length; i < ilen; ++i) {
5343
				if (callback.call(scope, array[i], i, array)) {
5344
					return i;
5345
				}
5346
			}
5347
			return -1;
5348
		};
5349
	helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
5350
		// Default to start of the array
5351
		if (helpers.isNullOrUndef(startIndex)) {
5352
			startIndex = -1;
5353
		}
5354
		for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
5355
			var currentItem = arrayToSearch[i];
5356
			if (filterCallback(currentItem)) {
5357
				return currentItem;
5358
			}
5359
		}
5360
	};
5361
	helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
5362
		// Default to end of the array
5363
		if (helpers.isNullOrUndef(startIndex)) {
5364
			startIndex = arrayToSearch.length;
5365
		}
5366
		for (var i = startIndex - 1; i >= 0; i--) {
5367
			var currentItem = arrayToSearch[i];
5368
			if (filterCallback(currentItem)) {
5369
				return currentItem;
5370
			}
5371
		}
5372
	};
5373
5374
	// -- Math methods
5375
	helpers.isNumber = function(n) {
5376
		return !isNaN(parseFloat(n)) && isFinite(n);
5377
	};
5378
	helpers.almostEquals = function(x, y, epsilon) {
5379
		return Math.abs(x - y) < epsilon;
5380
	};
5381
	helpers.almostWhole = function(x, epsilon) {
5382
		var rounded = Math.round(x);
5383
		return (((rounded - epsilon) < x) && ((rounded + epsilon) > x));
5384
	};
5385
	helpers.max = function(array) {
5386
		return array.reduce(function(max, value) {
5387
			if (!isNaN(value)) {
5388
				return Math.max(max, value);
5389
			}
5390
			return max;
5391
		}, Number.NEGATIVE_INFINITY);
5392
	};
5393
	helpers.min = function(array) {
5394
		return array.reduce(function(min, value) {
5395
			if (!isNaN(value)) {
5396
				return Math.min(min, value);
5397
			}
5398
			return min;
5399
		}, Number.POSITIVE_INFINITY);
5400
	};
5401
	helpers.sign = Math.sign ?
5402
		function(x) {
5403
			return Math.sign(x);
5404
		} :
5405
		function(x) {
5406
			x = +x; // convert to a number
5407
			if (x === 0 || isNaN(x)) {
5408
				return x;
5409
			}
5410
			return x > 0 ? 1 : -1;
5411
		};
5412
	helpers.log10 = Math.log10 ?
5413
		function(x) {
5414
			return Math.log10(x);
5415
		} :
5416
		function(x) {
5417
			var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
5418
			// Check for whole powers of 10,
5419
			// which due to floating point rounding error should be corrected.
5420
			var powerOf10 = Math.round(exponent);
5421
			var isPowerOf10 = x === Math.pow(10, powerOf10);
5422
5423
			return isPowerOf10 ? powerOf10 : exponent;
5424
		};
5425
	helpers.toRadians = function(degrees) {
5426
		return degrees * (Math.PI / 180);
5427
	};
5428
	helpers.toDegrees = function(radians) {
5429
		return radians * (180 / Math.PI);
5430
	};
5431
	// Gets the angle from vertical upright to the point about a centre.
5432
	helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
5433
		var distanceFromXCenter = anglePoint.x - centrePoint.x;
5434
		var distanceFromYCenter = anglePoint.y - centrePoint.y;
5435
		var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
5436
5437
		var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
5438
5439
		if (angle < (-0.5 * Math.PI)) {
5440
			angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
5441
		}
5442
5443
		return {
5444
			angle: angle,
5445
			distance: radialDistanceFromCenter
5446
		};
5447
	};
5448
	helpers.distanceBetweenPoints = function(pt1, pt2) {
5449
		return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
5450
	};
5451
	helpers.aliasPixel = function(pixelWidth) {
5452
		return (pixelWidth % 2 === 0) ? 0 : 0.5;
5453
	};
5454
	helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
5455
		// Props to Rob Spencer at scaled innovation for his post on splining between points
5456
		// http://scaledinnovation.com/analytics/splines/aboutSplines.html
5457
5458
		// This function must also respect "skipped" points
5459
5460
		var previous = firstPoint.skip ? middlePoint : firstPoint;
5461
		var current = middlePoint;
5462
		var next = afterPoint.skip ? middlePoint : afterPoint;
5463
5464
		var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
5465
		var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
5466
5467
		var s01 = d01 / (d01 + d12);
5468
		var s12 = d12 / (d01 + d12);
5469
5470
		// If all points are the same, s01 & s02 will be inf
5471
		s01 = isNaN(s01) ? 0 : s01;
5472
		s12 = isNaN(s12) ? 0 : s12;
5473
5474
		var fa = t * s01; // scaling factor for triangle Ta
5475
		var fb = t * s12;
5476
5477
		return {
5478
			previous: {
5479
				x: current.x - fa * (next.x - previous.x),
5480
				y: current.y - fa * (next.y - previous.y)
5481
			},
5482
			next: {
5483
				x: current.x + fb * (next.x - previous.x),
5484
				y: current.y + fb * (next.y - previous.y)
5485
			}
5486
		};
5487
	};
5488
	helpers.EPSILON = Number.EPSILON || 1e-14;
5489
	helpers.splineCurveMonotone = function(points) {
5490
		// This function calculates Bézier control points in a similar way than |splineCurve|,
5491
		// but preserves monotonicity of the provided data and ensures no local extremums are added
5492
		// between the dataset discrete points due to the interpolation.
5493
		// See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
5494
5495
		var pointsWithTangents = (points || []).map(function(point) {
5496
			return {
5497
				model: point._model,
5498
				deltaK: 0,
5499
				mK: 0
5500
			};
5501
		});
5502
5503
		// Calculate slopes (deltaK) and initialize tangents (mK)
5504
		var pointsLen = pointsWithTangents.length;
5505
		var i, pointBefore, pointCurrent, pointAfter;
5506
		for (i = 0; i < pointsLen; ++i) {
5507
			pointCurrent = pointsWithTangents[i];
5508
			if (pointCurrent.model.skip) {
5509
				continue;
5510
			}
5511
5512
			pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
5513
			pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
5514
			if (pointAfter && !pointAfter.model.skip) {
5515
				var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);
5516
5517
				// In the case of two points that appear at the same x pixel, slopeDeltaX is 0
5518
				pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;
5519
			}
5520
5521
			if (!pointBefore || pointBefore.model.skip) {
5522
				pointCurrent.mK = pointCurrent.deltaK;
5523
			} else if (!pointAfter || pointAfter.model.skip) {
5524
				pointCurrent.mK = pointBefore.deltaK;
5525
			} else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
5526
				pointCurrent.mK = 0;
5527
			} else {
5528
				pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
5529
			}
5530
		}
5531
5532
		// Adjust tangents to ensure monotonic properties
5533
		var alphaK, betaK, tauK, squaredMagnitude;
5534
		for (i = 0; i < pointsLen - 1; ++i) {
5535
			pointCurrent = pointsWithTangents[i];
5536
			pointAfter = pointsWithTangents[i + 1];
5537
			if (pointCurrent.model.skip || pointAfter.model.skip) {
5538
				continue;
5539
			}
5540
5541
			if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
5542
				pointCurrent.mK = pointAfter.mK = 0;
5543
				continue;
5544
			}
5545
5546
			alphaK = pointCurrent.mK / pointCurrent.deltaK;
5547
			betaK = pointAfter.mK / pointCurrent.deltaK;
5548
			squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
5549
			if (squaredMagnitude <= 9) {
5550
				continue;
5551
			}
5552
5553
			tauK = 3 / Math.sqrt(squaredMagnitude);
5554
			pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
5555
			pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
5556
		}
5557
5558
		// Compute control points
5559
		var deltaX;
5560
		for (i = 0; i < pointsLen; ++i) {
5561
			pointCurrent = pointsWithTangents[i];
5562
			if (pointCurrent.model.skip) {
5563
				continue;
5564
			}
5565
5566
			pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
5567
			pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
5568
			if (pointBefore && !pointBefore.model.skip) {
5569
				deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
5570
				pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
5571
				pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
5572
			}
5573
			if (pointAfter && !pointAfter.model.skip) {
5574
				deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
5575
				pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
5576
				pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
5577
			}
5578
		}
5579
	};
5580
	helpers.nextItem = function(collection, index, loop) {
5581
		if (loop) {
5582
			return index >= collection.length - 1 ? collection[0] : collection[index + 1];
5583
		}
5584
		return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
5585
	};
5586
	helpers.previousItem = function(collection, index, loop) {
5587
		if (loop) {
5588
			return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
5589
		}
5590
		return index <= 0 ? collection[0] : collection[index - 1];
5591
	};
5592
	// Implementation of the nice number algorithm used in determining where axis labels will go
5593
	helpers.niceNum = function(range, round) {
5594
		var exponent = Math.floor(helpers.log10(range));
5595
		var fraction = range / Math.pow(10, exponent);
5596
		var niceFraction;
5597
5598
		if (round) {
5599
			if (fraction < 1.5) {
5600
				niceFraction = 1;
5601
			} else if (fraction < 3) {
5602
				niceFraction = 2;
5603
			} else if (fraction < 7) {
5604
				niceFraction = 5;
5605
			} else {
5606
				niceFraction = 10;
5607
			}
5608
		} else if (fraction <= 1.0) {
5609
			niceFraction = 1;
5610
		} else if (fraction <= 2) {
5611
			niceFraction = 2;
5612
		} else if (fraction <= 5) {
5613
			niceFraction = 5;
5614
		} else {
5615
			niceFraction = 10;
5616
		}
5617
5618
		return niceFraction * Math.pow(10, exponent);
5619
	};
5620
	// Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
5621
	helpers.requestAnimFrame = (function() {
5622
		if (typeof window === 'undefined') {
5623
			return function(callback) {
5624
				callback();
5625
			};
5626
		}
5627
		return window.requestAnimationFrame ||
5628
			window.webkitRequestAnimationFrame ||
5629
			window.mozRequestAnimationFrame ||
5630
			window.oRequestAnimationFrame ||
5631
			window.msRequestAnimationFrame ||
5632
			function(callback) {
5633
				return window.setTimeout(callback, 1000 / 60);
5634
			};
5635
	}());
5636
	// -- DOM methods
5637
	helpers.getRelativePosition = function(evt, chart) {
5638
		var mouseX, mouseY;
5639
		var e = evt.originalEvent || evt;
5640
		var canvas = evt.target || evt.srcElement;
5641
		var boundingRect = canvas.getBoundingClientRect();
5642
5643
		var touches = e.touches;
5644
		if (touches && touches.length > 0) {
5645
			mouseX = touches[0].clientX;
5646
			mouseY = touches[0].clientY;
5647
5648
		} else {
5649
			mouseX = e.clientX;
5650
			mouseY = e.clientY;
5651
		}
5652
5653
		// Scale mouse coordinates into canvas coordinates
5654
		// by following the pattern laid out by 'jerryj' in the comments of
5655
		// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
5656
		var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left'));
5657
		var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top'));
5658
		var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right'));
5659
		var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom'));
5660
		var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
5661
		var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
5662
5663
		// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
5664
		// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
5665
		mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
5666
		mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);
5667
5668
		return {
5669
			x: mouseX,
5670
			y: mouseY
5671
		};
5672
5673
	};
5674
5675
	// Private helper function to convert max-width/max-height values that may be percentages into a number
5676
	function parseMaxStyle(styleValue, node, parentProperty) {
5677
		var valueInPixels;
5678
		if (typeof styleValue === 'string') {
5679
			valueInPixels = parseInt(styleValue, 10);
5680
5681
			if (styleValue.indexOf('%') !== -1) {
5682
				// percentage * size in dimension
5683
				valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
5684
			}
5685
		} else {
5686
			valueInPixels = styleValue;
5687
		}
5688
5689
		return valueInPixels;
5690
	}
5691
5692
	/**
5693
	 * Returns if the given value contains an effective constraint.
5694
	 * @private
5695
	 */
5696
	function isConstrainedValue(value) {
5697
		return value !== undefined && value !== null && value !== 'none';
5698
	}
5699
5700
	// Private helper to get a constraint dimension
5701
	// @param domNode : the node to check the constraint on
5702
	// @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight)
5703
	// @param percentageProperty : property of parent to use when calculating width as a percentage
5704
	// @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser
5705
	function getConstraintDimension(domNode, maxStyle, percentageProperty) {
5706
		var view = document.defaultView;
5707
		var parentNode = helpers._getParentNode(domNode);
5708
		var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
5709
		var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
5710
		var hasCNode = isConstrainedValue(constrainedNode);
5711
		var hasCContainer = isConstrainedValue(constrainedContainer);
5712
		var infinity = Number.POSITIVE_INFINITY;
5713
5714
		if (hasCNode || hasCContainer) {
5715
			return Math.min(
5716
				hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
5717
				hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
5718
		}
5719
5720
		return 'none';
5721
	}
5722
	// returns Number or undefined if no constraint
5723
	helpers.getConstraintWidth = function(domNode) {
5724
		return getConstraintDimension(domNode, 'max-width', 'clientWidth');
5725
	};
5726
	// returns Number or undefined if no constraint
5727
	helpers.getConstraintHeight = function(domNode) {
5728
		return getConstraintDimension(domNode, 'max-height', 'clientHeight');
5729
	};
5730
	/**
5731
	 * @private
5732
 	 */
5733
	helpers._calculatePadding = function(container, padding, parentDimension) {
5734
		padding = helpers.getStyle(container, padding);
5735
5736
		return padding.indexOf('%') > -1 ? parentDimension / parseInt(padding, 10) : parseInt(padding, 10);
5737
	};
5738
	/**
5739
	 * @private
5740
	 */
5741
	helpers._getParentNode = function(domNode) {
5742
		var parent = domNode.parentNode;
5743
		if (parent && parent.host) {
5744
			parent = parent.host;
5745
		}
5746
		return parent;
5747
	};
5748
	helpers.getMaximumWidth = function(domNode) {
5749
		var container = helpers._getParentNode(domNode);
5750
		if (!container) {
5751
			return domNode.clientWidth;
5752
		}
5753
5754
		var clientWidth = container.clientWidth;
5755
		var paddingLeft = helpers._calculatePadding(container, 'padding-left', clientWidth);
5756
		var paddingRight = helpers._calculatePadding(container, 'padding-right', clientWidth);
5757
5758
		var w = clientWidth - paddingLeft - paddingRight;
5759
		var cw = helpers.getConstraintWidth(domNode);
5760
		return isNaN(cw) ? w : Math.min(w, cw);
5761
	};
5762
	helpers.getMaximumHeight = function(domNode) {
5763
		var container = helpers._getParentNode(domNode);
5764
		if (!container) {
5765
			return domNode.clientHeight;
5766
		}
5767
5768
		var clientHeight = container.clientHeight;
5769
		var paddingTop = helpers._calculatePadding(container, 'padding-top', clientHeight);
5770
		var paddingBottom = helpers._calculatePadding(container, 'padding-bottom', clientHeight);
5771
5772
		var h = clientHeight - paddingTop - paddingBottom;
5773
		var ch = helpers.getConstraintHeight(domNode);
5774
		return isNaN(ch) ? h : Math.min(h, ch);
5775
	};
5776
	helpers.getStyle = function(el, property) {
5777
		return el.currentStyle ?
5778
			el.currentStyle[property] :
5779
			document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
5780
	};
5781
	helpers.retinaScale = function(chart, forceRatio) {
5782
		var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1;
5783
		if (pixelRatio === 1) {
5784
			return;
5785
		}
5786
5787
		var canvas = chart.canvas;
5788
		var height = chart.height;
5789
		var width = chart.width;
5790
5791
		canvas.height = height * pixelRatio;
5792
		canvas.width = width * pixelRatio;
5793
		chart.ctx.scale(pixelRatio, pixelRatio);
5794
5795
		// If no style has been set on the canvas, the render size is used as display size,
5796
		// making the chart visually bigger, so let's enforce it to the "correct" values.
5797
		// See https://github.com/chartjs/Chart.js/issues/3575
5798
		if (!canvas.style.height && !canvas.style.width) {
5799
			canvas.style.height = height + 'px';
5800
			canvas.style.width = width + 'px';
5801
		}
5802
	};
5803
	// -- Canvas methods
5804
	helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
5805
		return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
5806
	};
5807
	helpers.longestText = function(ctx, font, arrayOfThings, cache) {
5808
		cache = cache || {};
5809
		var data = cache.data = cache.data || {};
5810
		var gc = cache.garbageCollect = cache.garbageCollect || [];
5811
5812
		if (cache.font !== font) {
5813
			data = cache.data = {};
5814
			gc = cache.garbageCollect = [];
5815
			cache.font = font;
5816
		}
5817
5818
		ctx.font = font;
5819
		var longest = 0;
5820
		helpers.each(arrayOfThings, function(thing) {
5821
			// Undefined strings and arrays should not be measured
5822
			if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) {
5823
				longest = helpers.measureText(ctx, data, gc, longest, thing);
5824
			} else if (helpers.isArray(thing)) {
5825
				// if it is an array lets measure each element
5826
				// to do maybe simplify this function a bit so we can do this more recursively?
5827
				helpers.each(thing, function(nestedThing) {
5828
					// Undefined strings and arrays should not be measured
5829
					if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) {
5830
						longest = helpers.measureText(ctx, data, gc, longest, nestedThing);
5831
					}
5832
				});
5833
			}
5834
		});
5835
5836
		var gcLen = gc.length / 2;
5837
		if (gcLen > arrayOfThings.length) {
5838
			for (var i = 0; i < gcLen; i++) {
5839
				delete data[gc[i]];
5840
			}
5841
			gc.splice(0, gcLen);
5842
		}
5843
		return longest;
5844
	};
5845
	helpers.measureText = function(ctx, data, gc, longest, string) {
5846
		var textWidth = data[string];
5847
		if (!textWidth) {
5848
			textWidth = data[string] = ctx.measureText(string).width;
5849
			gc.push(string);
5850
		}
5851
		if (textWidth > longest) {
5852
			longest = textWidth;
5853
		}
5854
		return longest;
5855
	};
5856
	helpers.numberOfLabelLines = function(arrayOfThings) {
5857
		var numberOfLines = 1;
5858
		helpers.each(arrayOfThings, function(thing) {
5859
			if (helpers.isArray(thing)) {
5860
				if (thing.length > numberOfLines) {
5861
					numberOfLines = thing.length;
5862
				}
5863
			}
5864
		});
5865
		return numberOfLines;
5866
	};
5867
5868
	helpers.color = !color ?
5869
		function(value) {
5870
			console.error('Color.js not found!');
5871
			return value;
5872
		} :
5873
		function(value) {
5874
			/* global CanvasGradient */
5875
			if (value instanceof CanvasGradient) {
5876
				value = defaults.global.defaultColor;
5877
			}
5878
5879
			return color(value);
5880
		};
5881
5882
	helpers.getHoverColor = function(colorValue) {
5883
		/* global CanvasPattern */
5884
		return (colorValue instanceof CanvasPattern) ?
5885
			colorValue :
5886
			helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
5887
	};
5888
};
5889
5890
},{"26":26,"3":3,"34":34,"46":46}],29:[function(require,module,exports){
5891
'use strict';
5892
5893
var helpers = require(46);
5894
5895
/**
5896
 * Helper function to get relative position for an event
5897
 * @param {Event|IEvent} event - The event to get the position for
5898
 * @param {Chart} chart - The chart
5899
 * @returns {Point} the event position
5900
 */
5901
function getRelativePosition(e, chart) {
5902
	if (e.native) {
5903
		return {
5904
			x: e.x,
5905
			y: e.y
5906
		};
5907
	}
5908
5909
	return helpers.getRelativePosition(e, chart);
5910
}
5911
5912
/**
5913
 * Helper function to traverse all of the visible elements in the chart
5914
 * @param chart {chart} the chart
5915
 * @param handler {Function} the callback to execute for each visible item
5916
 */
5917
function parseVisibleItems(chart, handler) {
5918
	var datasets = chart.data.datasets;
5919
	var meta, i, j, ilen, jlen;
5920
5921
	for (i = 0, ilen = datasets.length; i < ilen; ++i) {
5922
		if (!chart.isDatasetVisible(i)) {
5923
			continue;
5924
		}
5925
5926
		meta = chart.getDatasetMeta(i);
5927
		for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
5928
			var element = meta.data[j];
5929
			if (!element._view.skip) {
5930
				handler(element);
5931
			}
5932
		}
5933
	}
5934
}
5935
5936
/**
5937
 * Helper function to get the items that intersect the event position
5938
 * @param items {ChartElement[]} elements to filter
5939
 * @param position {Point} the point to be nearest to
5940
 * @return {ChartElement[]} the nearest items
5941
 */
5942
function getIntersectItems(chart, position) {
5943
	var elements = [];
5944
5945
	parseVisibleItems(chart, function(element) {
5946
		if (element.inRange(position.x, position.y)) {
5947
			elements.push(element);
5948
		}
5949
	});
5950
5951
	return elements;
5952
}
5953
5954
/**
5955
 * Helper function to get the items nearest to the event position considering all visible items in teh chart
5956
 * @param chart {Chart} the chart to look at elements from
5957
 * @param position {Point} the point to be nearest to
5958
 * @param intersect {Boolean} if true, only consider items that intersect the position
5959
 * @param distanceMetric {Function} function to provide the distance between points
5960
 * @return {ChartElement[]} the nearest items
5961
 */
5962
function getNearestItems(chart, position, intersect, distanceMetric) {
5963
	var minDistance = Number.POSITIVE_INFINITY;
5964
	var nearestItems = [];
5965
5966
	parseVisibleItems(chart, function(element) {
5967
		if (intersect && !element.inRange(position.x, position.y)) {
5968
			return;
5969
		}
5970
5971
		var center = element.getCenterPoint();
5972
		var distance = distanceMetric(position, center);
5973
5974
		if (distance < minDistance) {
5975
			nearestItems = [element];
5976
			minDistance = distance;
5977
		} else if (distance === minDistance) {
5978
			// Can have multiple items at the same distance in which case we sort by size
5979
			nearestItems.push(element);
5980
		}
5981
	});
5982
5983
	return nearestItems;
5984
}
5985
5986
/**
5987
 * Get a distance metric function for two points based on the
5988
 * axis mode setting
5989
 * @param {String} axis the axis mode. x|y|xy
5990
 */
5991
function getDistanceMetricForAxis(axis) {
5992
	var useX = axis.indexOf('x') !== -1;
5993
	var useY = axis.indexOf('y') !== -1;
5994
5995
	return function(pt1, pt2) {
5996
		var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
5997
		var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
5998
		return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
5999
	};
6000
}
6001
6002
function indexMode(chart, e, options) {
6003
	var position = getRelativePosition(e, chart);
6004
	// Default axis for index mode is 'x' to match old behaviour
6005
	options.axis = options.axis || 'x';
6006
	var distanceMetric = getDistanceMetricForAxis(options.axis);
6007
	var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
6008
	var elements = [];
6009
6010
	if (!items.length) {
6011
		return [];
6012
	}
6013
6014
	chart.data.datasets.forEach(function(dataset, datasetIndex) {
6015
		if (chart.isDatasetVisible(datasetIndex)) {
6016
			var meta = chart.getDatasetMeta(datasetIndex);
6017
			var element = meta.data[items[0]._index];
6018
6019
			// don't count items that are skipped (null data)
6020
			if (element && !element._view.skip) {
6021
				elements.push(element);
6022
			}
6023
		}
6024
	});
6025
6026
	return elements;
6027
}
6028
6029
/**
6030
 * @interface IInteractionOptions
6031
 */
6032
/**
6033
 * If true, only consider items that intersect the point
6034
 * @name IInterfaceOptions#boolean
6035
 * @type Boolean
6036
 */
6037
6038
/**
6039
 * Contains interaction related functions
6040
 * @namespace Chart.Interaction
6041
 */
6042
module.exports = {
6043
	// Helper function for different modes
6044
	modes: {
6045
		single: function(chart, e) {
6046
			var position = getRelativePosition(e, chart);
6047
			var elements = [];
6048
6049
			parseVisibleItems(chart, function(element) {
6050
				if (element.inRange(position.x, position.y)) {
6051
					elements.push(element);
6052
					return elements;
6053
				}
6054
			});
6055
6056
			return elements.slice(0, 1);
6057
		},
6058
6059
		/**
6060
		 * @function Chart.Interaction.modes.label
6061
		 * @deprecated since version 2.4.0
6062
		 * @todo remove at version 3
6063
		 * @private
6064
		 */
6065
		label: indexMode,
6066
6067
		/**
6068
		 * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
6069
		 * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
6070
		 * @function Chart.Interaction.modes.index
6071
		 * @since v2.4.0
6072
		 * @param chart {chart} the chart we are returning items from
6073
		 * @param e {Event} the event we are find things at
6074
		 * @param options {IInteractionOptions} options to use during interaction
6075
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6076
		 */
6077
		index: indexMode,
6078
6079
		/**
6080
		 * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
6081
		 * If the options.intersect is false, we find the nearest item and return the items in that dataset
6082
		 * @function Chart.Interaction.modes.dataset
6083
		 * @param chart {chart} the chart we are returning items from
6084
		 * @param e {Event} the event we are find things at
6085
		 * @param options {IInteractionOptions} options to use during interaction
6086
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6087
		 */
6088
		dataset: function(chart, e, options) {
6089
			var position = getRelativePosition(e, chart);
6090
			options.axis = options.axis || 'xy';
6091
			var distanceMetric = getDistanceMetricForAxis(options.axis);
6092
			var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
6093
6094
			if (items.length > 0) {
6095
				items = chart.getDatasetMeta(items[0]._datasetIndex).data;
6096
			}
6097
6098
			return items;
6099
		},
6100
6101
		/**
6102
		 * @function Chart.Interaction.modes.x-axis
6103
		 * @deprecated since version 2.4.0. Use index mode and intersect == true
6104
		 * @todo remove at version 3
6105
		 * @private
6106
		 */
6107
		'x-axis': function(chart, e) {
6108
			return indexMode(chart, e, {intersect: false});
6109
		},
6110
6111
		/**
6112
		 * Point mode returns all elements that hit test based on the event position
6113
		 * of the event
6114
		 * @function Chart.Interaction.modes.intersect
6115
		 * @param chart {chart} the chart we are returning items from
6116
		 * @param e {Event} the event we are find things at
6117
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6118
		 */
6119
		point: function(chart, e) {
6120
			var position = getRelativePosition(e, chart);
6121
			return getIntersectItems(chart, position);
6122
		},
6123
6124
		/**
6125
		 * nearest mode returns the element closest to the point
6126
		 * @function Chart.Interaction.modes.intersect
6127
		 * @param chart {chart} the chart we are returning items from
6128
		 * @param e {Event} the event we are find things at
6129
		 * @param options {IInteractionOptions} options to use
6130
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6131
		 */
6132
		nearest: function(chart, e, options) {
6133
			var position = getRelativePosition(e, chart);
6134
			options.axis = options.axis || 'xy';
6135
			var distanceMetric = getDistanceMetricForAxis(options.axis);
6136
			var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric);
6137
6138
			// We have multiple items at the same distance from the event. Now sort by smallest
6139
			if (nearestItems.length > 1) {
6140
				nearestItems.sort(function(a, b) {
6141
					var sizeA = a.getArea();
6142
					var sizeB = b.getArea();
6143
					var ret = sizeA - sizeB;
6144
6145
					if (ret === 0) {
6146
						// if equal sort by dataset index
6147
						ret = a._datasetIndex - b._datasetIndex;
6148
					}
6149
6150
					return ret;
6151
				});
6152
			}
6153
6154
			// Return only 1 item
6155
			return nearestItems.slice(0, 1);
6156
		},
6157
6158
		/**
6159
		 * x mode returns the elements that hit-test at the current x coordinate
6160
		 * @function Chart.Interaction.modes.x
6161
		 * @param chart {chart} the chart we are returning items from
6162
		 * @param e {Event} the event we are find things at
6163
		 * @param options {IInteractionOptions} options to use
6164
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6165
		 */
6166
		x: function(chart, e, options) {
6167
			var position = getRelativePosition(e, chart);
6168
			var items = [];
6169
			var intersectsItem = false;
6170
6171
			parseVisibleItems(chart, function(element) {
6172
				if (element.inXRange(position.x)) {
6173
					items.push(element);
6174
				}
6175
6176
				if (element.inRange(position.x, position.y)) {
6177
					intersectsItem = true;
6178
				}
6179
			});
6180
6181
			// If we want to trigger on an intersect and we don't have any items
6182
			// that intersect the position, return nothing
6183
			if (options.intersect && !intersectsItem) {
6184
				items = [];
6185
			}
6186
			return items;
6187
		},
6188
6189
		/**
6190
		 * y mode returns the elements that hit-test at the current y coordinate
6191
		 * @function Chart.Interaction.modes.y
6192
		 * @param chart {chart} the chart we are returning items from
6193
		 * @param e {Event} the event we are find things at
6194
		 * @param options {IInteractionOptions} options to use
6195
		 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6196
		 */
6197
		y: function(chart, e, options) {
6198
			var position = getRelativePosition(e, chart);
6199
			var items = [];
6200
			var intersectsItem = false;
6201
6202
			parseVisibleItems(chart, function(element) {
6203
				if (element.inYRange(position.y)) {
6204
					items.push(element);
6205
				}
6206
6207
				if (element.inRange(position.x, position.y)) {
6208
					intersectsItem = true;
6209
				}
6210
			});
6211
6212
			// If we want to trigger on an intersect and we don't have any items
6213
			// that intersect the position, return nothing
6214
			if (options.intersect && !intersectsItem) {
6215
				items = [];
6216
			}
6217
			return items;
6218
		}
6219
	}
6220
};
6221
6222
},{"46":46}],30:[function(require,module,exports){
6223
'use strict';
6224
6225
var defaults = require(26);
6226
6227
defaults._set('global', {
6228
	responsive: true,
6229
	responsiveAnimationDuration: 0,
6230
	maintainAspectRatio: true,
6231
	events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
6232
	hover: {
6233
		onHover: null,
6234
		mode: 'nearest',
6235
		intersect: true,
6236
		animationDuration: 400
6237
	},
6238
	onClick: null,
6239
	defaultColor: 'rgba(0,0,0,0.1)',
6240
	defaultFontColor: '#666',
6241
	defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
6242
	defaultFontSize: 12,
6243
	defaultFontStyle: 'normal',
6244
	showLines: true,
6245
6246
	// Element defaults defined in element extensions
6247
	elements: {},
6248
6249
	// Layout options such as padding
6250
	layout: {
6251
		padding: {
6252
			top: 0,
6253
			right: 0,
6254
			bottom: 0,
6255
			left: 0
6256
		}
6257
	}
6258
});
6259
6260
module.exports = function() {
6261
6262
	// Occupy the global variable of Chart, and create a simple base class
6263
	var Chart = function(item, config) {
6264
		this.construct(item, config);
6265
		return this;
6266
	};
6267
6268
	Chart.Chart = Chart;
6269
6270
	return Chart;
6271
};
6272
6273
},{"26":26}],31:[function(require,module,exports){
6274
'use strict';
6275
6276
var helpers = require(46);
6277
6278
function filterByPosition(array, position) {
6279
	return helpers.where(array, function(v) {
6280
		return v.position === position;
6281
	});
6282
}
6283
6284
function sortByWeight(array, reverse) {
6285
	array.forEach(function(v, i) {
6286
		v._tmpIndex_ = i;
6287
		return v;
6288
	});
6289
	array.sort(function(a, b) {
6290
		var v0 = reverse ? b : a;
6291
		var v1 = reverse ? a : b;
6292
		return v0.weight === v1.weight ?
6293
			v0._tmpIndex_ - v1._tmpIndex_ :
6294
			v0.weight - v1.weight;
6295
	});
6296
	array.forEach(function(v) {
6297
		delete v._tmpIndex_;
6298
	});
6299
}
6300
6301
/**
6302
 * @interface ILayoutItem
6303
 * @prop {String} position - The position of the item in the chart layout. Possible values are
6304
 * 'left', 'top', 'right', 'bottom', and 'chartArea'
6305
 * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area
6306
 * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down
6307
 * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)
6308
 * @prop {Function} update - Takes two parameters: width and height. Returns size of item
6309
 * @prop {Function} getPadding -  Returns an object with padding on the edges
6310
 * @prop {Number} width - Width of item. Must be valid after update()
6311
 * @prop {Number} height - Height of item. Must be valid after update()
6312
 * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update
6313
 * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update
6314
 * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update
6315
 * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update
6316
 */
6317
6318
// The layout service is very self explanatory.  It's responsible for the layout within a chart.
6319
// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
6320
// It is this service's responsibility of carrying out that layout.
6321
module.exports = {
6322
	defaults: {},
6323
6324
	/**
6325
	 * Register a box to a chart.
6326
	 * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
6327
	 * @param {Chart} chart - the chart to use
6328
	 * @param {ILayoutItem} item - the item to add to be layed out
6329
	 */
6330
	addBox: function(chart, item) {
6331
		if (!chart.boxes) {
6332
			chart.boxes = [];
6333
		}
6334
6335
		// initialize item with default values
6336
		item.fullWidth = item.fullWidth || false;
6337
		item.position = item.position || 'top';
6338
		item.weight = item.weight || 0;
6339
6340
		chart.boxes.push(item);
6341
	},
6342
6343
	/**
6344
	 * Remove a layoutItem from a chart
6345
	 * @param {Chart} chart - the chart to remove the box from
6346
	 * @param {Object} layoutItem - the item to remove from the layout
6347
	 */
6348
	removeBox: function(chart, layoutItem) {
6349
		var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
6350
		if (index !== -1) {
6351
			chart.boxes.splice(index, 1);
6352
		}
6353
	},
6354
6355
	/**
6356
	 * Sets (or updates) options on the given `item`.
6357
	 * @param {Chart} chart - the chart in which the item lives (or will be added to)
6358
	 * @param {Object} item - the item to configure with the given options
6359
	 * @param {Object} options - the new item options.
6360
	 */
6361
	configure: function(chart, item, options) {
6362
		var props = ['fullWidth', 'position', 'weight'];
6363
		var ilen = props.length;
6364
		var i = 0;
6365
		var prop;
6366
6367
		for (; i < ilen; ++i) {
6368
			prop = props[i];
6369
			if (options.hasOwnProperty(prop)) {
6370
				item[prop] = options[prop];
6371
			}
6372
		}
6373
	},
6374
6375
	/**
6376
	 * Fits boxes of the given chart into the given size by having each box measure itself
6377
	 * then running a fitting algorithm
6378
	 * @param {Chart} chart - the chart
6379
	 * @param {Number} width - the width to fit into
6380
	 * @param {Number} height - the height to fit into
6381
	 */
6382
	update: function(chart, width, height) {
6383
		if (!chart) {
6384
			return;
6385
		}
6386
6387
		var layoutOptions = chart.options.layout || {};
6388
		var padding = helpers.options.toPadding(layoutOptions.padding);
6389
		var leftPadding = padding.left;
6390
		var rightPadding = padding.right;
6391
		var topPadding = padding.top;
6392
		var bottomPadding = padding.bottom;
6393
6394
		var leftBoxes = filterByPosition(chart.boxes, 'left');
6395
		var rightBoxes = filterByPosition(chart.boxes, 'right');
6396
		var topBoxes = filterByPosition(chart.boxes, 'top');
6397
		var bottomBoxes = filterByPosition(chart.boxes, 'bottom');
6398
		var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea');
6399
6400
		// Sort boxes by weight. A higher weight is further away from the chart area
6401
		sortByWeight(leftBoxes, true);
6402
		sortByWeight(rightBoxes, false);
6403
		sortByWeight(topBoxes, true);
6404
		sortByWeight(bottomBoxes, false);
6405
6406
		// Essentially we now have any number of boxes on each of the 4 sides.
6407
		// Our canvas looks like the following.
6408
		// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
6409
		// B1 is the bottom axis
6410
		// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
6411
		// These locations are single-box locations only, when trying to register a chartArea location that is already taken,
6412
		// an error will be thrown.
6413
		//
6414
		// |----------------------------------------------------|
6415
		// |                  T1 (Full Width)                   |
6416
		// |----------------------------------------------------|
6417
		// |    |    |                 T2                  |    |
6418
		// |    |----|-------------------------------------|----|
6419
		// |    |    | C1 |                           | C2 |    |
6420
		// |    |    |----|                           |----|    |
6421
		// |    |    |                                     |    |
6422
		// | L1 | L2 |           ChartArea (C0)            | R1 |
6423
		// |    |    |                                     |    |
6424
		// |    |    |----|                           |----|    |
6425
		// |    |    | C3 |                           | C4 |    |
6426
		// |    |----|-------------------------------------|----|
6427
		// |    |    |                 B1                  |    |
6428
		// |----------------------------------------------------|
6429
		// |                  B2 (Full Width)                   |
6430
		// |----------------------------------------------------|
6431
		//
6432
		// What we do to find the best sizing, we do the following
6433
		// 1. Determine the minimum size of the chart area.
6434
		// 2. Split the remaining width equally between each vertical axis
6435
		// 3. Split the remaining height equally between each horizontal axis
6436
		// 4. Give each layout the maximum size it can be. The layout will return it's minimum size
6437
		// 5. Adjust the sizes of each axis based on it's minimum reported size.
6438
		// 6. Refit each axis
6439
		// 7. Position each axis in the final location
6440
		// 8. Tell the chart the final location of the chart area
6441
		// 9. Tell any axes that overlay the chart area the positions of the chart area
6442
6443
		// Step 1
6444
		var chartWidth = width - leftPadding - rightPadding;
6445
		var chartHeight = height - topPadding - bottomPadding;
6446
		var chartAreaWidth = chartWidth / 2; // min 50%
6447
		var chartAreaHeight = chartHeight / 2; // min 50%
6448
6449
		// Step 2
6450
		var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);
6451
6452
		// Step 3
6453
		var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);
6454
6455
		// Step 4
6456
		var maxChartAreaWidth = chartWidth;
6457
		var maxChartAreaHeight = chartHeight;
6458
		var minBoxSizes = [];
6459
6460
		function getMinimumBoxSize(box) {
6461
			var minSize;
6462
			var isHorizontal = box.isHorizontal();
6463
6464
			if (isHorizontal) {
6465
				minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
6466
				maxChartAreaHeight -= minSize.height;
6467
			} else {
6468
				minSize = box.update(verticalBoxWidth, maxChartAreaHeight);
6469
				maxChartAreaWidth -= minSize.width;
6470
			}
6471
6472
			minBoxSizes.push({
6473
				horizontal: isHorizontal,
6474
				minSize: minSize,
6475
				box: box,
6476
			});
6477
		}
6478
6479
		helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);
6480
6481
		// If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478)
6482
		var maxHorizontalLeftPadding = 0;
6483
		var maxHorizontalRightPadding = 0;
6484
		var maxVerticalTopPadding = 0;
6485
		var maxVerticalBottomPadding = 0;
6486
6487
		helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) {
6488
			if (horizontalBox.getPadding) {
6489
				var boxPadding = horizontalBox.getPadding();
6490
				maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left);
6491
				maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right);
6492
			}
6493
		});
6494
6495
		helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) {
6496
			if (verticalBox.getPadding) {
6497
				var boxPadding = verticalBox.getPadding();
6498
				maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top);
6499
				maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom);
6500
			}
6501
		});
6502
6503
		// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
6504
		// be if the axes are drawn at their minimum sizes.
6505
		// Steps 5 & 6
6506
		var totalLeftBoxesWidth = leftPadding;
6507
		var totalRightBoxesWidth = rightPadding;
6508
		var totalTopBoxesHeight = topPadding;
6509
		var totalBottomBoxesHeight = bottomPadding;
6510
6511
		// Function to fit a box
6512
		function fitBox(box) {
6513
			var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) {
6514
				return minBox.box === box;
6515
			});
6516
6517
			if (minBoxSize) {
6518
				if (box.isHorizontal()) {
6519
					var scaleMargin = {
6520
						left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding),
6521
						right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding),
6522
						top: 0,
6523
						bottom: 0
6524
					};
6525
6526
					// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
6527
					// on the margin. Sometimes they need to increase in size slightly
6528
					box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
6529
				} else {
6530
					box.update(minBoxSize.minSize.width, maxChartAreaHeight);
6531
				}
6532
			}
6533
		}
6534
6535
		// Update, and calculate the left and right margins for the horizontal boxes
6536
		helpers.each(leftBoxes.concat(rightBoxes), fitBox);
6537
6538
		helpers.each(leftBoxes, function(box) {
6539
			totalLeftBoxesWidth += box.width;
6540
		});
6541
6542
		helpers.each(rightBoxes, function(box) {
6543
			totalRightBoxesWidth += box.width;
6544
		});
6545
6546
		// Set the Left and Right margins for the horizontal boxes
6547
		helpers.each(topBoxes.concat(bottomBoxes), fitBox);
6548
6549
		// Figure out how much margin is on the top and bottom of the vertical boxes
6550
		helpers.each(topBoxes, function(box) {
6551
			totalTopBoxesHeight += box.height;
6552
		});
6553
6554
		helpers.each(bottomBoxes, function(box) {
6555
			totalBottomBoxesHeight += box.height;
6556
		});
6557
6558
		function finalFitVerticalBox(box) {
6559
			var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) {
6560
				return minSize.box === box;
6561
			});
6562
6563
			var scaleMargin = {
6564
				left: 0,
6565
				right: 0,
6566
				top: totalTopBoxesHeight,
6567
				bottom: totalBottomBoxesHeight
6568
			};
6569
6570
			if (minBoxSize) {
6571
				box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
6572
			}
6573
		}
6574
6575
		// Let the left layout know the final margin
6576
		helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);
6577
6578
		// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
6579
		totalLeftBoxesWidth = leftPadding;
6580
		totalRightBoxesWidth = rightPadding;
6581
		totalTopBoxesHeight = topPadding;
6582
		totalBottomBoxesHeight = bottomPadding;
6583
6584
		helpers.each(leftBoxes, function(box) {
6585
			totalLeftBoxesWidth += box.width;
6586
		});
6587
6588
		helpers.each(rightBoxes, function(box) {
6589
			totalRightBoxesWidth += box.width;
6590
		});
6591
6592
		helpers.each(topBoxes, function(box) {
6593
			totalTopBoxesHeight += box.height;
6594
		});
6595
		helpers.each(bottomBoxes, function(box) {
6596
			totalBottomBoxesHeight += box.height;
6597
		});
6598
6599
		// We may be adding some padding to account for rotated x axis labels
6600
		var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0);
6601
		totalLeftBoxesWidth += leftPaddingAddition;
6602
		totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0);
6603
6604
		var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0);
6605
		totalTopBoxesHeight += topPaddingAddition;
6606
		totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0);
6607
6608
		// Figure out if our chart area changed. This would occur if the dataset layout label rotation
6609
		// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
6610
		// without calling `fit` again
6611
		var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
6612
		var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;
6613
6614
		if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
6615
			helpers.each(leftBoxes, function(box) {
6616
				box.height = newMaxChartAreaHeight;
6617
			});
6618
6619
			helpers.each(rightBoxes, function(box) {
6620
				box.height = newMaxChartAreaHeight;
6621
			});
6622
6623
			helpers.each(topBoxes, function(box) {
6624
				if (!box.fullWidth) {
6625
					box.width = newMaxChartAreaWidth;
6626
				}
6627
			});
6628
6629
			helpers.each(bottomBoxes, function(box) {
6630
				if (!box.fullWidth) {
6631
					box.width = newMaxChartAreaWidth;
6632
				}
6633
			});
6634
6635
			maxChartAreaHeight = newMaxChartAreaHeight;
6636
			maxChartAreaWidth = newMaxChartAreaWidth;
6637
		}
6638
6639
		// Step 7 - Position the boxes
6640
		var left = leftPadding + leftPaddingAddition;
6641
		var top = topPadding + topPaddingAddition;
6642
6643
		function placeBox(box) {
6644
			if (box.isHorizontal()) {
6645
				box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth;
6646
				box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth;
6647
				box.top = top;
6648
				box.bottom = top + box.height;
6649
6650
				// Move to next point
6651
				top = box.bottom;
6652
6653
			} else {
6654
6655
				box.left = left;
6656
				box.right = left + box.width;
6657
				box.top = totalTopBoxesHeight;
6658
				box.bottom = totalTopBoxesHeight + maxChartAreaHeight;
6659
6660
				// Move to next point
6661
				left = box.right;
6662
			}
6663
		}
6664
6665
		helpers.each(leftBoxes.concat(topBoxes), placeBox);
6666
6667
		// Account for chart width and height
6668
		left += maxChartAreaWidth;
6669
		top += maxChartAreaHeight;
6670
6671
		helpers.each(rightBoxes, placeBox);
6672
		helpers.each(bottomBoxes, placeBox);
6673
6674
		// Step 8
6675
		chart.chartArea = {
6676
			left: totalLeftBoxesWidth,
6677
			top: totalTopBoxesHeight,
6678
			right: totalLeftBoxesWidth + maxChartAreaWidth,
6679
			bottom: totalTopBoxesHeight + maxChartAreaHeight
6680
		};
6681
6682
		// Step 9
6683
		helpers.each(chartAreaBoxes, function(box) {
6684
			box.left = chart.chartArea.left;
6685
			box.top = chart.chartArea.top;
6686
			box.right = chart.chartArea.right;
6687
			box.bottom = chart.chartArea.bottom;
6688
6689
			box.update(maxChartAreaWidth, maxChartAreaHeight);
6690
		});
6691
	}
6692
};
6693
6694
},{"46":46}],32:[function(require,module,exports){
6695
'use strict';
6696
6697
var defaults = require(26);
6698
var helpers = require(46);
6699
6700
defaults._set('global', {
6701
	plugins: {}
6702
});
6703
6704
/**
6705
 * The plugin service singleton
6706
 * @namespace Chart.plugins
6707
 * @since 2.1.0
6708
 */
6709
module.exports = {
6710
	/**
6711
	 * Globally registered plugins.
6712
	 * @private
6713
	 */
6714
	_plugins: [],
6715
6716
	/**
6717
	 * This identifier is used to invalidate the descriptors cache attached to each chart
6718
	 * when a global plugin is registered or unregistered. In this case, the cache ID is
6719
	 * incremented and descriptors are regenerated during following API calls.
6720
	 * @private
6721
	 */
6722
	_cacheId: 0,
6723
6724
	/**
6725
	 * Registers the given plugin(s) if not already registered.
6726
	 * @param {Array|Object} plugins plugin instance(s).
6727
	 */
6728
	register: function(plugins) {
6729
		var p = this._plugins;
6730
		([]).concat(plugins).forEach(function(plugin) {
6731
			if (p.indexOf(plugin) === -1) {
6732
				p.push(plugin);
6733
			}
6734
		});
6735
6736
		this._cacheId++;
6737
	},
6738
6739
	/**
6740
	 * Unregisters the given plugin(s) only if registered.
6741
	 * @param {Array|Object} plugins plugin instance(s).
6742
	 */
6743
	unregister: function(plugins) {
6744
		var p = this._plugins;
6745
		([]).concat(plugins).forEach(function(plugin) {
6746
			var idx = p.indexOf(plugin);
6747
			if (idx !== -1) {
6748
				p.splice(idx, 1);
6749
			}
6750
		});
6751
6752
		this._cacheId++;
6753
	},
6754
6755
	/**
6756
	 * Remove all registered plugins.
6757
	 * @since 2.1.5
6758
	 */
6759
	clear: function() {
6760
		this._plugins = [];
6761
		this._cacheId++;
6762
	},
6763
6764
	/**
6765
	 * Returns the number of registered plugins?
6766
	 * @returns {Number}
6767
	 * @since 2.1.5
6768
	 */
6769
	count: function() {
6770
		return this._plugins.length;
6771
	},
6772
6773
	/**
6774
	 * Returns all registered plugin instances.
6775
	 * @returns {Array} array of plugin objects.
6776
	 * @since 2.1.5
6777
	 */
6778
	getAll: function() {
6779
		return this._plugins;
6780
	},
6781
6782
	/**
6783
	 * Calls enabled plugins for `chart` on the specified hook and with the given args.
6784
	 * This method immediately returns as soon as a plugin explicitly returns false. The
6785
	 * returned value can be used, for instance, to interrupt the current action.
6786
	 * @param {Object} chart - The chart instance for which plugins should be called.
6787
	 * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
6788
	 * @param {Array} [args] - Extra arguments to apply to the hook call.
6789
	 * @returns {Boolean} false if any of the plugins return false, else returns true.
6790
	 */
6791
	notify: function(chart, hook, args) {
6792
		var descriptors = this.descriptors(chart);
6793
		var ilen = descriptors.length;
6794
		var i, descriptor, plugin, params, method;
6795
6796
		for (i = 0; i < ilen; ++i) {
6797
			descriptor = descriptors[i];
6798
			plugin = descriptor.plugin;
6799
			method = plugin[hook];
6800
			if (typeof method === 'function') {
6801
				params = [chart].concat(args || []);
6802
				params.push(descriptor.options);
6803
				if (method.apply(plugin, params) === false) {
6804
					return false;
6805
				}
6806
			}
6807
		}
6808
6809
		return true;
6810
	},
6811
6812
	/**
6813
	 * Returns descriptors of enabled plugins for the given chart.
6814
	 * @returns {Array} [{ plugin, options }]
6815
	 * @private
6816
	 */
6817
	descriptors: function(chart) {
6818
		var cache = chart.$plugins || (chart.$plugins = {});
6819
		if (cache.id === this._cacheId) {
6820
			return cache.descriptors;
6821
		}
6822
6823
		var plugins = [];
6824
		var descriptors = [];
6825
		var config = (chart && chart.config) || {};
6826
		var options = (config.options && config.options.plugins) || {};
6827
6828
		this._plugins.concat(config.plugins || []).forEach(function(plugin) {
6829
			var idx = plugins.indexOf(plugin);
6830
			if (idx !== -1) {
6831
				return;
6832
			}
6833
6834
			var id = plugin.id;
6835
			var opts = options[id];
6836
			if (opts === false) {
6837
				return;
6838
			}
6839
6840
			if (opts === true) {
6841
				opts = helpers.clone(defaults.global.plugins[id]);
6842
			}
6843
6844
			plugins.push(plugin);
6845
			descriptors.push({
6846
				plugin: plugin,
6847
				options: opts || {}
6848
			});
6849
		});
6850
6851
		cache.descriptors = descriptors;
6852
		cache.id = this._cacheId;
6853
		return descriptors;
6854
	},
6855
6856
	/**
6857
	 * Invalidates cache for the given chart: descriptors hold a reference on plugin option,
6858
	 * but in some cases, this reference can be changed by the user when updating options.
6859
	 * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
6860
	 * @private
6861
	 */
6862
	_invalidate: function(chart) {
6863
		delete chart.$plugins;
6864
	}
6865
};
6866
6867
/**
6868
 * Plugin extension hooks.
6869
 * @interface IPlugin
6870
 * @since 2.1.0
6871
 */
6872
/**
6873
 * @method IPlugin#beforeInit
6874
 * @desc Called before initializing `chart`.
6875
 * @param {Chart.Controller} chart - The chart instance.
6876
 * @param {Object} options - The plugin options.
6877
 */
6878
/**
6879
 * @method IPlugin#afterInit
6880
 * @desc Called after `chart` has been initialized and before the first update.
6881
 * @param {Chart.Controller} chart - The chart instance.
6882
 * @param {Object} options - The plugin options.
6883
 */
6884
/**
6885
 * @method IPlugin#beforeUpdate
6886
 * @desc Called before updating `chart`. If any plugin returns `false`, the update
6887
 * is cancelled (and thus subsequent render(s)) until another `update` is triggered.
6888
 * @param {Chart.Controller} chart - The chart instance.
6889
 * @param {Object} options - The plugin options.
6890
 * @returns {Boolean} `false` to cancel the chart update.
6891
 */
6892
/**
6893
 * @method IPlugin#afterUpdate
6894
 * @desc Called after `chart` has been updated and before rendering. Note that this
6895
 * hook will not be called if the chart update has been previously cancelled.
6896
 * @param {Chart.Controller} chart - The chart instance.
6897
 * @param {Object} options - The plugin options.
6898
 */
6899
/**
6900
 * @method IPlugin#beforeDatasetsUpdate
6901
 * @desc Called before updating the `chart` datasets. If any plugin returns `false`,
6902
 * the datasets update is cancelled until another `update` is triggered.
6903
 * @param {Chart.Controller} chart - The chart instance.
6904
 * @param {Object} options - The plugin options.
6905
 * @returns {Boolean} false to cancel the datasets update.
6906
 * @since version 2.1.5
6907
*/
6908
/**
6909
 * @method IPlugin#afterDatasetsUpdate
6910
 * @desc Called after the `chart` datasets have been updated. Note that this hook
6911
 * will not be called if the datasets update has been previously cancelled.
6912
 * @param {Chart.Controller} chart - The chart instance.
6913
 * @param {Object} options - The plugin options.
6914
 * @since version 2.1.5
6915
 */
6916
/**
6917
 * @method IPlugin#beforeDatasetUpdate
6918
 * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin
6919
 * returns `false`, the datasets update is cancelled until another `update` is triggered.
6920
 * @param {Chart} chart - The chart instance.
6921
 * @param {Object} args - The call arguments.
6922
 * @param {Number} args.index - The dataset index.
6923
 * @param {Object} args.meta - The dataset metadata.
6924
 * @param {Object} options - The plugin options.
6925
 * @returns {Boolean} `false` to cancel the chart datasets drawing.
6926
 */
6927
/**
6928
 * @method IPlugin#afterDatasetUpdate
6929
 * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note
6930
 * that this hook will not be called if the datasets update has been previously cancelled.
6931
 * @param {Chart} chart - The chart instance.
6932
 * @param {Object} args - The call arguments.
6933
 * @param {Number} args.index - The dataset index.
6934
 * @param {Object} args.meta - The dataset metadata.
6935
 * @param {Object} options - The plugin options.
6936
 */
6937
/**
6938
 * @method IPlugin#beforeLayout
6939
 * @desc Called before laying out `chart`. If any plugin returns `false`,
6940
 * the layout update is cancelled until another `update` is triggered.
6941
 * @param {Chart.Controller} chart - The chart instance.
6942
 * @param {Object} options - The plugin options.
6943
 * @returns {Boolean} `false` to cancel the chart layout.
6944
 */
6945
/**
6946
 * @method IPlugin#afterLayout
6947
 * @desc Called after the `chart` has been layed out. Note that this hook will not
6948
 * be called if the layout update has been previously cancelled.
6949
 * @param {Chart.Controller} chart - The chart instance.
6950
 * @param {Object} options - The plugin options.
6951
 */
6952
/**
6953
 * @method IPlugin#beforeRender
6954
 * @desc Called before rendering `chart`. If any plugin returns `false`,
6955
 * the rendering is cancelled until another `render` is triggered.
6956
 * @param {Chart.Controller} chart - The chart instance.
6957
 * @param {Object} options - The plugin options.
6958
 * @returns {Boolean} `false` to cancel the chart rendering.
6959
 */
6960
/**
6961
 * @method IPlugin#afterRender
6962
 * @desc Called after the `chart` has been fully rendered (and animation completed). Note
6963
 * that this hook will not be called if the rendering has been previously cancelled.
6964
 * @param {Chart.Controller} chart - The chart instance.
6965
 * @param {Object} options - The plugin options.
6966
 */
6967
/**
6968
 * @method IPlugin#beforeDraw
6969
 * @desc Called before drawing `chart` at every animation frame specified by the given
6970
 * easing value. If any plugin returns `false`, the frame drawing is cancelled until
6971
 * another `render` is triggered.
6972
 * @param {Chart.Controller} chart - The chart instance.
6973
 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
6974
 * @param {Object} options - The plugin options.
6975
 * @returns {Boolean} `false` to cancel the chart drawing.
6976
 */
6977
/**
6978
 * @method IPlugin#afterDraw
6979
 * @desc Called after the `chart` has been drawn for the specific easing value. Note
6980
 * that this hook will not be called if the drawing has been previously cancelled.
6981
 * @param {Chart.Controller} chart - The chart instance.
6982
 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
6983
 * @param {Object} options - The plugin options.
6984
 */
6985
/**
6986
 * @method IPlugin#beforeDatasetsDraw
6987
 * @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
6988
 * the datasets drawing is cancelled until another `render` is triggered.
6989
 * @param {Chart.Controller} chart - The chart instance.
6990
 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
6991
 * @param {Object} options - The plugin options.
6992
 * @returns {Boolean} `false` to cancel the chart datasets drawing.
6993
 */
6994
/**
6995
 * @method IPlugin#afterDatasetsDraw
6996
 * @desc Called after the `chart` datasets have been drawn. Note that this hook
6997
 * will not be called if the datasets drawing has been previously cancelled.
6998
 * @param {Chart.Controller} chart - The chart instance.
6999
 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
7000
 * @param {Object} options - The plugin options.
7001
 */
7002
/**
7003
 * @method IPlugin#beforeDatasetDraw
7004
 * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets
7005
 * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing
7006
 * is cancelled until another `render` is triggered.
7007
 * @param {Chart} chart - The chart instance.
7008
 * @param {Object} args - The call arguments.
7009
 * @param {Number} args.index - The dataset index.
7010
 * @param {Object} args.meta - The dataset metadata.
7011
 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7012
 * @param {Object} options - The plugin options.
7013
 * @returns {Boolean} `false` to cancel the chart datasets drawing.
7014
 */
7015
/**
7016
 * @method IPlugin#afterDatasetDraw
7017
 * @desc Called after the `chart` datasets at the given `args.index` have been drawn
7018
 * (datasets are drawn in the reverse order). Note that this hook will not be called
7019
 * if the datasets drawing has been previously cancelled.
7020
 * @param {Chart} chart - The chart instance.
7021
 * @param {Object} args - The call arguments.
7022
 * @param {Number} args.index - The dataset index.
7023
 * @param {Object} args.meta - The dataset metadata.
7024
 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7025
 * @param {Object} options - The plugin options.
7026
 */
7027
/**
7028
 * @method IPlugin#beforeTooltipDraw
7029
 * @desc Called before drawing the `tooltip`. If any plugin returns `false`,
7030
 * the tooltip drawing is cancelled until another `render` is triggered.
7031
 * @param {Chart} chart - The chart instance.
7032
 * @param {Object} args - The call arguments.
7033
 * @param {Object} args.tooltip - The tooltip.
7034
 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7035
 * @param {Object} options - The plugin options.
7036
 * @returns {Boolean} `false` to cancel the chart tooltip drawing.
7037
 */
7038
/**
7039
 * @method IPlugin#afterTooltipDraw
7040
 * @desc Called after drawing the `tooltip`. Note that this hook will not
7041
 * be called if the tooltip drawing has been previously cancelled.
7042
 * @param {Chart} chart - The chart instance.
7043
 * @param {Object} args - The call arguments.
7044
 * @param {Object} args.tooltip - The tooltip.
7045
 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7046
 * @param {Object} options - The plugin options.
7047
 */
7048
/**
7049
 * @method IPlugin#beforeEvent
7050
 * @desc Called before processing the specified `event`. If any plugin returns `false`,
7051
 * the event will be discarded.
7052
 * @param {Chart.Controller} chart - The chart instance.
7053
 * @param {IEvent} event - The event object.
7054
 * @param {Object} options - The plugin options.
7055
 */
7056
/**
7057
 * @method IPlugin#afterEvent
7058
 * @desc Called after the `event` has been consumed. Note that this hook
7059
 * will not be called if the `event` has been previously discarded.
7060
 * @param {Chart.Controller} chart - The chart instance.
7061
 * @param {IEvent} event - The event object.
7062
 * @param {Object} options - The plugin options.
7063
 */
7064
/**
7065
 * @method IPlugin#resize
7066
 * @desc Called after the chart as been resized.
7067
 * @param {Chart.Controller} chart - The chart instance.
7068
 * @param {Number} size - The new canvas display size (eq. canvas.style width & height).
7069
 * @param {Object} options - The plugin options.
7070
 */
7071
/**
7072
 * @method IPlugin#destroy
7073
 * @desc Called after the chart as been destroyed.
7074
 * @param {Chart.Controller} chart - The chart instance.
7075
 * @param {Object} options - The plugin options.
7076
 */
7077
7078
},{"26":26,"46":46}],33:[function(require,module,exports){
7079
'use strict';
7080
7081
var defaults = require(26);
7082
var Element = require(27);
7083
var helpers = require(46);
7084
var Ticks = require(35);
7085
7086
defaults._set('scale', {
7087
	display: true,
7088
	position: 'left',
7089
	offset: false,
7090
7091
	// grid line settings
7092
	gridLines: {
7093
		display: true,
7094
		color: 'rgba(0, 0, 0, 0.1)',
7095
		lineWidth: 1,
7096
		drawBorder: true,
7097
		drawOnChartArea: true,
7098
		drawTicks: true,
7099
		tickMarkLength: 10,
7100
		zeroLineWidth: 1,
7101
		zeroLineColor: 'rgba(0,0,0,0.25)',
7102
		zeroLineBorderDash: [],
7103
		zeroLineBorderDashOffset: 0.0,
7104
		offsetGridLines: false,
7105
		borderDash: [],
7106
		borderDashOffset: 0.0
7107
	},
7108
7109
	// scale label
7110
	scaleLabel: {
7111
		// display property
7112
		display: false,
7113
7114
		// actual label
7115
		labelString: '',
7116
7117
		// line height
7118
		lineHeight: 1.2,
7119
7120
		// top/bottom padding
7121
		padding: {
7122
			top: 4,
7123
			bottom: 4
7124
		}
7125
	},
7126
7127
	// label settings
7128
	ticks: {
7129
		beginAtZero: false,
7130
		minRotation: 0,
7131
		maxRotation: 50,
7132
		mirror: false,
7133
		padding: 0,
7134
		reverse: false,
7135
		display: true,
7136
		autoSkip: true,
7137
		autoSkipPadding: 0,
7138
		labelOffset: 0,
7139
		// We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
7140
		callback: Ticks.formatters.values,
7141
		minor: {},
7142
		major: {}
7143
	}
7144
});
7145
7146
function labelsFromTicks(ticks) {
7147
	var labels = [];
7148
	var i, ilen;
7149
7150
	for (i = 0, ilen = ticks.length; i < ilen; ++i) {
7151
		labels.push(ticks[i].label);
7152
	}
7153
7154
	return labels;
7155
}
7156
7157
function getLineValue(scale, index, offsetGridLines) {
7158
	var lineValue = scale.getPixelForTick(index);
7159
7160
	if (offsetGridLines) {
7161
		if (index === 0) {
7162
			lineValue -= (scale.getPixelForTick(1) - lineValue) / 2;
7163
		} else {
7164
			lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2;
7165
		}
7166
	}
7167
	return lineValue;
7168
}
7169
7170
function computeTextSize(context, tick, font) {
7171
	return helpers.isArray(tick) ?
7172
		helpers.longestText(context, font, tick) :
7173
		context.measureText(tick).width;
7174
}
7175
7176
function parseFontOptions(options) {
7177
	var valueOrDefault = helpers.valueOrDefault;
7178
	var globalDefaults = defaults.global;
7179
	var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
7180
	var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle);
7181
	var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily);
7182
7183
	return {
7184
		size: size,
7185
		style: style,
7186
		family: family,
7187
		font: helpers.fontString(size, style, family)
7188
	};
7189
}
7190
7191
function parseLineHeight(options) {
7192
	return helpers.options.toLineHeight(
7193
		helpers.valueOrDefault(options.lineHeight, 1.2),
7194
		helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize));
7195
}
7196
7197
module.exports = Element.extend({
7198
	/**
7199
	 * Get the padding needed for the scale
7200
	 * @method getPadding
7201
	 * @private
7202
	 * @returns {Padding} the necessary padding
7203
	 */
7204
	getPadding: function() {
7205
		var me = this;
7206
		return {
7207
			left: me.paddingLeft || 0,
7208
			top: me.paddingTop || 0,
7209
			right: me.paddingRight || 0,
7210
			bottom: me.paddingBottom || 0
7211
		};
7212
	},
7213
7214
	/**
7215
	 * Returns the scale tick objects ({label, major})
7216
	 * @since 2.7
7217
	 */
7218
	getTicks: function() {
7219
		return this._ticks;
7220
	},
7221
7222
	// These methods are ordered by lifecyle. Utilities then follow.
7223
	// Any function defined here is inherited by all scale types.
7224
	// Any function can be extended by the scale type
7225
7226
	mergeTicksOptions: function() {
7227
		var ticks = this.options.ticks;
7228
		if (ticks.minor === false) {
7229
			ticks.minor = {
7230
				display: false
7231
			};
7232
		}
7233
		if (ticks.major === false) {
7234
			ticks.major = {
7235
				display: false
7236
			};
7237
		}
7238
		for (var key in ticks) {
7239
			if (key !== 'major' && key !== 'minor') {
7240
				if (typeof ticks.minor[key] === 'undefined') {
7241
					ticks.minor[key] = ticks[key];
7242
				}
7243
				if (typeof ticks.major[key] === 'undefined') {
7244
					ticks.major[key] = ticks[key];
7245
				}
7246
			}
7247
		}
7248
	},
7249
	beforeUpdate: function() {
7250
		helpers.callback(this.options.beforeUpdate, [this]);
7251
	},
7252
7253
	update: function(maxWidth, maxHeight, margins) {
7254
		var me = this;
7255
		var i, ilen, labels, label, ticks, tick;
7256
7257
		// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
7258
		me.beforeUpdate();
7259
7260
		// Absorb the master measurements
7261
		me.maxWidth = maxWidth;
7262
		me.maxHeight = maxHeight;
7263
		me.margins = helpers.extend({
7264
			left: 0,
7265
			right: 0,
7266
			top: 0,
7267
			bottom: 0
7268
		}, margins);
7269
		me.longestTextCache = me.longestTextCache || {};
7270
7271
		// Dimensions
7272
		me.beforeSetDimensions();
7273
		me.setDimensions();
7274
		me.afterSetDimensions();
7275
7276
		// Data min/max
7277
		me.beforeDataLimits();
7278
		me.determineDataLimits();
7279
		me.afterDataLimits();
7280
7281
		// Ticks - `this.ticks` is now DEPRECATED!
7282
		// Internal ticks are now stored as objects in the PRIVATE `this._ticks` member
7283
		// and must not be accessed directly from outside this class. `this.ticks` being
7284
		// around for long time and not marked as private, we can't change its structure
7285
		// without unexpected breaking changes. If you need to access the scale ticks,
7286
		// use scale.getTicks() instead.
7287
7288
		me.beforeBuildTicks();
7289
7290
		// New implementations should return an array of objects but for BACKWARD COMPAT,
7291
		// we still support no return (`this.ticks` internally set by calling this method).
7292
		ticks = me.buildTicks() || [];
7293
7294
		me.afterBuildTicks();
7295
7296
		me.beforeTickToLabelConversion();
7297
7298
		// New implementations should return the formatted tick labels but for BACKWARD
7299
		// COMPAT, we still support no return (`this.ticks` internally changed by calling
7300
		// this method and supposed to contain only string values).
7301
		labels = me.convertTicksToLabels(ticks) || me.ticks;
7302
7303
		me.afterTickToLabelConversion();
7304
7305
		me.ticks = labels;   // BACKWARD COMPATIBILITY
7306
7307
		// IMPORTANT: from this point, we consider that `this.ticks` will NEVER change!
7308
7309
		// BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
7310
		for (i = 0, ilen = labels.length; i < ilen; ++i) {
7311
			label = labels[i];
7312
			tick = ticks[i];
7313
			if (!tick) {
7314
				ticks.push(tick = {
7315
					label: label,
7316
					major: false
7317
				});
7318
			} else {
7319
				tick.label = label;
7320
			}
7321
		}
7322
7323
		me._ticks = ticks;
7324
7325
		// Tick Rotation
7326
		me.beforeCalculateTickRotation();
7327
		me.calculateTickRotation();
7328
		me.afterCalculateTickRotation();
7329
		// Fit
7330
		me.beforeFit();
7331
		me.fit();
7332
		me.afterFit();
7333
		//
7334
		me.afterUpdate();
7335
7336
		return me.minSize;
7337
7338
	},
7339
	afterUpdate: function() {
7340
		helpers.callback(this.options.afterUpdate, [this]);
7341
	},
7342
7343
	//
7344
7345
	beforeSetDimensions: function() {
7346
		helpers.callback(this.options.beforeSetDimensions, [this]);
7347
	},
7348
	setDimensions: function() {
7349
		var me = this;
7350
		// Set the unconstrained dimension before label rotation
7351
		if (me.isHorizontal()) {
7352
			// Reset position before calculating rotation
7353
			me.width = me.maxWidth;
7354
			me.left = 0;
7355
			me.right = me.width;
7356
		} else {
7357
			me.height = me.maxHeight;
7358
7359
			// Reset position before calculating rotation
7360
			me.top = 0;
7361
			me.bottom = me.height;
7362
		}
7363
7364
		// Reset padding
7365
		me.paddingLeft = 0;
7366
		me.paddingTop = 0;
7367
		me.paddingRight = 0;
7368
		me.paddingBottom = 0;
7369
	},
7370
	afterSetDimensions: function() {
7371
		helpers.callback(this.options.afterSetDimensions, [this]);
7372
	},
7373
7374
	// Data limits
7375
	beforeDataLimits: function() {
7376
		helpers.callback(this.options.beforeDataLimits, [this]);
7377
	},
7378
	determineDataLimits: helpers.noop,
7379
	afterDataLimits: function() {
7380
		helpers.callback(this.options.afterDataLimits, [this]);
7381
	},
7382
7383
	//
7384
	beforeBuildTicks: function() {
7385
		helpers.callback(this.options.beforeBuildTicks, [this]);
7386
	},
7387
	buildTicks: helpers.noop,
7388
	afterBuildTicks: function() {
7389
		helpers.callback(this.options.afterBuildTicks, [this]);
7390
	},
7391
7392
	beforeTickToLabelConversion: function() {
7393
		helpers.callback(this.options.beforeTickToLabelConversion, [this]);
7394
	},
7395
	convertTicksToLabels: function() {
7396
		var me = this;
7397
		// Convert ticks to strings
7398
		var tickOpts = me.options.ticks;
7399
		me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this);
7400
	},
7401
	afterTickToLabelConversion: function() {
7402
		helpers.callback(this.options.afterTickToLabelConversion, [this]);
7403
	},
7404
7405
	//
7406
7407
	beforeCalculateTickRotation: function() {
7408
		helpers.callback(this.options.beforeCalculateTickRotation, [this]);
7409
	},
7410
	calculateTickRotation: function() {
7411
		var me = this;
7412
		var context = me.ctx;
7413
		var tickOpts = me.options.ticks;
7414
		var labels = labelsFromTicks(me._ticks);
7415
7416
		// Get the width of each grid by calculating the difference
7417
		// between x offsets between 0 and 1.
7418
		var tickFont = parseFontOptions(tickOpts);
7419
		context.font = tickFont.font;
7420
7421
		var labelRotation = tickOpts.minRotation || 0;
7422
7423
		if (labels.length && me.options.display && me.isHorizontal()) {
7424
			var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache);
7425
			var labelWidth = originalLabelWidth;
7426
			var cosRotation, sinRotation;
7427
7428
			// Allow 3 pixels x2 padding either side for label readability
7429
			var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;
7430
7431
			// Max label rotation can be set or default to 90 - also act as a loop counter
7432
			while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) {
7433
				var angleRadians = helpers.toRadians(labelRotation);
7434
				cosRotation = Math.cos(angleRadians);
7435
				sinRotation = Math.sin(angleRadians);
7436
7437
				if (sinRotation * originalLabelWidth > me.maxHeight) {
7438
					// go back one step
7439
					labelRotation--;
7440
					break;
7441
				}
7442
7443
				labelRotation++;
7444
				labelWidth = cosRotation * originalLabelWidth;
7445
			}
7446
		}
7447
7448
		me.labelRotation = labelRotation;
7449
	},
7450
	afterCalculateTickRotation: function() {
7451
		helpers.callback(this.options.afterCalculateTickRotation, [this]);
7452
	},
7453
7454
	//
7455
7456
	beforeFit: function() {
7457
		helpers.callback(this.options.beforeFit, [this]);
7458
	},
7459
	fit: function() {
7460
		var me = this;
7461
		// Reset
7462
		var minSize = me.minSize = {
7463
			width: 0,
7464
			height: 0
7465
		};
7466
7467
		var labels = labelsFromTicks(me._ticks);
7468
7469
		var opts = me.options;
7470
		var tickOpts = opts.ticks;
7471
		var scaleLabelOpts = opts.scaleLabel;
7472
		var gridLineOpts = opts.gridLines;
7473
		var display = opts.display;
7474
		var isHorizontal = me.isHorizontal();
7475
7476
		var tickFont = parseFontOptions(tickOpts);
7477
		var tickMarkLength = opts.gridLines.tickMarkLength;
7478
7479
		// Width
7480
		if (isHorizontal) {
7481
			// subtract the margins to line up with the chartArea if we are a full width scale
7482
			minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
7483
		} else {
7484
			minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
7485
		}
7486
7487
		// height
7488
		if (isHorizontal) {
7489
			minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
7490
		} else {
7491
			minSize.height = me.maxHeight; // fill all the height
7492
		}
7493
7494
		// Are we showing a title for the scale?
7495
		if (scaleLabelOpts.display && display) {
7496
			var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts);
7497
			var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding);
7498
			var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height;
7499
7500
			if (isHorizontal) {
7501
				minSize.height += deltaHeight;
7502
			} else {
7503
				minSize.width += deltaHeight;
7504
			}
7505
		}
7506
7507
		// Don't bother fitting the ticks if we are not showing them
7508
		if (tickOpts.display && display) {
7509
			var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache);
7510
			var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels);
7511
			var lineSpace = tickFont.size * 0.5;
7512
			var tickPadding = me.options.ticks.padding;
7513
7514
			if (isHorizontal) {
7515
				// A horizontal axis is more constrained by the height.
7516
				me.longestLabelWidth = largestTextWidth;
7517
7518
				var angleRadians = helpers.toRadians(me.labelRotation);
7519
				var cosRotation = Math.cos(angleRadians);
7520
				var sinRotation = Math.sin(angleRadians);
7521
7522
				// TODO - improve this calculation
7523
				var labelHeight = (sinRotation * largestTextWidth)
7524
					+ (tickFont.size * tallestLabelHeightInLines)
7525
					+ (lineSpace * (tallestLabelHeightInLines - 1))
7526
					+ lineSpace; // padding
7527
7528
				minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);
7529
7530
				me.ctx.font = tickFont.font;
7531
				var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font);
7532
				var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font);
7533
7534
				// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
7535
				// which means that the right padding is dominated by the font height
7536
				if (me.labelRotation !== 0) {
7537
					me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges
7538
					me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3;
7539
				} else {
7540
					me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
7541
					me.paddingRight = lastLabelWidth / 2 + 3;
7542
				}
7543
			} else {
7544
				// A vertical axis is more constrained by the width. Labels are the
7545
				// dominant factor here, so get that length first and account for padding
7546
				if (tickOpts.mirror) {
7547
					largestTextWidth = 0;
7548
				} else {
7549
					// use lineSpace for consistency with horizontal axis
7550
					// tickPadding is not implemented for horizontal
7551
					largestTextWidth += tickPadding + lineSpace;
7552
				}
7553
7554
				minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth);
7555
7556
				me.paddingTop = tickFont.size / 2;
7557
				me.paddingBottom = tickFont.size / 2;
7558
			}
7559
		}
7560
7561
		me.handleMargins();
7562
7563
		me.width = minSize.width;
7564
		me.height = minSize.height;
7565
	},
7566
7567
	/**
7568
	 * Handle margins and padding interactions
7569
	 * @private
7570
	 */
7571
	handleMargins: function() {
7572
		var me = this;
7573
		if (me.margins) {
7574
			me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
7575
			me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
7576
			me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
7577
			me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
7578
		}
7579
	},
7580
7581
	afterFit: function() {
7582
		helpers.callback(this.options.afterFit, [this]);
7583
	},
7584
7585
	// Shared Methods
7586
	isHorizontal: function() {
7587
		return this.options.position === 'top' || this.options.position === 'bottom';
7588
	},
7589
	isFullWidth: function() {
7590
		return (this.options.fullWidth);
7591
	},
7592
7593
	// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
7594
	getRightValue: function(rawValue) {
7595
		// Null and undefined values first
7596
		if (helpers.isNullOrUndef(rawValue)) {
7597
			return NaN;
7598
		}
7599
		// isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
7600
		if (typeof rawValue === 'number' && !isFinite(rawValue)) {
7601
			return NaN;
7602
		}
7603
		// If it is in fact an object, dive in one more level
7604
		if (rawValue) {
7605
			if (this.isHorizontal()) {
7606
				if (rawValue.x !== undefined) {
7607
					return this.getRightValue(rawValue.x);
7608
				}
7609
			} else if (rawValue.y !== undefined) {
7610
				return this.getRightValue(rawValue.y);
7611
			}
7612
		}
7613
7614
		// Value is good, return it
7615
		return rawValue;
7616
	},
7617
7618
	/**
7619
	 * Used to get the value to display in the tooltip for the data at the given index
7620
	 * @param index
7621
	 * @param datasetIndex
7622
	 */
7623
	getLabelForIndex: helpers.noop,
7624
7625
	/**
7626
	 * Returns the location of the given data point. Value can either be an index or a numerical value
7627
	 * The coordinate (0, 0) is at the upper-left corner of the canvas
7628
	 * @param value
7629
	 * @param index
7630
	 * @param datasetIndex
7631
	 */
7632
	getPixelForValue: helpers.noop,
7633
7634
	/**
7635
	 * Used to get the data value from a given pixel. This is the inverse of getPixelForValue
7636
	 * The coordinate (0, 0) is at the upper-left corner of the canvas
7637
	 * @param pixel
7638
	 */
7639
	getValueForPixel: helpers.noop,
7640
7641
	/**
7642
	 * Returns the location of the tick at the given index
7643
	 * The coordinate (0, 0) is at the upper-left corner of the canvas
7644
	 */
7645
	getPixelForTick: function(index) {
7646
		var me = this;
7647
		var offset = me.options.offset;
7648
		if (me.isHorizontal()) {
7649
			var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
7650
			var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
7651
			var pixel = (tickWidth * index) + me.paddingLeft;
7652
7653
			if (offset) {
7654
				pixel += tickWidth / 2;
7655
			}
7656
7657
			var finalVal = me.left + Math.round(pixel);
7658
			finalVal += me.isFullWidth() ? me.margins.left : 0;
7659
			return finalVal;
7660
		}
7661
		var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
7662
		return me.top + (index * (innerHeight / (me._ticks.length - 1)));
7663
	},
7664
7665
	/**
7666
	 * Utility for getting the pixel location of a percentage of scale
7667
	 * The coordinate (0, 0) is at the upper-left corner of the canvas
7668
	 */
7669
	getPixelForDecimal: function(decimal) {
7670
		var me = this;
7671
		if (me.isHorizontal()) {
7672
			var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
7673
			var valueOffset = (innerWidth * decimal) + me.paddingLeft;
7674
7675
			var finalVal = me.left + Math.round(valueOffset);
7676
			finalVal += me.isFullWidth() ? me.margins.left : 0;
7677
			return finalVal;
7678
		}
7679
		return me.top + (decimal * me.height);
7680
	},
7681
7682
	/**
7683
	 * Returns the pixel for the minimum chart value
7684
	 * The coordinate (0, 0) is at the upper-left corner of the canvas
7685
	 */
7686
	getBasePixel: function() {
7687
		return this.getPixelForValue(this.getBaseValue());
7688
	},
7689
7690
	getBaseValue: function() {
7691
		var me = this;
7692
		var min = me.min;
7693
		var max = me.max;
7694
7695
		return me.beginAtZero ? 0 :
7696
			min < 0 && max < 0 ? max :
7697
			min > 0 && max > 0 ? min :
7698
			0;
7699
	},
7700
7701
	/**
7702
	 * Returns a subset of ticks to be plotted to avoid overlapping labels.
7703
	 * @private
7704
	 */
7705
	_autoSkip: function(ticks) {
7706
		var skipRatio;
7707
		var me = this;
7708
		var isHorizontal = me.isHorizontal();
7709
		var optionTicks = me.options.ticks.minor;
7710
		var tickCount = ticks.length;
7711
		var labelRotationRadians = helpers.toRadians(me.labelRotation);
7712
		var cosRotation = Math.cos(labelRotationRadians);
7713
		var longestRotatedLabel = me.longestLabelWidth * cosRotation;
7714
		var result = [];
7715
		var i, tick, shouldSkip;
7716
7717
		// figure out the maximum number of gridlines to show
7718
		var maxTicks;
7719
		if (optionTicks.maxTicksLimit) {
7720
			maxTicks = optionTicks.maxTicksLimit;
7721
		}
7722
7723
		if (isHorizontal) {
7724
			skipRatio = false;
7725
7726
			if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) {
7727
				skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight)));
7728
			}
7729
7730
			// if they defined a max number of optionTicks,
7731
			// increase skipRatio until that number is met
7732
			if (maxTicks && tickCount > maxTicks) {
7733
				skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks));
7734
			}
7735
		}
7736
7737
		for (i = 0; i < tickCount; i++) {
7738
			tick = ticks[i];
7739
7740
			// Since we always show the last tick,we need may need to hide the last shown one before
7741
			shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount);
7742
			if (shouldSkip && i !== tickCount - 1) {
7743
				// leave tick in place but make sure it's not displayed (#4635)
7744
				delete tick.label;
7745
			}
7746
			result.push(tick);
7747
		}
7748
		return result;
7749
	},
7750
7751
	// Actually draw the scale on the canvas
7752
	// @param {rectangle} chartArea : the area of the chart to draw full grid lines on
7753
	draw: function(chartArea) {
7754
		var me = this;
7755
		var options = me.options;
7756
		if (!options.display) {
7757
			return;
7758
		}
7759
7760
		var context = me.ctx;
7761
		var globalDefaults = defaults.global;
7762
		var optionTicks = options.ticks.minor;
7763
		var optionMajorTicks = options.ticks.major || optionTicks;
7764
		var gridLines = options.gridLines;
7765
		var scaleLabel = options.scaleLabel;
7766
7767
		var isRotated = me.labelRotation !== 0;
7768
		var isHorizontal = me.isHorizontal();
7769
7770
		var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks();
7771
		var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
7772
		var tickFont = parseFontOptions(optionTicks);
7773
		var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor);
7774
		var majorTickFont = parseFontOptions(optionMajorTicks);
7775
7776
		var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;
7777
7778
		var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
7779
		var scaleLabelFont = parseFontOptions(scaleLabel);
7780
		var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding);
7781
		var labelRotationRadians = helpers.toRadians(me.labelRotation);
7782
7783
		var itemsToDraw = [];
7784
7785
		var axisWidth = me.options.gridLines.lineWidth;
7786
		var xTickStart = options.position === 'right' ? me.left : me.right - axisWidth - tl;
7787
		var xTickEnd = options.position === 'right' ? me.left + tl : me.right;
7788
		var yTickStart = options.position === 'bottom' ? me.top + axisWidth : me.bottom - tl - axisWidth;
7789
		var yTickEnd = options.position === 'bottom' ? me.top + axisWidth + tl : me.bottom + axisWidth;
7790
7791
		helpers.each(ticks, function(tick, index) {
7792
			// autoskipper skipped this tick (#4635)
7793
			if (helpers.isNullOrUndef(tick.label)) {
7794
				return;
7795
			}
7796
7797
			var label = tick.label;
7798
			var lineWidth, lineColor, borderDash, borderDashOffset;
7799
			if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) {
7800
				// Draw the first index specially
7801
				lineWidth = gridLines.zeroLineWidth;
7802
				lineColor = gridLines.zeroLineColor;
7803
				borderDash = gridLines.zeroLineBorderDash;
7804
				borderDashOffset = gridLines.zeroLineBorderDashOffset;
7805
			} else {
7806
				lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index);
7807
				lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index);
7808
				borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
7809
				borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
7810
			}
7811
7812
			// Common properties
7813
			var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;
7814
			var textAlign = 'middle';
7815
			var textBaseline = 'middle';
7816
			var tickPadding = optionTicks.padding;
7817
7818
			if (isHorizontal) {
7819
				var labelYOffset = tl + tickPadding;
7820
7821
				if (options.position === 'bottom') {
7822
					// bottom
7823
					textBaseline = !isRotated ? 'top' : 'middle';
7824
					textAlign = !isRotated ? 'center' : 'right';
7825
					labelY = me.top + labelYOffset;
7826
				} else {
7827
					// top
7828
					textBaseline = !isRotated ? 'bottom' : 'middle';
7829
					textAlign = !isRotated ? 'center' : 'left';
7830
					labelY = me.bottom - labelYOffset;
7831
				}
7832
7833
				var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
7834
				if (xLineValue < me.left) {
7835
					lineColor = 'rgba(0,0,0,0)';
7836
				}
7837
				xLineValue += helpers.aliasPixel(lineWidth);
7838
7839
				labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)
7840
7841
				tx1 = tx2 = x1 = x2 = xLineValue;
7842
				ty1 = yTickStart;
7843
				ty2 = yTickEnd;
7844
				y1 = chartArea.top;
7845
				y2 = chartArea.bottom + axisWidth;
7846
			} else {
7847
				var isLeft = options.position === 'left';
7848
				var labelXOffset;
7849
7850
				if (optionTicks.mirror) {
7851
					textAlign = isLeft ? 'left' : 'right';
7852
					labelXOffset = tickPadding;
7853
				} else {
7854
					textAlign = isLeft ? 'right' : 'left';
7855
					labelXOffset = tl + tickPadding;
7856
				}
7857
7858
				labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset;
7859
7860
				var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
7861
				if (yLineValue < me.top) {
7862
					lineColor = 'rgba(0,0,0,0)';
7863
				}
7864
				yLineValue += helpers.aliasPixel(lineWidth);
7865
7866
				labelY = me.getPixelForTick(index) + optionTicks.labelOffset;
7867
7868
				tx1 = xTickStart;
7869
				tx2 = xTickEnd;
7870
				x1 = chartArea.left;
7871
				x2 = chartArea.right + axisWidth;
7872
				ty1 = ty2 = y1 = y2 = yLineValue;
7873
			}
7874
7875
			itemsToDraw.push({
7876
				tx1: tx1,
7877
				ty1: ty1,
7878
				tx2: tx2,
7879
				ty2: ty2,
7880
				x1: x1,
7881
				y1: y1,
7882
				x2: x2,
7883
				y2: y2,
7884
				labelX: labelX,
7885
				labelY: labelY,
7886
				glWidth: lineWidth,
7887
				glColor: lineColor,
7888
				glBorderDash: borderDash,
7889
				glBorderDashOffset: borderDashOffset,
7890
				rotation: -1 * labelRotationRadians,
7891
				label: label,
7892
				major: tick.major,
7893
				textBaseline: textBaseline,
7894
				textAlign: textAlign
7895
			});
7896
		});
7897
7898
		// Draw all of the tick labels, tick marks, and grid lines at the correct places
7899
		helpers.each(itemsToDraw, function(itemToDraw) {
7900
			if (gridLines.display) {
7901
				context.save();
7902
				context.lineWidth = itemToDraw.glWidth;
7903
				context.strokeStyle = itemToDraw.glColor;
7904
				if (context.setLineDash) {
7905
					context.setLineDash(itemToDraw.glBorderDash);
7906
					context.lineDashOffset = itemToDraw.glBorderDashOffset;
7907
				}
7908
7909
				context.beginPath();
7910
7911
				if (gridLines.drawTicks) {
7912
					context.moveTo(itemToDraw.tx1, itemToDraw.ty1);
7913
					context.lineTo(itemToDraw.tx2, itemToDraw.ty2);
7914
				}
7915
7916
				if (gridLines.drawOnChartArea) {
7917
					context.moveTo(itemToDraw.x1, itemToDraw.y1);
7918
					context.lineTo(itemToDraw.x2, itemToDraw.y2);
7919
				}
7920
7921
				context.stroke();
7922
				context.restore();
7923
			}
7924
7925
			if (optionTicks.display) {
7926
				// Make sure we draw text in the correct color and font
7927
				context.save();
7928
				context.translate(itemToDraw.labelX, itemToDraw.labelY);
7929
				context.rotate(itemToDraw.rotation);
7930
				context.font = itemToDraw.major ? majorTickFont.font : tickFont.font;
7931
				context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor;
7932
				context.textBaseline = itemToDraw.textBaseline;
7933
				context.textAlign = itemToDraw.textAlign;
7934
7935
				var label = itemToDraw.label;
7936
				if (helpers.isArray(label)) {
7937
					var lineCount = label.length;
7938
					var lineHeight = tickFont.size * 1.5;
7939
					var y = me.isHorizontal() ? 0 : -lineHeight * (lineCount - 1) / 2;
7940
7941
					for (var i = 0; i < lineCount; ++i) {
7942
						// We just make sure the multiline element is a string here..
7943
						context.fillText('' + label[i], 0, y);
7944
						// apply same lineSpacing as calculated @ L#320
7945
						y += lineHeight;
7946
					}
7947
				} else {
7948
					context.fillText(label, 0, 0);
7949
				}
7950
				context.restore();
7951
			}
7952
		});
7953
7954
		if (scaleLabel.display) {
7955
			// Draw the scale label
7956
			var scaleLabelX;
7957
			var scaleLabelY;
7958
			var rotation = 0;
7959
			var halfLineHeight = parseLineHeight(scaleLabel) / 2;
7960
7961
			if (isHorizontal) {
7962
				scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
7963
				scaleLabelY = options.position === 'bottom'
7964
					? me.bottom - halfLineHeight - scaleLabelPadding.bottom
7965
					: me.top + halfLineHeight + scaleLabelPadding.top;
7966
			} else {
7967
				var isLeft = options.position === 'left';
7968
				scaleLabelX = isLeft
7969
					? me.left + halfLineHeight + scaleLabelPadding.top
7970
					: me.right - halfLineHeight - scaleLabelPadding.top;
7971
				scaleLabelY = me.top + ((me.bottom - me.top) / 2);
7972
				rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
7973
			}
7974
7975
			context.save();
7976
			context.translate(scaleLabelX, scaleLabelY);
7977
			context.rotate(rotation);
7978
			context.textAlign = 'center';
7979
			context.textBaseline = 'middle';
7980
			context.fillStyle = scaleLabelFontColor; // render in correct colour
7981
			context.font = scaleLabelFont.font;
7982
			context.fillText(scaleLabel.labelString, 0, 0);
7983
			context.restore();
7984
		}
7985
7986
		if (gridLines.drawBorder) {
7987
			// Draw the line at the edge of the axis
7988
			context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0);
7989
			context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0);
7990
			var x1 = me.left;
7991
			var x2 = me.right + axisWidth;
7992
			var y1 = me.top;
7993
			var y2 = me.bottom + axisWidth;
7994
7995
			var aliasPixel = helpers.aliasPixel(context.lineWidth);
7996
			if (isHorizontal) {
7997
				y1 = y2 = options.position === 'top' ? me.bottom : me.top;
7998
				y1 += aliasPixel;
7999
				y2 += aliasPixel;
8000
			} else {
8001
				x1 = x2 = options.position === 'left' ? me.right : me.left;
8002
				x1 += aliasPixel;
8003
				x2 += aliasPixel;
8004
			}
8005
8006
			context.beginPath();
8007
			context.moveTo(x1, y1);
8008
			context.lineTo(x2, y2);
8009
			context.stroke();
8010
		}
8011
	}
8012
});
8013
8014
},{"26":26,"27":27,"35":35,"46":46}],34:[function(require,module,exports){
8015
'use strict';
8016
8017
var defaults = require(26);
8018
var helpers = require(46);
8019
var layouts = require(31);
8020
8021
module.exports = {
8022
	// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
8023
	// use the new chart options to grab the correct scale
8024
	constructors: {},
8025
	// Use a registration function so that we can move to an ES6 map when we no longer need to support
8026
	// old browsers
8027
8028
	// Scale config defaults
8029
	defaults: {},
8030
	registerScaleType: function(type, scaleConstructor, scaleDefaults) {
8031
		this.constructors[type] = scaleConstructor;
8032
		this.defaults[type] = helpers.clone(scaleDefaults);
8033
	},
8034
	getScaleConstructor: function(type) {
8035
		return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
8036
	},
8037
	getScaleDefaults: function(type) {
8038
		// Return the scale defaults merged with the global settings so that we always use the latest ones
8039
		return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {};
8040
	},
8041
	updateScaleDefaults: function(type, additions) {
8042
		var me = this;
8043
		if (me.defaults.hasOwnProperty(type)) {
8044
			me.defaults[type] = helpers.extend(me.defaults[type], additions);
8045
		}
8046
	},
8047
	addScalesToLayout: function(chart) {
8048
		// Adds each scale to the chart.boxes array to be sized accordingly
8049
		helpers.each(chart.scales, function(scale) {
8050
			// Set ILayoutItem parameters for backwards compatibility
8051
			scale.fullWidth = scale.options.fullWidth;
8052
			scale.position = scale.options.position;
8053
			scale.weight = scale.options.weight;
8054
			layouts.addBox(chart, scale);
8055
		});
8056
	}
8057
};
8058
8059
},{"26":26,"31":31,"46":46}],35:[function(require,module,exports){
8060
'use strict';
8061
8062
var helpers = require(46);
8063
8064
/**
8065
 * Namespace to hold static tick generation functions
8066
 * @namespace Chart.Ticks
8067
 */
8068
module.exports = {
8069
	/**
8070
	 * Namespace to hold formatters for different types of ticks
8071
	 * @namespace Chart.Ticks.formatters
8072
	 */
8073
	formatters: {
8074
		/**
8075
		 * Formatter for value labels
8076
		 * @method Chart.Ticks.formatters.values
8077
		 * @param value the value to display
8078
		 * @return {String|Array} the label to display
8079
		 */
8080
		values: function(value) {
8081
			return helpers.isArray(value) ? value : '' + value;
8082
		},
8083
8084
		/**
8085
		 * Formatter for linear numeric ticks
8086
		 * @method Chart.Ticks.formatters.linear
8087
		 * @param tickValue {Number} the value to be formatted
8088
		 * @param index {Number} the position of the tickValue parameter in the ticks array
8089
		 * @param ticks {Array<Number>} the list of ticks being converted
8090
		 * @return {String} string representation of the tickValue parameter
8091
		 */
8092
		linear: function(tickValue, index, ticks) {
8093
			// If we have lots of ticks, don't use the ones
8094
			var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
8095
8096
			// If we have a number like 2.5 as the delta, figure out how many decimal places we need
8097
			if (Math.abs(delta) > 1) {
8098
				if (tickValue !== Math.floor(tickValue)) {
8099
					// not an integer
8100
					delta = tickValue - Math.floor(tickValue);
8101
				}
8102
			}
8103
8104
			var logDelta = helpers.log10(Math.abs(delta));
8105
			var tickString = '';
8106
8107
			if (tickValue !== 0) {
8108
				var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1]));
8109
				if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation
8110
					var logTick = helpers.log10(Math.abs(tickValue));
8111
					tickString = tickValue.toExponential(Math.floor(logTick) - Math.floor(logDelta));
8112
				} else {
8113
					var numDecimal = -1 * Math.floor(logDelta);
8114
					numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
8115
					tickString = tickValue.toFixed(numDecimal);
8116
				}
8117
			} else {
8118
				tickString = '0'; // never show decimal places for 0
8119
			}
8120
8121
			return tickString;
8122
		},
8123
8124
		logarithmic: function(tickValue, index, ticks) {
8125
			var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));
8126
8127
			if (tickValue === 0) {
8128
				return '0';
8129
			} else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
8130
				return tickValue.toExponential();
8131
			}
8132
			return '';
8133
		}
8134
	}
8135
};
8136
8137
},{"46":46}],36:[function(require,module,exports){
8138
'use strict';
8139
8140
var defaults = require(26);
8141
var Element = require(27);
8142
var helpers = require(46);
8143
8144
defaults._set('global', {
8145
	tooltips: {
8146
		enabled: true,
8147
		custom: null,
8148
		mode: 'nearest',
8149
		position: 'average',
8150
		intersect: true,
8151
		backgroundColor: 'rgba(0,0,0,0.8)',
8152
		titleFontStyle: 'bold',
8153
		titleSpacing: 2,
8154
		titleMarginBottom: 6,
8155
		titleFontColor: '#fff',
8156
		titleAlign: 'left',
8157
		bodySpacing: 2,
8158
		bodyFontColor: '#fff',
8159
		bodyAlign: 'left',
8160
		footerFontStyle: 'bold',
8161
		footerSpacing: 2,
8162
		footerMarginTop: 6,
8163
		footerFontColor: '#fff',
8164
		footerAlign: 'left',
8165
		yPadding: 6,
8166
		xPadding: 6,
8167
		caretPadding: 2,
8168
		caretSize: 5,
8169
		cornerRadius: 6,
8170
		multiKeyBackground: '#fff',
8171
		displayColors: true,
8172
		borderColor: 'rgba(0,0,0,0)',
8173
		borderWidth: 0,
8174
		callbacks: {
8175
			// Args are: (tooltipItems, data)
8176
			beforeTitle: helpers.noop,
8177
			title: function(tooltipItems, data) {
8178
				// Pick first xLabel for now
8179
				var title = '';
8180
				var labels = data.labels;
8181
				var labelCount = labels ? labels.length : 0;
8182
8183
				if (tooltipItems.length > 0) {
8184
					var item = tooltipItems[0];
8185
8186
					if (item.xLabel) {
8187
						title = item.xLabel;
8188
					} else if (labelCount > 0 && item.index < labelCount) {
8189
						title = labels[item.index];
8190
					}
8191
				}
8192
8193
				return title;
8194
			},
8195
			afterTitle: helpers.noop,
8196
8197
			// Args are: (tooltipItems, data)
8198
			beforeBody: helpers.noop,
8199
8200
			// Args are: (tooltipItem, data)
8201
			beforeLabel: helpers.noop,
8202
			label: function(tooltipItem, data) {
8203
				var label = data.datasets[tooltipItem.datasetIndex].label || '';
8204
8205
				if (label) {
8206
					label += ': ';
8207
				}
8208
				label += tooltipItem.yLabel;
8209
				return label;
8210
			},
8211
			labelColor: function(tooltipItem, chart) {
8212
				var meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
8213
				var activeElement = meta.data[tooltipItem.index];
8214
				var view = activeElement._view;
8215
				return {
8216
					borderColor: view.borderColor,
8217
					backgroundColor: view.backgroundColor
8218
				};
8219
			},
8220
			labelTextColor: function() {
8221
				return this._options.bodyFontColor;
8222
			},
8223
			afterLabel: helpers.noop,
8224
8225
			// Args are: (tooltipItems, data)
8226
			afterBody: helpers.noop,
8227
8228
			// Args are: (tooltipItems, data)
8229
			beforeFooter: helpers.noop,
8230
			footer: helpers.noop,
8231
			afterFooter: helpers.noop
8232
		}
8233
	}
8234
});
8235
8236
var positioners = {
8237
	/**
8238
	 * Average mode places the tooltip at the average position of the elements shown
8239
	 * @function Chart.Tooltip.positioners.average
8240
	 * @param elements {ChartElement[]} the elements being displayed in the tooltip
8241
	 * @returns {Point} tooltip position
8242
	 */
8243
	average: function(elements) {
8244
		if (!elements.length) {
8245
			return false;
8246
		}
8247
8248
		var i, len;
8249
		var x = 0;
8250
		var y = 0;
8251
		var count = 0;
8252
8253
		for (i = 0, len = elements.length; i < len; ++i) {
8254
			var el = elements[i];
8255
			if (el && el.hasValue()) {
8256
				var pos = el.tooltipPosition();
8257
				x += pos.x;
8258
				y += pos.y;
8259
				++count;
8260
			}
8261
		}
8262
8263
		return {
8264
			x: Math.round(x / count),
8265
			y: Math.round(y / count)
8266
		};
8267
	},
8268
8269
	/**
8270
	 * Gets the tooltip position nearest of the item nearest to the event position
8271
	 * @function Chart.Tooltip.positioners.nearest
8272
	 * @param elements {Chart.Element[]} the tooltip elements
8273
	 * @param eventPosition {Point} the position of the event in canvas coordinates
8274
	 * @returns {Point} the tooltip position
8275
	 */
8276
	nearest: function(elements, eventPosition) {
8277
		var x = eventPosition.x;
8278
		var y = eventPosition.y;
8279
		var minDistance = Number.POSITIVE_INFINITY;
8280
		var i, len, nearestElement;
8281
8282
		for (i = 0, len = elements.length; i < len; ++i) {
8283
			var el = elements[i];
8284
			if (el && el.hasValue()) {
8285
				var center = el.getCenterPoint();
8286
				var d = helpers.distanceBetweenPoints(eventPosition, center);
8287
8288
				if (d < minDistance) {
8289
					minDistance = d;
8290
					nearestElement = el;
8291
				}
8292
			}
8293
		}
8294
8295
		if (nearestElement) {
8296
			var tp = nearestElement.tooltipPosition();
8297
			x = tp.x;
8298
			y = tp.y;
8299
		}
8300
8301
		return {
8302
			x: x,
8303
			y: y
8304
		};
8305
	}
8306
};
8307
8308
/**
8309
 * Helper method to merge the opacity into a color
8310
 */
8311
function mergeOpacity(colorString, opacity) {
8312
	var color = helpers.color(colorString);
8313
	return color.alpha(opacity * color.alpha()).rgbaString();
8314
}
8315
8316
// Helper to push or concat based on if the 2nd parameter is an array or not
8317
function pushOrConcat(base, toPush) {
8318
	if (toPush) {
8319
		if (helpers.isArray(toPush)) {
8320
			// base = base.concat(toPush);
8321
			Array.prototype.push.apply(base, toPush);
8322
		} else {
8323
			base.push(toPush);
8324
		}
8325
	}
8326
8327
	return base;
8328
}
8329
8330
/**
8331
 * Returns array of strings split by newline
8332
 * @param {String} value - The value to split by newline.
8333
 * @returns {Array} value if newline present - Returned from String split() method
8334
 * @function
8335
 */
8336
function splitNewlines(str) {
8337
	if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) {
8338
		return str.split('\n');
8339
	}
8340
	return str;
8341
}
8342
8343
8344
// Private helper to create a tooltip item model
8345
// @param element : the chart element (point, arc, bar) to create the tooltip item for
8346
// @return : new tooltip item
8347
function createTooltipItem(element) {
8348
	var xScale = element._xScale;
8349
	var yScale = element._yScale || element._scale; // handle radar || polarArea charts
8350
	var index = element._index;
8351
	var datasetIndex = element._datasetIndex;
8352
8353
	return {
8354
		xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
8355
		yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
8356
		index: index,
8357
		datasetIndex: datasetIndex,
8358
		x: element._model.x,
8359
		y: element._model.y
8360
	};
8361
}
8362
8363
/**
8364
 * Helper to get the reset model for the tooltip
8365
 * @param tooltipOpts {Object} the tooltip options
8366
 */
8367
function getBaseModel(tooltipOpts) {
8368
	var globalDefaults = defaults.global;
8369
	var valueOrDefault = helpers.valueOrDefault;
8370
8371
	return {
8372
		// Positioning
8373
		xPadding: tooltipOpts.xPadding,
8374
		yPadding: tooltipOpts.yPadding,
8375
		xAlign: tooltipOpts.xAlign,
8376
		yAlign: tooltipOpts.yAlign,
8377
8378
		// Body
8379
		bodyFontColor: tooltipOpts.bodyFontColor,
8380
		_bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
8381
		_bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
8382
		_bodyAlign: tooltipOpts.bodyAlign,
8383
		bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
8384
		bodySpacing: tooltipOpts.bodySpacing,
8385
8386
		// Title
8387
		titleFontColor: tooltipOpts.titleFontColor,
8388
		_titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
8389
		_titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
8390
		titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
8391
		_titleAlign: tooltipOpts.titleAlign,
8392
		titleSpacing: tooltipOpts.titleSpacing,
8393
		titleMarginBottom: tooltipOpts.titleMarginBottom,
8394
8395
		// Footer
8396
		footerFontColor: tooltipOpts.footerFontColor,
8397
		_footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
8398
		_footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
8399
		footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
8400
		_footerAlign: tooltipOpts.footerAlign,
8401
		footerSpacing: tooltipOpts.footerSpacing,
8402
		footerMarginTop: tooltipOpts.footerMarginTop,
8403
8404
		// Appearance
8405
		caretSize: tooltipOpts.caretSize,
8406
		cornerRadius: tooltipOpts.cornerRadius,
8407
		backgroundColor: tooltipOpts.backgroundColor,
8408
		opacity: 0,
8409
		legendColorBackground: tooltipOpts.multiKeyBackground,
8410
		displayColors: tooltipOpts.displayColors,
8411
		borderColor: tooltipOpts.borderColor,
8412
		borderWidth: tooltipOpts.borderWidth
8413
	};
8414
}
8415
8416
/**
8417
 * Get the size of the tooltip
8418
 */
8419
function getTooltipSize(tooltip, model) {
8420
	var ctx = tooltip._chart.ctx;
8421
8422
	var height = model.yPadding * 2; // Tooltip Padding
8423
	var width = 0;
8424
8425
	// Count of all lines in the body
8426
	var body = model.body;
8427
	var combinedBodyLength = body.reduce(function(count, bodyItem) {
8428
		return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
8429
	}, 0);
8430
	combinedBodyLength += model.beforeBody.length + model.afterBody.length;
8431
8432
	var titleLineCount = model.title.length;
8433
	var footerLineCount = model.footer.length;
8434
	var titleFontSize = model.titleFontSize;
8435
	var bodyFontSize = model.bodyFontSize;
8436
	var footerFontSize = model.footerFontSize;
8437
8438
	height += titleLineCount * titleFontSize; // Title Lines
8439
	height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
8440
	height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
8441
	height += combinedBodyLength * bodyFontSize; // Body Lines
8442
	height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
8443
	height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
8444
	height += footerLineCount * (footerFontSize); // Footer Lines
8445
	height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing
8446
8447
	// Title width
8448
	var widthPadding = 0;
8449
	var maxLineWidth = function(line) {
8450
		width = Math.max(width, ctx.measureText(line).width + widthPadding);
8451
	};
8452
8453
	ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
8454
	helpers.each(model.title, maxLineWidth);
8455
8456
	// Body width
8457
	ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
8458
	helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth);
8459
8460
	// Body lines may include some extra width due to the color box
8461
	widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
8462
	helpers.each(body, function(bodyItem) {
8463
		helpers.each(bodyItem.before, maxLineWidth);
8464
		helpers.each(bodyItem.lines, maxLineWidth);
8465
		helpers.each(bodyItem.after, maxLineWidth);
8466
	});
8467
8468
	// Reset back to 0
8469
	widthPadding = 0;
8470
8471
	// Footer width
8472
	ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
8473
	helpers.each(model.footer, maxLineWidth);
8474
8475
	// Add padding
8476
	width += 2 * model.xPadding;
8477
8478
	return {
8479
		width: width,
8480
		height: height
8481
	};
8482
}
8483
8484
/**
8485
 * Helper to get the alignment of a tooltip given the size
8486
 */
8487
function determineAlignment(tooltip, size) {
8488
	var model = tooltip._model;
8489
	var chart = tooltip._chart;
8490
	var chartArea = tooltip._chart.chartArea;
8491
	var xAlign = 'center';
8492
	var yAlign = 'center';
8493
8494
	if (model.y < size.height) {
8495
		yAlign = 'top';
8496
	} else if (model.y > (chart.height - size.height)) {
8497
		yAlign = 'bottom';
8498
	}
8499
8500
	var lf, rf; // functions to determine left, right alignment
8501
	var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
8502
	var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
8503
	var midX = (chartArea.left + chartArea.right) / 2;
8504
	var midY = (chartArea.top + chartArea.bottom) / 2;
8505
8506
	if (yAlign === 'center') {
8507
		lf = function(x) {
8508
			return x <= midX;
8509
		};
8510
		rf = function(x) {
8511
			return x > midX;
8512
		};
8513
	} else {
8514
		lf = function(x) {
8515
			return x <= (size.width / 2);
8516
		};
8517
		rf = function(x) {
8518
			return x >= (chart.width - (size.width / 2));
8519
		};
8520
	}
8521
8522
	olf = function(x) {
8523
		return x + size.width + model.caretSize + model.caretPadding > chart.width;
8524
	};
8525
	orf = function(x) {
8526
		return x - size.width - model.caretSize - model.caretPadding < 0;
8527
	};
8528
	yf = function(y) {
8529
		return y <= midY ? 'top' : 'bottom';
8530
	};
8531
8532
	if (lf(model.x)) {
8533
		xAlign = 'left';
8534
8535
		// Is tooltip too wide and goes over the right side of the chart.?
8536
		if (olf(model.x)) {
8537
			xAlign = 'center';
8538
			yAlign = yf(model.y);
8539
		}
8540
	} else if (rf(model.x)) {
8541
		xAlign = 'right';
8542
8543
		// Is tooltip too wide and goes outside left edge of canvas?
8544
		if (orf(model.x)) {
8545
			xAlign = 'center';
8546
			yAlign = yf(model.y);
8547
		}
8548
	}
8549
8550
	var opts = tooltip._options;
8551
	return {
8552
		xAlign: opts.xAlign ? opts.xAlign : xAlign,
8553
		yAlign: opts.yAlign ? opts.yAlign : yAlign
8554
	};
8555
}
8556
8557
/**
8558
 * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
8559
 */
8560
function getBackgroundPoint(vm, size, alignment, chart) {
8561
	// Background Position
8562
	var x = vm.x;
8563
	var y = vm.y;
8564
8565
	var caretSize = vm.caretSize;
8566
	var caretPadding = vm.caretPadding;
8567
	var cornerRadius = vm.cornerRadius;
8568
	var xAlign = alignment.xAlign;
8569
	var yAlign = alignment.yAlign;
8570
	var paddingAndSize = caretSize + caretPadding;
8571
	var radiusAndPadding = cornerRadius + caretPadding;
8572
8573
	if (xAlign === 'right') {
8574
		x -= size.width;
8575
	} else if (xAlign === 'center') {
8576
		x -= (size.width / 2);
8577
		if (x + size.width > chart.width) {
8578
			x = chart.width - size.width;
8579
		}
8580
		if (x < 0) {
8581
			x = 0;
8582
		}
8583
	}
8584
8585
	if (yAlign === 'top') {
8586
		y += paddingAndSize;
8587
	} else if (yAlign === 'bottom') {
8588
		y -= size.height + paddingAndSize;
8589
	} else {
8590
		y -= (size.height / 2);
8591
	}
8592
8593
	if (yAlign === 'center') {
8594
		if (xAlign === 'left') {
8595
			x += paddingAndSize;
8596
		} else if (xAlign === 'right') {
8597
			x -= paddingAndSize;
8598
		}
8599
	} else if (xAlign === 'left') {
8600
		x -= radiusAndPadding;
8601
	} else if (xAlign === 'right') {
8602
		x += radiusAndPadding;
8603
	}
8604
8605
	return {
8606
		x: x,
8607
		y: y
8608
	};
8609
}
8610
8611
/**
8612
 * Helper to build before and after body lines
8613
 */
8614
function getBeforeAfterBodyLines(callback) {
8615
	return pushOrConcat([], splitNewlines(callback));
8616
}
8617
8618
var exports = module.exports = Element.extend({
8619
	initialize: function() {
8620
		this._model = getBaseModel(this._options);
8621
		this._lastActive = [];
8622
	},
8623
8624
	// Get the title
8625
	// Args are: (tooltipItem, data)
8626
	getTitle: function() {
8627
		var me = this;
8628
		var opts = me._options;
8629
		var callbacks = opts.callbacks;
8630
8631
		var beforeTitle = callbacks.beforeTitle.apply(me, arguments);
8632
		var title = callbacks.title.apply(me, arguments);
8633
		var afterTitle = callbacks.afterTitle.apply(me, arguments);
8634
8635
		var lines = [];
8636
		lines = pushOrConcat(lines, splitNewlines(beforeTitle));
8637
		lines = pushOrConcat(lines, splitNewlines(title));
8638
		lines = pushOrConcat(lines, splitNewlines(afterTitle));
8639
8640
		return lines;
8641
	},
8642
8643
	// Args are: (tooltipItem, data)
8644
	getBeforeBody: function() {
8645
		return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments));
8646
	},
8647
8648
	// Args are: (tooltipItem, data)
8649
	getBody: function(tooltipItems, data) {
8650
		var me = this;
8651
		var callbacks = me._options.callbacks;
8652
		var bodyItems = [];
8653
8654
		helpers.each(tooltipItems, function(tooltipItem) {
8655
			var bodyItem = {
8656
				before: [],
8657
				lines: [],
8658
				after: []
8659
			};
8660
			pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data)));
8661
			pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
8662
			pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data)));
8663
8664
			bodyItems.push(bodyItem);
8665
		});
8666
8667
		return bodyItems;
8668
	},
8669
8670
	// Args are: (tooltipItem, data)
8671
	getAfterBody: function() {
8672
		return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments));
8673
	},
8674
8675
	// Get the footer and beforeFooter and afterFooter lines
8676
	// Args are: (tooltipItem, data)
8677
	getFooter: function() {
8678
		var me = this;
8679
		var callbacks = me._options.callbacks;
8680
8681
		var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
8682
		var footer = callbacks.footer.apply(me, arguments);
8683
		var afterFooter = callbacks.afterFooter.apply(me, arguments);
8684
8685
		var lines = [];
8686
		lines = pushOrConcat(lines, splitNewlines(beforeFooter));
8687
		lines = pushOrConcat(lines, splitNewlines(footer));
8688
		lines = pushOrConcat(lines, splitNewlines(afterFooter));
8689
8690
		return lines;
8691
	},
8692
8693
	update: function(changed) {
8694
		var me = this;
8695
		var opts = me._options;
8696
8697
		// Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
8698
		// that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time
8699
		// which breaks any animations.
8700
		var existingModel = me._model;
8701
		var model = me._model = getBaseModel(opts);
8702
		var active = me._active;
8703
8704
		var data = me._data;
8705
8706
		// In the case where active.length === 0 we need to keep these at existing values for good animations
8707
		var alignment = {
8708
			xAlign: existingModel.xAlign,
8709
			yAlign: existingModel.yAlign
8710
		};
8711
		var backgroundPoint = {
8712
			x: existingModel.x,
8713
			y: existingModel.y
8714
		};
8715
		var tooltipSize = {
8716
			width: existingModel.width,
8717
			height: existingModel.height
8718
		};
8719
		var tooltipPosition = {
8720
			x: existingModel.caretX,
8721
			y: existingModel.caretY
8722
		};
8723
8724
		var i, len;
8725
8726
		if (active.length) {
8727
			model.opacity = 1;
8728
8729
			var labelColors = [];
8730
			var labelTextColors = [];
8731
			tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition);
8732
8733
			var tooltipItems = [];
8734
			for (i = 0, len = active.length; i < len; ++i) {
8735
				tooltipItems.push(createTooltipItem(active[i]));
8736
			}
8737
8738
			// If the user provided a filter function, use it to modify the tooltip items
8739
			if (opts.filter) {
8740
				tooltipItems = tooltipItems.filter(function(a) {
8741
					return opts.filter(a, data);
8742
				});
8743
			}
8744
8745
			// If the user provided a sorting function, use it to modify the tooltip items
8746
			if (opts.itemSort) {
8747
				tooltipItems = tooltipItems.sort(function(a, b) {
8748
					return opts.itemSort(a, b, data);
8749
				});
8750
			}
8751
8752
			// Determine colors for boxes
8753
			helpers.each(tooltipItems, function(tooltipItem) {
8754
				labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
8755
				labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
8756
			});
8757
8758
8759
			// Build the Text Lines
8760
			model.title = me.getTitle(tooltipItems, data);
8761
			model.beforeBody = me.getBeforeBody(tooltipItems, data);
8762
			model.body = me.getBody(tooltipItems, data);
8763
			model.afterBody = me.getAfterBody(tooltipItems, data);
8764
			model.footer = me.getFooter(tooltipItems, data);
8765
8766
			// Initial positioning and colors
8767
			model.x = Math.round(tooltipPosition.x);
8768
			model.y = Math.round(tooltipPosition.y);
8769
			model.caretPadding = opts.caretPadding;
8770
			model.labelColors = labelColors;
8771
			model.labelTextColors = labelTextColors;
8772
8773
			// data points
8774
			model.dataPoints = tooltipItems;
8775
8776
			// We need to determine alignment of the tooltip
8777
			tooltipSize = getTooltipSize(this, model);
8778
			alignment = determineAlignment(this, tooltipSize);
8779
			// Final Size and Position
8780
			backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart);
8781
		} else {
8782
			model.opacity = 0;
8783
		}
8784
8785
		model.xAlign = alignment.xAlign;
8786
		model.yAlign = alignment.yAlign;
8787
		model.x = backgroundPoint.x;
8788
		model.y = backgroundPoint.y;
8789
		model.width = tooltipSize.width;
8790
		model.height = tooltipSize.height;
8791
8792
		// Point where the caret on the tooltip points to
8793
		model.caretX = tooltipPosition.x;
8794
		model.caretY = tooltipPosition.y;
8795
8796
		me._model = model;
8797
8798
		if (changed && opts.custom) {
8799
			opts.custom.call(me, model);
8800
		}
8801
8802
		return me;
8803
	},
8804
8805
	drawCaret: function(tooltipPoint, size) {
8806
		var ctx = this._chart.ctx;
8807
		var vm = this._view;
8808
		var caretPosition = this.getCaretPosition(tooltipPoint, size, vm);
8809
8810
		ctx.lineTo(caretPosition.x1, caretPosition.y1);
8811
		ctx.lineTo(caretPosition.x2, caretPosition.y2);
8812
		ctx.lineTo(caretPosition.x3, caretPosition.y3);
8813
	},
8814
	getCaretPosition: function(tooltipPoint, size, vm) {
8815
		var x1, x2, x3, y1, y2, y3;
8816
		var caretSize = vm.caretSize;
8817
		var cornerRadius = vm.cornerRadius;
8818
		var xAlign = vm.xAlign;
8819
		var yAlign = vm.yAlign;
8820
		var ptX = tooltipPoint.x;
8821
		var ptY = tooltipPoint.y;
8822
		var width = size.width;
8823
		var height = size.height;
8824
8825
		if (yAlign === 'center') {
8826
			y2 = ptY + (height / 2);
8827
8828
			if (xAlign === 'left') {
8829
				x1 = ptX;
8830
				x2 = x1 - caretSize;
8831
				x3 = x1;
8832
8833
				y1 = y2 + caretSize;
8834
				y3 = y2 - caretSize;
8835
			} else {
8836
				x1 = ptX + width;
8837
				x2 = x1 + caretSize;
8838
				x3 = x1;
8839
8840
				y1 = y2 - caretSize;
8841
				y3 = y2 + caretSize;
8842
			}
8843
		} else {
8844
			if (xAlign === 'left') {
8845
				x2 = ptX + cornerRadius + (caretSize);
8846
				x1 = x2 - caretSize;
8847
				x3 = x2 + caretSize;
8848
			} else if (xAlign === 'right') {
8849
				x2 = ptX + width - cornerRadius - caretSize;
8850
				x1 = x2 - caretSize;
8851
				x3 = x2 + caretSize;
8852
			} else {
8853
				x2 = vm.caretX;
8854
				x1 = x2 - caretSize;
8855
				x3 = x2 + caretSize;
8856
			}
8857
			if (yAlign === 'top') {
8858
				y1 = ptY;
8859
				y2 = y1 - caretSize;
8860
				y3 = y1;
8861
			} else {
8862
				y1 = ptY + height;
8863
				y2 = y1 + caretSize;
8864
				y3 = y1;
8865
				// invert drawing order
8866
				var tmp = x3;
8867
				x3 = x1;
8868
				x1 = tmp;
8869
			}
8870
		}
8871
		return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3};
8872
	},
8873
8874
	drawTitle: function(pt, vm, ctx, opacity) {
8875
		var title = vm.title;
8876
8877
		if (title.length) {
8878
			ctx.textAlign = vm._titleAlign;
8879
			ctx.textBaseline = 'top';
8880
8881
			var titleFontSize = vm.titleFontSize;
8882
			var titleSpacing = vm.titleSpacing;
8883
8884
			ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity);
8885
			ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
8886
8887
			var i, len;
8888
			for (i = 0, len = title.length; i < len; ++i) {
8889
				ctx.fillText(title[i], pt.x, pt.y);
8890
				pt.y += titleFontSize + titleSpacing; // Line Height and spacing
8891
8892
				if (i + 1 === title.length) {
8893
					pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
8894
				}
8895
			}
8896
		}
8897
	},
8898
8899
	drawBody: function(pt, vm, ctx, opacity) {
8900
		var bodyFontSize = vm.bodyFontSize;
8901
		var bodySpacing = vm.bodySpacing;
8902
		var body = vm.body;
8903
8904
		ctx.textAlign = vm._bodyAlign;
8905
		ctx.textBaseline = 'top';
8906
		ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
8907
8908
		// Before Body
8909
		var xLinePadding = 0;
8910
		var fillLineOfText = function(line) {
8911
			ctx.fillText(line, pt.x + xLinePadding, pt.y);
8912
			pt.y += bodyFontSize + bodySpacing;
8913
		};
8914
8915
		// Before body lines
8916
		ctx.fillStyle = mergeOpacity(vm.bodyFontColor, opacity);
8917
		helpers.each(vm.beforeBody, fillLineOfText);
8918
8919
		var drawColorBoxes = vm.displayColors;
8920
		xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;
8921
8922
		// Draw body lines now
8923
		helpers.each(body, function(bodyItem, i) {
8924
			var textColor = mergeOpacity(vm.labelTextColors[i], opacity);
8925
			ctx.fillStyle = textColor;
8926
			helpers.each(bodyItem.before, fillLineOfText);
8927
8928
			helpers.each(bodyItem.lines, function(line) {
8929
				// Draw Legend-like boxes if needed
8930
				if (drawColorBoxes) {
8931
					// Fill a white rect so that colours merge nicely if the opacity is < 1
8932
					ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity);
8933
					ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
8934
8935
					// Border
8936
					ctx.lineWidth = 1;
8937
					ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity);
8938
					ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
8939
8940
					// Inner square
8941
					ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity);
8942
					ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
8943
					ctx.fillStyle = textColor;
8944
				}
8945
8946
				fillLineOfText(line);
8947
			});
8948
8949
			helpers.each(bodyItem.after, fillLineOfText);
8950
		});
8951
8952
		// Reset back to 0 for after body
8953
		xLinePadding = 0;
8954
8955
		// After body lines
8956
		helpers.each(vm.afterBody, fillLineOfText);
8957
		pt.y -= bodySpacing; // Remove last body spacing
8958
	},
8959
8960
	drawFooter: function(pt, vm, ctx, opacity) {
8961
		var footer = vm.footer;
8962
8963
		if (footer.length) {
8964
			pt.y += vm.footerMarginTop;
8965
8966
			ctx.textAlign = vm._footerAlign;
8967
			ctx.textBaseline = 'top';
8968
8969
			ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity);
8970
			ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
8971
8972
			helpers.each(footer, function(line) {
8973
				ctx.fillText(line, pt.x, pt.y);
8974
				pt.y += vm.footerFontSize + vm.footerSpacing;
8975
			});
8976
		}
8977
	},
8978
8979
	drawBackground: function(pt, vm, ctx, tooltipSize, opacity) {
8980
		ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);
8981
		ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity);
8982
		ctx.lineWidth = vm.borderWidth;
8983
		var xAlign = vm.xAlign;
8984
		var yAlign = vm.yAlign;
8985
		var x = pt.x;
8986
		var y = pt.y;
8987
		var width = tooltipSize.width;
8988
		var height = tooltipSize.height;
8989
		var radius = vm.cornerRadius;
8990
8991
		ctx.beginPath();
8992
		ctx.moveTo(x + radius, y);
8993
		if (yAlign === 'top') {
8994
			this.drawCaret(pt, tooltipSize);
8995
		}
8996
		ctx.lineTo(x + width - radius, y);
8997
		ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
8998
		if (yAlign === 'center' && xAlign === 'right') {
8999
			this.drawCaret(pt, tooltipSize);
9000
		}
9001
		ctx.lineTo(x + width, y + height - radius);
9002
		ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
9003
		if (yAlign === 'bottom') {
9004
			this.drawCaret(pt, tooltipSize);
9005
		}
9006
		ctx.lineTo(x + radius, y + height);
9007
		ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
9008
		if (yAlign === 'center' && xAlign === 'left') {
9009
			this.drawCaret(pt, tooltipSize);
9010
		}
9011
		ctx.lineTo(x, y + radius);
9012
		ctx.quadraticCurveTo(x, y, x + radius, y);
9013
		ctx.closePath();
9014
9015
		ctx.fill();
9016
9017
		if (vm.borderWidth > 0) {
9018
			ctx.stroke();
9019
		}
9020
	},
9021
9022
	draw: function() {
9023
		var ctx = this._chart.ctx;
9024
		var vm = this._view;
9025
9026
		if (vm.opacity === 0) {
9027
			return;
9028
		}
9029
9030
		var tooltipSize = {
9031
			width: vm.width,
9032
			height: vm.height
9033
		};
9034
		var pt = {
9035
			x: vm.x,
9036
			y: vm.y
9037
		};
9038
9039
		// IE11/Edge does not like very small opacities, so snap to 0
9040
		var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
9041
9042
		// Truthy/falsey value for empty tooltip
9043
		var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;
9044
9045
		if (this._options.enabled && hasTooltipContent) {
9046
			// Draw Background
9047
			this.drawBackground(pt, vm, ctx, tooltipSize, opacity);
9048
9049
			// Draw Title, Body, and Footer
9050
			pt.x += vm.xPadding;
9051
			pt.y += vm.yPadding;
9052
9053
			// Titles
9054
			this.drawTitle(pt, vm, ctx, opacity);
9055
9056
			// Body
9057
			this.drawBody(pt, vm, ctx, opacity);
9058
9059
			// Footer
9060
			this.drawFooter(pt, vm, ctx, opacity);
9061
		}
9062
	},
9063
9064
	/**
9065
	 * Handle an event
9066
	 * @private
9067
	 * @param {IEvent} event - The event to handle
9068
	 * @returns {Boolean} true if the tooltip changed
9069
	 */
9070
	handleEvent: function(e) {
9071
		var me = this;
9072
		var options = me._options;
9073
		var changed = false;
9074
9075
		me._lastActive = me._lastActive || [];
9076
9077
		// Find Active Elements for tooltips
9078
		if (e.type === 'mouseout') {
9079
			me._active = [];
9080
		} else {
9081
			me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
9082
		}
9083
9084
		// Remember Last Actives
9085
		changed = !helpers.arrayEquals(me._active, me._lastActive);
9086
9087
		// Only handle target event on tooltip change
9088
		if (changed) {
9089
			me._lastActive = me._active;
9090
9091
			if (options.enabled || options.custom) {
9092
				me._eventPosition = {
9093
					x: e.x,
9094
					y: e.y
9095
				};
9096
9097
				me.update(true);
9098
				me.pivot();
9099
			}
9100
		}
9101
9102
		return changed;
9103
	}
9104
});
9105
9106
/**
9107
 * @namespace Chart.Tooltip.positioners
9108
 */
9109
exports.positioners = positioners;
9110
9111
9112
},{"26":26,"27":27,"46":46}],37:[function(require,module,exports){
9113
'use strict';
9114
9115
var defaults = require(26);
9116
var Element = require(27);
9117
var helpers = require(46);
9118
9119
defaults._set('global', {
9120
	elements: {
9121
		arc: {
9122
			backgroundColor: defaults.global.defaultColor,
9123
			borderColor: '#fff',
9124
			borderWidth: 2
9125
		}
9126
	}
9127
});
9128
9129
module.exports = Element.extend({
9130
	inLabelRange: function(mouseX) {
9131
		var vm = this._view;
9132
9133
		if (vm) {
9134
			return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
9135
		}
9136
		return false;
9137
	},
9138
9139
	inRange: function(chartX, chartY) {
9140
		var vm = this._view;
9141
9142
		if (vm) {
9143
			var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY});
9144
			var	angle = pointRelativePosition.angle;
9145
			var distance = pointRelativePosition.distance;
9146
9147
			// Sanitise angle range
9148
			var startAngle = vm.startAngle;
9149
			var endAngle = vm.endAngle;
9150
			while (endAngle < startAngle) {
9151
				endAngle += 2.0 * Math.PI;
9152
			}
9153
			while (angle > endAngle) {
9154
				angle -= 2.0 * Math.PI;
9155
			}
9156
			while (angle < startAngle) {
9157
				angle += 2.0 * Math.PI;
9158
			}
9159
9160
			// Check if within the range of the open/close angle
9161
			var betweenAngles = (angle >= startAngle && angle <= endAngle);
9162
			var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
9163
9164
			return (betweenAngles && withinRadius);
9165
		}
9166
		return false;
9167
	},
9168
9169
	getCenterPoint: function() {
9170
		var vm = this._view;
9171
		var halfAngle = (vm.startAngle + vm.endAngle) / 2;
9172
		var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
9173
		return {
9174
			x: vm.x + Math.cos(halfAngle) * halfRadius,
9175
			y: vm.y + Math.sin(halfAngle) * halfRadius
9176
		};
9177
	},
9178
9179
	getArea: function() {
9180
		var vm = this._view;
9181
		return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
9182
	},
9183
9184
	tooltipPosition: function() {
9185
		var vm = this._view;
9186
		var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
9187
		var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
9188
9189
		return {
9190
			x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
9191
			y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
9192
		};
9193
	},
9194
9195
	draw: function() {
9196
		var ctx = this._chart.ctx;
9197
		var vm = this._view;
9198
		var sA = vm.startAngle;
9199
		var eA = vm.endAngle;
9200
9201
		ctx.beginPath();
9202
9203
		ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
9204
		ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
9205
9206
		ctx.closePath();
9207
		ctx.strokeStyle = vm.borderColor;
9208
		ctx.lineWidth = vm.borderWidth;
9209
9210
		ctx.fillStyle = vm.backgroundColor;
9211
9212
		ctx.fill();
9213
		ctx.lineJoin = 'bevel';
9214
9215
		if (vm.borderWidth) {
9216
			ctx.stroke();
9217
		}
9218
	}
9219
});
9220
9221
},{"26":26,"27":27,"46":46}],38:[function(require,module,exports){
9222
'use strict';
9223
9224
var defaults = require(26);
9225
var Element = require(27);
9226
var helpers = require(46);
9227
9228
var globalDefaults = defaults.global;
9229
9230
defaults._set('global', {
9231
	elements: {
9232
		line: {
9233
			tension: 0.4,
9234
			backgroundColor: globalDefaults.defaultColor,
9235
			borderWidth: 3,
9236
			borderColor: globalDefaults.defaultColor,
9237
			borderCapStyle: 'butt',
9238
			borderDash: [],
9239
			borderDashOffset: 0.0,
9240
			borderJoinStyle: 'miter',
9241
			capBezierPoints: true,
9242
			fill: true, // do we fill in the area between the line and its base axis
9243
		}
9244
	}
9245
});
9246
9247
module.exports = Element.extend({
9248
	draw: function() {
9249
		var me = this;
9250
		var vm = me._view;
9251
		var ctx = me._chart.ctx;
9252
		var spanGaps = vm.spanGaps;
9253
		var points = me._children.slice(); // clone array
9254
		var globalOptionLineElements = globalDefaults.elements.line;
9255
		var lastDrawnIndex = -1;
9256
		var index, current, previous, currentVM;
9257
9258
		// If we are looping, adding the first point again
9259
		if (me._loop && points.length) {
9260
			points.push(points[0]);
9261
		}
9262
9263
		ctx.save();
9264
9265
		// Stroke Line Options
9266
		ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
9267
9268
		// IE 9 and 10 do not support line dash
9269
		if (ctx.setLineDash) {
9270
			ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
9271
		}
9272
9273
		ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
9274
		ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
9275
		ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
9276
		ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
9277
9278
		// Stroke Line
9279
		ctx.beginPath();
9280
		lastDrawnIndex = -1;
9281
9282
		for (index = 0; index < points.length; ++index) {
9283
			current = points[index];
9284
			previous = helpers.previousItem(points, index);
9285
			currentVM = current._view;
9286
9287
			// First point moves to it's starting position no matter what
9288
			if (index === 0) {
9289
				if (!currentVM.skip) {
9290
					ctx.moveTo(currentVM.x, currentVM.y);
9291
					lastDrawnIndex = index;
9292
				}
9293
			} else {
9294
				previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];
9295
9296
				if (!currentVM.skip) {
9297
					if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
9298
						// There was a gap and this is the first point after the gap
9299
						ctx.moveTo(currentVM.x, currentVM.y);
9300
					} else {
9301
						// Line to next point
9302
						helpers.canvas.lineTo(ctx, previous._view, current._view);
9303
					}
9304
					lastDrawnIndex = index;
9305
				}
9306
			}
9307
		}
9308
9309
		ctx.stroke();
9310
		ctx.restore();
9311
	}
9312
});
9313
9314
},{"26":26,"27":27,"46":46}],39:[function(require,module,exports){
9315
'use strict';
9316
9317
var defaults = require(26);
9318
var Element = require(27);
9319
var helpers = require(46);
9320
9321
var defaultColor = defaults.global.defaultColor;
9322
9323
defaults._set('global', {
9324
	elements: {
9325
		point: {
9326
			radius: 3,
9327
			pointStyle: 'circle',
9328
			backgroundColor: defaultColor,
9329
			borderColor: defaultColor,
9330
			borderWidth: 1,
9331
			// Hover
9332
			hitRadius: 1,
9333
			hoverRadius: 4,
9334
			hoverBorderWidth: 1
9335
		}
9336
	}
9337
});
9338
9339
function xRange(mouseX) {
9340
	var vm = this._view;
9341
	return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false;
9342
}
9343
9344
function yRange(mouseY) {
9345
	var vm = this._view;
9346
	return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false;
9347
}
9348
9349
module.exports = Element.extend({
9350
	inRange: function(mouseX, mouseY) {
9351
		var vm = this._view;
9352
		return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
9353
	},
9354
9355
	inLabelRange: xRange,
9356
	inXRange: xRange,
9357
	inYRange: yRange,
9358
9359
	getCenterPoint: function() {
9360
		var vm = this._view;
9361
		return {
9362
			x: vm.x,
9363
			y: vm.y
9364
		};
9365
	},
9366
9367
	getArea: function() {
9368
		return Math.PI * Math.pow(this._view.radius, 2);
9369
	},
9370
9371
	tooltipPosition: function() {
9372
		var vm = this._view;
9373
		return {
9374
			x: vm.x,
9375
			y: vm.y,
9376
			padding: vm.radius + vm.borderWidth
9377
		};
9378
	},
9379
9380
	draw: function(chartArea) {
9381
		var vm = this._view;
9382
		var model = this._model;
9383
		var ctx = this._chart.ctx;
9384
		var pointStyle = vm.pointStyle;
9385
		var rotation = vm.rotation;
9386
		var radius = vm.radius;
9387
		var x = vm.x;
9388
		var y = vm.y;
9389
		var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)
9390
9391
		if (vm.skip) {
9392
			return;
9393
		}
9394
9395
		// Clipping for Points.
9396
		if (chartArea === undefined || (model.x >= chartArea.left && chartArea.right * errMargin >= model.x && model.y >= chartArea.top && chartArea.bottom * errMargin >= model.y)) {
9397
			ctx.strokeStyle = vm.borderColor || defaultColor;
9398
			ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth);
9399
			ctx.fillStyle = vm.backgroundColor || defaultColor;
9400
			helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation);
9401
		}
9402
	}
9403
});
9404
9405
},{"26":26,"27":27,"46":46}],40:[function(require,module,exports){
9406
'use strict';
9407
9408
var defaults = require(26);
9409
var Element = require(27);
9410
9411
defaults._set('global', {
9412
	elements: {
9413
		rectangle: {
9414
			backgroundColor: defaults.global.defaultColor,
9415
			borderColor: defaults.global.defaultColor,
9416
			borderSkipped: 'bottom',
9417
			borderWidth: 0
9418
		}
9419
	}
9420
});
9421
9422
function isVertical(bar) {
9423
	return bar._view.width !== undefined;
9424
}
9425
9426
/**
9427
 * Helper function to get the bounds of the bar regardless of the orientation
9428
 * @param bar {Chart.Element.Rectangle} the bar
9429
 * @return {Bounds} bounds of the bar
9430
 * @private
9431
 */
9432
function getBarBounds(bar) {
9433
	var vm = bar._view;
9434
	var x1, x2, y1, y2;
9435
9436
	if (isVertical(bar)) {
9437
		// vertical
9438
		var halfWidth = vm.width / 2;
9439
		x1 = vm.x - halfWidth;
9440
		x2 = vm.x + halfWidth;
9441
		y1 = Math.min(vm.y, vm.base);
9442
		y2 = Math.max(vm.y, vm.base);
9443
	} else {
9444
		// horizontal bar
9445
		var halfHeight = vm.height / 2;
9446
		x1 = Math.min(vm.x, vm.base);
9447
		x2 = Math.max(vm.x, vm.base);
9448
		y1 = vm.y - halfHeight;
9449
		y2 = vm.y + halfHeight;
9450
	}
9451
9452
	return {
9453
		left: x1,
9454
		top: y1,
9455
		right: x2,
9456
		bottom: y2
9457
	};
9458
}
9459
9460
module.exports = Element.extend({
9461
	draw: function() {
9462
		var ctx = this._chart.ctx;
9463
		var vm = this._view;
9464
		var left, right, top, bottom, signX, signY, borderSkipped;
9465
		var borderWidth = vm.borderWidth;
9466
9467
		if (!vm.horizontal) {
9468
			// bar
9469
			left = vm.x - vm.width / 2;
9470
			right = vm.x + vm.width / 2;
9471
			top = vm.y;
9472
			bottom = vm.base;
9473
			signX = 1;
9474
			signY = bottom > top ? 1 : -1;
9475
			borderSkipped = vm.borderSkipped || 'bottom';
9476
		} else {
9477
			// horizontal bar
9478
			left = vm.base;
9479
			right = vm.x;
9480
			top = vm.y - vm.height / 2;
9481
			bottom = vm.y + vm.height / 2;
9482
			signX = right > left ? 1 : -1;
9483
			signY = 1;
9484
			borderSkipped = vm.borderSkipped || 'left';
9485
		}
9486
9487
		// Canvas doesn't allow us to stroke inside the width so we can
9488
		// adjust the sizes to fit if we're setting a stroke on the line
9489
		if (borderWidth) {
9490
			// borderWidth shold be less than bar width and bar height.
9491
			var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
9492
			borderWidth = borderWidth > barSize ? barSize : borderWidth;
9493
			var halfStroke = borderWidth / 2;
9494
			// Adjust borderWidth when bar top position is near vm.base(zero).
9495
			var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
9496
			var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
9497
			var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
9498
			var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);
9499
			// not become a vertical line?
9500
			if (borderLeft !== borderRight) {
9501
				top = borderTop;
9502
				bottom = borderBottom;
9503
			}
9504
			// not become a horizontal line?
9505
			if (borderTop !== borderBottom) {
9506
				left = borderLeft;
9507
				right = borderRight;
9508
			}
9509
		}
9510
9511
		ctx.beginPath();
9512
		ctx.fillStyle = vm.backgroundColor;
9513
		ctx.strokeStyle = vm.borderColor;
9514
		ctx.lineWidth = borderWidth;
9515
9516
		// Corner points, from bottom-left to bottom-right clockwise
9517
		// | 1 2 |
9518
		// | 0 3 |
9519
		var corners = [
9520
			[left, bottom],
9521
			[left, top],
9522
			[right, top],
9523
			[right, bottom]
9524
		];
9525
9526
		// Find first (starting) corner with fallback to 'bottom'
9527
		var borders = ['bottom', 'left', 'top', 'right'];
9528
		var startCorner = borders.indexOf(borderSkipped, 0);
9529
		if (startCorner === -1) {
9530
			startCorner = 0;
9531
		}
9532
9533
		function cornerAt(index) {
9534
			return corners[(startCorner + index) % 4];
9535
		}
9536
9537
		// Draw rectangle from 'startCorner'
9538
		var corner = cornerAt(0);
9539
		ctx.moveTo(corner[0], corner[1]);
9540
9541
		for (var i = 1; i < 4; i++) {
9542
			corner = cornerAt(i);
9543
			ctx.lineTo(corner[0], corner[1]);
9544
		}
9545
9546
		ctx.fill();
9547
		if (borderWidth) {
9548
			ctx.stroke();
9549
		}
9550
	},
9551
9552
	height: function() {
9553
		var vm = this._view;
9554
		return vm.base - vm.y;
9555
	},
9556
9557
	inRange: function(mouseX, mouseY) {
9558
		var inRange = false;
9559
9560
		if (this._view) {
9561
			var bounds = getBarBounds(this);
9562
			inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
9563
		}
9564
9565
		return inRange;
9566
	},
9567
9568
	inLabelRange: function(mouseX, mouseY) {
9569
		var me = this;
9570
		if (!me._view) {
9571
			return false;
9572
		}
9573
9574
		var inRange = false;
9575
		var bounds = getBarBounds(me);
9576
9577
		if (isVertical(me)) {
9578
			inRange = mouseX >= bounds.left && mouseX <= bounds.right;
9579
		} else {
9580
			inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
9581
		}
9582
9583
		return inRange;
9584
	},
9585
9586
	inXRange: function(mouseX) {
9587
		var bounds = getBarBounds(this);
9588
		return mouseX >= bounds.left && mouseX <= bounds.right;
9589
	},
9590
9591
	inYRange: function(mouseY) {
9592
		var bounds = getBarBounds(this);
9593
		return mouseY >= bounds.top && mouseY <= bounds.bottom;
9594
	},
9595
9596
	getCenterPoint: function() {
9597
		var vm = this._view;
9598
		var x, y;
9599
		if (isVertical(this)) {
9600
			x = vm.x;
9601
			y = (vm.y + vm.base) / 2;
9602
		} else {
9603
			x = (vm.x + vm.base) / 2;
9604
			y = vm.y;
9605
		}
9606
9607
		return {x: x, y: y};
9608
	},
9609
9610
	getArea: function() {
9611
		var vm = this._view;
9612
		return vm.width * Math.abs(vm.y - vm.base);
9613
	},
9614
9615
	tooltipPosition: function() {
9616
		var vm = this._view;
9617
		return {
9618
			x: vm.x,
9619
			y: vm.y
9620
		};
9621
	}
9622
});
9623
9624
},{"26":26,"27":27}],41:[function(require,module,exports){
9625
'use strict';
9626
9627
module.exports = {};
9628
module.exports.Arc = require(37);
9629
module.exports.Line = require(38);
9630
module.exports.Point = require(39);
9631
module.exports.Rectangle = require(40);
9632
9633
},{"37":37,"38":38,"39":39,"40":40}],42:[function(require,module,exports){
9634
'use strict';
9635
9636
var helpers = require(43);
9637
9638
/**
9639
 * @namespace Chart.helpers.canvas
9640
 */
9641
var exports = module.exports = {
9642
	/**
9643
	 * Clears the entire canvas associated to the given `chart`.
9644
	 * @param {Chart} chart - The chart for which to clear the canvas.
9645
	 */
9646
	clear: function(chart) {
9647
		chart.ctx.clearRect(0, 0, chart.width, chart.height);
9648
	},
9649
9650
	/**
9651
	 * Creates a "path" for a rectangle with rounded corners at position (x, y) with a
9652
	 * given size (width, height) and the same `radius` for all corners.
9653
	 * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
9654
	 * @param {Number} x - The x axis of the coordinate for the rectangle starting point.
9655
	 * @param {Number} y - The y axis of the coordinate for the rectangle starting point.
9656
	 * @param {Number} width - The rectangle's width.
9657
	 * @param {Number} height - The rectangle's height.
9658
	 * @param {Number} radius - The rounded amount (in pixels) for the four corners.
9659
	 * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
9660
	 */
9661
	roundedRect: function(ctx, x, y, width, height, radius) {
9662
		if (radius) {
9663
			// NOTE(SB) `epsilon` helps to prevent minor artifacts appearing
9664
			// on Chrome when `r` is exactly half the height or the width.
9665
			var epsilon = 0.0000001;
9666
			var r = Math.min(radius, (height / 2) - epsilon, (width / 2) - epsilon);
9667
9668
			ctx.moveTo(x + r, y);
9669
			ctx.lineTo(x + width - r, y);
9670
			ctx.arcTo(x + width, y, x + width, y + r, r);
9671
			ctx.lineTo(x + width, y + height - r);
9672
			ctx.arcTo(x + width, y + height, x + width - r, y + height, r);
9673
			ctx.lineTo(x + r, y + height);
9674
			ctx.arcTo(x, y + height, x, y + height - r, r);
9675
			ctx.lineTo(x, y + r);
9676
			ctx.arcTo(x, y, x + r, y, r);
9677
			ctx.closePath();
9678
			ctx.moveTo(x, y);
9679
		} else {
9680
			ctx.rect(x, y, width, height);
9681
		}
9682
	},
9683
9684
	drawPoint: function(ctx, style, radius, x, y, rotation) {
9685
		var type, edgeLength, xOffset, yOffset, height, size;
9686
		rotation = rotation || 0;
9687
9688
		if (style && typeof style === 'object') {
9689
			type = style.toString();
9690
			if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
9691
				ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height);
9692
				return;
9693
			}
9694
		}
9695
9696
		if (isNaN(radius) || radius <= 0) {
9697
			return;
9698
		}
9699
9700
		ctx.save();
9701
		ctx.translate(x, y);
9702
		ctx.rotate(rotation * Math.PI / 180);
9703
		ctx.beginPath();
9704
9705
		switch (style) {
9706
		// Default includes circle
9707
		default:
9708
			ctx.arc(0, 0, radius, 0, Math.PI * 2);
9709
			ctx.closePath();
9710
			break;
9711
		case 'triangle':
9712
			edgeLength = 3 * radius / Math.sqrt(3);
9713
			height = edgeLength * Math.sqrt(3) / 2;
9714
			ctx.moveTo(-edgeLength / 2, height / 3);
9715
			ctx.lineTo(edgeLength / 2, height / 3);
9716
			ctx.lineTo(0, -2 * height / 3);
9717
			ctx.closePath();
9718
			break;
9719
		case 'rect':
9720
			size = 1 / Math.SQRT2 * radius;
9721
			ctx.rect(-size, -size, 2 * size, 2 * size);
9722
			break;
9723
		case 'rectRounded':
9724
			var offset = radius / Math.SQRT2;
9725
			var leftX = -offset;
9726
			var topY = -offset;
9727
			var sideSize = Math.SQRT2 * radius;
9728
9729
			// NOTE(SB) the rounded rect implementation changed to use `arcTo`
9730
			// instead of `quadraticCurveTo` since it generates better results
9731
			// when rect is almost a circle. 0.425 (instead of 0.5) produces
9732
			// results visually closer to the previous impl.
9733
			this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius * 0.425);
9734
			break;
9735
		case 'rectRot':
9736
			size = 1 / Math.SQRT2 * radius;
9737
			ctx.moveTo(-size, 0);
9738
			ctx.lineTo(0, size);
9739
			ctx.lineTo(size, 0);
9740
			ctx.lineTo(0, -size);
9741
			ctx.closePath();
9742
			break;
9743
		case 'cross':
9744
			ctx.moveTo(0, radius);
9745
			ctx.lineTo(0, -radius);
9746
			ctx.moveTo(-radius, 0);
9747
			ctx.lineTo(radius, 0);
9748
			break;
9749
		case 'crossRot':
9750
			xOffset = Math.cos(Math.PI / 4) * radius;
9751
			yOffset = Math.sin(Math.PI / 4) * radius;
9752
			ctx.moveTo(-xOffset, -yOffset);
9753
			ctx.lineTo(xOffset, yOffset);
9754
			ctx.moveTo(-xOffset, yOffset);
9755
			ctx.lineTo(xOffset, -yOffset);
9756
			break;
9757
		case 'star':
9758
			ctx.moveTo(0, radius);
9759
			ctx.lineTo(0, -radius);
9760
			ctx.moveTo(-radius, 0);
9761
			ctx.lineTo(radius, 0);
9762
			xOffset = Math.cos(Math.PI / 4) * radius;
9763
			yOffset = Math.sin(Math.PI / 4) * radius;
9764
			ctx.moveTo(-xOffset, -yOffset);
9765
			ctx.lineTo(xOffset, yOffset);
9766
			ctx.moveTo(-xOffset, yOffset);
9767
			ctx.lineTo(xOffset, -yOffset);
9768
			break;
9769
		case 'line':
9770
			ctx.moveTo(-radius, 0);
9771
			ctx.lineTo(radius, 0);
9772
			break;
9773
		case 'dash':
9774
			ctx.moveTo(0, 0);
9775
			ctx.lineTo(radius, 0);
9776
			break;
9777
		}
9778
9779
		ctx.fill();
9780
		ctx.stroke();
9781
		ctx.restore();
9782
	},
9783
9784
	clipArea: function(ctx, area) {
9785
		ctx.save();
9786
		ctx.beginPath();
9787
		ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
9788
		ctx.clip();
9789
	},
9790
9791
	unclipArea: function(ctx) {
9792
		ctx.restore();
9793
	},
9794
9795
	lineTo: function(ctx, previous, target, flip) {
9796
		if (target.steppedLine) {
9797
			if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) {
9798
				ctx.lineTo(previous.x, target.y);
9799
			} else {
9800
				ctx.lineTo(target.x, previous.y);
9801
			}
9802
			ctx.lineTo(target.x, target.y);
9803
			return;
9804
		}
9805
9806
		if (!target.tension) {
9807
			ctx.lineTo(target.x, target.y);
9808
			return;
9809
		}
9810
9811
		ctx.bezierCurveTo(
9812
			flip ? previous.controlPointPreviousX : previous.controlPointNextX,
9813
			flip ? previous.controlPointPreviousY : previous.controlPointNextY,
9814
			flip ? target.controlPointNextX : target.controlPointPreviousX,
9815
			flip ? target.controlPointNextY : target.controlPointPreviousY,
9816
			target.x,
9817
			target.y);
9818
	}
9819
};
9820
9821
// DEPRECATIONS
9822
9823
/**
9824
 * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
9825
 * @namespace Chart.helpers.clear
9826
 * @deprecated since version 2.7.0
9827
 * @todo remove at version 3
9828
 * @private
9829
 */
9830
helpers.clear = exports.clear;
9831
9832
/**
9833
 * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
9834
 * @namespace Chart.helpers.drawRoundedRectangle
9835
 * @deprecated since version 2.7.0
9836
 * @todo remove at version 3
9837
 * @private
9838
 */
9839
helpers.drawRoundedRectangle = function(ctx) {
9840
	ctx.beginPath();
9841
	exports.roundedRect.apply(exports, arguments);
9842
};
9843
9844
},{"43":43}],43:[function(require,module,exports){
9845
'use strict';
9846
9847
/**
9848
 * @namespace Chart.helpers
9849
 */
9850
var helpers = {
9851
	/**
9852
	 * An empty function that can be used, for example, for optional callback.
9853
	 */
9854
	noop: function() {},
9855
9856
	/**
9857
	 * Returns a unique id, sequentially generated from a global variable.
9858
	 * @returns {Number}
9859
	 * @function
9860
	 */
9861
	uid: (function() {
9862
		var id = 0;
9863
		return function() {
9864
			return id++;
9865
		};
9866
	}()),
9867
9868
	/**
9869
	 * Returns true if `value` is neither null nor undefined, else returns false.
9870
	 * @param {*} value - The value to test.
9871
	 * @returns {Boolean}
9872
	 * @since 2.7.0
9873
	 */
9874
	isNullOrUndef: function(value) {
9875
		return value === null || typeof value === 'undefined';
9876
	},
9877
9878
	/**
9879
	 * Returns true if `value` is an array, else returns false.
9880
	 * @param {*} value - The value to test.
9881
	 * @returns {Boolean}
9882
	 * @function
9883
	 */
9884
	isArray: Array.isArray ? Array.isArray : function(value) {
9885
		return Object.prototype.toString.call(value) === '[object Array]';
9886
	},
9887
9888
	/**
9889
	 * Returns true if `value` is an object (excluding null), else returns false.
9890
	 * @param {*} value - The value to test.
9891
	 * @returns {Boolean}
9892
	 * @since 2.7.0
9893
	 */
9894
	isObject: function(value) {
9895
		return value !== null && Object.prototype.toString.call(value) === '[object Object]';
9896
	},
9897
9898
	/**
9899
	 * Returns `value` if defined, else returns `defaultValue`.
9900
	 * @param {*} value - The value to return if defined.
9901
	 * @param {*} defaultValue - The value to return if `value` is undefined.
9902
	 * @returns {*}
9903
	 */
9904
	valueOrDefault: function(value, defaultValue) {
9905
		return typeof value === 'undefined' ? defaultValue : value;
9906
	},
9907
9908
	/**
9909
	 * Returns value at the given `index` in array if defined, else returns `defaultValue`.
9910
	 * @param {Array} value - The array to lookup for value at `index`.
9911
	 * @param {Number} index - The index in `value` to lookup for value.
9912
	 * @param {*} defaultValue - The value to return if `value[index]` is undefined.
9913
	 * @returns {*}
9914
	 */
9915
	valueAtIndexOrDefault: function(value, index, defaultValue) {
9916
		return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
9917
	},
9918
9919
	/**
9920
	 * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
9921
	 * value returned by `fn`. If `fn` is not a function, this method returns undefined.
9922
	 * @param {Function} fn - The function to call.
9923
	 * @param {Array|undefined|null} args - The arguments with which `fn` should be called.
9924
	 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
9925
	 * @returns {*}
9926
	 */
9927
	callback: function(fn, args, thisArg) {
9928
		if (fn && typeof fn.call === 'function') {
9929
			return fn.apply(thisArg, args);
9930
		}
9931
	},
9932
9933
	/**
9934
	 * Note(SB) for performance sake, this method should only be used when loopable type
9935
	 * is unknown or in none intensive code (not called often and small loopable). Else
9936
	 * it's preferable to use a regular for() loop and save extra function calls.
9937
	 * @param {Object|Array} loopable - The object or array to be iterated.
9938
	 * @param {Function} fn - The function to call for each item.
9939
	 * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
9940
	 * @param {Boolean} [reverse] - If true, iterates backward on the loopable.
9941
	 */
9942
	each: function(loopable, fn, thisArg, reverse) {
9943
		var i, len, keys;
9944
		if (helpers.isArray(loopable)) {
9945
			len = loopable.length;
9946
			if (reverse) {
9947
				for (i = len - 1; i >= 0; i--) {
9948
					fn.call(thisArg, loopable[i], i);
9949
				}
9950
			} else {
9951
				for (i = 0; i < len; i++) {
9952
					fn.call(thisArg, loopable[i], i);
9953
				}
9954
			}
9955
		} else if (helpers.isObject(loopable)) {
9956
			keys = Object.keys(loopable);
9957
			len = keys.length;
9958
			for (i = 0; i < len; i++) {
9959
				fn.call(thisArg, loopable[keys[i]], keys[i]);
9960
			}
9961
		}
9962
	},
9963
9964
	/**
9965
	 * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
9966
	 * @see http://stackoverflow.com/a/14853974
9967
	 * @param {Array} a0 - The array to compare
9968
	 * @param {Array} a1 - The array to compare
9969
	 * @returns {Boolean}
9970
	 */
9971
	arrayEquals: function(a0, a1) {
9972
		var i, ilen, v0, v1;
9973
9974
		if (!a0 || !a1 || a0.length !== a1.length) {
9975
			return false;
9976
		}
9977
9978
		for (i = 0, ilen = a0.length; i < ilen; ++i) {
9979
			v0 = a0[i];
9980
			v1 = a1[i];
9981
9982
			if (v0 instanceof Array && v1 instanceof Array) {
9983
				if (!helpers.arrayEquals(v0, v1)) {
9984
					return false;
9985
				}
9986
			} else if (v0 !== v1) {
9987
				// NOTE: two different object instances will never be equal: {x:20} != {x:20}
9988
				return false;
9989
			}
9990
		}
9991
9992
		return true;
9993
	},
9994
9995
	/**
9996
	 * Returns a deep copy of `source` without keeping references on objects and arrays.
9997
	 * @param {*} source - The value to clone.
9998
	 * @returns {*}
9999
	 */
10000
	clone: function(source) {
10001
		if (helpers.isArray(source)) {
10002
			return source.map(helpers.clone);
10003
		}
10004
10005
		if (helpers.isObject(source)) {
10006
			var target = {};
10007
			var keys = Object.keys(source);
10008
			var klen = keys.length;
10009
			var k = 0;
10010
10011
			for (; k < klen; ++k) {
10012
				target[keys[k]] = helpers.clone(source[keys[k]]);
10013
			}
10014
10015
			return target;
10016
		}
10017
10018
		return source;
10019
	},
10020
10021
	/**
10022
	 * The default merger when Chart.helpers.merge is called without merger option.
10023
	 * Note(SB): this method is also used by configMerge and scaleMerge as fallback.
10024
	 * @private
10025
	 */
10026
	_merger: function(key, target, source, options) {
10027
		var tval = target[key];
10028
		var sval = source[key];
10029
10030
		if (helpers.isObject(tval) && helpers.isObject(sval)) {
10031
			helpers.merge(tval, sval, options);
10032
		} else {
10033
			target[key] = helpers.clone(sval);
10034
		}
10035
	},
10036
10037
	/**
10038
	 * Merges source[key] in target[key] only if target[key] is undefined.
10039
	 * @private
10040
	 */
10041
	_mergerIf: function(key, target, source) {
10042
		var tval = target[key];
10043
		var sval = source[key];
10044
10045
		if (helpers.isObject(tval) && helpers.isObject(sval)) {
10046
			helpers.mergeIf(tval, sval);
10047
		} else if (!target.hasOwnProperty(key)) {
10048
			target[key] = helpers.clone(sval);
10049
		}
10050
	},
10051
10052
	/**
10053
	 * Recursively deep copies `source` properties into `target` with the given `options`.
10054
	 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
10055
	 * @param {Object} target - The target object in which all sources are merged into.
10056
	 * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
10057
	 * @param {Object} [options] - Merging options:
10058
	 * @param {Function} [options.merger] - The merge method (key, target, source, options)
10059
	 * @returns {Object} The `target` object.
10060
	 */
10061
	merge: function(target, source, options) {
10062
		var sources = helpers.isArray(source) ? source : [source];
10063
		var ilen = sources.length;
10064
		var merge, i, keys, klen, k;
10065
10066
		if (!helpers.isObject(target)) {
10067
			return target;
10068
		}
10069
10070
		options = options || {};
10071
		merge = options.merger || helpers._merger;
10072
10073
		for (i = 0; i < ilen; ++i) {
10074
			source = sources[i];
10075
			if (!helpers.isObject(source)) {
10076
				continue;
10077
			}
10078
10079
			keys = Object.keys(source);
10080
			for (k = 0, klen = keys.length; k < klen; ++k) {
10081
				merge(keys[k], target, source, options);
10082
			}
10083
		}
10084
10085
		return target;
10086
	},
10087
10088
	/**
10089
	 * Recursively deep copies `source` properties into `target` *only* if not defined in target.
10090
	 * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
10091
	 * @param {Object} target - The target object in which all sources are merged into.
10092
	 * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
10093
	 * @returns {Object} The `target` object.
10094
	 */
10095
	mergeIf: function(target, source) {
10096
		return helpers.merge(target, source, {merger: helpers._mergerIf});
10097
	},
10098
10099
	/**
10100
	 * Applies the contents of two or more objects together into the first object.
10101
	 * @param {Object} target - The target object in which all objects are merged into.
10102
	 * @param {Object} arg1 - Object containing additional properties to merge in target.
10103
	 * @param {Object} argN - Additional objects containing properties to merge in target.
10104
	 * @returns {Object} The `target` object.
10105
	 */
10106
	extend: function(target) {
10107
		var setFn = function(value, key) {
10108
			target[key] = value;
10109
		};
10110
		for (var i = 1, ilen = arguments.length; i < ilen; ++i) {
10111
			helpers.each(arguments[i], setFn);
10112
		}
10113
		return target;
10114
	},
10115
10116
	/**
10117
	 * Basic javascript inheritance based on the model created in Backbone.js
10118
	 */
10119
	inherits: function(extensions) {
10120
		var me = this;
10121
		var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
10122
			return me.apply(this, arguments);
10123
		};
10124
10125
		var Surrogate = function() {
10126
			this.constructor = ChartElement;
10127
		};
10128
10129
		Surrogate.prototype = me.prototype;
10130
		ChartElement.prototype = new Surrogate();
10131
		ChartElement.extend = helpers.inherits;
10132
10133
		if (extensions) {
10134
			helpers.extend(ChartElement.prototype, extensions);
10135
		}
10136
10137
		ChartElement.__super__ = me.prototype;
10138
		return ChartElement;
10139
	}
10140
};
10141
10142
module.exports = helpers;
10143
10144
// DEPRECATIONS
10145
10146
/**
10147
 * Provided for backward compatibility, use Chart.helpers.callback instead.
10148
 * @function Chart.helpers.callCallback
10149
 * @deprecated since version 2.6.0
10150
 * @todo remove at version 3
10151
 * @private
10152
 */
10153
helpers.callCallback = helpers.callback;
10154
10155
/**
10156
 * Provided for backward compatibility, use Array.prototype.indexOf instead.
10157
 * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
10158
 * @function Chart.helpers.indexOf
10159
 * @deprecated since version 2.7.0
10160
 * @todo remove at version 3
10161
 * @private
10162
 */
10163
helpers.indexOf = function(array, item, fromIndex) {
10164
	return Array.prototype.indexOf.call(array, item, fromIndex);
10165
};
10166
10167
/**
10168
 * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
10169
 * @function Chart.helpers.getValueOrDefault
10170
 * @deprecated since version 2.7.0
10171
 * @todo remove at version 3
10172
 * @private
10173
 */
10174
helpers.getValueOrDefault = helpers.valueOrDefault;
10175
10176
/**
10177
 * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
10178
 * @function Chart.helpers.getValueAtIndexOrDefault
10179
 * @deprecated since version 2.7.0
10180
 * @todo remove at version 3
10181
 * @private
10182
 */
10183
helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
10184
10185
},{}],44:[function(require,module,exports){
10186
'use strict';
10187
10188
var helpers = require(43);
10189
10190
/**
10191
 * Easing functions adapted from Robert Penner's easing equations.
10192
 * @namespace Chart.helpers.easingEffects
10193
 * @see http://www.robertpenner.com/easing/
10194
 */
10195
var effects = {
10196
	linear: function(t) {
10197
		return t;
10198
	},
10199
10200
	easeInQuad: function(t) {
10201
		return t * t;
10202
	},
10203
10204
	easeOutQuad: function(t) {
10205
		return -t * (t - 2);
10206
	},
10207
10208
	easeInOutQuad: function(t) {
10209
		if ((t /= 0.5) < 1) {
10210
			return 0.5 * t * t;
10211
		}
10212
		return -0.5 * ((--t) * (t - 2) - 1);
10213
	},
10214
10215
	easeInCubic: function(t) {
10216
		return t * t * t;
10217
	},
10218
10219
	easeOutCubic: function(t) {
10220
		return (t = t - 1) * t * t + 1;
10221
	},
10222
10223
	easeInOutCubic: function(t) {
10224
		if ((t /= 0.5) < 1) {
10225
			return 0.5 * t * t * t;
10226
		}
10227
		return 0.5 * ((t -= 2) * t * t + 2);
10228
	},
10229
10230
	easeInQuart: function(t) {
10231
		return t * t * t * t;
10232
	},
10233
10234
	easeOutQuart: function(t) {
10235
		return -((t = t - 1) * t * t * t - 1);
10236
	},
10237
10238
	easeInOutQuart: function(t) {
10239
		if ((t /= 0.5) < 1) {
10240
			return 0.5 * t * t * t * t;
10241
		}
10242
		return -0.5 * ((t -= 2) * t * t * t - 2);
10243
	},
10244
10245
	easeInQuint: function(t) {
10246
		return t * t * t * t * t;
10247
	},
10248
10249
	easeOutQuint: function(t) {
10250
		return (t = t - 1) * t * t * t * t + 1;
10251
	},
10252
10253
	easeInOutQuint: function(t) {
10254
		if ((t /= 0.5) < 1) {
10255
			return 0.5 * t * t * t * t * t;
10256
		}
10257
		return 0.5 * ((t -= 2) * t * t * t * t + 2);
10258
	},
10259
10260
	easeInSine: function(t) {
10261
		return -Math.cos(t * (Math.PI / 2)) + 1;
10262
	},
10263
10264
	easeOutSine: function(t) {
10265
		return Math.sin(t * (Math.PI / 2));
10266
	},
10267
10268
	easeInOutSine: function(t) {
10269
		return -0.5 * (Math.cos(Math.PI * t) - 1);
10270
	},
10271
10272
	easeInExpo: function(t) {
10273
		return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
10274
	},
10275
10276
	easeOutExpo: function(t) {
10277
		return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
10278
	},
10279
10280
	easeInOutExpo: function(t) {
10281
		if (t === 0) {
10282
			return 0;
10283
		}
10284
		if (t === 1) {
10285
			return 1;
10286
		}
10287
		if ((t /= 0.5) < 1) {
10288
			return 0.5 * Math.pow(2, 10 * (t - 1));
10289
		}
10290
		return 0.5 * (-Math.pow(2, -10 * --t) + 2);
10291
	},
10292
10293
	easeInCirc: function(t) {
10294
		if (t >= 1) {
10295
			return t;
10296
		}
10297
		return -(Math.sqrt(1 - t * t) - 1);
10298
	},
10299
10300
	easeOutCirc: function(t) {
10301
		return Math.sqrt(1 - (t = t - 1) * t);
10302
	},
10303
10304
	easeInOutCirc: function(t) {
10305
		if ((t /= 0.5) < 1) {
10306
			return -0.5 * (Math.sqrt(1 - t * t) - 1);
10307
		}
10308
		return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
10309
	},
10310
10311
	easeInElastic: function(t) {
10312
		var s = 1.70158;
10313
		var p = 0;
10314
		var a = 1;
10315
		if (t === 0) {
10316
			return 0;
10317
		}
10318
		if (t === 1) {
10319
			return 1;
10320
		}
10321
		if (!p) {
10322
			p = 0.3;
10323
		}
10324
		if (a < 1) {
10325
			a = 1;
10326
			s = p / 4;
10327
		} else {
10328
			s = p / (2 * Math.PI) * Math.asin(1 / a);
10329
		}
10330
		return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
10331
	},
10332
10333
	easeOutElastic: function(t) {
10334
		var s = 1.70158;
10335
		var p = 0;
10336
		var a = 1;
10337
		if (t === 0) {
10338
			return 0;
10339
		}
10340
		if (t === 1) {
10341
			return 1;
10342
		}
10343
		if (!p) {
10344
			p = 0.3;
10345
		}
10346
		if (a < 1) {
10347
			a = 1;
10348
			s = p / 4;
10349
		} else {
10350
			s = p / (2 * Math.PI) * Math.asin(1 / a);
10351
		}
10352
		return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
10353
	},
10354
10355
	easeInOutElastic: function(t) {
10356
		var s = 1.70158;
10357
		var p = 0;
10358
		var a = 1;
10359
		if (t === 0) {
10360
			return 0;
10361
		}
10362
		if ((t /= 0.5) === 2) {
10363
			return 1;
10364
		}
10365
		if (!p) {
10366
			p = 0.45;
10367
		}
10368
		if (a < 1) {
10369
			a = 1;
10370
			s = p / 4;
10371
		} else {
10372
			s = p / (2 * Math.PI) * Math.asin(1 / a);
10373
		}
10374
		if (t < 1) {
10375
			return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
10376
		}
10377
		return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
10378
	},
10379
	easeInBack: function(t) {
10380
		var s = 1.70158;
10381
		return t * t * ((s + 1) * t - s);
10382
	},
10383
10384
	easeOutBack: function(t) {
10385
		var s = 1.70158;
10386
		return (t = t - 1) * t * ((s + 1) * t + s) + 1;
10387
	},
10388
10389
	easeInOutBack: function(t) {
10390
		var s = 1.70158;
10391
		if ((t /= 0.5) < 1) {
10392
			return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
10393
		}
10394
		return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
10395
	},
10396
10397
	easeInBounce: function(t) {
10398
		return 1 - effects.easeOutBounce(1 - t);
10399
	},
10400
10401
	easeOutBounce: function(t) {
10402
		if (t < (1 / 2.75)) {
10403
			return 7.5625 * t * t;
10404
		}
10405
		if (t < (2 / 2.75)) {
10406
			return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
10407
		}
10408
		if (t < (2.5 / 2.75)) {
10409
			return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
10410
		}
10411
		return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
10412
	},
10413
10414
	easeInOutBounce: function(t) {
10415
		if (t < 0.5) {
10416
			return effects.easeInBounce(t * 2) * 0.5;
10417
		}
10418
		return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
10419
	}
10420
};
10421
10422
module.exports = {
10423
	effects: effects
10424
};
10425
10426
// DEPRECATIONS
10427
10428
/**
10429
 * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
10430
 * @function Chart.helpers.easingEffects
10431
 * @deprecated since version 2.7.0
10432
 * @todo remove at version 3
10433
 * @private
10434
 */
10435
helpers.easingEffects = effects;
10436
10437
},{"43":43}],45:[function(require,module,exports){
10438
'use strict';
10439
10440
var helpers = require(43);
10441
10442
/**
10443
 * @alias Chart.helpers.options
10444
 * @namespace
10445
 */
10446
module.exports = {
10447
	/**
10448
	 * Converts the given line height `value` in pixels for a specific font `size`.
10449
	 * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
10450
	 * @param {Number} size - The font size (in pixels) used to resolve relative `value`.
10451
	 * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid).
10452
	 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
10453
	 * @since 2.7.0
10454
	 */
10455
	toLineHeight: function(value, size) {
10456
		var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
10457
		if (!matches || matches[1] === 'normal') {
10458
			return size * 1.2;
10459
		}
10460
10461
		value = +matches[2];
10462
10463
		switch (matches[3]) {
10464
		case 'px':
10465
			return value;
10466
		case '%':
10467
			value /= 100;
10468
			break;
10469
		default:
10470
			break;
10471
		}
10472
10473
		return size * value;
10474
	},
10475
10476
	/**
10477
	 * Converts the given value into a padding object with pre-computed width/height.
10478
	 * @param {Number|Object} value - If a number, set the value to all TRBL component,
10479
	 *  else, if and object, use defined properties and sets undefined ones to 0.
10480
	 * @returns {Object} The padding values (top, right, bottom, left, width, height)
10481
	 * @since 2.7.0
10482
	 */
10483
	toPadding: function(value) {
10484
		var t, r, b, l;
10485
10486
		if (helpers.isObject(value)) {
10487
			t = +value.top || 0;
10488
			r = +value.right || 0;
10489
			b = +value.bottom || 0;
10490
			l = +value.left || 0;
10491
		} else {
10492
			t = r = b = l = +value || 0;
10493
		}
10494
10495
		return {
10496
			top: t,
10497
			right: r,
10498
			bottom: b,
10499
			left: l,
10500
			height: t + b,
10501
			width: l + r
10502
		};
10503
	},
10504
10505
	/**
10506
	 * Evaluates the given `inputs` sequentially and returns the first defined value.
10507
	 * @param {Array[]} inputs - An array of values, falling back to the last value.
10508
	 * @param {Object} [context] - If defined and the current value is a function, the value
10509
	 * is called with `context` as first argument and the result becomes the new input.
10510
	 * @param {Number} [index] - If defined and the current value is an array, the value
10511
	 * at `index` become the new input.
10512
	 * @since 2.7.0
10513
	 */
10514
	resolve: function(inputs, context, index) {
10515
		var i, ilen, value;
10516
10517
		for (i = 0, ilen = inputs.length; i < ilen; ++i) {
10518
			value = inputs[i];
10519
			if (value === undefined) {
10520
				continue;
10521
			}
10522
			if (context !== undefined && typeof value === 'function') {
10523
				value = value(context);
10524
			}
10525
			if (index !== undefined && helpers.isArray(value)) {
10526
				value = value[index];
10527
			}
10528
			if (value !== undefined) {
10529
				return value;
10530
			}
10531
		}
10532
	}
10533
};
10534
10535
},{"43":43}],46:[function(require,module,exports){
10536
'use strict';
10537
10538
module.exports = require(43);
10539
module.exports.easing = require(44);
10540
module.exports.canvas = require(42);
10541
module.exports.options = require(45);
10542
10543
},{"42":42,"43":43,"44":44,"45":45}],47:[function(require,module,exports){
10544
/**
10545
 * Platform fallback implementation (minimal).
10546
 * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
10547
 */
10548
10549
module.exports = {
10550
	acquireContext: function(item) {
10551
		if (item && item.canvas) {
10552
			// Support for any object associated to a canvas (including a context2d)
10553
			item = item.canvas;
10554
		}
10555
10556
		return item && item.getContext('2d') || null;
10557
	}
10558
};
10559
10560
},{}],48:[function(require,module,exports){
10561
/**
10562
 * Chart.Platform implementation for targeting a web browser
10563
 */
10564
10565
'use strict';
10566
10567
var helpers = require(46);
10568
10569
var EXPANDO_KEY = '$chartjs';
10570
var CSS_PREFIX = 'chartjs-';
10571
var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
10572
var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
10573
var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];
10574
10575
/**
10576
 * DOM event types -> Chart.js event types.
10577
 * Note: only events with different types are mapped.
10578
 * @see https://developer.mozilla.org/en-US/docs/Web/Events
10579
 */
10580
var EVENT_TYPES = {
10581
	touchstart: 'mousedown',
10582
	touchmove: 'mousemove',
10583
	touchend: 'mouseup',
10584
	pointerenter: 'mouseenter',
10585
	pointerdown: 'mousedown',
10586
	pointermove: 'mousemove',
10587
	pointerup: 'mouseup',
10588
	pointerleave: 'mouseout',
10589
	pointerout: 'mouseout'
10590
};
10591
10592
/**
10593
 * The "used" size is the final value of a dimension property after all calculations have
10594
 * been performed. This method uses the computed style of `element` but returns undefined
10595
 * if the computed style is not expressed in pixels. That can happen in some cases where
10596
 * `element` has a size relative to its parent and this last one is not yet displayed,
10597
 * for example because of `display: none` on a parent node.
10598
 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
10599
 * @returns {Number} Size in pixels or undefined if unknown.
10600
 */
10601
function readUsedSize(element, property) {
10602
	var value = helpers.getStyle(element, property);
10603
	var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
10604
	return matches ? Number(matches[1]) : undefined;
10605
}
10606
10607
/**
10608
 * Initializes the canvas style and render size without modifying the canvas display size,
10609
 * since responsiveness is handled by the controller.resize() method. The config is used
10610
 * to determine the aspect ratio to apply in case no explicit height has been specified.
10611
 */
10612
function initCanvas(canvas, config) {
10613
	var style = canvas.style;
10614
10615
	// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
10616
	// returns null or '' if no explicit value has been set to the canvas attribute.
10617
	var renderHeight = canvas.getAttribute('height');
10618
	var renderWidth = canvas.getAttribute('width');
10619
10620
	// Chart.js modifies some canvas values that we want to restore on destroy
10621
	canvas[EXPANDO_KEY] = {
10622
		initial: {
10623
			height: renderHeight,
10624
			width: renderWidth,
10625
			style: {
10626
				display: style.display,
10627
				height: style.height,
10628
				width: style.width
10629
			}
10630
		}
10631
	};
10632
10633
	// Force canvas to display as block to avoid extra space caused by inline
10634
	// elements, which would interfere with the responsive resize process.
10635
	// https://github.com/chartjs/Chart.js/issues/2538
10636
	style.display = style.display || 'block';
10637
10638
	if (renderWidth === null || renderWidth === '') {
10639
		var displayWidth = readUsedSize(canvas, 'width');
10640
		if (displayWidth !== undefined) {
10641
			canvas.width = displayWidth;
10642
		}
10643
	}
10644
10645
	if (renderHeight === null || renderHeight === '') {
10646
		if (canvas.style.height === '') {
10647
			// If no explicit render height and style height, let's apply the aspect ratio,
10648
			// which one can be specified by the user but also by charts as default option
10649
			// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
10650
			canvas.height = canvas.width / (config.options.aspectRatio || 2);
10651
		} else {
10652
			var displayHeight = readUsedSize(canvas, 'height');
10653
			if (displayWidth !== undefined) {
10654
				canvas.height = displayHeight;
10655
			}
10656
		}
10657
	}
10658
10659
	return canvas;
10660
}
10661
10662
/**
10663
 * Detects support for options object argument in addEventListener.
10664
 * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
10665
 * @private
10666
 */
10667
var supportsEventListenerOptions = (function() {
10668
	var supports = false;
10669
	try {
10670
		var options = Object.defineProperty({}, 'passive', {
10671
			get: function() {
10672
				supports = true;
10673
			}
10674
		});
10675
		window.addEventListener('e', null, options);
10676
	} catch (e) {
10677
		// continue regardless of error
10678
	}
10679
	return supports;
10680
}());
10681
10682
// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
10683
// https://github.com/chartjs/Chart.js/issues/4287
10684
var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;
10685
10686
function addEventListener(node, type, listener) {
10687
	node.addEventListener(type, listener, eventListenerOptions);
10688
}
10689
10690
function removeEventListener(node, type, listener) {
10691
	node.removeEventListener(type, listener, eventListenerOptions);
10692
}
10693
10694
function createEvent(type, chart, x, y, nativeEvent) {
10695
	return {
10696
		type: type,
10697
		chart: chart,
10698
		native: nativeEvent || null,
10699
		x: x !== undefined ? x : null,
10700
		y: y !== undefined ? y : null,
10701
	};
10702
}
10703
10704
function fromNativeEvent(event, chart) {
10705
	var type = EVENT_TYPES[event.type] || event.type;
10706
	var pos = helpers.getRelativePosition(event, chart);
10707
	return createEvent(type, chart, pos.x, pos.y, event);
10708
}
10709
10710
function throttled(fn, thisArg) {
10711
	var ticking = false;
10712
	var args = [];
10713
10714
	return function() {
10715
		args = Array.prototype.slice.call(arguments);
10716
		thisArg = thisArg || this;
10717
10718
		if (!ticking) {
10719
			ticking = true;
10720
			helpers.requestAnimFrame.call(window, function() {
10721
				ticking = false;
10722
				fn.apply(thisArg, args);
10723
			});
10724
		}
10725
	};
10726
}
10727
10728
// Implementation based on https://github.com/marcj/css-element-queries
10729
function createResizer(handler) {
10730
	var resizer = document.createElement('div');
10731
	var cls = CSS_PREFIX + 'size-monitor';
10732
	var maxSize = 1000000;
10733
	var style =
10734
		'position:absolute;' +
10735
		'left:0;' +
10736
		'top:0;' +
10737
		'right:0;' +
10738
		'bottom:0;' +
10739
		'overflow:hidden;' +
10740
		'pointer-events:none;' +
10741
		'visibility:hidden;' +
10742
		'z-index:-1;';
10743
10744
	resizer.style.cssText = style;
10745
	resizer.className = cls;
10746
	resizer.innerHTML =
10747
		'<div class="' + cls + '-expand" style="' + style + '">' +
10748
			'<div style="' +
10749
				'position:absolute;' +
10750
				'width:' + maxSize + 'px;' +
10751
				'height:' + maxSize + 'px;' +
10752
				'left:0;' +
10753
				'top:0">' +
10754
			'</div>' +
10755
		'</div>' +
10756
		'<div class="' + cls + '-shrink" style="' + style + '">' +
10757
			'<div style="' +
10758
				'position:absolute;' +
10759
				'width:200%;' +
10760
				'height:200%;' +
10761
				'left:0; ' +
10762
				'top:0">' +
10763
			'</div>' +
10764
		'</div>';
10765
10766
	var expand = resizer.childNodes[0];
10767
	var shrink = resizer.childNodes[1];
10768
10769
	resizer._reset = function() {
10770
		expand.scrollLeft = maxSize;
10771
		expand.scrollTop = maxSize;
10772
		shrink.scrollLeft = maxSize;
10773
		shrink.scrollTop = maxSize;
10774
	};
10775
	var onScroll = function() {
10776
		resizer._reset();
10777
		handler();
10778
	};
10779
10780
	addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand'));
10781
	addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));
10782
10783
	return resizer;
10784
}
10785
10786
// https://davidwalsh.name/detect-node-insertion
10787
function watchForRender(node, handler) {
10788
	var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
10789
	var proxy = expando.renderProxy = function(e) {
10790
		if (e.animationName === CSS_RENDER_ANIMATION) {
10791
			handler();
10792
		}
10793
	};
10794
10795
	helpers.each(ANIMATION_START_EVENTS, function(type) {
10796
		addEventListener(node, type, proxy);
10797
	});
10798
10799
	// #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class
10800
	// is removed then added back immediately (same animation frame?). Accessing the
10801
	// `offsetParent` property will force a reflow and re-evaluate the CSS animation.
10802
	// https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics
10803
	// https://github.com/chartjs/Chart.js/issues/4737
10804
	expando.reflow = !!node.offsetParent;
10805
10806
	node.classList.add(CSS_RENDER_MONITOR);
10807
}
10808
10809
function unwatchForRender(node) {
10810
	var expando = node[EXPANDO_KEY] || {};
10811
	var proxy = expando.renderProxy;
10812
10813
	if (proxy) {
10814
		helpers.each(ANIMATION_START_EVENTS, function(type) {
10815
			removeEventListener(node, type, proxy);
10816
		});
10817
10818
		delete expando.renderProxy;
10819
	}
10820
10821
	node.classList.remove(CSS_RENDER_MONITOR);
10822
}
10823
10824
function addResizeListener(node, listener, chart) {
10825
	var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
10826
10827
	// Let's keep track of this added resizer and thus avoid DOM query when removing it.
10828
	var resizer = expando.resizer = createResizer(throttled(function() {
10829
		if (expando.resizer) {
10830
			return listener(createEvent('resize', chart));
10831
		}
10832
	}));
10833
10834
	// The resizer needs to be attached to the node parent, so we first need to be
10835
	// sure that `node` is attached to the DOM before injecting the resizer element.
10836
	watchForRender(node, function() {
10837
		if (expando.resizer) {
10838
			var container = node.parentNode;
10839
			if (container && container !== resizer.parentNode) {
10840
				container.insertBefore(resizer, container.firstChild);
10841
			}
10842
10843
			// The container size might have changed, let's reset the resizer state.
10844
			resizer._reset();
10845
		}
10846
	});
10847
}
10848
10849
function removeResizeListener(node) {
10850
	var expando = node[EXPANDO_KEY] || {};
10851
	var resizer = expando.resizer;
10852
10853
	delete expando.resizer;
10854
	unwatchForRender(node);
10855
10856
	if (resizer && resizer.parentNode) {
10857
		resizer.parentNode.removeChild(resizer);
10858
	}
10859
}
10860
10861
function injectCSS(platform, css) {
10862
	// http://stackoverflow.com/q/3922139
10863
	var style = platform._style || document.createElement('style');
10864
	if (!platform._style) {
10865
		platform._style = style;
10866
		css = '/* Chart.js */\n' + css;
10867
		style.setAttribute('type', 'text/css');
10868
		document.getElementsByTagName('head')[0].appendChild(style);
10869
	}
10870
10871
	style.appendChild(document.createTextNode(css));
10872
}
10873
10874
module.exports = {
10875
	/**
10876
	 * This property holds whether this platform is enabled for the current environment.
10877
	 * Currently used by platform.js to select the proper implementation.
10878
	 * @private
10879
	 */
10880
	_enabled: typeof window !== 'undefined' && typeof document !== 'undefined',
10881
10882
	initialize: function() {
10883
		var keyframes = 'from{opacity:0.99}to{opacity:1}';
10884
10885
		injectCSS(this,
10886
			// DOM rendering detection
10887
			// https://davidwalsh.name/detect-node-insertion
10888
			'@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
10889
			'@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
10890
			'.' + CSS_RENDER_MONITOR + '{' +
10891
				'-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
10892
				'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
10893
			'}'
10894
		);
10895
	},
10896
10897
	acquireContext: function(item, config) {
10898
		if (typeof item === 'string') {
10899
			item = document.getElementById(item);
10900
		} else if (item.length) {
10901
			// Support for array based queries (such as jQuery)
10902
			item = item[0];
10903
		}
10904
10905
		if (item && item.canvas) {
10906
			// Support for any object associated to a canvas (including a context2d)
10907
			item = item.canvas;
10908
		}
10909
10910
		// To prevent canvas fingerprinting, some add-ons undefine the getContext
10911
		// method, for example: https://github.com/kkapsner/CanvasBlocker
10912
		// https://github.com/chartjs/Chart.js/issues/2807
10913
		var context = item && item.getContext && item.getContext('2d');
10914
10915
		// `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
10916
		// inside an iframe or when running in a protected environment. We could guess the
10917
		// types from their toString() value but let's keep things flexible and assume it's
10918
		// a sufficient condition if the item has a context2D which has item as `canvas`.
10919
		// https://github.com/chartjs/Chart.js/issues/3887
10920
		// https://github.com/chartjs/Chart.js/issues/4102
10921
		// https://github.com/chartjs/Chart.js/issues/4152
10922
		if (context && context.canvas === item) {
10923
			initCanvas(item, config);
10924
			return context;
10925
		}
10926
10927
		return null;
10928
	},
10929
10930
	releaseContext: function(context) {
10931
		var canvas = context.canvas;
10932
		if (!canvas[EXPANDO_KEY]) {
10933
			return;
10934
		}
10935
10936
		var initial = canvas[EXPANDO_KEY].initial;
10937
		['height', 'width'].forEach(function(prop) {
10938
			var value = initial[prop];
10939
			if (helpers.isNullOrUndef(value)) {
10940
				canvas.removeAttribute(prop);
10941
			} else {
10942
				canvas.setAttribute(prop, value);
10943
			}
10944
		});
10945
10946
		helpers.each(initial.style || {}, function(value, key) {
10947
			canvas.style[key] = value;
10948
		});
10949
10950
		// The canvas render size might have been changed (and thus the state stack discarded),
10951
		// we can't use save() and restore() to restore the initial state. So make sure that at
10952
		// least the canvas context is reset to the default state by setting the canvas width.
10953
		// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
10954
		canvas.width = canvas.width;
10955
10956
		delete canvas[EXPANDO_KEY];
10957
	},
10958
10959
	addEventListener: function(chart, type, listener) {
10960
		var canvas = chart.canvas;
10961
		if (type === 'resize') {
10962
			// Note: the resize event is not supported on all browsers.
10963
			addResizeListener(canvas, listener, chart);
10964
			return;
10965
		}
10966
10967
		var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {});
10968
		var proxies = expando.proxies || (expando.proxies = {});
10969
		var proxy = proxies[chart.id + '_' + type] = function(event) {
10970
			listener(fromNativeEvent(event, chart));
10971
		};
10972
10973
		addEventListener(canvas, type, proxy);
10974
	},
10975
10976
	removeEventListener: function(chart, type, listener) {
10977
		var canvas = chart.canvas;
10978
		if (type === 'resize') {
10979
			// Note: the resize event is not supported on all browsers.
10980
			removeResizeListener(canvas, listener);
10981
			return;
10982
		}
10983
10984
		var expando = listener[EXPANDO_KEY] || {};
10985
		var proxies = expando.proxies || {};
10986
		var proxy = proxies[chart.id + '_' + type];
10987
		if (!proxy) {
10988
			return;
10989
		}
10990
10991
		removeEventListener(canvas, type, proxy);
10992
	}
10993
};
10994
10995
// DEPRECATIONS
10996
10997
/**
10998
 * Provided for backward compatibility, use EventTarget.addEventListener instead.
10999
 * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
11000
 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
11001
 * @function Chart.helpers.addEvent
11002
 * @deprecated since version 2.7.0
11003
 * @todo remove at version 3
11004
 * @private
11005
 */
11006
helpers.addEvent = addEventListener;
11007
11008
/**
11009
 * Provided for backward compatibility, use EventTarget.removeEventListener instead.
11010
 * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
11011
 * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
11012
 * @function Chart.helpers.removeEvent
11013
 * @deprecated since version 2.7.0
11014
 * @todo remove at version 3
11015
 * @private
11016
 */
11017
helpers.removeEvent = removeEventListener;
11018
11019
},{"46":46}],49:[function(require,module,exports){
11020
'use strict';
11021
11022
var helpers = require(46);
11023
var basic = require(47);
11024
var dom = require(48);
11025
11026
// @TODO Make possible to select another platform at build time.
11027
var implementation = dom._enabled ? dom : basic;
11028
11029
/**
11030
 * @namespace Chart.platform
11031
 * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
11032
 * @since 2.4.0
11033
 */
11034
module.exports = helpers.extend({
11035
	/**
11036
	 * @since 2.7.0
11037
	 */
11038
	initialize: function() {},
11039
11040
	/**
11041
	 * Called at chart construction time, returns a context2d instance implementing
11042
	 * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
11043
	 * @param {*} item - The native item from which to acquire context (platform specific)
11044
	 * @param {Object} options - The chart options
11045
	 * @returns {CanvasRenderingContext2D} context2d instance
11046
	 */
11047
	acquireContext: function() {},
11048
11049
	/**
11050
	 * Called at chart destruction time, releases any resources associated to the context
11051
	 * previously returned by the acquireContext() method.
11052
	 * @param {CanvasRenderingContext2D} context - The context2d instance
11053
	 * @returns {Boolean} true if the method succeeded, else false
11054
	 */
11055
	releaseContext: function() {},
11056
11057
	/**
11058
	 * Registers the specified listener on the given chart.
11059
	 * @param {Chart} chart - Chart from which to listen for event
11060
	 * @param {String} type - The ({@link IEvent}) type to listen for
11061
	 * @param {Function} listener - Receives a notification (an object that implements
11062
	 * the {@link IEvent} interface) when an event of the specified type occurs.
11063
	 */
11064
	addEventListener: function() {},
11065
11066
	/**
11067
	 * Removes the specified listener previously registered with addEventListener.
11068
	 * @param {Chart} chart -Chart from which to remove the listener
11069
	 * @param {String} type - The ({@link IEvent}) type to remove
11070
	 * @param {Function} listener - The listener function to remove from the event target.
11071
	 */
11072
	removeEventListener: function() {}
11073
11074
}, implementation);
11075
11076
/**
11077
 * @interface IPlatform
11078
 * Allows abstracting platform dependencies away from the chart
11079
 * @borrows Chart.platform.acquireContext as acquireContext
11080
 * @borrows Chart.platform.releaseContext as releaseContext
11081
 * @borrows Chart.platform.addEventListener as addEventListener
11082
 * @borrows Chart.platform.removeEventListener as removeEventListener
11083
 */
11084
11085
/**
11086
 * @interface IEvent
11087
 * @prop {String} type - The event type name, possible values are:
11088
 * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
11089
 * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize'
11090
 * @prop {*} native - The original native event (null for emulated events, e.g. 'resize')
11091
 * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events)
11092
 * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)
11093
 */
11094
11095
},{"46":46,"47":47,"48":48}],50:[function(require,module,exports){
11096
'use strict';
11097
11098
module.exports = {};
11099
module.exports.filler = require(51);
11100
module.exports.legend = require(52);
11101
module.exports.title = require(53);
11102
11103
},{"51":51,"52":52,"53":53}],51:[function(require,module,exports){
11104
/**
11105
 * Plugin based on discussion from the following Chart.js issues:
11106
 * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569
11107
 * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897
11108
 */
11109
11110
'use strict';
11111
11112
var defaults = require(26);
11113
var elements = require(41);
11114
var helpers = require(46);
11115
11116
defaults._set('global', {
11117
	plugins: {
11118
		filler: {
11119
			propagate: true
11120
		}
11121
	}
11122
});
11123
11124
var mappers = {
11125
	dataset: function(source) {
11126
		var index = source.fill;
11127
		var chart = source.chart;
11128
		var meta = chart.getDatasetMeta(index);
11129
		var visible = meta && chart.isDatasetVisible(index);
11130
		var points = (visible && meta.dataset._children) || [];
11131
		var length = points.length || 0;
11132
11133
		return !length ? null : function(point, i) {
11134
			return (i < length && points[i]._view) || null;
11135
		};
11136
	},
11137
11138
	boundary: function(source) {
11139
		var boundary = source.boundary;
11140
		var x = boundary ? boundary.x : null;
11141
		var y = boundary ? boundary.y : null;
11142
11143
		return function(point) {
11144
			return {
11145
				x: x === null ? point.x : x,
11146
				y: y === null ? point.y : y,
11147
			};
11148
		};
11149
	}
11150
};
11151
11152
// @todo if (fill[0] === '#')
11153
function decodeFill(el, index, count) {
11154
	var model = el._model || {};
11155
	var fill = model.fill;
11156
	var target;
11157
11158
	if (fill === undefined) {
11159
		fill = !!model.backgroundColor;
11160
	}
11161
11162
	if (fill === false || fill === null) {
11163
		return false;
11164
	}
11165
11166
	if (fill === true) {
11167
		return 'origin';
11168
	}
11169
11170
	target = parseFloat(fill, 10);
11171
	if (isFinite(target) && Math.floor(target) === target) {
11172
		if (fill[0] === '-' || fill[0] === '+') {
11173
			target = index + target;
11174
		}
11175
11176
		if (target === index || target < 0 || target >= count) {
11177
			return false;
11178
		}
11179
11180
		return target;
11181
	}
11182
11183
	switch (fill) {
11184
	// compatibility
11185
	case 'bottom':
11186
		return 'start';
11187
	case 'top':
11188
		return 'end';
11189
	case 'zero':
11190
		return 'origin';
11191
	// supported boundaries
11192
	case 'origin':
11193
	case 'start':
11194
	case 'end':
11195
		return fill;
11196
	// invalid fill values
11197
	default:
11198
		return false;
11199
	}
11200
}
11201
11202
function computeBoundary(source) {
11203
	var model = source.el._model || {};
11204
	var scale = source.el._scale || {};
11205
	var fill = source.fill;
11206
	var target = null;
11207
	var horizontal;
11208
11209
	if (isFinite(fill)) {
11210
		return null;
11211
	}
11212
11213
	// Backward compatibility: until v3, we still need to support boundary values set on
11214
	// the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
11215
	// controllers might still use it (e.g. the Smith chart).
11216
11217
	if (fill === 'start') {
11218
		target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
11219
	} else if (fill === 'end') {
11220
		target = model.scaleTop === undefined ? scale.top : model.scaleTop;
11221
	} else if (model.scaleZero !== undefined) {
11222
		target = model.scaleZero;
11223
	} else if (scale.getBasePosition) {
11224
		target = scale.getBasePosition();
11225
	} else if (scale.getBasePixel) {
11226
		target = scale.getBasePixel();
11227
	}
11228
11229
	if (target !== undefined && target !== null) {
11230
		if (target.x !== undefined && target.y !== undefined) {
11231
			return target;
11232
		}
11233
11234
		if (typeof target === 'number' && isFinite(target)) {
11235
			horizontal = scale.isHorizontal();
11236
			return {
11237
				x: horizontal ? target : null,
11238
				y: horizontal ? null : target
11239
			};
11240
		}
11241
	}
11242
11243
	return null;
11244
}
11245
11246
function resolveTarget(sources, index, propagate) {
11247
	var source = sources[index];
11248
	var fill = source.fill;
11249
	var visited = [index];
11250
	var target;
11251
11252
	if (!propagate) {
11253
		return fill;
11254
	}
11255
11256
	while (fill !== false && visited.indexOf(fill) === -1) {
11257
		if (!isFinite(fill)) {
11258
			return fill;
11259
		}
11260
11261
		target = sources[fill];
11262
		if (!target) {
11263
			return false;
11264
		}
11265
11266
		if (target.visible) {
11267
			return fill;
11268
		}
11269
11270
		visited.push(fill);
11271
		fill = target.fill;
11272
	}
11273
11274
	return false;
11275
}
11276
11277
function createMapper(source) {
11278
	var fill = source.fill;
11279
	var type = 'dataset';
11280
11281
	if (fill === false) {
11282
		return null;
11283
	}
11284
11285
	if (!isFinite(fill)) {
11286
		type = 'boundary';
11287
	}
11288
11289
	return mappers[type](source);
11290
}
11291
11292
function isDrawable(point) {
11293
	return point && !point.skip;
11294
}
11295
11296
function drawArea(ctx, curve0, curve1, len0, len1) {
11297
	var i;
11298
11299
	if (!len0 || !len1) {
11300
		return;
11301
	}
11302
11303
	// building first area curve (normal)
11304
	ctx.moveTo(curve0[0].x, curve0[0].y);
11305
	for (i = 1; i < len0; ++i) {
11306
		helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
11307
	}
11308
11309
	// joining the two area curves
11310
	ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);
11311
11312
	// building opposite area curve (reverse)
11313
	for (i = len1 - 1; i > 0; --i) {
11314
		helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
11315
	}
11316
}
11317
11318
function doFill(ctx, points, mapper, view, color, loop) {
11319
	var count = points.length;
11320
	var span = view.spanGaps;
11321
	var curve0 = [];
11322
	var curve1 = [];
11323
	var len0 = 0;
11324
	var len1 = 0;
11325
	var i, ilen, index, p0, p1, d0, d1;
11326
11327
	ctx.beginPath();
11328
11329
	for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
11330
		index = i % count;
11331
		p0 = points[index]._view;
11332
		p1 = mapper(p0, index, view);
11333
		d0 = isDrawable(p0);
11334
		d1 = isDrawable(p1);
11335
11336
		if (d0 && d1) {
11337
			len0 = curve0.push(p0);
11338
			len1 = curve1.push(p1);
11339
		} else if (len0 && len1) {
11340
			if (!span) {
11341
				drawArea(ctx, curve0, curve1, len0, len1);
11342
				len0 = len1 = 0;
11343
				curve0 = [];
11344
				curve1 = [];
11345
			} else {
11346
				if (d0) {
11347
					curve0.push(p0);
11348
				}
11349
				if (d1) {
11350
					curve1.push(p1);
11351
				}
11352
			}
11353
		}
11354
	}
11355
11356
	drawArea(ctx, curve0, curve1, len0, len1);
11357
11358
	ctx.closePath();
11359
	ctx.fillStyle = color;
11360
	ctx.fill();
11361
}
11362
11363
module.exports = {
11364
	id: 'filler',
11365
11366
	afterDatasetsUpdate: function(chart, options) {
11367
		var count = (chart.data.datasets || []).length;
11368
		var propagate = options.propagate;
11369
		var sources = [];
11370
		var meta, i, el, source;
11371
11372
		for (i = 0; i < count; ++i) {
11373
			meta = chart.getDatasetMeta(i);
11374
			el = meta.dataset;
11375
			source = null;
11376
11377
			if (el && el._model && el instanceof elements.Line) {
11378
				source = {
11379
					visible: chart.isDatasetVisible(i),
11380
					fill: decodeFill(el, i, count),
11381
					chart: chart,
11382
					el: el
11383
				};
11384
			}
11385
11386
			meta.$filler = source;
11387
			sources.push(source);
11388
		}
11389
11390
		for (i = 0; i < count; ++i) {
11391
			source = sources[i];
11392
			if (!source) {
11393
				continue;
11394
			}
11395
11396
			source.fill = resolveTarget(sources, i, propagate);
11397
			source.boundary = computeBoundary(source);
11398
			source.mapper = createMapper(source);
11399
		}
11400
	},
11401
11402
	beforeDatasetDraw: function(chart, args) {
11403
		var meta = args.meta.$filler;
11404
		if (!meta) {
11405
			return;
11406
		}
11407
11408
		var ctx = chart.ctx;
11409
		var el = meta.el;
11410
		var view = el._view;
11411
		var points = el._children || [];
11412
		var mapper = meta.mapper;
11413
		var color = view.backgroundColor || defaults.global.defaultColor;
11414
11415
		if (mapper && color && points.length) {
11416
			helpers.canvas.clipArea(ctx, chart.chartArea);
11417
			doFill(ctx, points, mapper, view, color, el._loop);
11418
			helpers.canvas.unclipArea(ctx);
11419
		}
11420
	}
11421
};
11422
11423
},{"26":26,"41":41,"46":46}],52:[function(require,module,exports){
11424
'use strict';
11425
11426
var defaults = require(26);
11427
var Element = require(27);
11428
var helpers = require(46);
11429
var layouts = require(31);
11430
11431
var noop = helpers.noop;
11432
11433
defaults._set('global', {
11434
	legend: {
11435
		display: true,
11436
		position: 'top',
11437
		fullWidth: true,
11438
		reverse: false,
11439
		weight: 1000,
11440
11441
		// a callback that will handle
11442
		onClick: function(e, legendItem) {
11443
			var index = legendItem.datasetIndex;
11444
			var ci = this.chart;
11445
			var meta = ci.getDatasetMeta(index);
11446
11447
			// See controller.isDatasetVisible comment
11448
			meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
11449
11450
			// We hid a dataset ... rerender the chart
11451
			ci.update();
11452
		},
11453
11454
		onHover: null,
11455
11456
		labels: {
11457
			boxWidth: 40,
11458
			padding: 10,
11459
			// Generates labels shown in the legend
11460
			// Valid properties to return:
11461
			// text : text to display
11462
			// fillStyle : fill of coloured box
11463
			// strokeStyle: stroke of coloured box
11464
			// hidden : if this legend item refers to a hidden item
11465
			// lineCap : cap style for line
11466
			// lineDash
11467
			// lineDashOffset :
11468
			// lineJoin :
11469
			// lineWidth :
11470
			generateLabels: function(chart) {
11471
				var data = chart.data;
11472
				return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
11473
					return {
11474
						text: dataset.label,
11475
						fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
11476
						hidden: !chart.isDatasetVisible(i),
11477
						lineCap: dataset.borderCapStyle,
11478
						lineDash: dataset.borderDash,
11479
						lineDashOffset: dataset.borderDashOffset,
11480
						lineJoin: dataset.borderJoinStyle,
11481
						lineWidth: dataset.borderWidth,
11482
						strokeStyle: dataset.borderColor,
11483
						pointStyle: dataset.pointStyle,
11484
11485
						// Below is extra data used for toggling the datasets
11486
						datasetIndex: i
11487
					};
11488
				}, this) : [];
11489
			}
11490
		}
11491
	},
11492
11493
	legendCallback: function(chart) {
11494
		var text = [];
11495
		text.push('<ul class="' + chart.id + '-legend">');
11496
		for (var i = 0; i < chart.data.datasets.length; i++) {
11497
			text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
11498
			if (chart.data.datasets[i].label) {
11499
				text.push(chart.data.datasets[i].label);
11500
			}
11501
			text.push('</li>');
11502
		}
11503
		text.push('</ul>');
11504
		return text.join('');
11505
	}
11506
});
11507
11508
/**
11509
 * Helper function to get the box width based on the usePointStyle option
11510
 * @param labelopts {Object} the label options on the legend
11511
 * @param fontSize {Number} the label font size
11512
 * @return {Number} width of the color box area
11513
 */
11514
function getBoxWidth(labelOpts, fontSize) {
11515
	return labelOpts.usePointStyle ?
11516
		fontSize * Math.SQRT2 :
11517
		labelOpts.boxWidth;
11518
}
11519
11520
/**
11521
 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
11522
 */
11523
var Legend = Element.extend({
11524
11525
	initialize: function(config) {
11526
		helpers.extend(this, config);
11527
11528
		// Contains hit boxes for each dataset (in dataset order)
11529
		this.legendHitBoxes = [];
11530
11531
		// Are we in doughnut mode which has a different data type
11532
		this.doughnutMode = false;
11533
	},
11534
11535
	// These methods are ordered by lifecycle. Utilities then follow.
11536
	// Any function defined here is inherited by all legend types.
11537
	// Any function can be extended by the legend type
11538
11539
	beforeUpdate: noop,
11540
	update: function(maxWidth, maxHeight, margins) {
11541
		var me = this;
11542
11543
		// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
11544
		me.beforeUpdate();
11545
11546
		// Absorb the master measurements
11547
		me.maxWidth = maxWidth;
11548
		me.maxHeight = maxHeight;
11549
		me.margins = margins;
11550
11551
		// Dimensions
11552
		me.beforeSetDimensions();
11553
		me.setDimensions();
11554
		me.afterSetDimensions();
11555
		// Labels
11556
		me.beforeBuildLabels();
11557
		me.buildLabels();
11558
		me.afterBuildLabels();
11559
11560
		// Fit
11561
		me.beforeFit();
11562
		me.fit();
11563
		me.afterFit();
11564
		//
11565
		me.afterUpdate();
11566
11567
		return me.minSize;
11568
	},
11569
	afterUpdate: noop,
11570
11571
	//
11572
11573
	beforeSetDimensions: noop,
11574
	setDimensions: function() {
11575
		var me = this;
11576
		// Set the unconstrained dimension before label rotation
11577
		if (me.isHorizontal()) {
11578
			// Reset position before calculating rotation
11579
			me.width = me.maxWidth;
11580
			me.left = 0;
11581
			me.right = me.width;
11582
		} else {
11583
			me.height = me.maxHeight;
11584
11585
			// Reset position before calculating rotation
11586
			me.top = 0;
11587
			me.bottom = me.height;
11588
		}
11589
11590
		// Reset padding
11591
		me.paddingLeft = 0;
11592
		me.paddingTop = 0;
11593
		me.paddingRight = 0;
11594
		me.paddingBottom = 0;
11595
11596
		// Reset minSize
11597
		me.minSize = {
11598
			width: 0,
11599
			height: 0
11600
		};
11601
	},
11602
	afterSetDimensions: noop,
11603
11604
	//
11605
11606
	beforeBuildLabels: noop,
11607
	buildLabels: function() {
11608
		var me = this;
11609
		var labelOpts = me.options.labels || {};
11610
		var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || [];
11611
11612
		if (labelOpts.filter) {
11613
			legendItems = legendItems.filter(function(item) {
11614
				return labelOpts.filter(item, me.chart.data);
11615
			});
11616
		}
11617
11618
		if (me.options.reverse) {
11619
			legendItems.reverse();
11620
		}
11621
11622
		me.legendItems = legendItems;
11623
	},
11624
	afterBuildLabels: noop,
11625
11626
	//
11627
11628
	beforeFit: noop,
11629
	fit: function() {
11630
		var me = this;
11631
		var opts = me.options;
11632
		var labelOpts = opts.labels;
11633
		var display = opts.display;
11634
11635
		var ctx = me.ctx;
11636
11637
		var globalDefault = defaults.global;
11638
		var valueOrDefault = helpers.valueOrDefault;
11639
		var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
11640
		var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
11641
		var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
11642
		var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
11643
11644
		// Reset hit boxes
11645
		var hitboxes = me.legendHitBoxes = [];
11646
11647
		var minSize = me.minSize;
11648
		var isHorizontal = me.isHorizontal();
11649
11650
		if (isHorizontal) {
11651
			minSize.width = me.maxWidth; // fill all the width
11652
			minSize.height = display ? 10 : 0;
11653
		} else {
11654
			minSize.width = display ? 10 : 0;
11655
			minSize.height = me.maxHeight; // fill all the height
11656
		}
11657
11658
		// Increase sizes here
11659
		if (display) {
11660
			ctx.font = labelFont;
11661
11662
			if (isHorizontal) {
11663
				// Labels
11664
11665
				// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
11666
				var lineWidths = me.lineWidths = [0];
11667
				var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;
11668
11669
				ctx.textAlign = 'left';
11670
				ctx.textBaseline = 'top';
11671
11672
				helpers.each(me.legendItems, function(legendItem, i) {
11673
					var boxWidth = getBoxWidth(labelOpts, fontSize);
11674
					var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
11675
11676
					if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
11677
						totalHeight += fontSize + (labelOpts.padding);
11678
						lineWidths[lineWidths.length] = me.left;
11679
					}
11680
11681
					// Store the hitbox width and height here. Final position will be updated in `draw`
11682
					hitboxes[i] = {
11683
						left: 0,
11684
						top: 0,
11685
						width: width,
11686
						height: fontSize
11687
					};
11688
11689
					lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
11690
				});
11691
11692
				minSize.height += totalHeight;
11693
11694
			} else {
11695
				var vPadding = labelOpts.padding;
11696
				var columnWidths = me.columnWidths = [];
11697
				var totalWidth = labelOpts.padding;
11698
				var currentColWidth = 0;
11699
				var currentColHeight = 0;
11700
				var itemHeight = fontSize + vPadding;
11701
11702
				helpers.each(me.legendItems, function(legendItem, i) {
11703
					var boxWidth = getBoxWidth(labelOpts, fontSize);
11704
					var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
11705
11706
					// If too tall, go to new column
11707
					if (currentColHeight + itemHeight > minSize.height) {
11708
						totalWidth += currentColWidth + labelOpts.padding;
11709
						columnWidths.push(currentColWidth); // previous column width
11710
11711
						currentColWidth = 0;
11712
						currentColHeight = 0;
11713
					}
11714
11715
					// Get max width
11716
					currentColWidth = Math.max(currentColWidth, itemWidth);
11717
					currentColHeight += itemHeight;
11718
11719
					// Store the hitbox width and height here. Final position will be updated in `draw`
11720
					hitboxes[i] = {
11721
						left: 0,
11722
						top: 0,
11723
						width: itemWidth,
11724
						height: fontSize
11725
					};
11726
				});
11727
11728
				totalWidth += currentColWidth;
11729
				columnWidths.push(currentColWidth);
11730
				minSize.width += totalWidth;
11731
			}
11732
		}
11733
11734
		me.width = minSize.width;
11735
		me.height = minSize.height;
11736
	},
11737
	afterFit: noop,
11738
11739
	// Shared Methods
11740
	isHorizontal: function() {
11741
		return this.options.position === 'top' || this.options.position === 'bottom';
11742
	},
11743
11744
	// Actually draw the legend on the canvas
11745
	draw: function() {
11746
		var me = this;
11747
		var opts = me.options;
11748
		var labelOpts = opts.labels;
11749
		var globalDefault = defaults.global;
11750
		var lineDefault = globalDefault.elements.line;
11751
		var legendWidth = me.width;
11752
		var lineWidths = me.lineWidths;
11753
11754
		if (opts.display) {
11755
			var ctx = me.ctx;
11756
			var valueOrDefault = helpers.valueOrDefault;
11757
			var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor);
11758
			var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
11759
			var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
11760
			var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
11761
			var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
11762
			var cursor;
11763
11764
			// Canvas setup
11765
			ctx.textAlign = 'left';
11766
			ctx.textBaseline = 'middle';
11767
			ctx.lineWidth = 0.5;
11768
			ctx.strokeStyle = fontColor; // for strikethrough effect
11769
			ctx.fillStyle = fontColor; // render in correct colour
11770
			ctx.font = labelFont;
11771
11772
			var boxWidth = getBoxWidth(labelOpts, fontSize);
11773
			var hitboxes = me.legendHitBoxes;
11774
11775
			// current position
11776
			var drawLegendBox = function(x, y, legendItem) {
11777
				if (isNaN(boxWidth) || boxWidth <= 0) {
11778
					return;
11779
				}
11780
11781
				// Set the ctx for the box
11782
				ctx.save();
11783
11784
				ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
11785
				ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
11786
				ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
11787
				ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
11788
				ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
11789
				ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
11790
				var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);
11791
11792
				if (ctx.setLineDash) {
11793
					// IE 9 and 10 do not support line dash
11794
					ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash));
11795
				}
11796
11797
				if (opts.labels && opts.labels.usePointStyle) {
11798
					// Recalculate x and y for drawPoint() because its expecting
11799
					// x and y to be center of figure (instead of top left)
11800
					var radius = fontSize * Math.SQRT2 / 2;
11801
					var offSet = radius / Math.SQRT2;
11802
					var centerX = x + offSet;
11803
					var centerY = y + offSet;
11804
11805
					// Draw pointStyle as legend symbol
11806
					helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
11807
				} else {
11808
					// Draw box as legend symbol
11809
					if (!isLineWidthZero) {
11810
						ctx.strokeRect(x, y, boxWidth, fontSize);
11811
					}
11812
					ctx.fillRect(x, y, boxWidth, fontSize);
11813
				}
11814
11815
				ctx.restore();
11816
			};
11817
			var fillText = function(x, y, legendItem, textWidth) {
11818
				var halfFontSize = fontSize / 2;
11819
				var xLeft = boxWidth + halfFontSize + x;
11820
				var yMiddle = y + halfFontSize;
11821
11822
				ctx.fillText(legendItem.text, xLeft, yMiddle);
11823
11824
				if (legendItem.hidden) {
11825
					// Strikethrough the text if hidden
11826
					ctx.beginPath();
11827
					ctx.lineWidth = 2;
11828
					ctx.moveTo(xLeft, yMiddle);
11829
					ctx.lineTo(xLeft + textWidth, yMiddle);
11830
					ctx.stroke();
11831
				}
11832
			};
11833
11834
			// Horizontal
11835
			var isHorizontal = me.isHorizontal();
11836
			if (isHorizontal) {
11837
				cursor = {
11838
					x: me.left + ((legendWidth - lineWidths[0]) / 2),
11839
					y: me.top + labelOpts.padding,
11840
					line: 0
11841
				};
11842
			} else {
11843
				cursor = {
11844
					x: me.left + labelOpts.padding,
11845
					y: me.top + labelOpts.padding,
11846
					line: 0
11847
				};
11848
			}
11849
11850
			var itemHeight = fontSize + labelOpts.padding;
11851
			helpers.each(me.legendItems, function(legendItem, i) {
11852
				var textWidth = ctx.measureText(legendItem.text).width;
11853
				var width = boxWidth + (fontSize / 2) + textWidth;
11854
				var x = cursor.x;
11855
				var y = cursor.y;
11856
11857
				if (isHorizontal) {
11858
					if (x + width >= legendWidth) {
11859
						y = cursor.y += itemHeight;
11860
						cursor.line++;
11861
						x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
11862
					}
11863
				} else if (y + itemHeight > me.bottom) {
11864
					x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
11865
					y = cursor.y = me.top + labelOpts.padding;
11866
					cursor.line++;
11867
				}
11868
11869
				drawLegendBox(x, y, legendItem);
11870
11871
				hitboxes[i].left = x;
11872
				hitboxes[i].top = y;
11873
11874
				// Fill the actual label
11875
				fillText(x, y, legendItem, textWidth);
11876
11877
				if (isHorizontal) {
11878
					cursor.x += width + (labelOpts.padding);
11879
				} else {
11880
					cursor.y += itemHeight;
11881
				}
11882
11883
			});
11884
		}
11885
	},
11886
11887
	/**
11888
	 * Handle an event
11889
	 * @private
11890
	 * @param {IEvent} event - The event to handle
11891
	 * @return {Boolean} true if a change occured
11892
	 */
11893
	handleEvent: function(e) {
11894
		var me = this;
11895
		var opts = me.options;
11896
		var type = e.type === 'mouseup' ? 'click' : e.type;
11897
		var changed = false;
11898
11899
		if (type === 'mousemove') {
11900
			if (!opts.onHover) {
11901
				return;
11902
			}
11903
		} else if (type === 'click') {
11904
			if (!opts.onClick) {
11905
				return;
11906
			}
11907
		} else {
11908
			return;
11909
		}
11910
11911
		// Chart event already has relative position in it
11912
		var x = e.x;
11913
		var y = e.y;
11914
11915
		if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
11916
			// See if we are touching one of the dataset boxes
11917
			var lh = me.legendHitBoxes;
11918
			for (var i = 0; i < lh.length; ++i) {
11919
				var hitBox = lh[i];
11920
11921
				if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
11922
					// Touching an element
11923
					if (type === 'click') {
11924
						// use e.native for backwards compatibility
11925
						opts.onClick.call(me, e.native, me.legendItems[i]);
11926
						changed = true;
11927
						break;
11928
					} else if (type === 'mousemove') {
11929
						// use e.native for backwards compatibility
11930
						opts.onHover.call(me, e.native, me.legendItems[i]);
11931
						changed = true;
11932
						break;
11933
					}
11934
				}
11935
			}
11936
		}
11937
11938
		return changed;
11939
	}
11940
});
11941
11942
function createNewLegendAndAttach(chart, legendOpts) {
11943
	var legend = new Legend({
11944
		ctx: chart.ctx,
11945
		options: legendOpts,
11946
		chart: chart
11947
	});
11948
11949
	layouts.configure(chart, legend, legendOpts);
11950
	layouts.addBox(chart, legend);
11951
	chart.legend = legend;
11952
}
11953
11954
module.exports = {
11955
	id: 'legend',
11956
11957
	/**
11958
	 * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making
11959
	 * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of
11960
	 * the plugin, which one will be re-exposed in the chart.js file.
11961
	 * https://github.com/chartjs/Chart.js/pull/2640
11962
	 * @private
11963
	 */
11964
	_element: Legend,
11965
11966
	beforeInit: function(chart) {
11967
		var legendOpts = chart.options.legend;
11968
11969
		if (legendOpts) {
11970
			createNewLegendAndAttach(chart, legendOpts);
11971
		}
11972
	},
11973
11974
	beforeUpdate: function(chart) {
11975
		var legendOpts = chart.options.legend;
11976
		var legend = chart.legend;
11977
11978
		if (legendOpts) {
11979
			helpers.mergeIf(legendOpts, defaults.global.legend);
11980
11981
			if (legend) {
11982
				layouts.configure(chart, legend, legendOpts);
11983
				legend.options = legendOpts;
11984
			} else {
11985
				createNewLegendAndAttach(chart, legendOpts);
11986
			}
11987
		} else if (legend) {
11988
			layouts.removeBox(chart, legend);
11989
			delete chart.legend;
11990
		}
11991
	},
11992
11993
	afterEvent: function(chart, e) {
11994
		var legend = chart.legend;
11995
		if (legend) {
11996
			legend.handleEvent(e);
11997
		}
11998
	}
11999
};
12000
12001
},{"26":26,"27":27,"31":31,"46":46}],53:[function(require,module,exports){
12002
'use strict';
12003
12004
var defaults = require(26);
12005
var Element = require(27);
12006
var helpers = require(46);
12007
var layouts = require(31);
12008
12009
var noop = helpers.noop;
12010
12011
defaults._set('global', {
12012
	title: {
12013
		display: false,
12014
		fontStyle: 'bold',
12015
		fullWidth: true,
12016
		lineHeight: 1.2,
12017
		padding: 10,
12018
		position: 'top',
12019
		text: '',
12020
		weight: 2000         // by default greater than legend (1000) to be above
12021
	}
12022
});
12023
12024
/**
12025
 * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
12026
 */
12027
var Title = Element.extend({
12028
	initialize: function(config) {
12029
		var me = this;
12030
		helpers.extend(me, config);
12031
12032
		// Contains hit boxes for each dataset (in dataset order)
12033
		me.legendHitBoxes = [];
12034
	},
12035
12036
	// These methods are ordered by lifecycle. Utilities then follow.
12037
12038
	beforeUpdate: noop,
12039
	update: function(maxWidth, maxHeight, margins) {
12040
		var me = this;
12041
12042
		// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
12043
		me.beforeUpdate();
12044
12045
		// Absorb the master measurements
12046
		me.maxWidth = maxWidth;
12047
		me.maxHeight = maxHeight;
12048
		me.margins = margins;
12049
12050
		// Dimensions
12051
		me.beforeSetDimensions();
12052
		me.setDimensions();
12053
		me.afterSetDimensions();
12054
		// Labels
12055
		me.beforeBuildLabels();
12056
		me.buildLabels();
12057
		me.afterBuildLabels();
12058
12059
		// Fit
12060
		me.beforeFit();
12061
		me.fit();
12062
		me.afterFit();
12063
		//
12064
		me.afterUpdate();
12065
12066
		return me.minSize;
12067
12068
	},
12069
	afterUpdate: noop,
12070
12071
	//
12072
12073
	beforeSetDimensions: noop,
12074
	setDimensions: function() {
12075
		var me = this;
12076
		// Set the unconstrained dimension before label rotation
12077
		if (me.isHorizontal()) {
12078
			// Reset position before calculating rotation
12079
			me.width = me.maxWidth;
12080
			me.left = 0;
12081
			me.right = me.width;
12082
		} else {
12083
			me.height = me.maxHeight;
12084
12085
			// Reset position before calculating rotation
12086
			me.top = 0;
12087
			me.bottom = me.height;
12088
		}
12089
12090
		// Reset padding
12091
		me.paddingLeft = 0;
12092
		me.paddingTop = 0;
12093
		me.paddingRight = 0;
12094
		me.paddingBottom = 0;
12095
12096
		// Reset minSize
12097
		me.minSize = {
12098
			width: 0,
12099
			height: 0
12100
		};
12101
	},
12102
	afterSetDimensions: noop,
12103
12104
	//
12105
12106
	beforeBuildLabels: noop,
12107
	buildLabels: noop,
12108
	afterBuildLabels: noop,
12109
12110
	//
12111
12112
	beforeFit: noop,
12113
	fit: function() {
12114
		var me = this;
12115
		var valueOrDefault = helpers.valueOrDefault;
12116
		var opts = me.options;
12117
		var display = opts.display;
12118
		var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize);
12119
		var minSize = me.minSize;
12120
		var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1;
12121
		var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
12122
		var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0;
12123
12124
		if (me.isHorizontal()) {
12125
			minSize.width = me.maxWidth; // fill all the width
12126
			minSize.height = textSize;
12127
		} else {
12128
			minSize.width = textSize;
12129
			minSize.height = me.maxHeight; // fill all the height
12130
		}
12131
12132
		me.width = minSize.width;
12133
		me.height = minSize.height;
12134
12135
	},
12136
	afterFit: noop,
12137
12138
	// Shared Methods
12139
	isHorizontal: function() {
12140
		var pos = this.options.position;
12141
		return pos === 'top' || pos === 'bottom';
12142
	},
12143
12144
	// Actually draw the title block on the canvas
12145
	draw: function() {
12146
		var me = this;
12147
		var ctx = me.ctx;
12148
		var valueOrDefault = helpers.valueOrDefault;
12149
		var opts = me.options;
12150
		var globalDefaults = defaults.global;
12151
12152
		if (opts.display) {
12153
			var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize);
12154
			var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle);
12155
			var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily);
12156
			var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);
12157
			var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
12158
			var offset = lineHeight / 2 + opts.padding;
12159
			var rotation = 0;
12160
			var top = me.top;
12161
			var left = me.left;
12162
			var bottom = me.bottom;
12163
			var right = me.right;
12164
			var maxWidth, titleX, titleY;
12165
12166
			ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
12167
			ctx.font = titleFont;
12168
12169
			// Horizontal
12170
			if (me.isHorizontal()) {
12171
				titleX = left + ((right - left) / 2); // midpoint of the width
12172
				titleY = top + offset;
12173
				maxWidth = right - left;
12174
			} else {
12175
				titleX = opts.position === 'left' ? left + offset : right - offset;
12176
				titleY = top + ((bottom - top) / 2);
12177
				maxWidth = bottom - top;
12178
				rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
12179
			}
12180
12181
			ctx.save();
12182
			ctx.translate(titleX, titleY);
12183
			ctx.rotate(rotation);
12184
			ctx.textAlign = 'center';
12185
			ctx.textBaseline = 'middle';
12186
12187
			var text = opts.text;
12188
			if (helpers.isArray(text)) {
12189
				var y = 0;
12190
				for (var i = 0; i < text.length; ++i) {
12191
					ctx.fillText(text[i], 0, y, maxWidth);
12192
					y += lineHeight;
12193
				}
12194
			} else {
12195
				ctx.fillText(text, 0, 0, maxWidth);
12196
			}
12197
12198
			ctx.restore();
12199
		}
12200
	}
12201
});
12202
12203
function createNewTitleBlockAndAttach(chart, titleOpts) {
12204
	var title = new Title({
12205
		ctx: chart.ctx,
12206
		options: titleOpts,
12207
		chart: chart
12208
	});
12209
12210
	layouts.configure(chart, title, titleOpts);
12211
	layouts.addBox(chart, title);
12212
	chart.titleBlock = title;
12213
}
12214
12215
module.exports = {
12216
	id: 'title',
12217
12218
	/**
12219
	 * Backward compatibility: since 2.1.5, the title is registered as a plugin, making
12220
	 * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of
12221
	 * the plugin, which one will be re-exposed in the chart.js file.
12222
	 * https://github.com/chartjs/Chart.js/pull/2640
12223
	 * @private
12224
	 */
12225
	_element: Title,
12226
12227
	beforeInit: function(chart) {
12228
		var titleOpts = chart.options.title;
12229
12230
		if (titleOpts) {
12231
			createNewTitleBlockAndAttach(chart, titleOpts);
12232
		}
12233
	},
12234
12235
	beforeUpdate: function(chart) {
12236
		var titleOpts = chart.options.title;
12237
		var titleBlock = chart.titleBlock;
12238
12239
		if (titleOpts) {
12240
			helpers.mergeIf(titleOpts, defaults.global.title);
12241
12242
			if (titleBlock) {
12243
				layouts.configure(chart, titleBlock, titleOpts);
12244
				titleBlock.options = titleOpts;
12245
			} else {
12246
				createNewTitleBlockAndAttach(chart, titleOpts);
12247
			}
12248
		} else if (titleBlock) {
12249
			layouts.removeBox(chart, titleBlock);
12250
			delete chart.titleBlock;
12251
		}
12252
	}
12253
};
12254
12255
},{"26":26,"27":27,"31":31,"46":46}],54:[function(require,module,exports){
12256
'use strict';
12257
12258
var Scale = require(33);
12259
var scaleService = require(34);
12260
12261
module.exports = function() {
12262
12263
	// Default config for a category scale
12264
	var defaultConfig = {
12265
		position: 'bottom'
12266
	};
12267
12268
	var DatasetScale = Scale.extend({
12269
		/**
12270
		* Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
12271
		* else fall back to data.labels
12272
		* @private
12273
		*/
12274
		getLabels: function() {
12275
			var data = this.chart.data;
12276
			return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
12277
		},
12278
12279
		determineDataLimits: function() {
12280
			var me = this;
12281
			var labels = me.getLabels();
12282
			me.minIndex = 0;
12283
			me.maxIndex = labels.length - 1;
12284
			var findIndex;
12285
12286
			if (me.options.ticks.min !== undefined) {
12287
				// user specified min value
12288
				findIndex = labels.indexOf(me.options.ticks.min);
12289
				me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
12290
			}
12291
12292
			if (me.options.ticks.max !== undefined) {
12293
				// user specified max value
12294
				findIndex = labels.indexOf(me.options.ticks.max);
12295
				me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
12296
			}
12297
12298
			me.min = labels[me.minIndex];
12299
			me.max = labels[me.maxIndex];
12300
		},
12301
12302
		buildTicks: function() {
12303
			var me = this;
12304
			var labels = me.getLabels();
12305
			// If we are viewing some subset of labels, slice the original array
12306
			me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
12307
		},
12308
12309
		getLabelForIndex: function(index, datasetIndex) {
12310
			var me = this;
12311
			var data = me.chart.data;
12312
			var isHorizontal = me.isHorizontal();
12313
12314
			if (data.yLabels && !isHorizontal) {
12315
				return me.getRightValue(data.datasets[datasetIndex].data[index]);
12316
			}
12317
			return me.ticks[index - me.minIndex];
12318
		},
12319
12320
		// Used to get data value locations.  Value can either be an index or a numerical value
12321
		getPixelForValue: function(value, index) {
12322
			var me = this;
12323
			var offset = me.options.offset;
12324
			// 1 is added because we need the length but we have the indexes
12325
			var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);
12326
12327
			// If value is a data object, then index is the index in the data array,
12328
			// not the index of the scale. We need to change that.
12329
			var valueCategory;
12330
			if (value !== undefined && value !== null) {
12331
				valueCategory = me.isHorizontal() ? value.x : value.y;
12332
			}
12333
			if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
12334
				var labels = me.getLabels();
12335
				value = valueCategory || value;
12336
				var idx = labels.indexOf(value);
12337
				index = idx !== -1 ? idx : index;
12338
			}
12339
12340
			if (me.isHorizontal()) {
12341
				var valueWidth = me.width / offsetAmt;
12342
				var widthOffset = (valueWidth * (index - me.minIndex));
12343
12344
				if (offset) {
12345
					widthOffset += (valueWidth / 2);
12346
				}
12347
12348
				return me.left + Math.round(widthOffset);
12349
			}
12350
			var valueHeight = me.height / offsetAmt;
12351
			var heightOffset = (valueHeight * (index - me.minIndex));
12352
12353
			if (offset) {
12354
				heightOffset += (valueHeight / 2);
12355
			}
12356
12357
			return me.top + Math.round(heightOffset);
12358
		},
12359
		getPixelForTick: function(index) {
12360
			return this.getPixelForValue(this.ticks[index], index + this.minIndex, null);
12361
		},
12362
		getValueForPixel: function(pixel) {
12363
			var me = this;
12364
			var offset = me.options.offset;
12365
			var value;
12366
			var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
12367
			var horz = me.isHorizontal();
12368
			var valueDimension = (horz ? me.width : me.height) / offsetAmt;
12369
12370
			pixel -= horz ? me.left : me.top;
12371
12372
			if (offset) {
12373
				pixel -= (valueDimension / 2);
12374
			}
12375
12376
			if (pixel <= 0) {
12377
				value = 0;
12378
			} else {
12379
				value = Math.round(pixel / valueDimension);
12380
			}
12381
12382
			return value + me.minIndex;
12383
		},
12384
		getBasePixel: function() {
12385
			return this.bottom;
12386
		}
12387
	});
12388
12389
	scaleService.registerScaleType('category', DatasetScale, defaultConfig);
12390
};
12391
12392
},{"33":33,"34":34}],55:[function(require,module,exports){
12393
'use strict';
12394
12395
var defaults = require(26);
12396
var helpers = require(46);
12397
var scaleService = require(34);
12398
var Ticks = require(35);
12399
12400
module.exports = function(Chart) {
12401
12402
	var defaultConfig = {
12403
		position: 'left',
12404
		ticks: {
12405
			callback: Ticks.formatters.linear
12406
		}
12407
	};
12408
12409
	var LinearScale = Chart.LinearScaleBase.extend({
12410
12411
		determineDataLimits: function() {
12412
			var me = this;
12413
			var opts = me.options;
12414
			var chart = me.chart;
12415
			var data = chart.data;
12416
			var datasets = data.datasets;
12417
			var isHorizontal = me.isHorizontal();
12418
			var DEFAULT_MIN = 0;
12419
			var DEFAULT_MAX = 1;
12420
12421
			function IDMatches(meta) {
12422
				return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
12423
			}
12424
12425
			// First Calculate the range
12426
			me.min = null;
12427
			me.max = null;
12428
12429
			var hasStacks = opts.stacked;
12430
			if (hasStacks === undefined) {
12431
				helpers.each(datasets, function(dataset, datasetIndex) {
12432
					if (hasStacks) {
12433
						return;
12434
					}
12435
12436
					var meta = chart.getDatasetMeta(datasetIndex);
12437
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
12438
						meta.stack !== undefined) {
12439
						hasStacks = true;
12440
					}
12441
				});
12442
			}
12443
12444
			if (opts.stacked || hasStacks) {
12445
				var valuesPerStack = {};
12446
12447
				helpers.each(datasets, function(dataset, datasetIndex) {
12448
					var meta = chart.getDatasetMeta(datasetIndex);
12449
					var key = [
12450
						meta.type,
12451
						// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
12452
						((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
12453
						meta.stack
12454
					].join('.');
12455
12456
					if (valuesPerStack[key] === undefined) {
12457
						valuesPerStack[key] = {
12458
							positiveValues: [],
12459
							negativeValues: []
12460
						};
12461
					}
12462
12463
					// Store these per type
12464
					var positiveValues = valuesPerStack[key].positiveValues;
12465
					var negativeValues = valuesPerStack[key].negativeValues;
12466
12467
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12468
						helpers.each(dataset.data, function(rawValue, index) {
12469
							var value = +me.getRightValue(rawValue);
12470
							if (isNaN(value) || meta.data[index].hidden) {
12471
								return;
12472
							}
12473
12474
							positiveValues[index] = positiveValues[index] || 0;
12475
							negativeValues[index] = negativeValues[index] || 0;
12476
12477
							if (opts.relativePoints) {
12478
								positiveValues[index] = 100;
12479
							} else if (value < 0) {
12480
								negativeValues[index] += value;
12481
							} else {
12482
								positiveValues[index] += value;
12483
							}
12484
						});
12485
					}
12486
				});
12487
12488
				helpers.each(valuesPerStack, function(valuesForType) {
12489
					var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
12490
					var minVal = helpers.min(values);
12491
					var maxVal = helpers.max(values);
12492
					me.min = me.min === null ? minVal : Math.min(me.min, minVal);
12493
					me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
12494
				});
12495
12496
			} else {
12497
				helpers.each(datasets, function(dataset, datasetIndex) {
12498
					var meta = chart.getDatasetMeta(datasetIndex);
12499
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12500
						helpers.each(dataset.data, function(rawValue, index) {
12501
							var value = +me.getRightValue(rawValue);
12502
							if (isNaN(value) || meta.data[index].hidden) {
12503
								return;
12504
							}
12505
12506
							if (me.min === null) {
12507
								me.min = value;
12508
							} else if (value < me.min) {
12509
								me.min = value;
12510
							}
12511
12512
							if (me.max === null) {
12513
								me.max = value;
12514
							} else if (value > me.max) {
12515
								me.max = value;
12516
							}
12517
						});
12518
					}
12519
				});
12520
			}
12521
12522
			me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
12523
			me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;
12524
12525
			// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
12526
			this.handleTickRangeOptions();
12527
		},
12528
		getTickLimit: function() {
12529
			var maxTicks;
12530
			var me = this;
12531
			var tickOpts = me.options.ticks;
12532
12533
			if (me.isHorizontal()) {
12534
				maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));
12535
			} else {
12536
				// The factor of 2 used to scale the font size has been experimentally determined.
12537
				var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize);
12538
				maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));
12539
			}
12540
12541
			return maxTicks;
12542
		},
12543
		// Called after the ticks are built. We need
12544
		handleDirectionalChanges: function() {
12545
			if (!this.isHorizontal()) {
12546
				// We are in a vertical orientation. The top value is the highest. So reverse the array
12547
				this.ticks.reverse();
12548
			}
12549
		},
12550
		getLabelForIndex: function(index, datasetIndex) {
12551
			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
12552
		},
12553
		// Utils
12554
		getPixelForValue: function(value) {
12555
			// This must be called after fit has been run so that
12556
			// this.left, this.top, this.right, and this.bottom have been defined
12557
			var me = this;
12558
			var start = me.start;
12559
12560
			var rightValue = +me.getRightValue(value);
12561
			var pixel;
12562
			var range = me.end - start;
12563
12564
			if (me.isHorizontal()) {
12565
				pixel = me.left + (me.width / range * (rightValue - start));
12566
			} else {
12567
				pixel = me.bottom - (me.height / range * (rightValue - start));
12568
			}
12569
			return pixel;
12570
		},
12571
		getValueForPixel: function(pixel) {
12572
			var me = this;
12573
			var isHorizontal = me.isHorizontal();
12574
			var innerDimension = isHorizontal ? me.width : me.height;
12575
			var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
12576
			return me.start + ((me.end - me.start) * offset);
12577
		},
12578
		getPixelForTick: function(index) {
12579
			return this.getPixelForValue(this.ticksAsNumbers[index]);
12580
		}
12581
	});
12582
12583
	scaleService.registerScaleType('linear', LinearScale, defaultConfig);
12584
};
12585
12586
},{"26":26,"34":34,"35":35,"46":46}],56:[function(require,module,exports){
12587
'use strict';
12588
12589
var helpers = require(46);
12590
var Scale = require(33);
12591
12592
/**
12593
 * Generate a set of linear ticks
12594
 * @param generationOptions the options used to generate the ticks
12595
 * @param dataRange the range of the data
12596
 * @returns {Array<Number>} array of tick values
12597
 */
12598
function generateTicks(generationOptions, dataRange) {
12599
	var ticks = [];
12600
	// To get a "nice" value for the tick spacing, we will use the appropriately named
12601
	// "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
12602
	// for details.
12603
12604
	var factor;
12605
	var precision;
12606
	var spacing;
12607
12608
	if (generationOptions.stepSize && generationOptions.stepSize > 0) {
12609
		spacing = generationOptions.stepSize;
12610
	} else {
12611
		var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
12612
		spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
12613
12614
		precision = generationOptions.precision;
12615
		if (precision !== undefined) {
12616
			// If the user specified a precision, round to that number of decimal places
12617
			factor = Math.pow(10, precision);
12618
			spacing = Math.ceil(spacing * factor) / factor;
12619
		}
12620
	}
12621
	var niceMin = Math.floor(dataRange.min / spacing) * spacing;
12622
	var niceMax = Math.ceil(dataRange.max / spacing) * spacing;
12623
12624
	// If min, max and stepSize is set and they make an evenly spaced scale use it.
12625
	if (!helpers.isNullOrUndef(generationOptions.min) && !helpers.isNullOrUndef(generationOptions.max) && generationOptions.stepSize) {
12626
		// If very close to our whole number, use it.
12627
		if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
12628
			niceMin = generationOptions.min;
12629
			niceMax = generationOptions.max;
12630
		}
12631
	}
12632
12633
	var numSpaces = (niceMax - niceMin) / spacing;
12634
	// If very close to our rounded value, use it.
12635
	if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
12636
		numSpaces = Math.round(numSpaces);
12637
	} else {
12638
		numSpaces = Math.ceil(numSpaces);
12639
	}
12640
12641
	precision = 1;
12642
	if (spacing < 1) {
12643
		precision = Math.pow(10, 1 - Math.floor(helpers.log10(spacing)));
12644
		niceMin = Math.round(niceMin * precision) / precision;
12645
		niceMax = Math.round(niceMax * precision) / precision;
12646
	}
12647
	ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
12648
	for (var j = 1; j < numSpaces; ++j) {
12649
		ticks.push(Math.round((niceMin + j * spacing) * precision) / precision);
12650
	}
12651
	ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);
12652
12653
	return ticks;
12654
}
12655
12656
module.exports = function(Chart) {
12657
12658
	var noop = helpers.noop;
12659
12660
	Chart.LinearScaleBase = Scale.extend({
12661
		getRightValue: function(value) {
12662
			if (typeof value === 'string') {
12663
				return +value;
12664
			}
12665
			return Scale.prototype.getRightValue.call(this, value);
12666
		},
12667
12668
		handleTickRangeOptions: function() {
12669
			var me = this;
12670
			var opts = me.options;
12671
			var tickOpts = opts.ticks;
12672
12673
			// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
12674
			// do nothing since that would make the chart weird. If the user really wants a weird chart
12675
			// axis, they can manually override it
12676
			if (tickOpts.beginAtZero) {
12677
				var minSign = helpers.sign(me.min);
12678
				var maxSign = helpers.sign(me.max);
12679
12680
				if (minSign < 0 && maxSign < 0) {
12681
					// move the top up to 0
12682
					me.max = 0;
12683
				} else if (minSign > 0 && maxSign > 0) {
12684
					// move the bottom down to 0
12685
					me.min = 0;
12686
				}
12687
			}
12688
12689
			var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
12690
			var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;
12691
12692
			if (tickOpts.min !== undefined) {
12693
				me.min = tickOpts.min;
12694
			} else if (tickOpts.suggestedMin !== undefined) {
12695
				if (me.min === null) {
12696
					me.min = tickOpts.suggestedMin;
12697
				} else {
12698
					me.min = Math.min(me.min, tickOpts.suggestedMin);
12699
				}
12700
			}
12701
12702
			if (tickOpts.max !== undefined) {
12703
				me.max = tickOpts.max;
12704
			} else if (tickOpts.suggestedMax !== undefined) {
12705
				if (me.max === null) {
12706
					me.max = tickOpts.suggestedMax;
12707
				} else {
12708
					me.max = Math.max(me.max, tickOpts.suggestedMax);
12709
				}
12710
			}
12711
12712
			if (setMin !== setMax) {
12713
				// We set the min or the max but not both.
12714
				// So ensure that our range is good
12715
				// Inverted or 0 length range can happen when
12716
				// ticks.min is set, and no datasets are visible
12717
				if (me.min >= me.max) {
12718
					if (setMin) {
12719
						me.max = me.min + 1;
12720
					} else {
12721
						me.min = me.max - 1;
12722
					}
12723
				}
12724
			}
12725
12726
			if (me.min === me.max) {
12727
				me.max++;
12728
12729
				if (!tickOpts.beginAtZero) {
12730
					me.min--;
12731
				}
12732
			}
12733
		},
12734
		getTickLimit: noop,
12735
		handleDirectionalChanges: noop,
12736
12737
		buildTicks: function() {
12738
			var me = this;
12739
			var opts = me.options;
12740
			var tickOpts = opts.ticks;
12741
12742
			// Figure out what the max number of ticks we can support it is based on the size of
12743
			// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
12744
			// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
12745
			// the graph. Make sure we always have at least 2 ticks
12746
			var maxTicks = me.getTickLimit();
12747
			maxTicks = Math.max(2, maxTicks);
12748
12749
			var numericGeneratorOptions = {
12750
				maxTicks: maxTicks,
12751
				min: tickOpts.min,
12752
				max: tickOpts.max,
12753
				precision: tickOpts.precision,
12754
				stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
12755
			};
12756
			var ticks = me.ticks = generateTicks(numericGeneratorOptions, me);
12757
12758
			me.handleDirectionalChanges();
12759
12760
			// At this point, we need to update our max and min given the tick values since we have expanded the
12761
			// range of the scale
12762
			me.max = helpers.max(ticks);
12763
			me.min = helpers.min(ticks);
12764
12765
			if (tickOpts.reverse) {
12766
				ticks.reverse();
12767
12768
				me.start = me.max;
12769
				me.end = me.min;
12770
			} else {
12771
				me.start = me.min;
12772
				me.end = me.max;
12773
			}
12774
		},
12775
		convertTicksToLabels: function() {
12776
			var me = this;
12777
			me.ticksAsNumbers = me.ticks.slice();
12778
			me.zeroLineIndex = me.ticks.indexOf(0);
12779
12780
			Scale.prototype.convertTicksToLabels.call(me);
12781
		}
12782
	});
12783
};
12784
12785
},{"33":33,"46":46}],57:[function(require,module,exports){
12786
'use strict';
12787
12788
var helpers = require(46);
12789
var Scale = require(33);
12790
var scaleService = require(34);
12791
var Ticks = require(35);
12792
12793
/**
12794
 * Generate a set of logarithmic ticks
12795
 * @param generationOptions the options used to generate the ticks
12796
 * @param dataRange the range of the data
12797
 * @returns {Array<Number>} array of tick values
12798
 */
12799
function generateTicks(generationOptions, dataRange) {
12800
	var ticks = [];
12801
	var valueOrDefault = helpers.valueOrDefault;
12802
12803
	// Figure out what the max number of ticks we can support it is based on the size of
12804
	// the axis area. For now, we say that the minimum tick spacing in pixels must be 50
12805
	// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
12806
	// the graph
12807
	var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));
12808
12809
	var endExp = Math.floor(helpers.log10(dataRange.max));
12810
	var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
12811
	var exp, significand;
12812
12813
	if (tickVal === 0) {
12814
		exp = Math.floor(helpers.log10(dataRange.minNotZero));
12815
		significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));
12816
12817
		ticks.push(tickVal);
12818
		tickVal = significand * Math.pow(10, exp);
12819
	} else {
12820
		exp = Math.floor(helpers.log10(tickVal));
12821
		significand = Math.floor(tickVal / Math.pow(10, exp));
12822
	}
12823
	var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;
12824
12825
	do {
12826
		ticks.push(tickVal);
12827
12828
		++significand;
12829
		if (significand === 10) {
12830
			significand = 1;
12831
			++exp;
12832
			precision = exp >= 0 ? 1 : precision;
12833
		}
12834
12835
		tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision;
12836
	} while (exp < endExp || (exp === endExp && significand < endSignificand));
12837
12838
	var lastTick = valueOrDefault(generationOptions.max, tickVal);
12839
	ticks.push(lastTick);
12840
12841
	return ticks;
12842
}
12843
12844
12845
module.exports = function(Chart) {
12846
12847
	var defaultConfig = {
12848
		position: 'left',
12849
12850
		// label settings
12851
		ticks: {
12852
			callback: Ticks.formatters.logarithmic
12853
		}
12854
	};
12855
12856
	var LogarithmicScale = Scale.extend({
12857
		determineDataLimits: function() {
12858
			var me = this;
12859
			var opts = me.options;
12860
			var chart = me.chart;
12861
			var data = chart.data;
12862
			var datasets = data.datasets;
12863
			var isHorizontal = me.isHorizontal();
12864
			function IDMatches(meta) {
12865
				return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
12866
			}
12867
12868
			// Calculate Range
12869
			me.min = null;
12870
			me.max = null;
12871
			me.minNotZero = null;
12872
12873
			var hasStacks = opts.stacked;
12874
			if (hasStacks === undefined) {
12875
				helpers.each(datasets, function(dataset, datasetIndex) {
12876
					if (hasStacks) {
12877
						return;
12878
					}
12879
12880
					var meta = chart.getDatasetMeta(datasetIndex);
12881
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
12882
						meta.stack !== undefined) {
12883
						hasStacks = true;
12884
					}
12885
				});
12886
			}
12887
12888
			if (opts.stacked || hasStacks) {
12889
				var valuesPerStack = {};
12890
12891
				helpers.each(datasets, function(dataset, datasetIndex) {
12892
					var meta = chart.getDatasetMeta(datasetIndex);
12893
					var key = [
12894
						meta.type,
12895
						// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
12896
						((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
12897
						meta.stack
12898
					].join('.');
12899
12900
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12901
						if (valuesPerStack[key] === undefined) {
12902
							valuesPerStack[key] = [];
12903
						}
12904
12905
						helpers.each(dataset.data, function(rawValue, index) {
12906
							var values = valuesPerStack[key];
12907
							var value = +me.getRightValue(rawValue);
12908
							// invalid, hidden and negative values are ignored
12909
							if (isNaN(value) || meta.data[index].hidden || value < 0) {
12910
								return;
12911
							}
12912
							values[index] = values[index] || 0;
12913
							values[index] += value;
12914
						});
12915
					}
12916
				});
12917
12918
				helpers.each(valuesPerStack, function(valuesForType) {
12919
					if (valuesForType.length > 0) {
12920
						var minVal = helpers.min(valuesForType);
12921
						var maxVal = helpers.max(valuesForType);
12922
						me.min = me.min === null ? minVal : Math.min(me.min, minVal);
12923
						me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
12924
					}
12925
				});
12926
12927
			} else {
12928
				helpers.each(datasets, function(dataset, datasetIndex) {
12929
					var meta = chart.getDatasetMeta(datasetIndex);
12930
					if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12931
						helpers.each(dataset.data, function(rawValue, index) {
12932
							var value = +me.getRightValue(rawValue);
12933
							// invalid, hidden and negative values are ignored
12934
							if (isNaN(value) || meta.data[index].hidden || value < 0) {
12935
								return;
12936
							}
12937
12938
							if (me.min === null) {
12939
								me.min = value;
12940
							} else if (value < me.min) {
12941
								me.min = value;
12942
							}
12943
12944
							if (me.max === null) {
12945
								me.max = value;
12946
							} else if (value > me.max) {
12947
								me.max = value;
12948
							}
12949
12950
							if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
12951
								me.minNotZero = value;
12952
							}
12953
						});
12954
					}
12955
				});
12956
			}
12957
12958
			// Common base implementation to handle ticks.min, ticks.max
12959
			this.handleTickRangeOptions();
12960
		},
12961
		handleTickRangeOptions: function() {
12962
			var me = this;
12963
			var opts = me.options;
12964
			var tickOpts = opts.ticks;
12965
			var valueOrDefault = helpers.valueOrDefault;
12966
			var DEFAULT_MIN = 1;
12967
			var DEFAULT_MAX = 10;
12968
12969
			me.min = valueOrDefault(tickOpts.min, me.min);
12970
			me.max = valueOrDefault(tickOpts.max, me.max);
12971
12972
			if (me.min === me.max) {
12973
				if (me.min !== 0 && me.min !== null) {
12974
					me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
12975
					me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
12976
				} else {
12977
					me.min = DEFAULT_MIN;
12978
					me.max = DEFAULT_MAX;
12979
				}
12980
			}
12981
			if (me.min === null) {
12982
				me.min = Math.pow(10, Math.floor(helpers.log10(me.max)) - 1);
12983
			}
12984
			if (me.max === null) {
12985
				me.max = me.min !== 0
12986
					? Math.pow(10, Math.floor(helpers.log10(me.min)) + 1)
12987
					: DEFAULT_MAX;
12988
			}
12989
			if (me.minNotZero === null) {
12990
				if (me.min > 0) {
12991
					me.minNotZero = me.min;
12992
				} else if (me.max < 1) {
12993
					me.minNotZero = Math.pow(10, Math.floor(helpers.log10(me.max)));
12994
				} else {
12995
					me.minNotZero = DEFAULT_MIN;
12996
				}
12997
			}
12998
		},
12999
		buildTicks: function() {
13000
			var me = this;
13001
			var opts = me.options;
13002
			var tickOpts = opts.ticks;
13003
			var reverse = !me.isHorizontal();
13004
13005
			var generationOptions = {
13006
				min: tickOpts.min,
13007
				max: tickOpts.max
13008
			};
13009
			var ticks = me.ticks = generateTicks(generationOptions, me);
13010
13011
			// At this point, we need to update our max and min given the tick values since we have expanded the
13012
			// range of the scale
13013
			me.max = helpers.max(ticks);
13014
			me.min = helpers.min(ticks);
13015
13016
			if (tickOpts.reverse) {
13017
				reverse = !reverse;
13018
				me.start = me.max;
13019
				me.end = me.min;
13020
			} else {
13021
				me.start = me.min;
13022
				me.end = me.max;
13023
			}
13024
			if (reverse) {
13025
				ticks.reverse();
13026
			}
13027
		},
13028
		convertTicksToLabels: function() {
13029
			this.tickValues = this.ticks.slice();
13030
13031
			Scale.prototype.convertTicksToLabels.call(this);
13032
		},
13033
		// Get the correct tooltip label
13034
		getLabelForIndex: function(index, datasetIndex) {
13035
			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
13036
		},
13037
		getPixelForTick: function(index) {
13038
			return this.getPixelForValue(this.tickValues[index]);
13039
		},
13040
		/**
13041
		 * Returns the value of the first tick.
13042
		 * @param {Number} value - The minimum not zero value.
13043
		 * @return {Number} The first tick value.
13044
		 * @private
13045
		 */
13046
		_getFirstTickValue: function(value) {
13047
			var exp = Math.floor(helpers.log10(value));
13048
			var significand = Math.floor(value / Math.pow(10, exp));
13049
13050
			return significand * Math.pow(10, exp);
13051
		},
13052
		getPixelForValue: function(value) {
13053
			var me = this;
13054
			var reverse = me.options.ticks.reverse;
13055
			var log10 = helpers.log10;
13056
			var firstTickValue = me._getFirstTickValue(me.minNotZero);
13057
			var offset = 0;
13058
			var innerDimension, pixel, start, end, sign;
13059
13060
			value = +me.getRightValue(value);
13061
			if (reverse) {
13062
				start = me.end;
13063
				end = me.start;
13064
				sign = -1;
13065
			} else {
13066
				start = me.start;
13067
				end = me.end;
13068
				sign = 1;
13069
			}
13070
			if (me.isHorizontal()) {
13071
				innerDimension = me.width;
13072
				pixel = reverse ? me.right : me.left;
13073
			} else {
13074
				innerDimension = me.height;
13075
				sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0)
13076
				pixel = reverse ? me.top : me.bottom;
13077
			}
13078
			if (value !== start) {
13079
				if (start === 0) { // include zero tick
13080
					offset = helpers.getValueOrDefault(
13081
						me.options.ticks.fontSize,
13082
						Chart.defaults.global.defaultFontSize
13083
					);
13084
					innerDimension -= offset;
13085
					start = firstTickValue;
13086
				}
13087
				if (value !== 0) {
13088
					offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start));
13089
				}
13090
				pixel += sign * offset;
13091
			}
13092
			return pixel;
13093
		},
13094
		getValueForPixel: function(pixel) {
13095
			var me = this;
13096
			var reverse = me.options.ticks.reverse;
13097
			var log10 = helpers.log10;
13098
			var firstTickValue = me._getFirstTickValue(me.minNotZero);
13099
			var innerDimension, start, end, value;
13100
13101
			if (reverse) {
13102
				start = me.end;
13103
				end = me.start;
13104
			} else {
13105
				start = me.start;
13106
				end = me.end;
13107
			}
13108
			if (me.isHorizontal()) {
13109
				innerDimension = me.width;
13110
				value = reverse ? me.right - pixel : pixel - me.left;
13111
			} else {
13112
				innerDimension = me.height;
13113
				value = reverse ? pixel - me.top : me.bottom - pixel;
13114
			}
13115
			if (value !== start) {
13116
				if (start === 0) { // include zero tick
13117
					var offset = helpers.getValueOrDefault(
13118
						me.options.ticks.fontSize,
13119
						Chart.defaults.global.defaultFontSize
13120
					);
13121
					value -= offset;
13122
					innerDimension -= offset;
13123
					start = firstTickValue;
13124
				}
13125
				value *= log10(end) - log10(start);
13126
				value /= innerDimension;
13127
				value = Math.pow(10, log10(start) + value);
13128
			}
13129
			return value;
13130
		}
13131
	});
13132
13133
	scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);
13134
};
13135
13136
},{"33":33,"34":34,"35":35,"46":46}],58:[function(require,module,exports){
13137
'use strict';
13138
13139
var defaults = require(26);
13140
var helpers = require(46);
13141
var scaleService = require(34);
13142
var Ticks = require(35);
13143
13144
module.exports = function(Chart) {
13145
13146
	var globalDefaults = defaults.global;
13147
13148
	var defaultConfig = {
13149
		display: true,
13150
13151
		// Boolean - Whether to animate scaling the chart from the centre
13152
		animate: true,
13153
		position: 'chartArea',
13154
13155
		angleLines: {
13156
			display: true,
13157
			color: 'rgba(0, 0, 0, 0.1)',
13158
			lineWidth: 1
13159
		},
13160
13161
		gridLines: {
13162
			circular: false
13163
		},
13164
13165
		// label settings
13166
		ticks: {
13167
			// Boolean - Show a backdrop to the scale label
13168
			showLabelBackdrop: true,
13169
13170
			// String - The colour of the label backdrop
13171
			backdropColor: 'rgba(255,255,255,0.75)',
13172
13173
			// Number - The backdrop padding above & below the label in pixels
13174
			backdropPaddingY: 2,
13175
13176
			// Number - The backdrop padding to the side of the label in pixels
13177
			backdropPaddingX: 2,
13178
13179
			callback: Ticks.formatters.linear
13180
		},
13181
13182
		pointLabels: {
13183
			// Boolean - if true, show point labels
13184
			display: true,
13185
13186
			// Number - Point label font size in pixels
13187
			fontSize: 10,
13188
13189
			// Function - Used to convert point labels
13190
			callback: function(label) {
13191
				return label;
13192
			}
13193
		}
13194
	};
13195
13196
	function getValueCount(scale) {
13197
		var opts = scale.options;
13198
		return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0;
13199
	}
13200
13201
	function getPointLabelFontOptions(scale) {
13202
		var pointLabelOptions = scale.options.pointLabels;
13203
		var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);
13204
		var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);
13205
		var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);
13206
		var font = helpers.fontString(fontSize, fontStyle, fontFamily);
13207
13208
		return {
13209
			size: fontSize,
13210
			style: fontStyle,
13211
			family: fontFamily,
13212
			font: font
13213
		};
13214
	}
13215
13216
	function measureLabelSize(ctx, fontSize, label) {
13217
		if (helpers.isArray(label)) {
13218
			return {
13219
				w: helpers.longestText(ctx, ctx.font, label),
13220
				h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize)
13221
			};
13222
		}
13223
13224
		return {
13225
			w: ctx.measureText(label).width,
13226
			h: fontSize
13227
		};
13228
	}
13229
13230
	function determineLimits(angle, pos, size, min, max) {
13231
		if (angle === min || angle === max) {
13232
			return {
13233
				start: pos - (size / 2),
13234
				end: pos + (size / 2)
13235
			};
13236
		} else if (angle < min || angle > max) {
13237
			return {
13238
				start: pos - size - 5,
13239
				end: pos
13240
			};
13241
		}
13242
13243
		return {
13244
			start: pos,
13245
			end: pos + size + 5
13246
		};
13247
	}
13248
13249
	/**
13250
	 * Helper function to fit a radial linear scale with point labels
13251
	 */
13252
	function fitWithPointLabels(scale) {
13253
		/*
13254
		 * Right, this is really confusing and there is a lot of maths going on here
13255
		 * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
13256
		 *
13257
		 * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
13258
		 *
13259
		 * Solution:
13260
		 *
13261
		 * We assume the radius of the polygon is half the size of the canvas at first
13262
		 * at each index we check if the text overlaps.
13263
		 *
13264
		 * Where it does, we store that angle and that index.
13265
		 *
13266
		 * After finding the largest index and angle we calculate how much we need to remove
13267
		 * from the shape radius to move the point inwards by that x.
13268
		 *
13269
		 * We average the left and right distances to get the maximum shape radius that can fit in the box
13270
		 * along with labels.
13271
		 *
13272
		 * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
13273
		 * on each side, removing that from the size, halving it and adding the left x protrusion width.
13274
		 *
13275
		 * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
13276
		 * and position it in the most space efficient manner
13277
		 *
13278
		 * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
13279
		 */
13280
13281
		var plFont = getPointLabelFontOptions(scale);
13282
13283
		// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
13284
		// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
13285
		var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
13286
		var furthestLimits = {
13287
			r: scale.width,
13288
			l: 0,
13289
			t: scale.height,
13290
			b: 0
13291
		};
13292
		var furthestAngles = {};
13293
		var i, textSize, pointPosition;
13294
13295
		scale.ctx.font = plFont.font;
13296
		scale._pointLabelSizes = [];
13297
13298
		var valueCount = getValueCount(scale);
13299
		for (i = 0; i < valueCount; i++) {
13300
			pointPosition = scale.getPointPosition(i, largestPossibleRadius);
13301
			textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || '');
13302
			scale._pointLabelSizes[i] = textSize;
13303
13304
			// Add quarter circle to make degree 0 mean top of circle
13305
			var angleRadians = scale.getIndexAngle(i);
13306
			var angle = helpers.toDegrees(angleRadians) % 360;
13307
			var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
13308
			var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
13309
13310
			if (hLimits.start < furthestLimits.l) {
13311
				furthestLimits.l = hLimits.start;
13312
				furthestAngles.l = angleRadians;
13313
			}
13314
13315
			if (hLimits.end > furthestLimits.r) {
13316
				furthestLimits.r = hLimits.end;
13317
				furthestAngles.r = angleRadians;
13318
			}
13319
13320
			if (vLimits.start < furthestLimits.t) {
13321
				furthestLimits.t = vLimits.start;
13322
				furthestAngles.t = angleRadians;
13323
			}
13324
13325
			if (vLimits.end > furthestLimits.b) {
13326
				furthestLimits.b = vLimits.end;
13327
				furthestAngles.b = angleRadians;
13328
			}
13329
		}
13330
13331
		scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles);
13332
	}
13333
13334
	/**
13335
	 * Helper function to fit a radial linear scale with no point labels
13336
	 */
13337
	function fit(scale) {
13338
		var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
13339
		scale.drawingArea = Math.round(largestPossibleRadius);
13340
		scale.setCenterPoint(0, 0, 0, 0);
13341
	}
13342
13343
	function getTextAlignForAngle(angle) {
13344
		if (angle === 0 || angle === 180) {
13345
			return 'center';
13346
		} else if (angle < 180) {
13347
			return 'left';
13348
		}
13349
13350
		return 'right';
13351
	}
13352
13353
	function fillText(ctx, text, position, fontSize) {
13354
		if (helpers.isArray(text)) {
13355
			var y = position.y;
13356
			var spacing = 1.5 * fontSize;
13357
13358
			for (var i = 0; i < text.length; ++i) {
13359
				ctx.fillText(text[i], position.x, y);
13360
				y += spacing;
13361
			}
13362
		} else {
13363
			ctx.fillText(text, position.x, position.y);
13364
		}
13365
	}
13366
13367
	function adjustPointPositionForLabelHeight(angle, textSize, position) {
13368
		if (angle === 90 || angle === 270) {
13369
			position.y -= (textSize.h / 2);
13370
		} else if (angle > 270 || angle < 90) {
13371
			position.y -= textSize.h;
13372
		}
13373
	}
13374
13375
	function drawPointLabels(scale) {
13376
		var ctx = scale.ctx;
13377
		var opts = scale.options;
13378
		var angleLineOpts = opts.angleLines;
13379
		var pointLabelOpts = opts.pointLabels;
13380
13381
		ctx.lineWidth = angleLineOpts.lineWidth;
13382
		ctx.strokeStyle = angleLineOpts.color;
13383
13384
		var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
13385
13386
		// Point Label Font
13387
		var plFont = getPointLabelFontOptions(scale);
13388
13389
		ctx.textBaseline = 'top';
13390
13391
		for (var i = getValueCount(scale) - 1; i >= 0; i--) {
13392
			if (angleLineOpts.display) {
13393
				var outerPosition = scale.getPointPosition(i, outerDistance);
13394
				ctx.beginPath();
13395
				ctx.moveTo(scale.xCenter, scale.yCenter);
13396
				ctx.lineTo(outerPosition.x, outerPosition.y);
13397
				ctx.stroke();
13398
				ctx.closePath();
13399
			}
13400
13401
			if (pointLabelOpts.display) {
13402
				// Extra 3px out for some label spacing
13403
				var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5);
13404
13405
				// Keep this in loop since we may support array properties here
13406
				var pointLabelFontColor = helpers.valueAtIndexOrDefault(pointLabelOpts.fontColor, i, globalDefaults.defaultFontColor);
13407
				ctx.font = plFont.font;
13408
				ctx.fillStyle = pointLabelFontColor;
13409
13410
				var angleRadians = scale.getIndexAngle(i);
13411
				var angle = helpers.toDegrees(angleRadians);
13412
				ctx.textAlign = getTextAlignForAngle(angle);
13413
				adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
13414
				fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size);
13415
			}
13416
		}
13417
	}
13418
13419
	function drawRadiusLine(scale, gridLineOpts, radius, index) {
13420
		var ctx = scale.ctx;
13421
		ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1);
13422
		ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
13423
13424
		if (scale.options.gridLines.circular) {
13425
			// Draw circular arcs between the points
13426
			ctx.beginPath();
13427
			ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
13428
			ctx.closePath();
13429
			ctx.stroke();
13430
		} else {
13431
			// Draw straight lines connecting each index
13432
			var valueCount = getValueCount(scale);
13433
13434
			if (valueCount === 0) {
13435
				return;
13436
			}
13437
13438
			ctx.beginPath();
13439
			var pointPosition = scale.getPointPosition(0, radius);
13440
			ctx.moveTo(pointPosition.x, pointPosition.y);
13441
13442
			for (var i = 1; i < valueCount; i++) {
13443
				pointPosition = scale.getPointPosition(i, radius);
13444
				ctx.lineTo(pointPosition.x, pointPosition.y);
13445
			}
13446
13447
			ctx.closePath();
13448
			ctx.stroke();
13449
		}
13450
	}
13451
13452
	function numberOrZero(param) {
13453
		return helpers.isNumber(param) ? param : 0;
13454
	}
13455
13456
	var LinearRadialScale = Chart.LinearScaleBase.extend({
13457
		setDimensions: function() {
13458
			var me = this;
13459
			var opts = me.options;
13460
			var tickOpts = opts.ticks;
13461
			// Set the unconstrained dimension before label rotation
13462
			me.width = me.maxWidth;
13463
			me.height = me.maxHeight;
13464
			me.xCenter = Math.round(me.width / 2);
13465
			me.yCenter = Math.round(me.height / 2);
13466
13467
			var minSize = helpers.min([me.height, me.width]);
13468
			var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
13469
			me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);
13470
		},
13471
		determineDataLimits: function() {
13472
			var me = this;
13473
			var chart = me.chart;
13474
			var min = Number.POSITIVE_INFINITY;
13475
			var max = Number.NEGATIVE_INFINITY;
13476
13477
			helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
13478
				if (chart.isDatasetVisible(datasetIndex)) {
13479
					var meta = chart.getDatasetMeta(datasetIndex);
13480
13481
					helpers.each(dataset.data, function(rawValue, index) {
13482
						var value = +me.getRightValue(rawValue);
13483
						if (isNaN(value) || meta.data[index].hidden) {
13484
							return;
13485
						}
13486
13487
						min = Math.min(value, min);
13488
						max = Math.max(value, max);
13489
					});
13490
				}
13491
			});
13492
13493
			me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
13494
			me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);
13495
13496
			// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
13497
			me.handleTickRangeOptions();
13498
		},
13499
		getTickLimit: function() {
13500
			var tickOpts = this.options.ticks;
13501
			var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
13502
			return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));
13503
		},
13504
		convertTicksToLabels: function() {
13505
			var me = this;
13506
13507
			Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me);
13508
13509
			// Point labels
13510
			me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
13511
		},
13512
		getLabelForIndex: function(index, datasetIndex) {
13513
			return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
13514
		},
13515
		fit: function() {
13516
			if (this.options.pointLabels.display) {
13517
				fitWithPointLabels(this);
13518
			} else {
13519
				fit(this);
13520
			}
13521
		},
13522
		/**
13523
		 * Set radius reductions and determine new radius and center point
13524
		 * @private
13525
		 */
13526
		setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
13527
			var me = this;
13528
			var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
13529
			var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
13530
			var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
13531
			var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b);
13532
13533
			radiusReductionLeft = numberOrZero(radiusReductionLeft);
13534
			radiusReductionRight = numberOrZero(radiusReductionRight);
13535
			radiusReductionTop = numberOrZero(radiusReductionTop);
13536
			radiusReductionBottom = numberOrZero(radiusReductionBottom);
13537
13538
			me.drawingArea = Math.min(
13539
				Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
13540
				Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
13541
			me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
13542
		},
13543
		setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
13544
			var me = this;
13545
			var maxRight = me.width - rightMovement - me.drawingArea;
13546
			var maxLeft = leftMovement + me.drawingArea;
13547
			var maxTop = topMovement + me.drawingArea;
13548
			var maxBottom = me.height - bottomMovement - me.drawingArea;
13549
13550
			me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
13551
			me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top);
13552
		},
13553
13554
		getIndexAngle: function(index) {
13555
			var angleMultiplier = (Math.PI * 2) / getValueCount(this);
13556
			var startAngle = this.chart.options && this.chart.options.startAngle ?
13557
				this.chart.options.startAngle :
13558
				0;
13559
13560
			var startAngleRadians = startAngle * Math.PI * 2 / 360;
13561
13562
			// Start from the top instead of right, so remove a quarter of the circle
13563
			return index * angleMultiplier + startAngleRadians;
13564
		},
13565
		getDistanceFromCenterForValue: function(value) {
13566
			var me = this;
13567
13568
			if (value === null) {
13569
				return 0; // null always in center
13570
			}
13571
13572
			// Take into account half font size + the yPadding of the top value
13573
			var scalingFactor = me.drawingArea / (me.max - me.min);
13574
			if (me.options.ticks.reverse) {
13575
				return (me.max - value) * scalingFactor;
13576
			}
13577
			return (value - me.min) * scalingFactor;
13578
		},
13579
		getPointPosition: function(index, distanceFromCenter) {
13580
			var me = this;
13581
			var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
13582
			return {
13583
				x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,
13584
				y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter
13585
			};
13586
		},
13587
		getPointPositionForValue: function(index, value) {
13588
			return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
13589
		},
13590
13591
		getBasePosition: function() {
13592
			var me = this;
13593
			var min = me.min;
13594
			var max = me.max;
13595
13596
			return me.getPointPositionForValue(0,
13597
				me.beginAtZero ? 0 :
13598
				min < 0 && max < 0 ? max :
13599
				min > 0 && max > 0 ? min :
13600
				0);
13601
		},
13602
13603
		draw: function() {
13604
			var me = this;
13605
			var opts = me.options;
13606
			var gridLineOpts = opts.gridLines;
13607
			var tickOpts = opts.ticks;
13608
			var valueOrDefault = helpers.valueOrDefault;
13609
13610
			if (opts.display) {
13611
				var ctx = me.ctx;
13612
				var startAngle = this.getIndexAngle(0);
13613
13614
				// Tick Font
13615
				var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
13616
				var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
13617
				var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
13618
				var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
13619
13620
				helpers.each(me.ticks, function(label, index) {
13621
					// Don't draw a centre value (if it is minimum)
13622
					if (index > 0 || tickOpts.reverse) {
13623
						var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
13624
13625
						// Draw circular lines around the scale
13626
						if (gridLineOpts.display && index !== 0) {
13627
							drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
13628
						}
13629
13630
						if (tickOpts.display) {
13631
							var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
13632
							ctx.font = tickLabelFont;
13633
13634
							ctx.save();
13635
							ctx.translate(me.xCenter, me.yCenter);
13636
							ctx.rotate(startAngle);
13637
13638
							if (tickOpts.showLabelBackdrop) {
13639
								var labelWidth = ctx.measureText(label).width;
13640
								ctx.fillStyle = tickOpts.backdropColor;
13641
								ctx.fillRect(
13642
									-labelWidth / 2 - tickOpts.backdropPaddingX,
13643
									-yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY,
13644
									labelWidth + tickOpts.backdropPaddingX * 2,
13645
									tickFontSize + tickOpts.backdropPaddingY * 2
13646
								);
13647
							}
13648
13649
							ctx.textAlign = 'center';
13650
							ctx.textBaseline = 'middle';
13651
							ctx.fillStyle = tickFontColor;
13652
							ctx.fillText(label, 0, -yCenterOffset);
13653
							ctx.restore();
13654
						}
13655
					}
13656
				});
13657
13658
				if (opts.angleLines.display || opts.pointLabels.display) {
13659
					drawPointLabels(me);
13660
				}
13661
			}
13662
		}
13663
	});
13664
13665
	scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig);
13666
};
13667
13668
},{"26":26,"34":34,"35":35,"46":46}],59:[function(require,module,exports){
13669
/* global window: false */
13670
'use strict';
13671
13672
var moment = require(1);
13673
moment = typeof moment === 'function' ? moment : window.moment;
13674
13675
var defaults = require(26);
13676
var helpers = require(46);
13677
var Scale = require(33);
13678
var scaleService = require(34);
13679
13680
// Integer constants are from the ES6 spec.
13681
var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;
13682
var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
13683
13684
var INTERVALS = {
13685
	millisecond: {
13686
		common: true,
13687
		size: 1,
13688
		steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
13689
	},
13690
	second: {
13691
		common: true,
13692
		size: 1000,
13693
		steps: [1, 2, 5, 10, 15, 30]
13694
	},
13695
	minute: {
13696
		common: true,
13697
		size: 60000,
13698
		steps: [1, 2, 5, 10, 15, 30]
13699
	},
13700
	hour: {
13701
		common: true,
13702
		size: 3600000,
13703
		steps: [1, 2, 3, 6, 12]
13704
	},
13705
	day: {
13706
		common: true,
13707
		size: 86400000,
13708
		steps: [1, 2, 5]
13709
	},
13710
	week: {
13711
		common: false,
13712
		size: 604800000,
13713
		steps: [1, 2, 3, 4]
13714
	},
13715
	month: {
13716
		common: true,
13717
		size: 2.628e9,
13718
		steps: [1, 2, 3]
13719
	},
13720
	quarter: {
13721
		common: false,
13722
		size: 7.884e9,
13723
		steps: [1, 2, 3, 4]
13724
	},
13725
	year: {
13726
		common: true,
13727
		size: 3.154e10
13728
	}
13729
};
13730
13731
var UNITS = Object.keys(INTERVALS);
13732
13733
function sorter(a, b) {
13734
	return a - b;
13735
}
13736
13737
function arrayUnique(items) {
13738
	var hash = {};
13739
	var out = [];
13740
	var i, ilen, item;
13741
13742
	for (i = 0, ilen = items.length; i < ilen; ++i) {
13743
		item = items[i];
13744
		if (!hash[item]) {
13745
			hash[item] = true;
13746
			out.push(item);
13747
		}
13748
	}
13749
13750
	return out;
13751
}
13752
13753
/**
13754
 * Returns an array of {time, pos} objects used to interpolate a specific `time` or position
13755
 * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is
13756
 * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other
13757
 * extremity (left + width or top + height). Note that it would be more optimized to directly
13758
 * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need
13759
 * to create the lookup table. The table ALWAYS contains at least two items: min and max.
13760
 *
13761
 * @param {Number[]} timestamps - timestamps sorted from lowest to highest.
13762
 * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min
13763
 * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}.
13764
 * If 'series', timestamps will be positioned at the same distance from each other. In this
13765
 * case, only timestamps that break the time linearity are registered, meaning that in the
13766
 * best case, all timestamps are linear, the table contains only min and max.
13767
 */
13768
function buildLookupTable(timestamps, min, max, distribution) {
13769
	if (distribution === 'linear' || !timestamps.length) {
13770
		return [
13771
			{time: min, pos: 0},
13772
			{time: max, pos: 1}
13773
		];
13774
	}
13775
13776
	var table = [];
13777
	var items = [min];
13778
	var i, ilen, prev, curr, next;
13779
13780
	for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
13781
		curr = timestamps[i];
13782
		if (curr > min && curr < max) {
13783
			items.push(curr);
13784
		}
13785
	}
13786
13787
	items.push(max);
13788
13789
	for (i = 0, ilen = items.length; i < ilen; ++i) {
13790
		next = items[i + 1];
13791
		prev = items[i - 1];
13792
		curr = items[i];
13793
13794
		// only add points that breaks the scale linearity
13795
		if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) {
13796
			table.push({time: curr, pos: i / (ilen - 1)});
13797
		}
13798
	}
13799
13800
	return table;
13801
}
13802
13803
// @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/
13804
function lookup(table, key, value) {
13805
	var lo = 0;
13806
	var hi = table.length - 1;
13807
	var mid, i0, i1;
13808
13809
	while (lo >= 0 && lo <= hi) {
13810
		mid = (lo + hi) >> 1;
13811
		i0 = table[mid - 1] || null;
13812
		i1 = table[mid];
13813
13814
		if (!i0) {
13815
			// given value is outside table (before first item)
13816
			return {lo: null, hi: i1};
13817
		} else if (i1[key] < value) {
13818
			lo = mid + 1;
13819
		} else if (i0[key] > value) {
13820
			hi = mid - 1;
13821
		} else {
13822
			return {lo: i0, hi: i1};
13823
		}
13824
	}
13825
13826
	// given value is outside table (after last item)
13827
	return {lo: i1, hi: null};
13828
}
13829
13830
/**
13831
 * Linearly interpolates the given source `value` using the table items `skey` values and
13832
 * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos')
13833
 * returns the position for a timestamp equal to 42. If value is out of bounds, values at
13834
 * index [0, 1] or [n - 1, n] are used for the interpolation.
13835
 */
13836
function interpolate(table, skey, sval, tkey) {
13837
	var range = lookup(table, skey, sval);
13838
13839
	// Note: the lookup table ALWAYS contains at least 2 items (min and max)
13840
	var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo;
13841
	var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi;
13842
13843
	var span = next[skey] - prev[skey];
13844
	var ratio = span ? (sval - prev[skey]) / span : 0;
13845
	var offset = (next[tkey] - prev[tkey]) * ratio;
13846
13847
	return prev[tkey] + offset;
13848
}
13849
13850
/**
13851
 * Convert the given value to a moment object using the given time options.
13852
 * @see http://momentjs.com/docs/#/parsing/
13853
 */
13854
function momentify(value, options) {
13855
	var parser = options.parser;
13856
	var format = options.parser || options.format;
13857
13858
	if (typeof parser === 'function') {
13859
		return parser(value);
13860
	}
13861
13862
	if (typeof value === 'string' && typeof format === 'string') {
13863
		return moment(value, format);
13864
	}
13865
13866
	if (!(value instanceof moment)) {
13867
		value = moment(value);
13868
	}
13869
13870
	if (value.isValid()) {
13871
		return value;
13872
	}
13873
13874
	// Labels are in an incompatible moment format and no `parser` has been provided.
13875
	// The user might still use the deprecated `format` option to convert his inputs.
13876
	if (typeof format === 'function') {
13877
		return format(value);
13878
	}
13879
13880
	return value;
13881
}
13882
13883
function parse(input, scale) {
13884
	if (helpers.isNullOrUndef(input)) {
13885
		return null;
13886
	}
13887
13888
	var options = scale.options.time;
13889
	var value = momentify(scale.getRightValue(input), options);
13890
	if (!value.isValid()) {
13891
		return null;
13892
	}
13893
13894
	if (options.round) {
13895
		value.startOf(options.round);
13896
	}
13897
13898
	return value.valueOf();
13899
}
13900
13901
/**
13902
 * Returns the number of unit to skip to be able to display up to `capacity` number of ticks
13903
 * in `unit` for the given `min` / `max` range and respecting the interval steps constraints.
13904
 */
13905
function determineStepSize(min, max, unit, capacity) {
13906
	var range = max - min;
13907
	var interval = INTERVALS[unit];
13908
	var milliseconds = interval.size;
13909
	var steps = interval.steps;
13910
	var i, ilen, factor;
13911
13912
	if (!steps) {
13913
		return Math.ceil(range / (capacity * milliseconds));
13914
	}
13915
13916
	for (i = 0, ilen = steps.length; i < ilen; ++i) {
13917
		factor = steps[i];
13918
		if (Math.ceil(range / (milliseconds * factor)) <= capacity) {
13919
			break;
13920
		}
13921
	}
13922
13923
	return factor;
13924
}
13925
13926
/**
13927
 * Figures out what unit results in an appropriate number of auto-generated ticks
13928
 */
13929
function determineUnitForAutoTicks(minUnit, min, max, capacity) {
13930
	var ilen = UNITS.length;
13931
	var i, interval, factor;
13932
13933
	for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
13934
		interval = INTERVALS[UNITS[i]];
13935
		factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER;
13936
13937
		if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
13938
			return UNITS[i];
13939
		}
13940
	}
13941
13942
	return UNITS[ilen - 1];
13943
}
13944
13945
/**
13946
 * Figures out what unit to format a set of ticks with
13947
 */
13948
function determineUnitForFormatting(ticks, minUnit, min, max) {
13949
	var duration = moment.duration(moment(max).diff(moment(min)));
13950
	var ilen = UNITS.length;
13951
	var i, unit;
13952
13953
	for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) {
13954
		unit = UNITS[i];
13955
		if (INTERVALS[unit].common && duration.as(unit) >= ticks.length) {
13956
			return unit;
13957
		}
13958
	}
13959
13960
	return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];
13961
}
13962
13963
function determineMajorUnit(unit) {
13964
	for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
13965
		if (INTERVALS[UNITS[i]].common) {
13966
			return UNITS[i];
13967
		}
13968
	}
13969
}
13970
13971
/**
13972
 * Generates a maximum of `capacity` timestamps between min and max, rounded to the
13973
 * `minor` unit, aligned on the `major` unit and using the given scale time `options`.
13974
 * Important: this method can return ticks outside the min and max range, it's the
13975
 * responsibility of the calling code to clamp values if needed.
13976
 */
13977
function generate(min, max, capacity, options) {
13978
	var timeOpts = options.time;
13979
	var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity);
13980
	var major = determineMajorUnit(minor);
13981
	var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize);
13982
	var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
13983
	var majorTicksEnabled = options.ticks.major.enabled;
13984
	var interval = INTERVALS[minor];
13985
	var first = moment(min);
13986
	var last = moment(max);
13987
	var ticks = [];
13988
	var time;
13989
13990
	if (!stepSize) {
13991
		stepSize = determineStepSize(min, max, minor, capacity);
13992
	}
13993
13994
	// For 'week' unit, handle the first day of week option
13995
	if (weekday) {
13996
		first = first.isoWeekday(weekday);
13997
		last = last.isoWeekday(weekday);
13998
	}
13999
14000
	// Align first/last ticks on unit
14001
	first = first.startOf(weekday ? 'day' : minor);
14002
	last = last.startOf(weekday ? 'day' : minor);
14003
14004
	// Make sure that the last tick include max
14005
	if (last < max) {
14006
		last.add(1, minor);
14007
	}
14008
14009
	time = moment(first);
14010
14011
	if (majorTicksEnabled && major && !weekday && !timeOpts.round) {
14012
		// Align the first tick on the previous `minor` unit aligned on the `major` unit:
14013
		// we first aligned time on the previous `major` unit then add the number of full
14014
		// stepSize there is between first and the previous major time.
14015
		time.startOf(major);
14016
		time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor);
14017
	}
14018
14019
	for (; time < last; time.add(stepSize, minor)) {
14020
		ticks.push(+time);
14021
	}
14022
14023
	ticks.push(+time);
14024
14025
	return ticks;
14026
}
14027
14028
/**
14029
 * Returns the right and left offsets from edges in the form of {left, right}.
14030
 * Offsets are added when the `offset` option is true.
14031
 */
14032
function computeOffsets(table, ticks, min, max, options) {
14033
	var left = 0;
14034
	var right = 0;
14035
	var upper, lower;
14036
14037
	if (options.offset && ticks.length) {
14038
		if (!options.time.min) {
14039
			upper = ticks.length > 1 ? ticks[1] : max;
14040
			lower = ticks[0];
14041
			left = (
14042
				interpolate(table, 'time', upper, 'pos') -
14043
				interpolate(table, 'time', lower, 'pos')
14044
			) / 2;
14045
		}
14046
		if (!options.time.max) {
14047
			upper = ticks[ticks.length - 1];
14048
			lower = ticks.length > 1 ? ticks[ticks.length - 2] : min;
14049
			right = (
14050
				interpolate(table, 'time', upper, 'pos') -
14051
				interpolate(table, 'time', lower, 'pos')
14052
			) / 2;
14053
		}
14054
	}
14055
14056
	return {left: left, right: right};
14057
}
14058
14059
function ticksFromTimestamps(values, majorUnit) {
14060
	var ticks = [];
14061
	var i, ilen, value, major;
14062
14063
	for (i = 0, ilen = values.length; i < ilen; ++i) {
14064
		value = values[i];
14065
		major = majorUnit ? value === +moment(value).startOf(majorUnit) : false;
14066
14067
		ticks.push({
14068
			value: value,
14069
			major: major
14070
		});
14071
	}
14072
14073
	return ticks;
14074
}
14075
14076
function determineLabelFormat(data, timeOpts) {
14077
	var i, momentDate, hasTime;
14078
	var ilen = data.length;
14079
14080
	// find the label with the most parts (milliseconds, minutes, etc.)
14081
	// format all labels with the same level of detail as the most specific label
14082
	for (i = 0; i < ilen; i++) {
14083
		momentDate = momentify(data[i], timeOpts);
14084
		if (momentDate.millisecond() !== 0) {
14085
			return 'MMM D, YYYY h:mm:ss.SSS a';
14086
		}
14087
		if (momentDate.second() !== 0 || momentDate.minute() !== 0 || momentDate.hour() !== 0) {
14088
			hasTime = true;
14089
		}
14090
	}
14091
	if (hasTime) {
14092
		return 'MMM D, YYYY h:mm:ss a';
14093
	}
14094
	return 'MMM D, YYYY';
14095
}
14096
14097
module.exports = function() {
14098
14099
	var defaultConfig = {
14100
		position: 'bottom',
14101
14102
		/**
14103
		 * Data distribution along the scale:
14104
		 * - 'linear': data are spread according to their time (distances can vary),
14105
		 * - 'series': data are spread at the same distance from each other.
14106
		 * @see https://github.com/chartjs/Chart.js/pull/4507
14107
		 * @since 2.7.0
14108
		 */
14109
		distribution: 'linear',
14110
14111
		/**
14112
		 * Scale boundary strategy (bypassed by min/max time options)
14113
		 * - `data`: make sure data are fully visible, ticks outside are removed
14114
		 * - `ticks`: make sure ticks are fully visible, data outside are truncated
14115
		 * @see https://github.com/chartjs/Chart.js/pull/4556
14116
		 * @since 2.7.0
14117
		 */
14118
		bounds: 'data',
14119
14120
		time: {
14121
			parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
14122
			format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
14123
			unit: false, // false == automatic or override with week, month, year, etc.
14124
			round: false, // none, or override with week, month, year, etc.
14125
			displayFormat: false, // DEPRECATED
14126
			isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/
14127
			minUnit: 'millisecond',
14128
14129
			// defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
14130
			displayFormats: {
14131
				millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
14132
				second: 'h:mm:ss a', // 11:20:01 AM
14133
				minute: 'h:mm a', // 11:20 AM
14134
				hour: 'hA', // 5PM
14135
				day: 'MMM D', // Sep 4
14136
				week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
14137
				month: 'MMM YYYY', // Sept 2015
14138
				quarter: '[Q]Q - YYYY', // Q3
14139
				year: 'YYYY' // 2015
14140
			},
14141
		},
14142
		ticks: {
14143
			autoSkip: false,
14144
14145
			/**
14146
			 * Ticks generation input values:
14147
			 * - 'auto': generates "optimal" ticks based on scale size and time options.
14148
			 * - 'data': generates ticks from data (including labels from data {t|x|y} objects).
14149
			 * - 'labels': generates ticks from user given `data.labels` values ONLY.
14150
			 * @see https://github.com/chartjs/Chart.js/pull/4507
14151
			 * @since 2.7.0
14152
			 */
14153
			source: 'auto',
14154
14155
			major: {
14156
				enabled: false
14157
			}
14158
		}
14159
	};
14160
14161
	var TimeScale = Scale.extend({
14162
		initialize: function() {
14163
			if (!moment) {
14164
				throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com');
14165
			}
14166
14167
			this.mergeTicksOptions();
14168
14169
			Scale.prototype.initialize.call(this);
14170
		},
14171
14172
		update: function() {
14173
			var me = this;
14174
			var options = me.options;
14175
14176
			// DEPRECATIONS: output a message only one time per update
14177
			if (options.time && options.time.format) {
14178
				console.warn('options.time.format is deprecated and replaced by options.time.parser.');
14179
			}
14180
14181
			return Scale.prototype.update.apply(me, arguments);
14182
		},
14183
14184
		/**
14185
		 * Allows data to be referenced via 't' attribute
14186
		 */
14187
		getRightValue: function(rawValue) {
14188
			if (rawValue && rawValue.t !== undefined) {
14189
				rawValue = rawValue.t;
14190
			}
14191
			return Scale.prototype.getRightValue.call(this, rawValue);
14192
		},
14193
14194
		determineDataLimits: function() {
14195
			var me = this;
14196
			var chart = me.chart;
14197
			var timeOpts = me.options.time;
14198
			var unit = timeOpts.unit || 'day';
14199
			var min = MAX_INTEGER;
14200
			var max = MIN_INTEGER;
14201
			var timestamps = [];
14202
			var datasets = [];
14203
			var labels = [];
14204
			var i, j, ilen, jlen, data, timestamp;
14205
14206
			// Convert labels to timestamps
14207
			for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) {
14208
				labels.push(parse(chart.data.labels[i], me));
14209
			}
14210
14211
			// Convert data to timestamps
14212
			for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
14213
				if (chart.isDatasetVisible(i)) {
14214
					data = chart.data.datasets[i].data;
14215
14216
					// Let's consider that all data have the same format.
14217
					if (helpers.isObject(data[0])) {
14218
						datasets[i] = [];
14219
14220
						for (j = 0, jlen = data.length; j < jlen; ++j) {
14221
							timestamp = parse(data[j], me);
14222
							timestamps.push(timestamp);
14223
							datasets[i][j] = timestamp;
14224
						}
14225
					} else {
14226
						timestamps.push.apply(timestamps, labels);
14227
						datasets[i] = labels.slice(0);
14228
					}
14229
				} else {
14230
					datasets[i] = [];
14231
				}
14232
			}
14233
14234
			if (labels.length) {
14235
				// Sort labels **after** data have been converted
14236
				labels = arrayUnique(labels).sort(sorter);
14237
				min = Math.min(min, labels[0]);
14238
				max = Math.max(max, labels[labels.length - 1]);
14239
			}
14240
14241
			if (timestamps.length) {
14242
				timestamps = arrayUnique(timestamps).sort(sorter);
14243
				min = Math.min(min, timestamps[0]);
14244
				max = Math.max(max, timestamps[timestamps.length - 1]);
14245
			}
14246
14247
			min = parse(timeOpts.min, me) || min;
14248
			max = parse(timeOpts.max, me) || max;
14249
14250
			// In case there is no valid min/max, set limits based on unit time option
14251
			min = min === MAX_INTEGER ? +moment().startOf(unit) : min;
14252
			max = max === MIN_INTEGER ? +moment().endOf(unit) + 1 : max;
14253
14254
			// Make sure that max is strictly higher than min (required by the lookup table)
14255
			me.min = Math.min(min, max);
14256
			me.max = Math.max(min + 1, max);
14257
14258
			// PRIVATE
14259
			me._horizontal = me.isHorizontal();
14260
			me._table = [];
14261
			me._timestamps = {
14262
				data: timestamps,
14263
				datasets: datasets,
14264
				labels: labels
14265
			};
14266
		},
14267
14268
		buildTicks: function() {
14269
			var me = this;
14270
			var min = me.min;
14271
			var max = me.max;
14272
			var options = me.options;
14273
			var timeOpts = options.time;
14274
			var timestamps = [];
14275
			var ticks = [];
14276
			var i, ilen, timestamp;
14277
14278
			switch (options.ticks.source) {
14279
			case 'data':
14280
				timestamps = me._timestamps.data;
14281
				break;
14282
			case 'labels':
14283
				timestamps = me._timestamps.labels;
14284
				break;
14285
			case 'auto':
14286
			default:
14287
				timestamps = generate(min, max, me.getLabelCapacity(min), options);
14288
			}
14289
14290
			if (options.bounds === 'ticks' && timestamps.length) {
14291
				min = timestamps[0];
14292
				max = timestamps[timestamps.length - 1];
14293
			}
14294
14295
			// Enforce limits with user min/max options
14296
			min = parse(timeOpts.min, me) || min;
14297
			max = parse(timeOpts.max, me) || max;
14298
14299
			// Remove ticks outside the min/max range
14300
			for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
14301
				timestamp = timestamps[i];
14302
				if (timestamp >= min && timestamp <= max) {
14303
					ticks.push(timestamp);
14304
				}
14305
			}
14306
14307
			me.min = min;
14308
			me.max = max;
14309
14310
			// PRIVATE
14311
			me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max);
14312
			me._majorUnit = determineMajorUnit(me._unit);
14313
			me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution);
14314
			me._offsets = computeOffsets(me._table, ticks, min, max, options);
14315
			me._labelFormat = determineLabelFormat(me._timestamps.data, timeOpts);
14316
14317
			return ticksFromTimestamps(ticks, me._majorUnit);
14318
		},
14319
14320
		getLabelForIndex: function(index, datasetIndex) {
14321
			var me = this;
14322
			var data = me.chart.data;
14323
			var timeOpts = me.options.time;
14324
			var label = data.labels && index < data.labels.length ? data.labels[index] : '';
14325
			var value = data.datasets[datasetIndex].data[index];
14326
14327
			if (helpers.isObject(value)) {
14328
				label = me.getRightValue(value);
14329
			}
14330
			if (timeOpts.tooltipFormat) {
14331
				return momentify(label, timeOpts).format(timeOpts.tooltipFormat);
14332
			}
14333
			if (typeof label === 'string') {
14334
				return label;
14335
			}
14336
14337
			return momentify(label, timeOpts).format(me._labelFormat);
14338
		},
14339
14340
		/**
14341
		 * Function to format an individual tick mark
14342
		 * @private
14343
		 */
14344
		tickFormatFunction: function(tick, index, ticks, formatOverride) {
14345
			var me = this;
14346
			var options = me.options;
14347
			var time = tick.valueOf();
14348
			var formats = options.time.displayFormats;
14349
			var minorFormat = formats[me._unit];
14350
			var majorUnit = me._majorUnit;
14351
			var majorFormat = formats[majorUnit];
14352
			var majorTime = tick.clone().startOf(majorUnit).valueOf();
14353
			var majorTickOpts = options.ticks.major;
14354
			var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime;
14355
			var label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat);
14356
			var tickOpts = major ? majorTickOpts : options.ticks.minor;
14357
			var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback);
14358
14359
			return formatter ? formatter(label, index, ticks) : label;
14360
		},
14361
14362
		convertTicksToLabels: function(ticks) {
14363
			var labels = [];
14364
			var i, ilen;
14365
14366
			for (i = 0, ilen = ticks.length; i < ilen; ++i) {
14367
				labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks));
14368
			}
14369
14370
			return labels;
14371
		},
14372
14373
		/**
14374
		 * @private
14375
		 */
14376
		getPixelForOffset: function(time) {
14377
			var me = this;
14378
			var size = me._horizontal ? me.width : me.height;
14379
			var start = me._horizontal ? me.left : me.top;
14380
			var pos = interpolate(me._table, 'time', time, 'pos');
14381
14382
			return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right);
14383
		},
14384
14385
		getPixelForValue: function(value, index, datasetIndex) {
14386
			var me = this;
14387
			var time = null;
14388
14389
			if (index !== undefined && datasetIndex !== undefined) {
14390
				time = me._timestamps.datasets[datasetIndex][index];
14391
			}
14392
14393
			if (time === null) {
14394
				time = parse(value, me);
14395
			}
14396
14397
			if (time !== null) {
14398
				return me.getPixelForOffset(time);
14399
			}
14400
		},
14401
14402
		getPixelForTick: function(index) {
14403
			var ticks = this.getTicks();
14404
			return index >= 0 && index < ticks.length ?
14405
				this.getPixelForOffset(ticks[index].value) :
14406
				null;
14407
		},
14408
14409
		getValueForPixel: function(pixel) {
14410
			var me = this;
14411
			var size = me._horizontal ? me.width : me.height;
14412
			var start = me._horizontal ? me.left : me.top;
14413
			var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right;
14414
			var time = interpolate(me._table, 'pos', pos, 'time');
14415
14416
			return moment(time);
14417
		},
14418
14419
		/**
14420
		 * Crude approximation of what the label width might be
14421
		 * @private
14422
		 */
14423
		getLabelWidth: function(label) {
14424
			var me = this;
14425
			var ticksOpts = me.options.ticks;
14426
			var tickLabelWidth = me.ctx.measureText(label).width;
14427
			var angle = helpers.toRadians(ticksOpts.maxRotation);
14428
			var cosRotation = Math.cos(angle);
14429
			var sinRotation = Math.sin(angle);
14430
			var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize);
14431
14432
			return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
14433
		},
14434
14435
		/**
14436
		 * @private
14437
		 */
14438
		getLabelCapacity: function(exampleTime) {
14439
			var me = this;
14440
14441
			var formatOverride = me.options.time.displayFormats.millisecond;	// Pick the longest format for guestimation
14442
14443
			var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride);
14444
			var tickLabelWidth = me.getLabelWidth(exampleLabel);
14445
			var innerWidth = me.isHorizontal() ? me.width : me.height;
14446
14447
			var capacity = Math.floor(innerWidth / tickLabelWidth);
14448
			return capacity > 0 ? capacity : 1;
14449
		}
14450
	});
14451
14452
	scaleService.registerScaleType('time', TimeScale, defaultConfig);
14453
};
14454
14455
},{"1":1,"26":26,"33":33,"34":34,"46":46}]},{},[7])(7)
14456
});
14457