blob: 24ffad00793b4ca70b1975c3b1e58b269f2d821c [file] [log] [blame]
Jian Lid7a5a742016-02-12 13:51:18 -08001/*!
2 * Chart.js
3 * http://chartjs.org/
Steven Burrows96ee21e2017-07-11 19:49:45 +01004 * Version: 2.6.0
Jian Lid7a5a742016-02-12 13:51:18 -08005 *
Steven Burrows96ee21e2017-07-11 19:49:45 +01006 * Copyright 2017 Nick Downie
Jian Lid7a5a742016-02-12 13:51:18 -08007 * Released under the MIT license
Jian Li46770fc2016-08-03 02:32:45 +09008 * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
Jian Lid7a5a742016-02-12 13:51:18 -08009 */
Jian Li46770fc2016-08-03 02:32:45 +090010(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
Jian Lid7a5a742016-02-12 13:51:18 -080011
Jian Li46770fc2016-08-03 02:32:45 +090012},{}],2:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +010013 /* 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})$/,
40 hex = /^#([a-fA-F0-9]{6})$/,
41 rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,
42 per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,
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 }
Jian Lid7a5a742016-02-12 13:51:18 -0800234
Jian Li46770fc2016-08-03 02:32:45 +0900235},{"6":6}],3:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +0100236 /* 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;
Jian Lid7a5a742016-02-12 13:51:18 -0800721
Jian Li46770fc2016-08-03 02:32:45 +0900722},{"2":2,"5":5}],4:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +0100723 /* MIT license */
Jian Lid7a5a742016-02-12 13:51:18 -0800724
Steven Burrows96ee21e2017-07-11 19:49:45 +0100725 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,
Jian Lid7a5a742016-02-12 13:51:18 -0800734
Steven Burrows96ee21e2017-07-11 19:49:45 +0100735 hsl2rgb: hsl2rgb,
736 hsl2hsv: hsl2hsv,
737 hsl2hwb: hsl2hwb,
738 hsl2cmyk: hsl2cmyk,
739 hsl2keyword: hsl2keyword,
Jian Lid7a5a742016-02-12 13:51:18 -0800740
Steven Burrows96ee21e2017-07-11 19:49:45 +0100741 hsv2rgb: hsv2rgb,
742 hsv2hsl: hsv2hsl,
743 hsv2hwb: hsv2hwb,
744 hsv2cmyk: hsv2cmyk,
745 hsv2keyword: hsv2keyword,
Jian Lid7a5a742016-02-12 13:51:18 -0800746
Steven Burrows96ee21e2017-07-11 19:49:45 +0100747 hwb2rgb: hwb2rgb,
748 hwb2hsl: hwb2hsl,
749 hwb2hsv: hwb2hsv,
750 hwb2cmyk: hwb2cmyk,
751 hwb2keyword: hwb2keyword,
Jian Lid7a5a742016-02-12 13:51:18 -0800752
Steven Burrows96ee21e2017-07-11 19:49:45 +0100753 cmyk2rgb: cmyk2rgb,
754 cmyk2hsl: cmyk2hsl,
755 cmyk2hsv: cmyk2hsv,
756 cmyk2hwb: cmyk2hwb,
757 cmyk2keyword: cmyk2keyword,
Jian Lid7a5a742016-02-12 13:51:18 -0800758
Steven Burrows96ee21e2017-07-11 19:49:45 +0100759 keyword2rgb: keyword2rgb,
760 keyword2hsl: keyword2hsl,
761 keyword2hsv: keyword2hsv,
762 keyword2hwb: keyword2hwb,
763 keyword2cmyk: keyword2cmyk,
764 keyword2lab: keyword2lab,
765 keyword2xyz: keyword2xyz,
Jian Lid7a5a742016-02-12 13:51:18 -0800766
Steven Burrows96ee21e2017-07-11 19:49:45 +0100767 xyz2rgb: xyz2rgb,
768 xyz2lab: xyz2lab,
769 xyz2lch: xyz2lch,
Jian Lid7a5a742016-02-12 13:51:18 -0800770
Steven Burrows96ee21e2017-07-11 19:49:45 +0100771 lab2xyz: lab2xyz,
772 lab2rgb: lab2rgb,
773 lab2lch: lab2lch,
Jian Lid7a5a742016-02-12 13:51:18 -0800774
Steven Burrows96ee21e2017-07-11 19:49:45 +0100775 lch2lab: lch2lab,
776 lch2xyz: lch2xyz,
777 lch2rgb: lch2rgb
778 }
Jian Lid7a5a742016-02-12 13:51:18 -0800779
Jian Lid7a5a742016-02-12 13:51:18 -0800780
Steven Burrows96ee21e2017-07-11 19:49:45 +0100781 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;
Jian Lid7a5a742016-02-12 13:51:18 -0800789
Steven Burrows96ee21e2017-07-11 19:49:45 +0100790 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;
Jian Lid7a5a742016-02-12 13:51:18 -0800798
Steven Burrows96ee21e2017-07-11 19:49:45 +0100799 h = Math.min(h * 60, 360);
Jian Lid7a5a742016-02-12 13:51:18 -0800800
Steven Burrows96ee21e2017-07-11 19:49:45 +0100801 if (h < 0)
802 h += 360;
Jian Lid7a5a742016-02-12 13:51:18 -0800803
Steven Burrows96ee21e2017-07-11 19:49:45 +0100804 l = (min + max) / 2;
Jian Lid7a5a742016-02-12 13:51:18 -0800805
Steven Burrows96ee21e2017-07-11 19:49:45 +0100806 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);
Jian Lid7a5a742016-02-12 13:51:18 -0800812
Steven Burrows96ee21e2017-07-11 19:49:45 +0100813 return [h, s * 100, l * 100];
814 }
Jian Lid7a5a742016-02-12 13:51:18 -0800815
Steven Burrows96ee21e2017-07-11 19:49:45 +0100816 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;
Jian Lid7a5a742016-02-12 13:51:18 -0800824
Steven Burrows96ee21e2017-07-11 19:49:45 +0100825 if (max == 0)
826 s = 0;
827 else
828 s = (delta/max * 1000)/10;
Jian Lid7a5a742016-02-12 13:51:18 -0800829
Steven Burrows96ee21e2017-07-11 19:49:45 +0100830 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;
Jian Lid7a5a742016-02-12 13:51:18 -0800838
Steven Burrows96ee21e2017-07-11 19:49:45 +0100839 h = Math.min(h * 60, 360);
Jian Lid7a5a742016-02-12 13:51:18 -0800840
Steven Burrows96ee21e2017-07-11 19:49:45 +0100841 if (h < 0)
842 h += 360;
Jian Lid7a5a742016-02-12 13:51:18 -0800843
Steven Burrows96ee21e2017-07-11 19:49:45 +0100844 v = ((max / 255) * 1000) / 10;
Jian Lid7a5a742016-02-12 13:51:18 -0800845
Steven Burrows96ee21e2017-07-11 19:49:45 +0100846 return [h, s, v];
847 }
Jian Lid7a5a742016-02-12 13:51:18 -0800848
Steven Burrows96ee21e2017-07-11 19:49:45 +0100849 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));
Jian Lid7a5a742016-02-12 13:51:18 -0800856
Steven Burrows96ee21e2017-07-11 19:49:45 +0100857 return [h, w * 100, b * 100];
858 }
Jian Lid7a5a742016-02-12 13:51:18 -0800859
Steven Burrows96ee21e2017-07-11 19:49:45 +0100860 function rgb2cmyk(rgb) {
861 var r = rgb[0] / 255,
862 g = rgb[1] / 255,
863 b = rgb[2] / 255,
864 c, m, y, k;
Jian Lid7a5a742016-02-12 13:51:18 -0800865
Steven Burrows96ee21e2017-07-11 19:49:45 +0100866 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 }
Jian Lid7a5a742016-02-12 13:51:18 -0800872
Steven Burrows96ee21e2017-07-11 19:49:45 +0100873 function rgb2keyword(rgb) {
874 return reverseKeywords[JSON.stringify(rgb)];
875 }
Jian Lid7a5a742016-02-12 13:51:18 -0800876
Steven Burrows96ee21e2017-07-11 19:49:45 +0100877 function rgb2xyz(rgb) {
878 var r = rgb[0] / 255,
879 g = rgb[1] / 255,
880 b = rgb[2] / 255;
Jian Lid7a5a742016-02-12 13:51:18 -0800881
Steven Burrows96ee21e2017-07-11 19:49:45 +0100882 // 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);
Jian Lid7a5a742016-02-12 13:51:18 -0800886
Steven Burrows96ee21e2017-07-11 19:49:45 +0100887 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);
Jian Lid7a5a742016-02-12 13:51:18 -0800890
Steven Burrows96ee21e2017-07-11 19:49:45 +0100891 return [x * 100, y *100, z * 100];
892 }
Jian Lid7a5a742016-02-12 13:51:18 -0800893
Steven Burrows96ee21e2017-07-11 19:49:45 +0100894 function rgb2lab(rgb) {
895 var xyz = rgb2xyz(rgb),
896 x = xyz[0],
897 y = xyz[1],
898 z = xyz[2],
899 l, a, b;
Jian Lid7a5a742016-02-12 13:51:18 -0800900
Steven Burrows96ee21e2017-07-11 19:49:45 +0100901 x /= 95.047;
902 y /= 100;
903 z /= 108.883;
Jian Lid7a5a742016-02-12 13:51:18 -0800904
Steven Burrows96ee21e2017-07-11 19:49:45 +0100905 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);
Jian Lid7a5a742016-02-12 13:51:18 -0800908
Steven Burrows96ee21e2017-07-11 19:49:45 +0100909 l = (116 * y) - 16;
910 a = 500 * (x - y);
911 b = 200 * (y - z);
Jian Lid7a5a742016-02-12 13:51:18 -0800912
Steven Burrows96ee21e2017-07-11 19:49:45 +0100913 return [l, a, b];
914 }
Jian Lid7a5a742016-02-12 13:51:18 -0800915
Steven Burrows96ee21e2017-07-11 19:49:45 +0100916 function rgb2lch(args) {
917 return lab2lch(rgb2lab(args));
918 }
Jian Lid7a5a742016-02-12 13:51:18 -0800919
Steven Burrows96ee21e2017-07-11 19:49:45 +0100920 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;
Jian Lid7a5a742016-02-12 13:51:18 -0800925
Steven Burrows96ee21e2017-07-11 19:49:45 +0100926 if (s == 0) {
927 val = l * 255;
928 return [val, val, val];
929 }
Jian Lid7a5a742016-02-12 13:51:18 -0800930
Steven Burrows96ee21e2017-07-11 19:49:45 +0100931 if (l < 0.5)
932 t2 = l * (1 + s);
933 else
934 t2 = l + s - l * s;
935 t1 = 2 * l - t2;
Jian Lid7a5a742016-02-12 13:51:18 -0800936
Steven Burrows96ee21e2017-07-11 19:49:45 +0100937 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--;
Jian Lid7a5a742016-02-12 13:51:18 -0800942
Steven Burrows96ee21e2017-07-11 19:49:45 +0100943 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;
Jian Lid7a5a742016-02-12 13:51:18 -0800951
Steven Burrows96ee21e2017-07-11 19:49:45 +0100952 rgb[i] = val * 255;
953 }
Jian Lid7a5a742016-02-12 13:51:18 -0800954
Steven Burrows96ee21e2017-07-11 19:49:45 +0100955 return rgb;
956 }
Jian Lid7a5a742016-02-12 13:51:18 -0800957
Steven Burrows96ee21e2017-07-11 19:49:45 +0100958 function hsl2hsv(hsl) {
959 var h = hsl[0],
960 s = hsl[1] / 100,
961 l = hsl[2] / 100,
962 sv, v;
Jian Lid7a5a742016-02-12 13:51:18 -0800963
Steven Burrows96ee21e2017-07-11 19:49:45 +0100964 if(l === 0) {
965 // no need to do calc on black
966 // also avoids divide by 0 error
967 return [0, 0, 0];
968 }
Jian Lid7a5a742016-02-12 13:51:18 -0800969
Steven Burrows96ee21e2017-07-11 19:49:45 +0100970 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 }
Jian Lid7a5a742016-02-12 13:51:18 -0800976
Steven Burrows96ee21e2017-07-11 19:49:45 +0100977 function hsl2hwb(args) {
978 return rgb2hwb(hsl2rgb(args));
979 }
Jian Lid7a5a742016-02-12 13:51:18 -0800980
Steven Burrows96ee21e2017-07-11 19:49:45 +0100981 function hsl2cmyk(args) {
982 return rgb2cmyk(hsl2rgb(args));
983 }
Jian Lid7a5a742016-02-12 13:51:18 -0800984
Steven Burrows96ee21e2017-07-11 19:49:45 +0100985 function hsl2keyword(args) {
986 return rgb2keyword(hsl2rgb(args));
987 }
Jian Lid7a5a742016-02-12 13:51:18 -0800988
Jian Lid7a5a742016-02-12 13:51:18 -0800989
Steven Burrows96ee21e2017-07-11 19:49:45 +0100990 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;
Jian Lid7a5a742016-02-12 13:51:18 -0800995
Steven Burrows96ee21e2017-07-11 19:49:45 +0100996 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;
Jian Lid7a5a742016-02-12 13:51:18 -08001001
Steven Burrows96ee21e2017-07-11 19:49:45 +01001002 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 }
Jian Lid7a5a742016-02-12 13:51:18 -08001017
Steven Burrows96ee21e2017-07-11 19:49:45 +01001018 function hsv2hsl(hsv) {
1019 var h = hsv[0],
1020 s = hsv[1] / 100,
1021 v = hsv[2] / 100,
1022 sl, l;
Jian Lid7a5a742016-02-12 13:51:18 -08001023
Steven Burrows96ee21e2017-07-11 19:49:45 +01001024 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 }
Jian Lid7a5a742016-02-12 13:51:18 -08001031
Steven Burrows96ee21e2017-07-11 19:49:45 +01001032 function hsv2hwb(args) {
1033 return rgb2hwb(hsv2rgb(args))
1034 }
Jian Lid7a5a742016-02-12 13:51:18 -08001035
Steven Burrows96ee21e2017-07-11 19:49:45 +01001036 function hsv2cmyk(args) {
1037 return rgb2cmyk(hsv2rgb(args));
1038 }
Jian Lid7a5a742016-02-12 13:51:18 -08001039
Steven Burrows96ee21e2017-07-11 19:49:45 +01001040 function hsv2keyword(args) {
1041 return rgb2keyword(hsv2rgb(args));
1042 }
Jian Lid7a5a742016-02-12 13:51:18 -08001043
Jian Li46770fc2016-08-03 02:32:45 +09001044// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
Steven Burrows96ee21e2017-07-11 19:49:45 +01001045 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;
Jian Lid7a5a742016-02-12 13:51:18 -08001051
Steven Burrows96ee21e2017-07-11 19:49:45 +01001052 // wh + bl cant be > 1
1053 if (ratio > 1) {
1054 wh /= ratio;
1055 bl /= ratio;
1056 }
Jian Lid7a5a742016-02-12 13:51:18 -08001057
Steven Burrows96ee21e2017-07-11 19:49:45 +01001058 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
Jian Lid7a5a742016-02-12 13:51:18 -08001065
Steven Burrows96ee21e2017-07-11 19:49:45 +01001066 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 }
Jian Lid7a5a742016-02-12 13:51:18 -08001076
Steven Burrows96ee21e2017-07-11 19:49:45 +01001077 return [r * 255, g * 255, b * 255];
1078 }
Jian Lid7a5a742016-02-12 13:51:18 -08001079
Steven Burrows96ee21e2017-07-11 19:49:45 +01001080 function hwb2hsl(args) {
1081 return rgb2hsl(hwb2rgb(args));
1082 }
Jian Lid7a5a742016-02-12 13:51:18 -08001083
Steven Burrows96ee21e2017-07-11 19:49:45 +01001084 function hwb2hsv(args) {
1085 return rgb2hsv(hwb2rgb(args));
1086 }
Jian Lid7a5a742016-02-12 13:51:18 -08001087
Steven Burrows96ee21e2017-07-11 19:49:45 +01001088 function hwb2cmyk(args) {
1089 return rgb2cmyk(hwb2rgb(args));
1090 }
Jian Lid7a5a742016-02-12 13:51:18 -08001091
Steven Burrows96ee21e2017-07-11 19:49:45 +01001092 function hwb2keyword(args) {
1093 return rgb2keyword(hwb2rgb(args));
1094 }
Jian Lid7a5a742016-02-12 13:51:18 -08001095
Steven Burrows96ee21e2017-07-11 19:49:45 +01001096 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;
Jian Lid7a5a742016-02-12 13:51:18 -08001102
Steven Burrows96ee21e2017-07-11 19:49:45 +01001103 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 }
Jian Lid7a5a742016-02-12 13:51:18 -08001108
Steven Burrows96ee21e2017-07-11 19:49:45 +01001109 function cmyk2hsl(args) {
1110 return rgb2hsl(cmyk2rgb(args));
1111 }
Jian Lid7a5a742016-02-12 13:51:18 -08001112
Steven Burrows96ee21e2017-07-11 19:49:45 +01001113 function cmyk2hsv(args) {
1114 return rgb2hsv(cmyk2rgb(args));
1115 }
Jian Lid7a5a742016-02-12 13:51:18 -08001116
Steven Burrows96ee21e2017-07-11 19:49:45 +01001117 function cmyk2hwb(args) {
1118 return rgb2hwb(cmyk2rgb(args));
1119 }
Jian Lid7a5a742016-02-12 13:51:18 -08001120
Steven Burrows96ee21e2017-07-11 19:49:45 +01001121 function cmyk2keyword(args) {
1122 return rgb2keyword(cmyk2rgb(args));
1123 }
Jian Lid7a5a742016-02-12 13:51:18 -08001124
Jian Lid7a5a742016-02-12 13:51:18 -08001125
Steven Burrows96ee21e2017-07-11 19:49:45 +01001126 function xyz2rgb(xyz) {
1127 var x = xyz[0] / 100,
1128 y = xyz[1] / 100,
1129 z = xyz[2] / 100,
1130 r, g, b;
Jian Lid7a5a742016-02-12 13:51:18 -08001131
Steven Burrows96ee21e2017-07-11 19:49:45 +01001132 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);
Jian Lid7a5a742016-02-12 13:51:18 -08001135
Steven Burrows96ee21e2017-07-11 19:49:45 +01001136 // assume sRGB
1137 r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
1138 : r = (r * 12.92);
Jian Lid7a5a742016-02-12 13:51:18 -08001139
Steven Burrows96ee21e2017-07-11 19:49:45 +01001140 g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
1141 : g = (g * 12.92);
Jian Lid7a5a742016-02-12 13:51:18 -08001142
Steven Burrows96ee21e2017-07-11 19:49:45 +01001143 b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
1144 : b = (b * 12.92);
Jian Lid7a5a742016-02-12 13:51:18 -08001145
Steven Burrows96ee21e2017-07-11 19:49:45 +01001146 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);
Jian Lid7a5a742016-02-12 13:51:18 -08001149
Steven Burrows96ee21e2017-07-11 19:49:45 +01001150 return [r * 255, g * 255, b * 255];
1151 }
Jian Lid7a5a742016-02-12 13:51:18 -08001152
Steven Burrows96ee21e2017-07-11 19:49:45 +01001153 function xyz2lab(xyz) {
1154 var x = xyz[0],
1155 y = xyz[1],
1156 z = xyz[2],
1157 l, a, b;
Jian Lid7a5a742016-02-12 13:51:18 -08001158
Steven Burrows96ee21e2017-07-11 19:49:45 +01001159 x /= 95.047;
1160 y /= 100;
1161 z /= 108.883;
Jian Lid7a5a742016-02-12 13:51:18 -08001162
Steven Burrows96ee21e2017-07-11 19:49:45 +01001163 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);
Jian Lid7a5a742016-02-12 13:51:18 -08001166
Steven Burrows96ee21e2017-07-11 19:49:45 +01001167 l = (116 * y) - 16;
1168 a = 500 * (x - y);
1169 b = 200 * (y - z);
Jian Lid7a5a742016-02-12 13:51:18 -08001170
Steven Burrows96ee21e2017-07-11 19:49:45 +01001171 return [l, a, b];
1172 }
Jian Lid7a5a742016-02-12 13:51:18 -08001173
Steven Burrows96ee21e2017-07-11 19:49:45 +01001174 function xyz2lch(args) {
1175 return lab2lch(xyz2lab(args));
1176 }
Jian Lid7a5a742016-02-12 13:51:18 -08001177
Steven Burrows96ee21e2017-07-11 19:49:45 +01001178 function lab2xyz(lab) {
1179 var l = lab[0],
1180 a = lab[1],
1181 b = lab[2],
1182 x, y, z, y2;
Jian Lid7a5a742016-02-12 13:51:18 -08001183
Steven Burrows96ee21e2017-07-11 19:49:45 +01001184 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 }
Jian Lid7a5a742016-02-12 13:51:18 -08001191
Steven Burrows96ee21e2017-07-11 19:49:45 +01001192 x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);
Jian Lid7a5a742016-02-12 13:51:18 -08001193
Steven Burrows96ee21e2017-07-11 19:49:45 +01001194 z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);
Jian Lid7a5a742016-02-12 13:51:18 -08001195
Steven Burrows96ee21e2017-07-11 19:49:45 +01001196 return [x, y, z];
1197 }
Jian Lid7a5a742016-02-12 13:51:18 -08001198
Steven Burrows96ee21e2017-07-11 19:49:45 +01001199 function lab2lch(lab) {
1200 var l = lab[0],
1201 a = lab[1],
1202 b = lab[2],
1203 hr, h, c;
Jian Lid7a5a742016-02-12 13:51:18 -08001204
Steven Burrows96ee21e2017-07-11 19:49:45 +01001205 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 }
Jian Lid7a5a742016-02-12 13:51:18 -08001213
Steven Burrows96ee21e2017-07-11 19:49:45 +01001214 function lab2rgb(args) {
1215 return xyz2rgb(lab2xyz(args));
1216 }
Jian Lid7a5a742016-02-12 13:51:18 -08001217
Steven Burrows96ee21e2017-07-11 19:49:45 +01001218 function lch2lab(lch) {
1219 var l = lch[0],
1220 c = lch[1],
1221 h = lch[2],
1222 a, b, hr;
Jian Lid7a5a742016-02-12 13:51:18 -08001223
Steven Burrows96ee21e2017-07-11 19:49:45 +01001224 hr = h / 360 * 2 * Math.PI;
1225 a = c * Math.cos(hr);
1226 b = c * Math.sin(hr);
1227 return [l, a, b];
1228 }
Jian Lid7a5a742016-02-12 13:51:18 -08001229
Steven Burrows96ee21e2017-07-11 19:49:45 +01001230 function lch2xyz(args) {
1231 return lab2xyz(lch2lab(args));
1232 }
Jian Lid7a5a742016-02-12 13:51:18 -08001233
Steven Burrows96ee21e2017-07-11 19:49:45 +01001234 function lch2rgb(args) {
1235 return lab2rgb(lch2lab(args));
1236 }
Jian Lid7a5a742016-02-12 13:51:18 -08001237
Steven Burrows96ee21e2017-07-11 19:49:45 +01001238 function keyword2rgb(keyword) {
1239 return cssKeywords[keyword];
1240 }
Jian Lid7a5a742016-02-12 13:51:18 -08001241
Steven Burrows96ee21e2017-07-11 19:49:45 +01001242 function keyword2hsl(args) {
1243 return rgb2hsl(keyword2rgb(args));
1244 }
Jian Lid7a5a742016-02-12 13:51:18 -08001245
Steven Burrows96ee21e2017-07-11 19:49:45 +01001246 function keyword2hsv(args) {
1247 return rgb2hsv(keyword2rgb(args));
1248 }
Jian Lid7a5a742016-02-12 13:51:18 -08001249
Steven Burrows96ee21e2017-07-11 19:49:45 +01001250 function keyword2hwb(args) {
1251 return rgb2hwb(keyword2rgb(args));
1252 }
Jian Lid7a5a742016-02-12 13:51:18 -08001253
Steven Burrows96ee21e2017-07-11 19:49:45 +01001254 function keyword2cmyk(args) {
1255 return rgb2cmyk(keyword2rgb(args));
1256 }
Jian Lid7a5a742016-02-12 13:51:18 -08001257
Steven Burrows96ee21e2017-07-11 19:49:45 +01001258 function keyword2lab(args) {
1259 return rgb2lab(keyword2rgb(args));
1260 }
Jian Lid7a5a742016-02-12 13:51:18 -08001261
Steven Burrows96ee21e2017-07-11 19:49:45 +01001262 function keyword2xyz(args) {
1263 return rgb2xyz(keyword2rgb(args));
1264 }
Jian Lid7a5a742016-02-12 13:51:18 -08001265
Steven Burrows96ee21e2017-07-11 19:49:45 +01001266 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 };
Jian Lid7a5a742016-02-12 13:51:18 -08001416
Steven Burrows96ee21e2017-07-11 19:49:45 +01001417 var reverseKeywords = {};
1418 for (var key in cssKeywords) {
1419 reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
1420 }
Jian Lid7a5a742016-02-12 13:51:18 -08001421
Jian Li46770fc2016-08-03 02:32:45 +09001422},{}],5:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01001423 var conversions = require(4);
Jian Lid7a5a742016-02-12 13:51:18 -08001424
Steven Burrows96ee21e2017-07-11 19:49:45 +01001425 var convert = function() {
1426 return new Converter();
Jian Lid7a5a742016-02-12 13:51:18 -08001427 }
1428
Steven Burrows96ee21e2017-07-11 19:49:45 +01001429 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);
Jian Lid7a5a742016-02-12 13:51:18 -08001439
Steven Burrows96ee21e2017-07-11 19:49:45 +01001440 var pair = /(\w+)2(\w+)/.exec(func),
1441 from = pair[1],
1442 to = pair[2];
Jian Lid7a5a742016-02-12 13:51:18 -08001443
Steven Burrows96ee21e2017-07-11 19:49:45 +01001444 // export rgb2hsl and ["rgb"]["hsl"]
1445 convert[from] = convert[from] || {};
Jian Lid7a5a742016-02-12 13:51:18 -08001446
Steven Burrows96ee21e2017-07-11 19:49:45 +01001447 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);
Jian Li46770fc2016-08-03 02:32:45 +09001461 }
Jian Lid7a5a742016-02-12 13:51:18 -08001462
1463
Steven Burrows96ee21e2017-07-11 19:49:45 +01001464 /* Converter does lazy conversion and caching */
1465 var Converter = function() {
1466 this.convs = {};
1467 };
Jian Lid7a5a742016-02-12 13:51:18 -08001468
Steven Burrows96ee21e2017-07-11 19:49:45 +01001469 /* 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 }
Jian Lid7a5a742016-02-12 13:51:18 -08001481
Steven Burrows96ee21e2017-07-11 19:49:45 +01001482 return this.setValues(space, values);
1483 };
Jian Lid7a5a742016-02-12 13:51:18 -08001484
Steven Burrows96ee21e2017-07-11 19:49:45 +01001485 /* 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 };
Jian Lid7a5a742016-02-12 13:51:18 -08001492
Steven Burrows96ee21e2017-07-11 19:49:45 +01001493 /* 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);
Jian Lid7a5a742016-02-12 13:51:18 -08001502
Steven Burrows96ee21e2017-07-11 19:49:45 +01001503 this.convs[space] = vals;
1504 }
1505 return vals;
1506 };
Jian Lid7a5a742016-02-12 13:51:18 -08001507
Steven Burrows96ee21e2017-07-11 19:49:45 +01001508 ["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;
Jian Li46770fc2016-08-03 02:32:45 +09001515},{"4":4}],6:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01001516 module.exports = {
1517 "aliceblue": [240, 248, 255],
1518 "antiquewhite": [250, 235, 215],
1519 "aqua": [0, 255, 255],
1520 "aquamarine": [127, 255, 212],
1521 "azure": [240, 255, 255],
1522 "beige": [245, 245, 220],
1523 "bisque": [255, 228, 196],
1524 "black": [0, 0, 0],
1525 "blanchedalmond": [255, 235, 205],
1526 "blue": [0, 0, 255],
1527 "blueviolet": [138, 43, 226],
1528 "brown": [165, 42, 42],
1529 "burlywood": [222, 184, 135],
1530 "cadetblue": [95, 158, 160],
1531 "chartreuse": [127, 255, 0],
1532 "chocolate": [210, 105, 30],
1533 "coral": [255, 127, 80],
1534 "cornflowerblue": [100, 149, 237],
1535 "cornsilk": [255, 248, 220],
1536 "crimson": [220, 20, 60],
1537 "cyan": [0, 255, 255],
1538 "darkblue": [0, 0, 139],
1539 "darkcyan": [0, 139, 139],
1540 "darkgoldenrod": [184, 134, 11],
1541 "darkgray": [169, 169, 169],
1542 "darkgreen": [0, 100, 0],
1543 "darkgrey": [169, 169, 169],
1544 "darkkhaki": [189, 183, 107],
1545 "darkmagenta": [139, 0, 139],
1546 "darkolivegreen": [85, 107, 47],
1547 "darkorange": [255, 140, 0],
1548 "darkorchid": [153, 50, 204],
1549 "darkred": [139, 0, 0],
1550 "darksalmon": [233, 150, 122],
1551 "darkseagreen": [143, 188, 143],
1552 "darkslateblue": [72, 61, 139],
1553 "darkslategray": [47, 79, 79],
1554 "darkslategrey": [47, 79, 79],
1555 "darkturquoise": [0, 206, 209],
1556 "darkviolet": [148, 0, 211],
1557 "deeppink": [255, 20, 147],
1558 "deepskyblue": [0, 191, 255],
1559 "dimgray": [105, 105, 105],
1560 "dimgrey": [105, 105, 105],
1561 "dodgerblue": [30, 144, 255],
1562 "firebrick": [178, 34, 34],
1563 "floralwhite": [255, 250, 240],
1564 "forestgreen": [34, 139, 34],
1565 "fuchsia": [255, 0, 255],
1566 "gainsboro": [220, 220, 220],
1567 "ghostwhite": [248, 248, 255],
1568 "gold": [255, 215, 0],
1569 "goldenrod": [218, 165, 32],
1570 "gray": [128, 128, 128],
1571 "green": [0, 128, 0],
1572 "greenyellow": [173, 255, 47],
1573 "grey": [128, 128, 128],
1574 "honeydew": [240, 255, 240],
1575 "hotpink": [255, 105, 180],
1576 "indianred": [205, 92, 92],
1577 "indigo": [75, 0, 130],
1578 "ivory": [255, 255, 240],
1579 "khaki": [240, 230, 140],
1580 "lavender": [230, 230, 250],
1581 "lavenderblush": [255, 240, 245],
1582 "lawngreen": [124, 252, 0],
1583 "lemonchiffon": [255, 250, 205],
1584 "lightblue": [173, 216, 230],
1585 "lightcoral": [240, 128, 128],
1586 "lightcyan": [224, 255, 255],
1587 "lightgoldenrodyellow": [250, 250, 210],
1588 "lightgray": [211, 211, 211],
1589 "lightgreen": [144, 238, 144],
1590 "lightgrey": [211, 211, 211],
1591 "lightpink": [255, 182, 193],
1592 "lightsalmon": [255, 160, 122],
1593 "lightseagreen": [32, 178, 170],
1594 "lightskyblue": [135, 206, 250],
1595 "lightslategray": [119, 136, 153],
1596 "lightslategrey": [119, 136, 153],
1597 "lightsteelblue": [176, 196, 222],
1598 "lightyellow": [255, 255, 224],
1599 "lime": [0, 255, 0],
1600 "limegreen": [50, 205, 50],
1601 "linen": [250, 240, 230],
1602 "magenta": [255, 0, 255],
1603 "maroon": [128, 0, 0],
1604 "mediumaquamarine": [102, 205, 170],
1605 "mediumblue": [0, 0, 205],
1606 "mediumorchid": [186, 85, 211],
1607 "mediumpurple": [147, 112, 219],
1608 "mediumseagreen": [60, 179, 113],
1609 "mediumslateblue": [123, 104, 238],
1610 "mediumspringgreen": [0, 250, 154],
1611 "mediumturquoise": [72, 209, 204],
1612 "mediumvioletred": [199, 21, 133],
1613 "midnightblue": [25, 25, 112],
1614 "mintcream": [245, 255, 250],
1615 "mistyrose": [255, 228, 225],
1616 "moccasin": [255, 228, 181],
1617 "navajowhite": [255, 222, 173],
1618 "navy": [0, 0, 128],
1619 "oldlace": [253, 245, 230],
1620 "olive": [128, 128, 0],
1621 "olivedrab": [107, 142, 35],
1622 "orange": [255, 165, 0],
1623 "orangered": [255, 69, 0],
1624 "orchid": [218, 112, 214],
1625 "palegoldenrod": [238, 232, 170],
1626 "palegreen": [152, 251, 152],
1627 "paleturquoise": [175, 238, 238],
1628 "palevioletred": [219, 112, 147],
1629 "papayawhip": [255, 239, 213],
1630 "peachpuff": [255, 218, 185],
1631 "peru": [205, 133, 63],
1632 "pink": [255, 192, 203],
1633 "plum": [221, 160, 221],
1634 "powderblue": [176, 224, 230],
1635 "purple": [128, 0, 128],
1636 "rebeccapurple": [102, 51, 153],
1637 "red": [255, 0, 0],
1638 "rosybrown": [188, 143, 143],
1639 "royalblue": [65, 105, 225],
1640 "saddlebrown": [139, 69, 19],
1641 "salmon": [250, 128, 114],
1642 "sandybrown": [244, 164, 96],
1643 "seagreen": [46, 139, 87],
1644 "seashell": [255, 245, 238],
1645 "sienna": [160, 82, 45],
1646 "silver": [192, 192, 192],
1647 "skyblue": [135, 206, 235],
1648 "slateblue": [106, 90, 205],
1649 "slategray": [112, 128, 144],
1650 "slategrey": [112, 128, 144],
1651 "snow": [255, 250, 250],
1652 "springgreen": [0, 255, 127],
1653 "steelblue": [70, 130, 180],
1654 "tan": [210, 180, 140],
1655 "teal": [0, 128, 128],
1656 "thistle": [216, 191, 216],
1657 "tomato": [255, 99, 71],
1658 "turquoise": [64, 224, 208],
1659 "violet": [238, 130, 238],
1660 "wheat": [245, 222, 179],
1661 "white": [255, 255, 255],
1662 "whitesmoke": [245, 245, 245],
1663 "yellow": [255, 255, 0],
1664 "yellowgreen": [154, 205, 50]
1665 };
Jian Li46770fc2016-08-03 02:32:45 +09001666},{}],7:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01001667 /**
1668 * @namespace Chart
1669 */
1670 var Chart = require(28)();
Jian Lid7a5a742016-02-12 13:51:18 -08001671
Steven Burrows96ee21e2017-07-11 19:49:45 +01001672 require(26)(Chart);
1673 require(40)(Chart);
1674 require(22)(Chart);
1675 require(25)(Chart);
1676 require(30)(Chart);
1677 require(21)(Chart);
1678 require(23)(Chart);
1679 require(24)(Chart);
1680 require(29)(Chart);
1681 require(32)(Chart);
1682 require(33)(Chart);
1683 require(31)(Chart);
1684 require(27)(Chart);
1685 require(34)(Chart);
Jian Lid7a5a742016-02-12 13:51:18 -08001686
Steven Burrows96ee21e2017-07-11 19:49:45 +01001687 require(35)(Chart);
1688 require(36)(Chart);
1689 require(37)(Chart);
1690 require(38)(Chart);
Jian Lid7a5a742016-02-12 13:51:18 -08001691
Steven Burrows96ee21e2017-07-11 19:49:45 +01001692 require(46)(Chart);
1693 require(44)(Chart);
1694 require(45)(Chart);
1695 require(47)(Chart);
1696 require(48)(Chart);
1697 require(49)(Chart);
Jian Lid7a5a742016-02-12 13:51:18 -08001698
Jian Li46770fc2016-08-03 02:32:45 +09001699// Controllers must be loaded after elements
1700// See Chart.core.datasetController.dataElementType
Steven Burrows96ee21e2017-07-11 19:49:45 +01001701 require(15)(Chart);
1702 require(16)(Chart);
1703 require(17)(Chart);
1704 require(18)(Chart);
1705 require(19)(Chart);
1706 require(20)(Chart);
Jian Li82101d92016-05-04 12:00:46 -07001707
Steven Burrows96ee21e2017-07-11 19:49:45 +01001708 require(8)(Chart);
1709 require(9)(Chart);
1710 require(10)(Chart);
1711 require(11)(Chart);
1712 require(12)(Chart);
1713 require(13)(Chart);
1714 require(14)(Chart);
Jian Li46770fc2016-08-03 02:32:45 +09001715
Steven Burrows96ee21e2017-07-11 19:49:45 +01001716// Loading built-it plugins
1717 var plugins = [];
Jian Li46770fc2016-08-03 02:32:45 +09001718
Steven Burrows96ee21e2017-07-11 19:49:45 +01001719 plugins.push(
1720 require(41)(Chart),
1721 require(42)(Chart),
1722 require(43)(Chart)
1723 );
Jian Li46770fc2016-08-03 02:32:45 +09001724
Steven Burrows96ee21e2017-07-11 19:49:45 +01001725 Chart.plugins.register(plugins);
Jian Li46770fc2016-08-03 02:32:45 +09001726
Steven Burrows96ee21e2017-07-11 19:49:45 +01001727 module.exports = Chart;
1728 if (typeof window !== 'undefined') {
1729 window.Chart = Chart;
1730 }
Jian Li46770fc2016-08-03 02:32:45 +09001731
Steven Burrows96ee21e2017-07-11 19:49:45 +01001732},{"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,"37":37,"38":38,"40":40,"41":41,"42":42,"43":43,"44":44,"45":45,"46":46,"47":47,"48":48,"49":49,"8":8,"9":9}],8:[function(require,module,exports){
1733 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09001734
Steven Burrows96ee21e2017-07-11 19:49:45 +01001735 module.exports = function(Chart) {
1736
1737 Chart.Bar = function(context, config) {
1738 config.type = 'bar';
1739
1740 return new Chart(context, config);
1741 };
1742
1743 };
1744
Jian Li46770fc2016-08-03 02:32:45 +09001745},{}],9:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01001746 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09001747
Steven Burrows96ee21e2017-07-11 19:49:45 +01001748 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09001749
Steven Burrows96ee21e2017-07-11 19:49:45 +01001750 Chart.Bubble = function(context, config) {
1751 config.type = 'bubble';
1752 return new Chart(context, config);
1753 };
Jian Li46770fc2016-08-03 02:32:45 +09001754
Steven Burrows96ee21e2017-07-11 19:49:45 +01001755 };
1756
Jian Li46770fc2016-08-03 02:32:45 +09001757},{}],10:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01001758 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09001759
Steven Burrows96ee21e2017-07-11 19:49:45 +01001760 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09001761
Steven Burrows96ee21e2017-07-11 19:49:45 +01001762 Chart.Doughnut = function(context, config) {
1763 config.type = 'doughnut';
Jian Li46770fc2016-08-03 02:32:45 +09001764
Steven Burrows96ee21e2017-07-11 19:49:45 +01001765 return new Chart(context, config);
1766 };
Jian Li46770fc2016-08-03 02:32:45 +09001767
Steven Burrows96ee21e2017-07-11 19:49:45 +01001768 };
1769
Jian Li46770fc2016-08-03 02:32:45 +09001770},{}],11:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01001771 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09001772
Steven Burrows96ee21e2017-07-11 19:49:45 +01001773 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09001774
Steven Burrows96ee21e2017-07-11 19:49:45 +01001775 Chart.Line = function(context, config) {
1776 config.type = 'line';
Jian Li46770fc2016-08-03 02:32:45 +09001777
Steven Burrows96ee21e2017-07-11 19:49:45 +01001778 return new Chart(context, config);
1779 };
Jian Li46770fc2016-08-03 02:32:45 +09001780
Steven Burrows96ee21e2017-07-11 19:49:45 +01001781 };
1782
Jian Li46770fc2016-08-03 02:32:45 +09001783},{}],12:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01001784 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09001785
Steven Burrows96ee21e2017-07-11 19:49:45 +01001786 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09001787
Steven Burrows96ee21e2017-07-11 19:49:45 +01001788 Chart.PolarArea = function(context, config) {
1789 config.type = 'polarArea';
Jian Li46770fc2016-08-03 02:32:45 +09001790
Steven Burrows96ee21e2017-07-11 19:49:45 +01001791 return new Chart(context, config);
1792 };
Jian Li46770fc2016-08-03 02:32:45 +09001793
Steven Burrows96ee21e2017-07-11 19:49:45 +01001794 };
1795
Jian Li46770fc2016-08-03 02:32:45 +09001796},{}],13:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01001797 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09001798
Steven Burrows96ee21e2017-07-11 19:49:45 +01001799 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09001800
Steven Burrows96ee21e2017-07-11 19:49:45 +01001801 Chart.Radar = function(context, config) {
1802 config.type = 'radar';
Jian Li46770fc2016-08-03 02:32:45 +09001803
Steven Burrows96ee21e2017-07-11 19:49:45 +01001804 return new Chart(context, config);
1805 };
1806
1807 };
Jian Li46770fc2016-08-03 02:32:45 +09001808
1809},{}],14:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01001810 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09001811
Steven Burrows96ee21e2017-07-11 19:49:45 +01001812 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09001813
Steven Burrows96ee21e2017-07-11 19:49:45 +01001814 var defaultConfig = {
1815 hover: {
1816 mode: 'single'
1817 },
Jian Li46770fc2016-08-03 02:32:45 +09001818
Steven Burrows96ee21e2017-07-11 19:49:45 +01001819 scales: {
1820 xAxes: [{
1821 type: 'linear', // scatter should not use a category axis
1822 position: 'bottom',
1823 id: 'x-axis-1' // need an ID so datasets can reference the scale
1824 }],
1825 yAxes: [{
1826 type: 'linear',
1827 position: 'left',
1828 id: 'y-axis-1'
1829 }]
1830 },
Jian Li46770fc2016-08-03 02:32:45 +09001831
Steven Burrows96ee21e2017-07-11 19:49:45 +01001832 tooltips: {
1833 callbacks: {
1834 title: function() {
1835 // Title doesn't make sense for scatter since we format the data as a point
1836 return '';
1837 },
1838 label: function(tooltipItem) {
1839 return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')';
1840 }
1841 }
1842 }
1843 };
Jian Li46770fc2016-08-03 02:32:45 +09001844
Steven Burrows96ee21e2017-07-11 19:49:45 +01001845 // Register the default config for this type
1846 Chart.defaults.scatter = defaultConfig;
Jian Li46770fc2016-08-03 02:32:45 +09001847
Steven Burrows96ee21e2017-07-11 19:49:45 +01001848 // Scatter charts use line controllers
1849 Chart.controllers.scatter = Chart.controllers.line;
Jian Li46770fc2016-08-03 02:32:45 +09001850
Steven Burrows96ee21e2017-07-11 19:49:45 +01001851 Chart.Scatter = function(context, config) {
1852 config.type = 'scatter';
1853 return new Chart(context, config);
1854 };
Jian Li46770fc2016-08-03 02:32:45 +09001855
Steven Burrows96ee21e2017-07-11 19:49:45 +01001856 };
1857
Jian Li46770fc2016-08-03 02:32:45 +09001858},{}],15:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01001859 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09001860
Steven Burrows96ee21e2017-07-11 19:49:45 +01001861 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09001862
Steven Burrows96ee21e2017-07-11 19:49:45 +01001863 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09001864
Steven Burrows96ee21e2017-07-11 19:49:45 +01001865 Chart.defaults.bar = {
1866 hover: {
1867 mode: 'label'
1868 },
Jian Li46770fc2016-08-03 02:32:45 +09001869
Steven Burrows96ee21e2017-07-11 19:49:45 +01001870 scales: {
1871 xAxes: [{
1872 type: 'category',
Jian Li46770fc2016-08-03 02:32:45 +09001873
Steven Burrows96ee21e2017-07-11 19:49:45 +01001874 // Specific to Bar Controller
1875 categoryPercentage: 0.8,
1876 barPercentage: 0.9,
Jian Li46770fc2016-08-03 02:32:45 +09001877
Steven Burrows96ee21e2017-07-11 19:49:45 +01001878 // grid line settings
1879 gridLines: {
1880 offsetGridLines: true
1881 }
1882 }],
1883 yAxes: [{
1884 type: 'linear'
1885 }]
1886 }
1887 };
Jian Li46770fc2016-08-03 02:32:45 +09001888
Steven Burrows96ee21e2017-07-11 19:49:45 +01001889 Chart.controllers.bar = Chart.DatasetController.extend({
Jian Li46770fc2016-08-03 02:32:45 +09001890
Steven Burrows96ee21e2017-07-11 19:49:45 +01001891 dataElementType: Chart.elements.Rectangle,
Jian Li46770fc2016-08-03 02:32:45 +09001892
Steven Burrows96ee21e2017-07-11 19:49:45 +01001893 initialize: function() {
1894 var me = this;
1895 var meta;
Jian Li46770fc2016-08-03 02:32:45 +09001896
Steven Burrows96ee21e2017-07-11 19:49:45 +01001897 Chart.DatasetController.prototype.initialize.apply(me, arguments);
Jian Li46770fc2016-08-03 02:32:45 +09001898
Steven Burrows96ee21e2017-07-11 19:49:45 +01001899 meta = me.getMeta();
1900 meta.stack = me.getDataset().stack;
1901 meta.bar = true;
1902 },
Jian Li46770fc2016-08-03 02:32:45 +09001903
Steven Burrows96ee21e2017-07-11 19:49:45 +01001904 update: function(reset) {
1905 var me = this;
1906 var elements = me.getMeta().data;
1907 var i, ilen;
Jian Li46770fc2016-08-03 02:32:45 +09001908
Steven Burrows96ee21e2017-07-11 19:49:45 +01001909 me._ruler = me.getRuler();
Jian Li46770fc2016-08-03 02:32:45 +09001910
Steven Burrows96ee21e2017-07-11 19:49:45 +01001911 for (i = 0, ilen = elements.length; i < ilen; ++i) {
1912 me.updateElement(elements[i], i, reset);
1913 }
1914 },
Jian Li46770fc2016-08-03 02:32:45 +09001915
Steven Burrows96ee21e2017-07-11 19:49:45 +01001916 updateElement: function(rectangle, index, reset) {
1917 var me = this;
1918 var chart = me.chart;
1919 var meta = me.getMeta();
1920 var dataset = me.getDataset();
1921 var custom = rectangle.custom || {};
1922 var rectangleOptions = chart.options.elements.rectangle;
Jian Li46770fc2016-08-03 02:32:45 +09001923
Steven Burrows96ee21e2017-07-11 19:49:45 +01001924 rectangle._xScale = me.getScaleForId(meta.xAxisID);
1925 rectangle._yScale = me.getScaleForId(meta.yAxisID);
1926 rectangle._datasetIndex = me.index;
1927 rectangle._index = index;
Jian Li46770fc2016-08-03 02:32:45 +09001928
Steven Burrows96ee21e2017-07-11 19:49:45 +01001929 rectangle._model = {
1930 datasetLabel: dataset.label,
1931 label: chart.data.labels[index],
1932 borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped,
1933 backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor),
1934 borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor),
1935 borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth)
1936 };
Jian Li46770fc2016-08-03 02:32:45 +09001937
Steven Burrows96ee21e2017-07-11 19:49:45 +01001938 me.updateElementGeometry(rectangle, index, reset);
Jian Li46770fc2016-08-03 02:32:45 +09001939
Steven Burrows96ee21e2017-07-11 19:49:45 +01001940 rectangle.pivot();
1941 },
Jian Li46770fc2016-08-03 02:32:45 +09001942
Steven Burrows96ee21e2017-07-11 19:49:45 +01001943 /**
1944 * @private
1945 */
1946 updateElementGeometry: function(rectangle, index, reset) {
1947 var me = this;
1948 var model = rectangle._model;
1949 var vscale = me.getValueScale();
1950 var base = vscale.getBasePixel();
1951 var horizontal = vscale.isHorizontal();
1952 var ruler = me._ruler || me.getRuler();
1953 var vpixels = me.calculateBarValuePixels(me.index, index);
1954 var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);
Jian Li46770fc2016-08-03 02:32:45 +09001955
Steven Burrows96ee21e2017-07-11 19:49:45 +01001956 model.horizontal = horizontal;
1957 model.base = reset? base : vpixels.base;
1958 model.x = horizontal? reset? base : vpixels.head : ipixels.center;
1959 model.y = horizontal? ipixels.center : reset? base : vpixels.head;
1960 model.height = horizontal? ipixels.size : undefined;
1961 model.width = horizontal? undefined : ipixels.size;
1962 },
Jian Li46770fc2016-08-03 02:32:45 +09001963
Steven Burrows96ee21e2017-07-11 19:49:45 +01001964 /**
1965 * @private
1966 */
1967 getValueScaleId: function() {
1968 return this.getMeta().yAxisID;
1969 },
Jian Li46770fc2016-08-03 02:32:45 +09001970
Steven Burrows96ee21e2017-07-11 19:49:45 +01001971 /**
1972 * @private
1973 */
1974 getIndexScaleId: function() {
1975 return this.getMeta().xAxisID;
1976 },
Jian Li46770fc2016-08-03 02:32:45 +09001977
Steven Burrows96ee21e2017-07-11 19:49:45 +01001978 /**
1979 * @private
1980 */
1981 getValueScale: function() {
1982 return this.getScaleForId(this.getValueScaleId());
1983 },
Jian Li46770fc2016-08-03 02:32:45 +09001984
Steven Burrows96ee21e2017-07-11 19:49:45 +01001985 /**
1986 * @private
1987 */
1988 getIndexScale: function() {
1989 return this.getScaleForId(this.getIndexScaleId());
1990 },
Jian Li46770fc2016-08-03 02:32:45 +09001991
Steven Burrows96ee21e2017-07-11 19:49:45 +01001992 /**
1993 * Returns the effective number of stacks based on groups and bar visibility.
1994 * @private
1995 */
1996 getStackCount: function(last) {
1997 var me = this;
1998 var chart = me.chart;
1999 var scale = me.getIndexScale();
2000 var stacked = scale.options.stacked;
2001 var ilen = last === undefined? chart.data.datasets.length : last + 1;
2002 var stacks = [];
2003 var i, meta;
Jian Li46770fc2016-08-03 02:32:45 +09002004
Steven Burrows96ee21e2017-07-11 19:49:45 +01002005 for (i = 0; i < ilen; ++i) {
2006 meta = chart.getDatasetMeta(i);
2007 if (meta.bar && chart.isDatasetVisible(i) &&
2008 (stacked === false ||
2009 (stacked === true && stacks.indexOf(meta.stack) === -1) ||
2010 (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {
2011 stacks.push(meta.stack);
2012 }
2013 }
Jian Li46770fc2016-08-03 02:32:45 +09002014
Steven Burrows96ee21e2017-07-11 19:49:45 +01002015 return stacks.length;
2016 },
Jian Li46770fc2016-08-03 02:32:45 +09002017
Steven Burrows96ee21e2017-07-11 19:49:45 +01002018 /**
2019 * Returns the stack index for the given dataset based on groups and bar visibility.
2020 * @private
2021 */
2022 getStackIndex: function(datasetIndex) {
2023 return this.getStackCount(datasetIndex) - 1;
2024 },
Jian Li46770fc2016-08-03 02:32:45 +09002025
Steven Burrows96ee21e2017-07-11 19:49:45 +01002026 /**
2027 * @private
2028 */
2029 getRuler: function() {
2030 var me = this;
2031 var scale = me.getIndexScale();
2032 var options = scale.options;
2033 var stackCount = me.getStackCount();
2034 var fullSize = scale.isHorizontal()? scale.width : scale.height;
2035 var tickSize = fullSize / scale.ticks.length;
2036 var categorySize = tickSize * options.categoryPercentage;
2037 var fullBarSize = categorySize / stackCount;
2038 var barSize = fullBarSize * options.barPercentage;
Jian Li46770fc2016-08-03 02:32:45 +09002039
Steven Burrows96ee21e2017-07-11 19:49:45 +01002040 barSize = Math.min(
2041 helpers.getValueOrDefault(options.barThickness, barSize),
2042 helpers.getValueOrDefault(options.maxBarThickness, Infinity));
Jian Li46770fc2016-08-03 02:32:45 +09002043
Steven Burrows96ee21e2017-07-11 19:49:45 +01002044 return {
2045 stackCount: stackCount,
2046 tickSize: tickSize,
2047 categorySize: categorySize,
2048 categorySpacing: tickSize - categorySize,
2049 fullBarSize: fullBarSize,
2050 barSize: barSize,
2051 barSpacing: fullBarSize - barSize,
2052 scale: scale
2053 };
2054 },
Jian Li46770fc2016-08-03 02:32:45 +09002055
Steven Burrows96ee21e2017-07-11 19:49:45 +01002056 /**
2057 * Note: pixel values are not clamped to the scale area.
2058 * @private
2059 */
2060 calculateBarValuePixels: function(datasetIndex, index) {
2061 var me = this;
2062 var chart = me.chart;
2063 var meta = me.getMeta();
2064 var scale = me.getValueScale();
2065 var datasets = chart.data.datasets;
2066 var value = Number(datasets[datasetIndex].data[index]);
2067 var stacked = scale.options.stacked;
2068 var stack = meta.stack;
2069 var start = 0;
2070 var i, imeta, ivalue, base, head, size;
Jian Li46770fc2016-08-03 02:32:45 +09002071
Steven Burrows96ee21e2017-07-11 19:49:45 +01002072 if (stacked || (stacked === undefined && stack !== undefined)) {
2073 for (i = 0; i < datasetIndex; ++i) {
2074 imeta = chart.getDatasetMeta(i);
Jian Li46770fc2016-08-03 02:32:45 +09002075
Steven Burrows96ee21e2017-07-11 19:49:45 +01002076 if (imeta.bar &&
2077 imeta.stack === stack &&
2078 imeta.controller.getValueScaleId() === scale.id &&
2079 chart.isDatasetVisible(i)) {
Jian Li46770fc2016-08-03 02:32:45 +09002080
Steven Burrows96ee21e2017-07-11 19:49:45 +01002081 ivalue = Number(datasets[i].data[index]);
2082 if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
2083 start += ivalue;
2084 }
2085 }
2086 }
2087 }
Jian Li46770fc2016-08-03 02:32:45 +09002088
Steven Burrows96ee21e2017-07-11 19:49:45 +01002089 base = scale.getPixelForValue(start);
2090 head = scale.getPixelForValue(start + value);
2091 size = (head - base) / 2;
Jian Li46770fc2016-08-03 02:32:45 +09002092
Steven Burrows96ee21e2017-07-11 19:49:45 +01002093 return {
2094 size: size,
2095 base: base,
2096 head: head,
2097 center: head + size / 2
2098 };
2099 },
Jian Li46770fc2016-08-03 02:32:45 +09002100
Steven Burrows96ee21e2017-07-11 19:49:45 +01002101 /**
2102 * @private
2103 */
2104 calculateBarIndexPixels: function(datasetIndex, index, ruler) {
2105 var me = this;
2106 var scale = ruler.scale;
2107 var isCombo = me.chart.isCombo;
2108 var stackIndex = me.getStackIndex(datasetIndex);
2109 var base = scale.getPixelForValue(null, index, datasetIndex, isCombo);
2110 var size = ruler.barSize;
Jian Li46770fc2016-08-03 02:32:45 +09002111
Steven Burrows96ee21e2017-07-11 19:49:45 +01002112 base -= isCombo? ruler.tickSize / 2 : 0;
2113 base += ruler.fullBarSize * stackIndex;
2114 base += ruler.categorySpacing / 2;
2115 base += ruler.barSpacing / 2;
Jian Li46770fc2016-08-03 02:32:45 +09002116
Steven Burrows96ee21e2017-07-11 19:49:45 +01002117 return {
2118 size: size,
2119 base: base,
2120 head: base + size,
2121 center: base + size / 2
2122 };
2123 },
Jian Li46770fc2016-08-03 02:32:45 +09002124
Steven Burrows96ee21e2017-07-11 19:49:45 +01002125 draw: function() {
2126 var me = this;
2127 var chart = me.chart;
2128 var elements = me.getMeta().data;
2129 var dataset = me.getDataset();
2130 var ilen = elements.length;
2131 var i = 0;
2132 var d;
Jian Li46770fc2016-08-03 02:32:45 +09002133
Steven Burrows96ee21e2017-07-11 19:49:45 +01002134 helpers.canvas.clipArea(chart.ctx, chart.chartArea);
Jian Li46770fc2016-08-03 02:32:45 +09002135
Steven Burrows96ee21e2017-07-11 19:49:45 +01002136 for (; i<ilen; ++i) {
2137 d = dataset.data[i];
2138 if (d !== null && d !== undefined && !isNaN(d)) {
2139 elements[i].draw();
2140 }
2141 }
Jian Li46770fc2016-08-03 02:32:45 +09002142
Steven Burrows96ee21e2017-07-11 19:49:45 +01002143 helpers.canvas.unclipArea(chart.ctx);
2144 },
Jian Li46770fc2016-08-03 02:32:45 +09002145
Steven Burrows96ee21e2017-07-11 19:49:45 +01002146 setHoverStyle: function(rectangle) {
2147 var dataset = this.chart.data.datasets[rectangle._datasetIndex];
2148 var index = rectangle._index;
2149 var custom = rectangle.custom || {};
2150 var model = rectangle._model;
Jian Li46770fc2016-08-03 02:32:45 +09002151
Steven Burrows96ee21e2017-07-11 19:49:45 +01002152 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
2153 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor));
2154 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
2155 },
Jian Li46770fc2016-08-03 02:32:45 +09002156
Steven Burrows96ee21e2017-07-11 19:49:45 +01002157 removeHoverStyle: function(rectangle) {
2158 var dataset = this.chart.data.datasets[rectangle._datasetIndex];
2159 var index = rectangle._index;
2160 var custom = rectangle.custom || {};
2161 var model = rectangle._model;
2162 var rectangleElementOptions = this.chart.options.elements.rectangle;
Jian Li46770fc2016-08-03 02:32:45 +09002163
Steven Burrows96ee21e2017-07-11 19:49:45 +01002164 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor);
2165 model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor);
2166 model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth);
2167 }
2168 });
Jian Li46770fc2016-08-03 02:32:45 +09002169
Jian Li46770fc2016-08-03 02:32:45 +09002170
Steven Burrows96ee21e2017-07-11 19:49:45 +01002171 // including horizontalBar in the bar file, instead of a file of its own
2172 // it extends bar (like pie extends doughnut)
2173 Chart.defaults.horizontalBar = {
2174 hover: {
2175 mode: 'label'
2176 },
Jian Li46770fc2016-08-03 02:32:45 +09002177
Steven Burrows96ee21e2017-07-11 19:49:45 +01002178 scales: {
2179 xAxes: [{
2180 type: 'linear',
2181 position: 'bottom'
2182 }],
2183 yAxes: [{
2184 position: 'left',
2185 type: 'category',
Jian Li46770fc2016-08-03 02:32:45 +09002186
Steven Burrows96ee21e2017-07-11 19:49:45 +01002187 // Specific to Horizontal Bar Controller
2188 categoryPercentage: 0.8,
2189 barPercentage: 0.9,
Jian Li46770fc2016-08-03 02:32:45 +09002190
Steven Burrows96ee21e2017-07-11 19:49:45 +01002191 // grid line settings
2192 gridLines: {
2193 offsetGridLines: true
2194 }
2195 }]
2196 },
2197 elements: {
2198 rectangle: {
2199 borderSkipped: 'left'
2200 }
2201 },
2202 tooltips: {
2203 callbacks: {
2204 title: function(tooltipItems, data) {
2205 // Pick first xLabel for now
2206 var title = '';
Jian Li46770fc2016-08-03 02:32:45 +09002207
Steven Burrows96ee21e2017-07-11 19:49:45 +01002208 if (tooltipItems.length > 0) {
2209 if (tooltipItems[0].yLabel) {
2210 title = tooltipItems[0].yLabel;
2211 } else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) {
2212 title = data.labels[tooltipItems[0].index];
2213 }
2214 }
Jian Li46770fc2016-08-03 02:32:45 +09002215
Steven Burrows96ee21e2017-07-11 19:49:45 +01002216 return title;
2217 },
2218 label: function(tooltipItem, data) {
2219 var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
2220 return datasetLabel + ': ' + tooltipItem.xLabel;
2221 }
2222 }
2223 }
2224 };
Jian Li46770fc2016-08-03 02:32:45 +09002225
Steven Burrows96ee21e2017-07-11 19:49:45 +01002226 Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
2227 /**
2228 * @private
2229 */
2230 getValueScaleId: function() {
2231 return this.getMeta().xAxisID;
2232 },
Jian Li46770fc2016-08-03 02:32:45 +09002233
Steven Burrows96ee21e2017-07-11 19:49:45 +01002234 /**
2235 * @private
2236 */
2237 getIndexScaleId: function() {
2238 return this.getMeta().yAxisID;
2239 }
2240 });
2241 };
Jian Li46770fc2016-08-03 02:32:45 +09002242
2243},{}],16:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01002244 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09002245
Steven Burrows96ee21e2017-07-11 19:49:45 +01002246 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09002247
Steven Burrows96ee21e2017-07-11 19:49:45 +01002248 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09002249
Steven Burrows96ee21e2017-07-11 19:49:45 +01002250 Chart.defaults.bubble = {
2251 hover: {
2252 mode: 'single'
2253 },
Jian Li46770fc2016-08-03 02:32:45 +09002254
Steven Burrows96ee21e2017-07-11 19:49:45 +01002255 scales: {
2256 xAxes: [{
2257 type: 'linear', // bubble should probably use a linear scale by default
2258 position: 'bottom',
2259 id: 'x-axis-0' // need an ID so datasets can reference the scale
2260 }],
2261 yAxes: [{
2262 type: 'linear',
2263 position: 'left',
2264 id: 'y-axis-0'
2265 }]
2266 },
Jian Li46770fc2016-08-03 02:32:45 +09002267
Steven Burrows96ee21e2017-07-11 19:49:45 +01002268 tooltips: {
2269 callbacks: {
2270 title: function() {
2271 // Title doesn't make sense for scatter since we format the data as a point
2272 return '';
2273 },
2274 label: function(tooltipItem, data) {
2275 var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
2276 var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
2277 return datasetLabel + ': (' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ', ' + dataPoint.r + ')';
2278 }
2279 }
2280 }
2281 };
Jian Li46770fc2016-08-03 02:32:45 +09002282
Steven Burrows96ee21e2017-07-11 19:49:45 +01002283 Chart.controllers.bubble = Chart.DatasetController.extend({
Jian Li46770fc2016-08-03 02:32:45 +09002284
Steven Burrows96ee21e2017-07-11 19:49:45 +01002285 dataElementType: Chart.elements.Point,
Jian Li46770fc2016-08-03 02:32:45 +09002286
Steven Burrows96ee21e2017-07-11 19:49:45 +01002287 update: function(reset) {
2288 var me = this;
2289 var meta = me.getMeta();
2290 var points = meta.data;
Jian Li46770fc2016-08-03 02:32:45 +09002291
Steven Burrows96ee21e2017-07-11 19:49:45 +01002292 // Update Points
2293 helpers.each(points, function(point, index) {
2294 me.updateElement(point, index, reset);
2295 });
2296 },
Jian Li46770fc2016-08-03 02:32:45 +09002297
Steven Burrows96ee21e2017-07-11 19:49:45 +01002298 updateElement: function(point, index, reset) {
2299 var me = this;
2300 var meta = me.getMeta();
2301 var xScale = me.getScaleForId(meta.xAxisID);
2302 var yScale = me.getScaleForId(meta.yAxisID);
Jian Li46770fc2016-08-03 02:32:45 +09002303
Steven Burrows96ee21e2017-07-11 19:49:45 +01002304 var custom = point.custom || {};
2305 var dataset = me.getDataset();
2306 var data = dataset.data[index];
2307 var pointElementOptions = me.chart.options.elements.point;
2308 var dsIndex = me.index;
Jian Li46770fc2016-08-03 02:32:45 +09002309
Steven Burrows96ee21e2017-07-11 19:49:45 +01002310 helpers.extend(point, {
2311 // Utility
2312 _xScale: xScale,
2313 _yScale: yScale,
2314 _datasetIndex: dsIndex,
2315 _index: index,
Jian Li46770fc2016-08-03 02:32:45 +09002316
Steven Burrows96ee21e2017-07-11 19:49:45 +01002317 // Desired view properties
2318 _model: {
2319 x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo),
2320 y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex),
2321 // Appearance
2322 radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data),
Jian Li46770fc2016-08-03 02:32:45 +09002323
Steven Burrows96ee21e2017-07-11 19:49:45 +01002324 // Tooltip
2325 hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius)
2326 }
2327 });
Jian Li46770fc2016-08-03 02:32:45 +09002328
Steven Burrows96ee21e2017-07-11 19:49:45 +01002329 // Trick to reset the styles of the point
2330 Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions);
Jian Li46770fc2016-08-03 02:32:45 +09002331
Steven Burrows96ee21e2017-07-11 19:49:45 +01002332 var model = point._model;
2333 model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y));
Jian Li46770fc2016-08-03 02:32:45 +09002334
Steven Burrows96ee21e2017-07-11 19:49:45 +01002335 point.pivot();
2336 },
Jian Li46770fc2016-08-03 02:32:45 +09002337
Steven Burrows96ee21e2017-07-11 19:49:45 +01002338 getRadius: function(value) {
2339 return value.r || this.chart.options.elements.point.radius;
2340 },
Jian Li46770fc2016-08-03 02:32:45 +09002341
Steven Burrows96ee21e2017-07-11 19:49:45 +01002342 setHoverStyle: function(point) {
2343 var me = this;
2344 Chart.DatasetController.prototype.setHoverStyle.call(me, point);
Jian Li46770fc2016-08-03 02:32:45 +09002345
Steven Burrows96ee21e2017-07-11 19:49:45 +01002346 // Radius
2347 var dataset = me.chart.data.datasets[point._datasetIndex];
2348 var index = point._index;
2349 var custom = point.custom || {};
2350 var model = point._model;
2351 model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]);
2352 },
Jian Li46770fc2016-08-03 02:32:45 +09002353
Steven Burrows96ee21e2017-07-11 19:49:45 +01002354 removeHoverStyle: function(point) {
2355 var me = this;
2356 Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point);
Jian Li46770fc2016-08-03 02:32:45 +09002357
Steven Burrows96ee21e2017-07-11 19:49:45 +01002358 var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index];
2359 var custom = point.custom || {};
2360 var model = point._model;
Jian Li46770fc2016-08-03 02:32:45 +09002361
Steven Burrows96ee21e2017-07-11 19:49:45 +01002362 model.radius = custom.radius ? custom.radius : me.getRadius(dataVal);
2363 }
2364 });
2365 };
Jian Li46770fc2016-08-03 02:32:45 +09002366
2367},{}],17:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01002368 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09002369
Steven Burrows96ee21e2017-07-11 19:49:45 +01002370 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09002371
Steven Burrows96ee21e2017-07-11 19:49:45 +01002372 var helpers = Chart.helpers,
2373 defaults = Chart.defaults;
Jian Li46770fc2016-08-03 02:32:45 +09002374
Steven Burrows96ee21e2017-07-11 19:49:45 +01002375 defaults.doughnut = {
2376 animation: {
2377 // Boolean - Whether we animate the rotation of the Doughnut
2378 animateRotate: true,
2379 // Boolean - Whether we animate scaling the Doughnut from the centre
2380 animateScale: false
2381 },
2382 aspectRatio: 1,
2383 hover: {
2384 mode: 'single'
2385 },
2386 legendCallback: function(chart) {
2387 var text = [];
2388 text.push('<ul class="' + chart.id + '-legend">');
Jian Li46770fc2016-08-03 02:32:45 +09002389
Steven Burrows96ee21e2017-07-11 19:49:45 +01002390 var data = chart.data;
2391 var datasets = data.datasets;
2392 var labels = data.labels;
Jian Li46770fc2016-08-03 02:32:45 +09002393
Steven Burrows96ee21e2017-07-11 19:49:45 +01002394 if (datasets.length) {
2395 for (var i = 0; i < datasets[0].data.length; ++i) {
2396 text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
2397 if (labels[i]) {
2398 text.push(labels[i]);
2399 }
2400 text.push('</li>');
2401 }
2402 }
Jian Li46770fc2016-08-03 02:32:45 +09002403
Steven Burrows96ee21e2017-07-11 19:49:45 +01002404 text.push('</ul>');
2405 return text.join('');
2406 },
2407 legend: {
2408 labels: {
2409 generateLabels: function(chart) {
2410 var data = chart.data;
2411 if (data.labels.length && data.datasets.length) {
2412 return data.labels.map(function(label, i) {
2413 var meta = chart.getDatasetMeta(0);
2414 var ds = data.datasets[0];
2415 var arc = meta.data[i];
2416 var custom = arc && arc.custom || {};
2417 var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
2418 var arcOpts = chart.options.elements.arc;
2419 var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
2420 var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
2421 var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
Jian Li46770fc2016-08-03 02:32:45 +09002422
Steven Burrows96ee21e2017-07-11 19:49:45 +01002423 return {
2424 text: label,
2425 fillStyle: fill,
2426 strokeStyle: stroke,
2427 lineWidth: bw,
2428 hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
Jian Li46770fc2016-08-03 02:32:45 +09002429
Steven Burrows96ee21e2017-07-11 19:49:45 +01002430 // Extra data used for toggling the correct item
2431 index: i
2432 };
2433 });
2434 }
2435 return [];
2436 }
2437 },
Jian Li46770fc2016-08-03 02:32:45 +09002438
Steven Burrows96ee21e2017-07-11 19:49:45 +01002439 onClick: function(e, legendItem) {
2440 var index = legendItem.index;
2441 var chart = this.chart;
2442 var i, ilen, meta;
Jian Li46770fc2016-08-03 02:32:45 +09002443
Steven Burrows96ee21e2017-07-11 19:49:45 +01002444 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
2445 meta = chart.getDatasetMeta(i);
2446 // toggle visibility of index if exists
2447 if (meta.data[index]) {
2448 meta.data[index].hidden = !meta.data[index].hidden;
2449 }
2450 }
Jian Li46770fc2016-08-03 02:32:45 +09002451
Steven Burrows96ee21e2017-07-11 19:49:45 +01002452 chart.update();
2453 }
2454 },
Jian Li46770fc2016-08-03 02:32:45 +09002455
Steven Burrows96ee21e2017-07-11 19:49:45 +01002456 // The percentage of the chart that we cut out of the middle.
2457 cutoutPercentage: 50,
Jian Li46770fc2016-08-03 02:32:45 +09002458
Steven Burrows96ee21e2017-07-11 19:49:45 +01002459 // The rotation of the chart, where the first data arc begins.
2460 rotation: Math.PI * -0.5,
Jian Li46770fc2016-08-03 02:32:45 +09002461
Steven Burrows96ee21e2017-07-11 19:49:45 +01002462 // The total circumference of the chart.
2463 circumference: Math.PI * 2.0,
Jian Li46770fc2016-08-03 02:32:45 +09002464
Steven Burrows96ee21e2017-07-11 19:49:45 +01002465 // Need to override these to give a nice default
2466 tooltips: {
2467 callbacks: {
2468 title: function() {
2469 return '';
2470 },
2471 label: function(tooltipItem, data) {
2472 var dataLabel = data.labels[tooltipItem.index];
2473 var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
Jian Li46770fc2016-08-03 02:32:45 +09002474
Steven Burrows96ee21e2017-07-11 19:49:45 +01002475 if (helpers.isArray(dataLabel)) {
2476 // show value on first line of multiline label
2477 // need to clone because we are changing the value
2478 dataLabel = dataLabel.slice();
2479 dataLabel[0] += value;
2480 } else {
2481 dataLabel += value;
2482 }
Jian Li46770fc2016-08-03 02:32:45 +09002483
Steven Burrows96ee21e2017-07-11 19:49:45 +01002484 return dataLabel;
2485 }
2486 }
Jian Lid7a5a742016-02-12 13:51:18 -08002487 }
Steven Burrows96ee21e2017-07-11 19:49:45 +01002488 };
2489
2490 defaults.pie = helpers.clone(defaults.doughnut);
2491 helpers.extend(defaults.pie, {
2492 cutoutPercentage: 0
2493 });
2494
2495
2496 Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
2497
2498 dataElementType: Chart.elements.Arc,
2499
2500 linkScales: helpers.noop,
2501
2502 // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
2503 getRingIndex: function(datasetIndex) {
2504 var ringIndex = 0;
2505
2506 for (var j = 0; j < datasetIndex; ++j) {
2507 if (this.chart.isDatasetVisible(j)) {
2508 ++ringIndex;
2509 }
2510 }
2511
2512 return ringIndex;
2513 },
2514
2515 update: function(reset) {
2516 var me = this;
2517 var chart = me.chart,
2518 chartArea = chart.chartArea,
2519 opts = chart.options,
2520 arcOpts = opts.elements.arc,
2521 availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth,
2522 availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth,
2523 minSize = Math.min(availableWidth, availableHeight),
2524 offset = {
2525 x: 0,
2526 y: 0
2527 },
2528 meta = me.getMeta(),
2529 cutoutPercentage = opts.cutoutPercentage,
2530 circumference = opts.circumference;
2531
2532 // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
2533 if (circumference < Math.PI * 2.0) {
2534 var startAngle = opts.rotation % (Math.PI * 2.0);
2535 startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
2536 var endAngle = startAngle + circumference;
2537 var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
2538 var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
2539 var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
2540 var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
2541 var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
2542 var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
2543 var cutout = cutoutPercentage / 100.0;
2544 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))};
2545 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))};
2546 var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
2547 minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
2548 offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
2549 }
2550
2551 chart.borderWidth = me.getMaxBorderWidth(meta.data);
2552 chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
2553 chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
2554 chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
2555 chart.offsetX = offset.x * chart.outerRadius;
2556 chart.offsetY = offset.y * chart.outerRadius;
2557
2558 meta.total = me.calculateTotal();
2559
2560 me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
2561 me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);
2562
2563 helpers.each(meta.data, function(arc, index) {
2564 me.updateElement(arc, index, reset);
2565 });
2566 },
2567
2568 updateElement: function(arc, index, reset) {
2569 var me = this;
2570 var chart = me.chart,
2571 chartArea = chart.chartArea,
2572 opts = chart.options,
2573 animationOpts = opts.animation,
2574 centerX = (chartArea.left + chartArea.right) / 2,
2575 centerY = (chartArea.top + chartArea.bottom) / 2,
2576 startAngle = opts.rotation, // non reset case handled later
2577 endAngle = opts.rotation, // non reset case handled later
2578 dataset = me.getDataset(),
2579 circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)),
2580 innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius,
2581 outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius,
2582 valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
2583
2584 helpers.extend(arc, {
2585 // Utility
2586 _datasetIndex: me.index,
2587 _index: index,
2588
2589 // Desired view properties
2590 _model: {
2591 x: centerX + chart.offsetX,
2592 y: centerY + chart.offsetY,
2593 startAngle: startAngle,
2594 endAngle: endAngle,
2595 circumference: circumference,
2596 outerRadius: outerRadius,
2597 innerRadius: innerRadius,
2598 label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
2599 }
2600 });
2601
2602 var model = arc._model;
2603 // Resets the visual styles
2604 this.removeHoverStyle(arc);
2605
2606 // Set correct angles if not resetting
2607 if (!reset || !animationOpts.animateRotate) {
2608 if (index === 0) {
2609 model.startAngle = opts.rotation;
2610 } else {
2611 model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
2612 }
2613
2614 model.endAngle = model.startAngle + model.circumference;
2615 }
2616
2617 arc.pivot();
2618 },
2619
2620 removeHoverStyle: function(arc) {
2621 Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
2622 },
2623
2624 calculateTotal: function() {
2625 var dataset = this.getDataset();
2626 var meta = this.getMeta();
2627 var total = 0;
2628 var value;
2629
2630 helpers.each(meta.data, function(element, index) {
2631 value = dataset.data[index];
2632 if (!isNaN(value) && !element.hidden) {
2633 total += Math.abs(value);
2634 }
2635 });
2636
2637 /* if (total === 0) {
2638 total = NaN;
2639 }*/
2640
2641 return total;
2642 },
2643
2644 calculateCircumference: function(value) {
2645 var total = this.getMeta().total;
2646 if (total > 0 && !isNaN(value)) {
2647 return (Math.PI * 2.0) * (value / total);
2648 }
2649 return 0;
2650 },
2651
2652 // gets the max border or hover width to properly scale pie charts
2653 getMaxBorderWidth: function(elements) {
2654 var max = 0,
2655 index = this.index,
2656 length = elements.length,
2657 borderWidth,
2658 hoverWidth;
2659
2660 for (var i = 0; i < length; i++) {
2661 borderWidth = elements[i]._model ? elements[i]._model.borderWidth : 0;
2662 hoverWidth = elements[i]._chart ? elements[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
2663
2664 max = borderWidth > max ? borderWidth : max;
2665 max = hoverWidth > max ? hoverWidth : max;
2666 }
2667 return max;
2668 }
2669 });
2670 };
Jian Li46770fc2016-08-03 02:32:45 +09002671
2672},{}],18:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01002673 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09002674
Steven Burrows96ee21e2017-07-11 19:49:45 +01002675 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09002676
Steven Burrows96ee21e2017-07-11 19:49:45 +01002677 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09002678
Steven Burrows96ee21e2017-07-11 19:49:45 +01002679 Chart.defaults.line = {
2680 showLines: true,
2681 spanGaps: false,
Jian Li46770fc2016-08-03 02:32:45 +09002682
Steven Burrows96ee21e2017-07-11 19:49:45 +01002683 hover: {
2684 mode: 'label'
2685 },
Jian Li46770fc2016-08-03 02:32:45 +09002686
Steven Burrows96ee21e2017-07-11 19:49:45 +01002687 scales: {
2688 xAxes: [{
2689 type: 'category',
2690 id: 'x-axis-0'
2691 }],
2692 yAxes: [{
2693 type: 'linear',
2694 id: 'y-axis-0'
2695 }]
2696 }
2697 };
Jian Li46770fc2016-08-03 02:32:45 +09002698
Steven Burrows96ee21e2017-07-11 19:49:45 +01002699 function lineEnabled(dataset, options) {
2700 return helpers.getValueOrDefault(dataset.showLine, options.showLines);
2701 }
Jian Li46770fc2016-08-03 02:32:45 +09002702
Steven Burrows96ee21e2017-07-11 19:49:45 +01002703 Chart.controllers.line = Chart.DatasetController.extend({
Jian Li46770fc2016-08-03 02:32:45 +09002704
Steven Burrows96ee21e2017-07-11 19:49:45 +01002705 datasetElementType: Chart.elements.Line,
Jian Li46770fc2016-08-03 02:32:45 +09002706
Steven Burrows96ee21e2017-07-11 19:49:45 +01002707 dataElementType: Chart.elements.Point,
Jian Li46770fc2016-08-03 02:32:45 +09002708
Steven Burrows96ee21e2017-07-11 19:49:45 +01002709 update: function(reset) {
2710 var me = this;
2711 var meta = me.getMeta();
2712 var line = meta.dataset;
2713 var points = meta.data || [];
2714 var options = me.chart.options;
2715 var lineElementOptions = options.elements.line;
2716 var scale = me.getScaleForId(meta.yAxisID);
2717 var i, ilen, custom;
2718 var dataset = me.getDataset();
2719 var showLine = lineEnabled(dataset, options);
Jian Li46770fc2016-08-03 02:32:45 +09002720
Steven Burrows96ee21e2017-07-11 19:49:45 +01002721 // Update Line
2722 if (showLine) {
2723 custom = line.custom || {};
Jian Li46770fc2016-08-03 02:32:45 +09002724
Steven Burrows96ee21e2017-07-11 19:49:45 +01002725 // Compatibility: If the properties are defined with only the old name, use those values
2726 if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
2727 dataset.lineTension = dataset.tension;
2728 }
Jian Li46770fc2016-08-03 02:32:45 +09002729
Steven Burrows96ee21e2017-07-11 19:49:45 +01002730 // Utility
2731 line._scale = scale;
2732 line._datasetIndex = me.index;
2733 // Data
2734 line._children = points;
2735 // Model
2736 line._model = {
2737 // Appearance
2738 // The default behavior of lines is to break at null values, according
2739 // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
2740 // This option gives lines the ability to span gaps
2741 spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,
2742 tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension),
2743 backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
2744 borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
2745 borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
2746 borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
2747 borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
2748 borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
2749 borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
2750 fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
2751 steppedLine: custom.steppedLine ? custom.steppedLine : helpers.getValueOrDefault(dataset.steppedLine, lineElementOptions.stepped),
2752 cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.getValueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),
2753 };
Jian Li46770fc2016-08-03 02:32:45 +09002754
Steven Burrows96ee21e2017-07-11 19:49:45 +01002755 line.pivot();
2756 }
Jian Li46770fc2016-08-03 02:32:45 +09002757
Steven Burrows96ee21e2017-07-11 19:49:45 +01002758 // Update Points
2759 for (i=0, ilen=points.length; i<ilen; ++i) {
2760 me.updateElement(points[i], i, reset);
2761 }
Jian Li46770fc2016-08-03 02:32:45 +09002762
Steven Burrows96ee21e2017-07-11 19:49:45 +01002763 if (showLine && line._model.tension !== 0) {
2764 me.updateBezierControlPoints();
2765 }
Jian Li46770fc2016-08-03 02:32:45 +09002766
Steven Burrows96ee21e2017-07-11 19:49:45 +01002767 // Now pivot the point for animation
2768 for (i=0, ilen=points.length; i<ilen; ++i) {
2769 points[i].pivot();
2770 }
2771 },
Jian Li46770fc2016-08-03 02:32:45 +09002772
Steven Burrows96ee21e2017-07-11 19:49:45 +01002773 getPointBackgroundColor: function(point, index) {
2774 var backgroundColor = this.chart.options.elements.point.backgroundColor;
2775 var dataset = this.getDataset();
2776 var custom = point.custom || {};
Jian Li46770fc2016-08-03 02:32:45 +09002777
Steven Burrows96ee21e2017-07-11 19:49:45 +01002778 if (custom.backgroundColor) {
2779 backgroundColor = custom.backgroundColor;
2780 } else if (dataset.pointBackgroundColor) {
2781 backgroundColor = helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
2782 } else if (dataset.backgroundColor) {
2783 backgroundColor = dataset.backgroundColor;
2784 }
Jian Li46770fc2016-08-03 02:32:45 +09002785
Steven Burrows96ee21e2017-07-11 19:49:45 +01002786 return backgroundColor;
2787 },
Jian Li46770fc2016-08-03 02:32:45 +09002788
Steven Burrows96ee21e2017-07-11 19:49:45 +01002789 getPointBorderColor: function(point, index) {
2790 var borderColor = this.chart.options.elements.point.borderColor;
2791 var dataset = this.getDataset();
2792 var custom = point.custom || {};
Jian Li46770fc2016-08-03 02:32:45 +09002793
Steven Burrows96ee21e2017-07-11 19:49:45 +01002794 if (custom.borderColor) {
2795 borderColor = custom.borderColor;
2796 } else if (dataset.pointBorderColor) {
2797 borderColor = helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
2798 } else if (dataset.borderColor) {
2799 borderColor = dataset.borderColor;
2800 }
Jian Li46770fc2016-08-03 02:32:45 +09002801
Steven Burrows96ee21e2017-07-11 19:49:45 +01002802 return borderColor;
2803 },
Jian Li46770fc2016-08-03 02:32:45 +09002804
Steven Burrows96ee21e2017-07-11 19:49:45 +01002805 getPointBorderWidth: function(point, index) {
2806 var borderWidth = this.chart.options.elements.point.borderWidth;
2807 var dataset = this.getDataset();
2808 var custom = point.custom || {};
Jian Li46770fc2016-08-03 02:32:45 +09002809
Steven Burrows96ee21e2017-07-11 19:49:45 +01002810 if (!isNaN(custom.borderWidth)) {
2811 borderWidth = custom.borderWidth;
2812 } else if (!isNaN(dataset.pointBorderWidth)) {
2813 borderWidth = helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
2814 } else if (!isNaN(dataset.borderWidth)) {
2815 borderWidth = dataset.borderWidth;
2816 }
Jian Li46770fc2016-08-03 02:32:45 +09002817
Steven Burrows96ee21e2017-07-11 19:49:45 +01002818 return borderWidth;
2819 },
Jian Li46770fc2016-08-03 02:32:45 +09002820
Steven Burrows96ee21e2017-07-11 19:49:45 +01002821 updateElement: function(point, index, reset) {
2822 var me = this;
2823 var meta = me.getMeta();
2824 var custom = point.custom || {};
2825 var dataset = me.getDataset();
2826 var datasetIndex = me.index;
2827 var value = dataset.data[index];
2828 var yScale = me.getScaleForId(meta.yAxisID);
2829 var xScale = me.getScaleForId(meta.xAxisID);
2830 var pointOptions = me.chart.options.elements.point;
2831 var x, y;
2832 var labels = me.chart.data.labels || [];
2833 var includeOffset = (labels.length === 1 || dataset.data.length === 1) || me.chart.isCombo;
Jian Li46770fc2016-08-03 02:32:45 +09002834
Steven Burrows96ee21e2017-07-11 19:49:45 +01002835 // Compatibility: If the properties are defined with only the old name, use those values
2836 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
2837 dataset.pointRadius = dataset.radius;
2838 }
2839 if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
2840 dataset.pointHitRadius = dataset.hitRadius;
2841 }
Jian Li46770fc2016-08-03 02:32:45 +09002842
Steven Burrows96ee21e2017-07-11 19:49:45 +01002843 x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex, includeOffset);
2844 y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
Jian Li46770fc2016-08-03 02:32:45 +09002845
Steven Burrows96ee21e2017-07-11 19:49:45 +01002846 // Utility
2847 point._xScale = xScale;
2848 point._yScale = yScale;
2849 point._datasetIndex = datasetIndex;
2850 point._index = index;
Jian Li46770fc2016-08-03 02:32:45 +09002851
Steven Burrows96ee21e2017-07-11 19:49:45 +01002852 // Desired view properties
2853 point._model = {
2854 x: x,
2855 y: y,
2856 skip: custom.skip || isNaN(x) || isNaN(y),
2857 // Appearance
2858 radius: custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
2859 pointStyle: custom.pointStyle || helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
2860 backgroundColor: me.getPointBackgroundColor(point, index),
2861 borderColor: me.getPointBorderColor(point, index),
2862 borderWidth: me.getPointBorderWidth(point, index),
2863 tension: meta.dataset._model ? meta.dataset._model.tension : 0,
2864 steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,
2865 // Tooltip
2866 hitRadius: custom.hitRadius || helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
2867 };
2868 },
Jian Li46770fc2016-08-03 02:32:45 +09002869
Steven Burrows96ee21e2017-07-11 19:49:45 +01002870 calculatePointY: function(value, index, datasetIndex) {
2871 var me = this;
2872 var chart = me.chart;
2873 var meta = me.getMeta();
2874 var yScale = me.getScaleForId(meta.yAxisID);
2875 var sumPos = 0;
2876 var sumNeg = 0;
2877 var i, ds, dsMeta;
Jian Li46770fc2016-08-03 02:32:45 +09002878
Steven Burrows96ee21e2017-07-11 19:49:45 +01002879 if (yScale.options.stacked) {
2880 for (i = 0; i < datasetIndex; i++) {
2881 ds = chart.data.datasets[i];
2882 dsMeta = chart.getDatasetMeta(i);
2883 if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
2884 var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
2885 if (stackedRightValue < 0) {
2886 sumNeg += stackedRightValue || 0;
2887 } else {
2888 sumPos += stackedRightValue || 0;
2889 }
2890 }
2891 }
Jian Li46770fc2016-08-03 02:32:45 +09002892
Steven Burrows96ee21e2017-07-11 19:49:45 +01002893 var rightValue = Number(yScale.getRightValue(value));
2894 if (rightValue < 0) {
2895 return yScale.getPixelForValue(sumNeg + rightValue);
2896 }
2897 return yScale.getPixelForValue(sumPos + rightValue);
2898 }
Jian Li46770fc2016-08-03 02:32:45 +09002899
Steven Burrows96ee21e2017-07-11 19:49:45 +01002900 return yScale.getPixelForValue(value);
2901 },
Jian Li46770fc2016-08-03 02:32:45 +09002902
Steven Burrows96ee21e2017-07-11 19:49:45 +01002903 updateBezierControlPoints: function() {
2904 var me = this;
2905 var meta = me.getMeta();
2906 var area = me.chart.chartArea;
2907 var points = (meta.data || []);
2908 var i, ilen, point, model, controlPoints;
Jian Li46770fc2016-08-03 02:32:45 +09002909
Steven Burrows96ee21e2017-07-11 19:49:45 +01002910 // Only consider points that are drawn in case the spanGaps option is used
2911 if (meta.dataset._model.spanGaps) {
2912 points = points.filter(function(pt) {
2913 return !pt._model.skip;
2914 });
2915 }
Jian Li46770fc2016-08-03 02:32:45 +09002916
Steven Burrows96ee21e2017-07-11 19:49:45 +01002917 function capControlPoint(pt, min, max) {
2918 return Math.max(Math.min(pt, max), min);
2919 }
Jian Li46770fc2016-08-03 02:32:45 +09002920
Steven Burrows96ee21e2017-07-11 19:49:45 +01002921 if (meta.dataset._model.cubicInterpolationMode === 'monotone') {
2922 helpers.splineCurveMonotone(points);
2923 } else {
2924 for (i = 0, ilen = points.length; i < ilen; ++i) {
2925 point = points[i];
2926 model = point._model;
2927 controlPoints = helpers.splineCurve(
2928 helpers.previousItem(points, i)._model,
2929 model,
2930 helpers.nextItem(points, i)._model,
2931 meta.dataset._model.tension
2932 );
2933 model.controlPointPreviousX = controlPoints.previous.x;
2934 model.controlPointPreviousY = controlPoints.previous.y;
2935 model.controlPointNextX = controlPoints.next.x;
2936 model.controlPointNextY = controlPoints.next.y;
2937 }
2938 }
Jian Li46770fc2016-08-03 02:32:45 +09002939
Steven Burrows96ee21e2017-07-11 19:49:45 +01002940 if (me.chart.options.elements.line.capBezierPoints) {
2941 for (i = 0, ilen = points.length; i < ilen; ++i) {
2942 model = points[i]._model;
2943 model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
2944 model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
2945 model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
2946 model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
2947 }
2948 }
2949 },
Jian Li46770fc2016-08-03 02:32:45 +09002950
Steven Burrows96ee21e2017-07-11 19:49:45 +01002951 draw: function() {
2952 var me = this;
2953 var chart = me.chart;
2954 var meta = me.getMeta();
2955 var points = meta.data || [];
2956 var area = chart.chartArea;
2957 var ilen = points.length;
2958 var i = 0;
Jian Li46770fc2016-08-03 02:32:45 +09002959
Steven Burrows96ee21e2017-07-11 19:49:45 +01002960 Chart.canvasHelpers.clipArea(chart.ctx, area);
Jian Li46770fc2016-08-03 02:32:45 +09002961
Steven Burrows96ee21e2017-07-11 19:49:45 +01002962 if (lineEnabled(me.getDataset(), chart.options)) {
2963 meta.dataset.draw();
2964 }
Jian Li46770fc2016-08-03 02:32:45 +09002965
Steven Burrows96ee21e2017-07-11 19:49:45 +01002966 Chart.canvasHelpers.unclipArea(chart.ctx);
Jian Li46770fc2016-08-03 02:32:45 +09002967
Steven Burrows96ee21e2017-07-11 19:49:45 +01002968 // Draw the points
2969 for (; i<ilen; ++i) {
2970 points[i].draw(area);
2971 }
2972 },
Jian Li46770fc2016-08-03 02:32:45 +09002973
Steven Burrows96ee21e2017-07-11 19:49:45 +01002974 setHoverStyle: function(point) {
2975 // Point
2976 var dataset = this.chart.data.datasets[point._datasetIndex];
2977 var index = point._index;
2978 var custom = point.custom || {};
2979 var model = point._model;
Jian Li46770fc2016-08-03 02:32:45 +09002980
Steven Burrows96ee21e2017-07-11 19:49:45 +01002981 model.radius = custom.hoverRadius || helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
2982 model.backgroundColor = custom.hoverBackgroundColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
2983 model.borderColor = custom.hoverBorderColor || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
2984 model.borderWidth = custom.hoverBorderWidth || helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
2985 },
Jian Li46770fc2016-08-03 02:32:45 +09002986
Steven Burrows96ee21e2017-07-11 19:49:45 +01002987 removeHoverStyle: function(point) {
2988 var me = this;
2989 var dataset = me.chart.data.datasets[point._datasetIndex];
2990 var index = point._index;
2991 var custom = point.custom || {};
2992 var model = point._model;
Jian Li46770fc2016-08-03 02:32:45 +09002993
Steven Burrows96ee21e2017-07-11 19:49:45 +01002994 // Compatibility: If the properties are defined with only the old name, use those values
2995 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
2996 dataset.pointRadius = dataset.radius;
2997 }
Jian Li46770fc2016-08-03 02:32:45 +09002998
Steven Burrows96ee21e2017-07-11 19:49:45 +01002999 model.radius = custom.radius || helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);
3000 model.backgroundColor = me.getPointBackgroundColor(point, index);
3001 model.borderColor = me.getPointBorderColor(point, index);
3002 model.borderWidth = me.getPointBorderWidth(point, index);
3003 }
3004 });
3005 };
Jian Li46770fc2016-08-03 02:32:45 +09003006
3007},{}],19:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01003008 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09003009
Steven Burrows96ee21e2017-07-11 19:49:45 +01003010 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09003011
Steven Burrows96ee21e2017-07-11 19:49:45 +01003012 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09003013
Steven Burrows96ee21e2017-07-11 19:49:45 +01003014 Chart.defaults.polarArea = {
Jian Li46770fc2016-08-03 02:32:45 +09003015
Steven Burrows96ee21e2017-07-11 19:49:45 +01003016 scale: {
3017 type: 'radialLinear',
3018 angleLines: {
3019 display: false
3020 },
3021 gridLines: {
3022 circular: true
3023 },
3024 pointLabels: {
3025 display: false
3026 },
3027 ticks: {
3028 beginAtZero: true
3029 }
3030 },
Jian Li46770fc2016-08-03 02:32:45 +09003031
Steven Burrows96ee21e2017-07-11 19:49:45 +01003032 // Boolean - Whether to animate the rotation of the chart
3033 animation: {
3034 animateRotate: true,
3035 animateScale: true
3036 },
Jian Li46770fc2016-08-03 02:32:45 +09003037
Steven Burrows96ee21e2017-07-11 19:49:45 +01003038 startAngle: -0.5 * Math.PI,
3039 aspectRatio: 1,
3040 legendCallback: function(chart) {
3041 var text = [];
3042 text.push('<ul class="' + chart.id + '-legend">');
Jian Li46770fc2016-08-03 02:32:45 +09003043
Steven Burrows96ee21e2017-07-11 19:49:45 +01003044 var data = chart.data;
3045 var datasets = data.datasets;
3046 var labels = data.labels;
Jian Li46770fc2016-08-03 02:32:45 +09003047
Steven Burrows96ee21e2017-07-11 19:49:45 +01003048 if (datasets.length) {
3049 for (var i = 0; i < datasets[0].data.length; ++i) {
3050 text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
3051 if (labels[i]) {
3052 text.push(labels[i]);
3053 }
3054 text.push('</li>');
3055 }
3056 }
Jian Li46770fc2016-08-03 02:32:45 +09003057
Steven Burrows96ee21e2017-07-11 19:49:45 +01003058 text.push('</ul>');
3059 return text.join('');
3060 },
3061 legend: {
3062 labels: {
3063 generateLabels: function(chart) {
3064 var data = chart.data;
3065 if (data.labels.length && data.datasets.length) {
3066 return data.labels.map(function(label, i) {
3067 var meta = chart.getDatasetMeta(0);
3068 var ds = data.datasets[0];
3069 var arc = meta.data[i];
3070 var custom = arc.custom || {};
3071 var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
3072 var arcOpts = chart.options.elements.arc;
3073 var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
3074 var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
3075 var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
Jian Li46770fc2016-08-03 02:32:45 +09003076
Steven Burrows96ee21e2017-07-11 19:49:45 +01003077 return {
3078 text: label,
3079 fillStyle: fill,
3080 strokeStyle: stroke,
3081 lineWidth: bw,
3082 hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
Jian Li46770fc2016-08-03 02:32:45 +09003083
Steven Burrows96ee21e2017-07-11 19:49:45 +01003084 // Extra data used for toggling the correct item
3085 index: i
3086 };
3087 });
3088 }
3089 return [];
3090 }
3091 },
Jian Li46770fc2016-08-03 02:32:45 +09003092
Steven Burrows96ee21e2017-07-11 19:49:45 +01003093 onClick: function(e, legendItem) {
3094 var index = legendItem.index;
3095 var chart = this.chart;
3096 var i, ilen, meta;
Jian Li46770fc2016-08-03 02:32:45 +09003097
Steven Burrows96ee21e2017-07-11 19:49:45 +01003098 for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
3099 meta = chart.getDatasetMeta(i);
3100 meta.data[index].hidden = !meta.data[index].hidden;
3101 }
Jian Li46770fc2016-08-03 02:32:45 +09003102
Steven Burrows96ee21e2017-07-11 19:49:45 +01003103 chart.update();
3104 }
3105 },
Jian Li46770fc2016-08-03 02:32:45 +09003106
Steven Burrows96ee21e2017-07-11 19:49:45 +01003107 // Need to override these to give a nice default
3108 tooltips: {
3109 callbacks: {
3110 title: function() {
3111 return '';
3112 },
3113 label: function(tooltipItem, data) {
3114 return data.labels[tooltipItem.index] + ': ' + tooltipItem.yLabel;
3115 }
3116 }
3117 }
3118 };
Jian Li46770fc2016-08-03 02:32:45 +09003119
Steven Burrows96ee21e2017-07-11 19:49:45 +01003120 Chart.controllers.polarArea = Chart.DatasetController.extend({
Jian Li46770fc2016-08-03 02:32:45 +09003121
Steven Burrows96ee21e2017-07-11 19:49:45 +01003122 dataElementType: Chart.elements.Arc,
Jian Li46770fc2016-08-03 02:32:45 +09003123
Steven Burrows96ee21e2017-07-11 19:49:45 +01003124 linkScales: helpers.noop,
Jian Li46770fc2016-08-03 02:32:45 +09003125
Steven Burrows96ee21e2017-07-11 19:49:45 +01003126 update: function(reset) {
3127 var me = this;
3128 var chart = me.chart;
3129 var chartArea = chart.chartArea;
3130 var meta = me.getMeta();
3131 var opts = chart.options;
3132 var arcOpts = opts.elements.arc;
3133 var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
3134 chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
3135 chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
3136 chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
Jian Li46770fc2016-08-03 02:32:45 +09003137
Steven Burrows96ee21e2017-07-11 19:49:45 +01003138 me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
3139 me.innerRadius = me.outerRadius - chart.radiusLength;
Jian Li46770fc2016-08-03 02:32:45 +09003140
Steven Burrows96ee21e2017-07-11 19:49:45 +01003141 meta.count = me.countVisibleElements();
Jian Li46770fc2016-08-03 02:32:45 +09003142
Steven Burrows96ee21e2017-07-11 19:49:45 +01003143 helpers.each(meta.data, function(arc, index) {
3144 me.updateElement(arc, index, reset);
3145 });
3146 },
Jian Li46770fc2016-08-03 02:32:45 +09003147
Steven Burrows96ee21e2017-07-11 19:49:45 +01003148 updateElement: function(arc, index, reset) {
3149 var me = this;
3150 var chart = me.chart;
3151 var dataset = me.getDataset();
3152 var opts = chart.options;
3153 var animationOpts = opts.animation;
3154 var scale = chart.scale;
3155 var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
3156 var labels = chart.data.labels;
Jian Li46770fc2016-08-03 02:32:45 +09003157
Steven Burrows96ee21e2017-07-11 19:49:45 +01003158 var circumference = me.calculateCircumference(dataset.data[index]);
3159 var centerX = scale.xCenter;
3160 var centerY = scale.yCenter;
Jian Li46770fc2016-08-03 02:32:45 +09003161
Steven Burrows96ee21e2017-07-11 19:49:45 +01003162 // If there is NaN data before us, we need to calculate the starting angle correctly.
3163 // We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data
3164 var visibleCount = 0;
3165 var meta = me.getMeta();
3166 for (var i = 0; i < index; ++i) {
3167 if (!isNaN(dataset.data[i]) && !meta.data[i].hidden) {
3168 ++visibleCount;
3169 }
3170 }
Jian Li46770fc2016-08-03 02:32:45 +09003171
Steven Burrows96ee21e2017-07-11 19:49:45 +01003172 // var negHalfPI = -0.5 * Math.PI;
3173 var datasetStartAngle = opts.startAngle;
3174 var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
3175 var startAngle = datasetStartAngle + (circumference * visibleCount);
3176 var endAngle = startAngle + (arc.hidden ? 0 : circumference);
Jian Li46770fc2016-08-03 02:32:45 +09003177
Steven Burrows96ee21e2017-07-11 19:49:45 +01003178 var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
Jian Li46770fc2016-08-03 02:32:45 +09003179
Steven Burrows96ee21e2017-07-11 19:49:45 +01003180 helpers.extend(arc, {
3181 // Utility
3182 _datasetIndex: me.index,
3183 _index: index,
3184 _scale: scale,
Jian Li46770fc2016-08-03 02:32:45 +09003185
Steven Burrows96ee21e2017-07-11 19:49:45 +01003186 // Desired view properties
3187 _model: {
3188 x: centerX,
3189 y: centerY,
3190 innerRadius: 0,
3191 outerRadius: reset ? resetRadius : distance,
3192 startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
3193 endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
3194 label: getValueAtIndexOrDefault(labels, index, labels[index])
3195 }
3196 });
Jian Li46770fc2016-08-03 02:32:45 +09003197
Steven Burrows96ee21e2017-07-11 19:49:45 +01003198 // Apply border and fill style
3199 me.removeHoverStyle(arc);
Jian Li46770fc2016-08-03 02:32:45 +09003200
Steven Burrows96ee21e2017-07-11 19:49:45 +01003201 arc.pivot();
3202 },
Jian Li46770fc2016-08-03 02:32:45 +09003203
Steven Burrows96ee21e2017-07-11 19:49:45 +01003204 removeHoverStyle: function(arc) {
3205 Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
3206 },
Jian Li46770fc2016-08-03 02:32:45 +09003207
Steven Burrows96ee21e2017-07-11 19:49:45 +01003208 countVisibleElements: function() {
3209 var dataset = this.getDataset();
3210 var meta = this.getMeta();
3211 var count = 0;
Jian Li46770fc2016-08-03 02:32:45 +09003212
Steven Burrows96ee21e2017-07-11 19:49:45 +01003213 helpers.each(meta.data, function(element, index) {
3214 if (!isNaN(dataset.data[index]) && !element.hidden) {
3215 count++;
3216 }
3217 });
Jian Li46770fc2016-08-03 02:32:45 +09003218
Steven Burrows96ee21e2017-07-11 19:49:45 +01003219 return count;
3220 },
Jian Li46770fc2016-08-03 02:32:45 +09003221
Steven Burrows96ee21e2017-07-11 19:49:45 +01003222 calculateCircumference: function(value) {
3223 var count = this.getMeta().count;
3224 if (count > 0 && !isNaN(value)) {
3225 return (2 * Math.PI) / count;
3226 }
3227 return 0;
3228 }
3229 });
3230 };
Jian Li46770fc2016-08-03 02:32:45 +09003231
3232},{}],20:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01003233 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09003234
Steven Burrows96ee21e2017-07-11 19:49:45 +01003235 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09003236
Steven Burrows96ee21e2017-07-11 19:49:45 +01003237 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09003238
Steven Burrows96ee21e2017-07-11 19:49:45 +01003239 Chart.defaults.radar = {
3240 aspectRatio: 1,
3241 scale: {
3242 type: 'radialLinear'
3243 },
3244 elements: {
3245 line: {
3246 tension: 0 // no bezier in radar
3247 }
3248 }
3249 };
Jian Li46770fc2016-08-03 02:32:45 +09003250
Steven Burrows96ee21e2017-07-11 19:49:45 +01003251 Chart.controllers.radar = Chart.DatasetController.extend({
Jian Li46770fc2016-08-03 02:32:45 +09003252
Steven Burrows96ee21e2017-07-11 19:49:45 +01003253 datasetElementType: Chart.elements.Line,
Jian Li46770fc2016-08-03 02:32:45 +09003254
Steven Burrows96ee21e2017-07-11 19:49:45 +01003255 dataElementType: Chart.elements.Point,
Jian Li46770fc2016-08-03 02:32:45 +09003256
Steven Burrows96ee21e2017-07-11 19:49:45 +01003257 linkScales: helpers.noop,
Jian Li46770fc2016-08-03 02:32:45 +09003258
Steven Burrows96ee21e2017-07-11 19:49:45 +01003259 update: function(reset) {
3260 var me = this;
3261 var meta = me.getMeta();
3262 var line = meta.dataset;
3263 var points = meta.data;
3264 var custom = line.custom || {};
3265 var dataset = me.getDataset();
3266 var lineElementOptions = me.chart.options.elements.line;
3267 var scale = me.chart.scale;
Jian Li46770fc2016-08-03 02:32:45 +09003268
Steven Burrows96ee21e2017-07-11 19:49:45 +01003269 // Compatibility: If the properties are defined with only the old name, use those values
3270 if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
3271 dataset.lineTension = dataset.tension;
3272 }
Jian Li46770fc2016-08-03 02:32:45 +09003273
Steven Burrows96ee21e2017-07-11 19:49:45 +01003274 helpers.extend(meta.dataset, {
3275 // Utility
3276 _datasetIndex: me.index,
3277 _scale: scale,
3278 // Data
3279 _children: points,
3280 _loop: true,
3281 // Model
3282 _model: {
3283 // Appearance
3284 tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, lineElementOptions.tension),
3285 backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
3286 borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
3287 borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
3288 fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
3289 borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
3290 borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
3291 borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
3292 borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
3293 }
3294 });
Jian Li46770fc2016-08-03 02:32:45 +09003295
Steven Burrows96ee21e2017-07-11 19:49:45 +01003296 meta.dataset.pivot();
Jian Li46770fc2016-08-03 02:32:45 +09003297
Steven Burrows96ee21e2017-07-11 19:49:45 +01003298 // Update Points
3299 helpers.each(points, function(point, index) {
3300 me.updateElement(point, index, reset);
3301 }, me);
Jian Li46770fc2016-08-03 02:32:45 +09003302
Steven Burrows96ee21e2017-07-11 19:49:45 +01003303 // Update bezier control points
3304 me.updateBezierControlPoints();
3305 },
3306 updateElement: function(point, index, reset) {
3307 var me = this;
3308 var custom = point.custom || {};
3309 var dataset = me.getDataset();
3310 var scale = me.chart.scale;
3311 var pointElementOptions = me.chart.options.elements.point;
3312 var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
Jian Li46770fc2016-08-03 02:32:45 +09003313
Steven Burrows96ee21e2017-07-11 19:49:45 +01003314 // Compatibility: If the properties are defined with only the old name, use those values
3315 if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
3316 dataset.pointRadius = dataset.radius;
3317 }
3318 if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
3319 dataset.pointHitRadius = dataset.hitRadius;
3320 }
Jian Li46770fc2016-08-03 02:32:45 +09003321
Steven Burrows96ee21e2017-07-11 19:49:45 +01003322 helpers.extend(point, {
3323 // Utility
3324 _datasetIndex: me.index,
3325 _index: index,
3326 _scale: scale,
Jian Li46770fc2016-08-03 02:32:45 +09003327
Steven Burrows96ee21e2017-07-11 19:49:45 +01003328 // Desired view properties
3329 _model: {
3330 x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
3331 y: reset ? scale.yCenter : pointPosition.y,
Jian Li46770fc2016-08-03 02:32:45 +09003332
Steven Burrows96ee21e2017-07-11 19:49:45 +01003333 // Appearance
3334 tension: custom.tension ? custom.tension : helpers.getValueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension),
3335 radius: custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
3336 backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor),
3337 borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
3338 borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
3339 pointStyle: custom.pointStyle ? custom.pointStyle : helpers.getValueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),
Jian Li46770fc2016-08-03 02:32:45 +09003340
Steven Burrows96ee21e2017-07-11 19:49:45 +01003341 // Tooltip
3342 hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius)
3343 }
3344 });
Jian Li46770fc2016-08-03 02:32:45 +09003345
Steven Burrows96ee21e2017-07-11 19:49:45 +01003346 point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
3347 },
3348 updateBezierControlPoints: function() {
3349 var chartArea = this.chart.chartArea;
3350 var meta = this.getMeta();
Jian Li46770fc2016-08-03 02:32:45 +09003351
Steven Burrows96ee21e2017-07-11 19:49:45 +01003352 helpers.each(meta.data, function(point, index) {
3353 var model = point._model;
3354 var controlPoints = helpers.splineCurve(
3355 helpers.previousItem(meta.data, index, true)._model,
3356 model,
3357 helpers.nextItem(meta.data, index, true)._model,
3358 model.tension
3359 );
Jian Li46770fc2016-08-03 02:32:45 +09003360
Steven Burrows96ee21e2017-07-11 19:49:45 +01003361 // Prevent the bezier going outside of the bounds of the graph
3362 model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left);
3363 model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top);
Jian Li46770fc2016-08-03 02:32:45 +09003364
Steven Burrows96ee21e2017-07-11 19:49:45 +01003365 model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left);
3366 model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top);
Jian Li46770fc2016-08-03 02:32:45 +09003367
Steven Burrows96ee21e2017-07-11 19:49:45 +01003368 // Now pivot the point for animation
3369 point.pivot();
3370 });
3371 },
Jian Li46770fc2016-08-03 02:32:45 +09003372
Steven Burrows96ee21e2017-07-11 19:49:45 +01003373 setHoverStyle: function(point) {
3374 // Point
3375 var dataset = this.chart.data.datasets[point._datasetIndex];
3376 var custom = point.custom || {};
3377 var index = point._index;
3378 var model = point._model;
Jian Li46770fc2016-08-03 02:32:45 +09003379
Steven Burrows96ee21e2017-07-11 19:49:45 +01003380 model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.getValueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
3381 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
3382 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
3383 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
3384 },
Jian Li46770fc2016-08-03 02:32:45 +09003385
Steven Burrows96ee21e2017-07-11 19:49:45 +01003386 removeHoverStyle: function(point) {
3387 var dataset = this.chart.data.datasets[point._datasetIndex];
3388 var custom = point.custom || {};
3389 var index = point._index;
3390 var model = point._model;
3391 var pointElementOptions = this.chart.options.elements.point;
Jian Li46770fc2016-08-03 02:32:45 +09003392
Steven Burrows96ee21e2017-07-11 19:49:45 +01003393 model.radius = custom.radius ? custom.radius : helpers.getValueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius);
3394 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor);
3395 model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor);
3396 model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth);
3397 }
3398 });
3399 };
Jian Li46770fc2016-08-03 02:32:45 +09003400
3401},{}],21:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01003402 /* global window: false */
3403 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09003404
Steven Burrows96ee21e2017-07-11 19:49:45 +01003405 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09003406
Steven Burrows96ee21e2017-07-11 19:49:45 +01003407 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09003408
Steven Burrows96ee21e2017-07-11 19:49:45 +01003409 Chart.defaults.global.animation = {
3410 duration: 1000,
3411 easing: 'easeOutQuart',
3412 onProgress: helpers.noop,
3413 onComplete: helpers.noop
3414 };
Jian Li46770fc2016-08-03 02:32:45 +09003415
Steven Burrows96ee21e2017-07-11 19:49:45 +01003416 Chart.Animation = Chart.Element.extend({
3417 chart: null, // the animation associated chart instance
3418 currentStep: 0, // the current animation step
3419 numSteps: 60, // default number of steps
3420 easing: '', // the easing to use for this animation
3421 render: null, // render function used by the animation service
Jian Li46770fc2016-08-03 02:32:45 +09003422
Steven Burrows96ee21e2017-07-11 19:49:45 +01003423 onAnimationProgress: null, // user specified callback to fire on each step of the animation
3424 onAnimationComplete: null, // user specified callback to fire when the animation finishes
3425 });
Jian Li46770fc2016-08-03 02:32:45 +09003426
Steven Burrows96ee21e2017-07-11 19:49:45 +01003427 Chart.animationService = {
3428 frameDuration: 17,
3429 animations: [],
3430 dropFrames: 0,
3431 request: null,
Jian Li46770fc2016-08-03 02:32:45 +09003432
Steven Burrows96ee21e2017-07-11 19:49:45 +01003433 /**
3434 * @param {Chart} chart - The chart to animate.
3435 * @param {Chart.Animation} animation - The animation that we will animate.
3436 * @param {Number} duration - The animation duration in ms.
3437 * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
3438 */
3439 addAnimation: function(chart, animation, duration, lazy) {
3440 var animations = this.animations;
3441 var i, ilen;
Jian Li46770fc2016-08-03 02:32:45 +09003442
Steven Burrows96ee21e2017-07-11 19:49:45 +01003443 animation.chart = chart;
Jian Li46770fc2016-08-03 02:32:45 +09003444
Steven Burrows96ee21e2017-07-11 19:49:45 +01003445 if (!lazy) {
3446 chart.animating = true;
3447 }
Jian Li46770fc2016-08-03 02:32:45 +09003448
Steven Burrows96ee21e2017-07-11 19:49:45 +01003449 for (i=0, ilen=animations.length; i < ilen; ++i) {
3450 if (animations[i].chart === chart) {
3451 animations[i] = animation;
3452 return;
3453 }
3454 }
Jian Li46770fc2016-08-03 02:32:45 +09003455
Steven Burrows96ee21e2017-07-11 19:49:45 +01003456 animations.push(animation);
Jian Li46770fc2016-08-03 02:32:45 +09003457
Steven Burrows96ee21e2017-07-11 19:49:45 +01003458 // If there are no animations queued, manually kickstart a digest, for lack of a better word
3459 if (animations.length === 1) {
3460 this.requestAnimationFrame();
3461 }
3462 },
Jian Li46770fc2016-08-03 02:32:45 +09003463
Steven Burrows96ee21e2017-07-11 19:49:45 +01003464 cancelAnimation: function(chart) {
3465 var index = helpers.findIndex(this.animations, function(animation) {
3466 return animation.chart === chart;
3467 });
Jian Li46770fc2016-08-03 02:32:45 +09003468
Steven Burrows96ee21e2017-07-11 19:49:45 +01003469 if (index !== -1) {
3470 this.animations.splice(index, 1);
3471 chart.animating = false;
3472 }
3473 },
Jian Li46770fc2016-08-03 02:32:45 +09003474
Steven Burrows96ee21e2017-07-11 19:49:45 +01003475 requestAnimationFrame: function() {
3476 var me = this;
3477 if (me.request === null) {
3478 // Skip animation frame requests until the active one is executed.
3479 // This can happen when processing mouse events, e.g. 'mousemove'
3480 // and 'mouseout' events will trigger multiple renders.
3481 me.request = helpers.requestAnimFrame.call(window, function() {
3482 me.request = null;
3483 me.startDigest();
3484 });
3485 }
3486 },
Jian Li46770fc2016-08-03 02:32:45 +09003487
Steven Burrows96ee21e2017-07-11 19:49:45 +01003488 /**
3489 * @private
3490 */
3491 startDigest: function() {
3492 var me = this;
3493 var startTime = Date.now();
3494 var framesToDrop = 0;
Jian Li46770fc2016-08-03 02:32:45 +09003495
Steven Burrows96ee21e2017-07-11 19:49:45 +01003496 if (me.dropFrames > 1) {
3497 framesToDrop = Math.floor(me.dropFrames);
3498 me.dropFrames = me.dropFrames % 1;
3499 }
Jian Li46770fc2016-08-03 02:32:45 +09003500
Steven Burrows96ee21e2017-07-11 19:49:45 +01003501 me.advance(1 + framesToDrop);
Jian Li46770fc2016-08-03 02:32:45 +09003502
Steven Burrows96ee21e2017-07-11 19:49:45 +01003503 var endTime = Date.now();
Jian Li46770fc2016-08-03 02:32:45 +09003504
Steven Burrows96ee21e2017-07-11 19:49:45 +01003505 me.dropFrames += (endTime - startTime) / me.frameDuration;
Jian Li46770fc2016-08-03 02:32:45 +09003506
Steven Burrows96ee21e2017-07-11 19:49:45 +01003507 // Do we have more stuff to animate?
3508 if (me.animations.length > 0) {
3509 me.requestAnimationFrame();
3510 }
3511 },
Jian Li46770fc2016-08-03 02:32:45 +09003512
Steven Burrows96ee21e2017-07-11 19:49:45 +01003513 /**
3514 * @private
3515 */
3516 advance: function(count) {
3517 var animations = this.animations;
3518 var animation, chart;
3519 var i = 0;
Jian Li46770fc2016-08-03 02:32:45 +09003520
Steven Burrows96ee21e2017-07-11 19:49:45 +01003521 while (i < animations.length) {
3522 animation = animations[i];
3523 chart = animation.chart;
3524
3525 animation.currentStep = (animation.currentStep || 0) + count;
3526 animation.currentStep = Math.min(animation.currentStep, animation.numSteps);
3527
3528 helpers.callback(animation.render, [chart, animation], chart);
3529 helpers.callback(animation.onAnimationProgress, [animation], chart);
3530
3531 if (animation.currentStep >= animation.numSteps) {
3532 helpers.callback(animation.onAnimationComplete, [animation], chart);
3533 chart.animating = false;
3534 animations.splice(i, 1);
3535 } else {
3536 ++i;
3537 }
3538 }
3539 }
3540 };
3541
3542 /**
3543 * Provided for backward compatibility, use Chart.Animation instead
3544 * @prop Chart.Animation#animationObject
3545 * @deprecated since version 2.6.0
3546 * @todo remove at version 3
3547 */
3548 Object.defineProperty(Chart.Animation.prototype, 'animationObject', {
3549 get: function() {
3550 return this;
3551 }
3552 });
3553
3554 /**
3555 * Provided for backward compatibility, use Chart.Animation#chart instead
3556 * @prop Chart.Animation#chartInstance
3557 * @deprecated since version 2.6.0
3558 * @todo remove at version 3
3559 */
3560 Object.defineProperty(Chart.Animation.prototype, 'chartInstance', {
3561 get: function() {
3562 return this.chart;
3563 },
3564 set: function(value) {
3565 this.chart = value;
3566 }
3567 });
3568
3569 };
3570
Jian Li46770fc2016-08-03 02:32:45 +09003571},{}],22:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01003572 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09003573
Steven Burrows96ee21e2017-07-11 19:49:45 +01003574 module.exports = function(Chart) {
3575 // Global Chart canvas helpers object for drawing items to canvas
3576 var helpers = Chart.canvasHelpers = {};
Jian Li46770fc2016-08-03 02:32:45 +09003577
Steven Burrows96ee21e2017-07-11 19:49:45 +01003578 helpers.drawPoint = function(ctx, pointStyle, radius, x, y) {
3579 var type, edgeLength, xOffset, yOffset, height, size;
Jian Li46770fc2016-08-03 02:32:45 +09003580
Steven Burrows96ee21e2017-07-11 19:49:45 +01003581 if (typeof pointStyle === 'object') {
3582 type = pointStyle.toString();
3583 if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
3584 ctx.drawImage(pointStyle, x - pointStyle.width / 2, y - pointStyle.height / 2, pointStyle.width, pointStyle.height);
3585 return;
3586 }
3587 }
Jian Li46770fc2016-08-03 02:32:45 +09003588
Steven Burrows96ee21e2017-07-11 19:49:45 +01003589 if (isNaN(radius) || radius <= 0) {
3590 return;
3591 }
Jian Li46770fc2016-08-03 02:32:45 +09003592
Steven Burrows96ee21e2017-07-11 19:49:45 +01003593 switch (pointStyle) {
3594 // Default includes circle
3595 default:
3596 ctx.beginPath();
3597 ctx.arc(x, y, radius, 0, Math.PI * 2);
3598 ctx.closePath();
3599 ctx.fill();
3600 break;
3601 case 'triangle':
3602 ctx.beginPath();
3603 edgeLength = 3 * radius / Math.sqrt(3);
3604 height = edgeLength * Math.sqrt(3) / 2;
3605 ctx.moveTo(x - edgeLength / 2, y + height / 3);
3606 ctx.lineTo(x + edgeLength / 2, y + height / 3);
3607 ctx.lineTo(x, y - 2 * height / 3);
3608 ctx.closePath();
3609 ctx.fill();
3610 break;
3611 case 'rect':
3612 size = 1 / Math.SQRT2 * radius;
3613 ctx.beginPath();
3614 ctx.fillRect(x - size, y - size, 2 * size, 2 * size);
3615 ctx.strokeRect(x - size, y - size, 2 * size, 2 * size);
3616 break;
3617 case 'rectRounded':
3618 var offset = radius / Math.SQRT2;
3619 var leftX = x - offset;
3620 var topY = y - offset;
3621 var sideSize = Math.SQRT2 * radius;
3622 Chart.helpers.drawRoundedRectangle(ctx, leftX, topY, sideSize, sideSize, radius / 2);
3623 ctx.fill();
3624 break;
3625 case 'rectRot':
3626 size = 1 / Math.SQRT2 * radius;
3627 ctx.beginPath();
3628 ctx.moveTo(x - size, y);
3629 ctx.lineTo(x, y + size);
3630 ctx.lineTo(x + size, y);
3631 ctx.lineTo(x, y - size);
3632 ctx.closePath();
3633 ctx.fill();
3634 break;
3635 case 'cross':
3636 ctx.beginPath();
3637 ctx.moveTo(x, y + radius);
3638 ctx.lineTo(x, y - radius);
3639 ctx.moveTo(x - radius, y);
3640 ctx.lineTo(x + radius, y);
3641 ctx.closePath();
3642 break;
3643 case 'crossRot':
3644 ctx.beginPath();
3645 xOffset = Math.cos(Math.PI / 4) * radius;
3646 yOffset = Math.sin(Math.PI / 4) * radius;
3647 ctx.moveTo(x - xOffset, y - yOffset);
3648 ctx.lineTo(x + xOffset, y + yOffset);
3649 ctx.moveTo(x - xOffset, y + yOffset);
3650 ctx.lineTo(x + xOffset, y - yOffset);
3651 ctx.closePath();
3652 break;
3653 case 'star':
3654 ctx.beginPath();
3655 ctx.moveTo(x, y + radius);
3656 ctx.lineTo(x, y - radius);
3657 ctx.moveTo(x - radius, y);
3658 ctx.lineTo(x + radius, y);
3659 xOffset = Math.cos(Math.PI / 4) * radius;
3660 yOffset = Math.sin(Math.PI / 4) * radius;
3661 ctx.moveTo(x - xOffset, y - yOffset);
3662 ctx.lineTo(x + xOffset, y + yOffset);
3663 ctx.moveTo(x - xOffset, y + yOffset);
3664 ctx.lineTo(x + xOffset, y - yOffset);
3665 ctx.closePath();
3666 break;
3667 case 'line':
3668 ctx.beginPath();
3669 ctx.moveTo(x - radius, y);
3670 ctx.lineTo(x + radius, y);
3671 ctx.closePath();
3672 break;
3673 case 'dash':
3674 ctx.beginPath();
3675 ctx.moveTo(x, y);
3676 ctx.lineTo(x + radius, y);
3677 ctx.closePath();
3678 break;
3679 }
Jian Li46770fc2016-08-03 02:32:45 +09003680
Steven Burrows96ee21e2017-07-11 19:49:45 +01003681 ctx.stroke();
3682 };
3683
3684 helpers.clipArea = function(ctx, clipArea) {
3685 ctx.save();
3686 ctx.beginPath();
3687 ctx.rect(clipArea.left, clipArea.top, clipArea.right - clipArea.left, clipArea.bottom - clipArea.top);
3688 ctx.clip();
3689 };
3690
3691 helpers.unclipArea = function(ctx) {
3692 ctx.restore();
3693 };
3694
3695 helpers.lineTo = function(ctx, previous, target, flip) {
3696 if (target.steppedLine) {
3697 if (target.steppedLine === 'after') {
3698 ctx.lineTo(previous.x, target.y);
3699 } else {
3700 ctx.lineTo(target.x, previous.y);
3701 }
3702 ctx.lineTo(target.x, target.y);
3703 return;
3704 }
3705
3706 if (!target.tension) {
3707 ctx.lineTo(target.x, target.y);
3708 return;
3709 }
3710
3711 ctx.bezierCurveTo(
3712 flip? previous.controlPointPreviousX : previous.controlPointNextX,
3713 flip? previous.controlPointPreviousY : previous.controlPointNextY,
3714 flip? target.controlPointNextX : target.controlPointPreviousX,
3715 flip? target.controlPointNextY : target.controlPointPreviousY,
3716 target.x,
3717 target.y);
3718 };
3719
3720 Chart.helpers.canvas = helpers;
3721 };
3722
Jian Li46770fc2016-08-03 02:32:45 +09003723},{}],23:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01003724 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09003725
Steven Burrows96ee21e2017-07-11 19:49:45 +01003726 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09003727
Steven Burrows96ee21e2017-07-11 19:49:45 +01003728 var helpers = Chart.helpers;
3729 var plugins = Chart.plugins;
3730 var platform = Chart.platform;
Jian Li46770fc2016-08-03 02:32:45 +09003731
Steven Burrows96ee21e2017-07-11 19:49:45 +01003732 // Create a dictionary of chart types, to allow for extension of existing types
3733 Chart.types = {};
Jian Li46770fc2016-08-03 02:32:45 +09003734
Steven Burrows96ee21e2017-07-11 19:49:45 +01003735 // Store a reference to each instance - allowing us to globally resize chart instances on window resize.
3736 // Destroy method on the chart will remove the instance of the chart from this reference.
3737 Chart.instances = {};
Jian Li46770fc2016-08-03 02:32:45 +09003738
Steven Burrows96ee21e2017-07-11 19:49:45 +01003739 // Controllers available for dataset visualization eg. bar, line, slice, etc.
3740 Chart.controllers = {};
Jian Li46770fc2016-08-03 02:32:45 +09003741
Steven Burrows96ee21e2017-07-11 19:49:45 +01003742 /**
3743 * Initializes the given config with global and chart default values.
3744 */
3745 function initConfig(config) {
3746 config = config || {};
Jian Li46770fc2016-08-03 02:32:45 +09003747
Steven Burrows96ee21e2017-07-11 19:49:45 +01003748 // Do NOT use configMerge() for the data object because this method merges arrays
3749 // and so would change references to labels and datasets, preventing data updates.
3750 var data = config.data = config.data || {};
3751 data.datasets = data.datasets || [];
3752 data.labels = data.labels || [];
Jian Li46770fc2016-08-03 02:32:45 +09003753
Steven Burrows96ee21e2017-07-11 19:49:45 +01003754 config.options = helpers.configMerge(
3755 Chart.defaults.global,
3756 Chart.defaults[config.type],
3757 config.options || {});
Jian Li46770fc2016-08-03 02:32:45 +09003758
Steven Burrows96ee21e2017-07-11 19:49:45 +01003759 return config;
3760 }
Jian Li46770fc2016-08-03 02:32:45 +09003761
Steven Burrows96ee21e2017-07-11 19:49:45 +01003762 /**
3763 * Updates the config of the chart
3764 * @param chart {Chart} chart to update the options for
3765 */
3766 function updateConfig(chart) {
3767 var newOptions = chart.options;
Jian Li46770fc2016-08-03 02:32:45 +09003768
Steven Burrows96ee21e2017-07-11 19:49:45 +01003769 // Update Scale(s) with options
3770 if (newOptions.scale) {
3771 chart.scale.options = newOptions.scale;
3772 } else if (newOptions.scales) {
3773 newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) {
3774 chart.scales[scaleOptions.id].options = scaleOptions;
3775 });
3776 }
Jian Li46770fc2016-08-03 02:32:45 +09003777
Steven Burrows96ee21e2017-07-11 19:49:45 +01003778 // Tooltip
3779 chart.tooltip._options = newOptions.tooltips;
3780 }
Jian Li46770fc2016-08-03 02:32:45 +09003781
Steven Burrows96ee21e2017-07-11 19:49:45 +01003782 function positionIsHorizontal(position) {
3783 return position === 'top' || position === 'bottom';
3784 }
Jian Li46770fc2016-08-03 02:32:45 +09003785
Steven Burrows96ee21e2017-07-11 19:49:45 +01003786 helpers.extend(Chart.prototype, /** @lends Chart */ {
3787 /**
3788 * @private
3789 */
3790 construct: function(item, config) {
3791 var me = this;
Jian Li46770fc2016-08-03 02:32:45 +09003792
Steven Burrows96ee21e2017-07-11 19:49:45 +01003793 config = initConfig(config);
Jian Li46770fc2016-08-03 02:32:45 +09003794
Steven Burrows96ee21e2017-07-11 19:49:45 +01003795 var context = platform.acquireContext(item, config);
3796 var canvas = context && context.canvas;
3797 var height = canvas && canvas.height;
3798 var width = canvas && canvas.width;
Jian Li46770fc2016-08-03 02:32:45 +09003799
Steven Burrows96ee21e2017-07-11 19:49:45 +01003800 me.id = helpers.uid();
3801 me.ctx = context;
3802 me.canvas = canvas;
3803 me.config = config;
3804 me.width = width;
3805 me.height = height;
3806 me.aspectRatio = height? width / height : null;
3807 me.options = config.options;
3808 me._bufferedRender = false;
Jian Li46770fc2016-08-03 02:32:45 +09003809
Steven Burrows96ee21e2017-07-11 19:49:45 +01003810 /**
3811 * Provided for backward compatibility, Chart and Chart.Controller have been merged,
3812 * the "instance" still need to be defined since it might be called from plugins.
3813 * @prop Chart#chart
3814 * @deprecated since version 2.6.0
3815 * @todo remove at version 3
3816 * @private
3817 */
3818 me.chart = me;
3819 me.controller = me; // chart.chart.controller #inception
Jian Li46770fc2016-08-03 02:32:45 +09003820
Steven Burrows96ee21e2017-07-11 19:49:45 +01003821 // Add the chart instance to the global namespace
3822 Chart.instances[me.id] = me;
Jian Li46770fc2016-08-03 02:32:45 +09003823
Steven Burrows96ee21e2017-07-11 19:49:45 +01003824 // Define alias to the config data: `chart.data === chart.config.data`
3825 Object.defineProperty(me, 'data', {
3826 get: function() {
3827 return me.config.data;
3828 },
3829 set: function(value) {
3830 me.config.data = value;
3831 }
3832 });
Jian Li46770fc2016-08-03 02:32:45 +09003833
Steven Burrows96ee21e2017-07-11 19:49:45 +01003834 if (!context || !canvas) {
3835 // The given item is not a compatible context2d element, let's return before finalizing
3836 // the chart initialization but after setting basic chart / controller properties that
3837 // can help to figure out that the chart is not valid (e.g chart.canvas !== null);
3838 // https://github.com/chartjs/Chart.js/issues/2807
3839 console.error("Failed to create chart: can't acquire context from the given item");
3840 return;
3841 }
Jian Li46770fc2016-08-03 02:32:45 +09003842
Steven Burrows96ee21e2017-07-11 19:49:45 +01003843 me.initialize();
3844 me.update();
3845 },
Jian Li46770fc2016-08-03 02:32:45 +09003846
Steven Burrows96ee21e2017-07-11 19:49:45 +01003847 /**
3848 * @private
3849 */
3850 initialize: function() {
3851 var me = this;
Jian Li46770fc2016-08-03 02:32:45 +09003852
Steven Burrows96ee21e2017-07-11 19:49:45 +01003853 // Before init plugin notification
3854 plugins.notify(me, 'beforeInit');
Jian Li46770fc2016-08-03 02:32:45 +09003855
Steven Burrows96ee21e2017-07-11 19:49:45 +01003856 helpers.retinaScale(me);
Jian Li46770fc2016-08-03 02:32:45 +09003857
Steven Burrows96ee21e2017-07-11 19:49:45 +01003858 me.bindEvents();
Jian Li46770fc2016-08-03 02:32:45 +09003859
Steven Burrows96ee21e2017-07-11 19:49:45 +01003860 if (me.options.responsive) {
3861 // Initial resize before chart draws (must be silent to preserve initial animations).
3862 me.resize(true);
3863 }
Jian Li46770fc2016-08-03 02:32:45 +09003864
Steven Burrows96ee21e2017-07-11 19:49:45 +01003865 // Make sure scales have IDs and are built before we build any controllers.
3866 me.ensureScalesHaveIDs();
3867 me.buildScales();
3868 me.initToolTip();
Jian Li46770fc2016-08-03 02:32:45 +09003869
Steven Burrows96ee21e2017-07-11 19:49:45 +01003870 // After init plugin notification
3871 plugins.notify(me, 'afterInit');
Jian Li46770fc2016-08-03 02:32:45 +09003872
Steven Burrows96ee21e2017-07-11 19:49:45 +01003873 return me;
3874 },
Jian Li46770fc2016-08-03 02:32:45 +09003875
Steven Burrows96ee21e2017-07-11 19:49:45 +01003876 clear: function() {
3877 helpers.clear(this);
3878 return this;
3879 },
Jian Li46770fc2016-08-03 02:32:45 +09003880
Steven Burrows96ee21e2017-07-11 19:49:45 +01003881 stop: function() {
3882 // Stops any current animation loop occurring
3883 Chart.animationService.cancelAnimation(this);
3884 return this;
3885 },
Jian Li46770fc2016-08-03 02:32:45 +09003886
Steven Burrows96ee21e2017-07-11 19:49:45 +01003887 resize: function(silent) {
3888 var me = this;
3889 var options = me.options;
3890 var canvas = me.canvas;
3891 var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;
Jian Li46770fc2016-08-03 02:32:45 +09003892
Steven Burrows96ee21e2017-07-11 19:49:45 +01003893 // the canvas render width and height will be casted to integers so make sure that
3894 // the canvas display style uses the same integer values to avoid blurring effect.
3895 var newWidth = Math.floor(helpers.getMaximumWidth(canvas));
3896 var newHeight = Math.floor(aspectRatio? newWidth / aspectRatio : helpers.getMaximumHeight(canvas));
Jian Li46770fc2016-08-03 02:32:45 +09003897
Steven Burrows96ee21e2017-07-11 19:49:45 +01003898 if (me.width === newWidth && me.height === newHeight) {
3899 return;
3900 }
Jian Li46770fc2016-08-03 02:32:45 +09003901
Steven Burrows96ee21e2017-07-11 19:49:45 +01003902 canvas.width = me.width = newWidth;
3903 canvas.height = me.height = newHeight;
3904 canvas.style.width = newWidth + 'px';
3905 canvas.style.height = newHeight + 'px';
Jian Li46770fc2016-08-03 02:32:45 +09003906
Steven Burrows96ee21e2017-07-11 19:49:45 +01003907 helpers.retinaScale(me);
Jian Li46770fc2016-08-03 02:32:45 +09003908
Steven Burrows96ee21e2017-07-11 19:49:45 +01003909 if (!silent) {
3910 // Notify any plugins about the resize
3911 var newSize = {width: newWidth, height: newHeight};
3912 plugins.notify(me, 'resize', [newSize]);
Jian Li46770fc2016-08-03 02:32:45 +09003913
Steven Burrows96ee21e2017-07-11 19:49:45 +01003914 // Notify of resize
3915 if (me.options.onResize) {
3916 me.options.onResize(me, newSize);
3917 }
Jian Li46770fc2016-08-03 02:32:45 +09003918
Steven Burrows96ee21e2017-07-11 19:49:45 +01003919 me.stop();
3920 me.update(me.options.responsiveAnimationDuration);
3921 }
3922 },
Jian Li46770fc2016-08-03 02:32:45 +09003923
Steven Burrows96ee21e2017-07-11 19:49:45 +01003924 ensureScalesHaveIDs: function() {
3925 var options = this.options;
3926 var scalesOptions = options.scales || {};
3927 var scaleOptions = options.scale;
Jian Li46770fc2016-08-03 02:32:45 +09003928
Steven Burrows96ee21e2017-07-11 19:49:45 +01003929 helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
3930 xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
3931 });
Jian Li46770fc2016-08-03 02:32:45 +09003932
Steven Burrows96ee21e2017-07-11 19:49:45 +01003933 helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
3934 yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
3935 });
Jian Li46770fc2016-08-03 02:32:45 +09003936
Steven Burrows96ee21e2017-07-11 19:49:45 +01003937 if (scaleOptions) {
3938 scaleOptions.id = scaleOptions.id || 'scale';
3939 }
3940 },
Jian Li46770fc2016-08-03 02:32:45 +09003941
Steven Burrows96ee21e2017-07-11 19:49:45 +01003942 /**
3943 * Builds a map of scale ID to scale object for future lookup.
3944 */
3945 buildScales: function() {
3946 var me = this;
3947 var options = me.options;
3948 var scales = me.scales = {};
3949 var items = [];
Jian Li46770fc2016-08-03 02:32:45 +09003950
Steven Burrows96ee21e2017-07-11 19:49:45 +01003951 if (options.scales) {
3952 items = items.concat(
3953 (options.scales.xAxes || []).map(function(xAxisOptions) {
3954 return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};
3955 }),
3956 (options.scales.yAxes || []).map(function(yAxisOptions) {
3957 return {options: yAxisOptions, dtype: 'linear', dposition: 'left'};
3958 })
3959 );
3960 }
Jian Li46770fc2016-08-03 02:32:45 +09003961
Steven Burrows96ee21e2017-07-11 19:49:45 +01003962 if (options.scale) {
3963 items.push({
3964 options: options.scale,
3965 dtype: 'radialLinear',
3966 isDefault: true,
3967 dposition: 'chartArea'
3968 });
3969 }
Jian Li46770fc2016-08-03 02:32:45 +09003970
Steven Burrows96ee21e2017-07-11 19:49:45 +01003971 helpers.each(items, function(item) {
3972 var scaleOptions = item.options;
3973 var scaleType = helpers.getValueOrDefault(scaleOptions.type, item.dtype);
3974 var scaleClass = Chart.scaleService.getScaleConstructor(scaleType);
3975 if (!scaleClass) {
3976 return;
3977 }
Jian Li46770fc2016-08-03 02:32:45 +09003978
Steven Burrows96ee21e2017-07-11 19:49:45 +01003979 if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
3980 scaleOptions.position = item.dposition;
3981 }
Jian Li46770fc2016-08-03 02:32:45 +09003982
Steven Burrows96ee21e2017-07-11 19:49:45 +01003983 var scale = new scaleClass({
3984 id: scaleOptions.id,
3985 options: scaleOptions,
3986 ctx: me.ctx,
3987 chart: me
3988 });
Jian Li46770fc2016-08-03 02:32:45 +09003989
Steven Burrows96ee21e2017-07-11 19:49:45 +01003990 scales[scale.id] = scale;
Jian Li46770fc2016-08-03 02:32:45 +09003991
Steven Burrows96ee21e2017-07-11 19:49:45 +01003992 // TODO(SB): I think we should be able to remove this custom case (options.scale)
3993 // and consider it as a regular scale part of the "scales"" map only! This would
3994 // make the logic easier and remove some useless? custom code.
3995 if (item.isDefault) {
3996 me.scale = scale;
3997 }
3998 });
Jian Li46770fc2016-08-03 02:32:45 +09003999
Steven Burrows96ee21e2017-07-11 19:49:45 +01004000 Chart.scaleService.addScalesToLayout(this);
4001 },
Jian Li46770fc2016-08-03 02:32:45 +09004002
Steven Burrows96ee21e2017-07-11 19:49:45 +01004003 buildOrUpdateControllers: function() {
4004 var me = this;
4005 var types = [];
4006 var newControllers = [];
Jian Li46770fc2016-08-03 02:32:45 +09004007
Steven Burrows96ee21e2017-07-11 19:49:45 +01004008 helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4009 var meta = me.getDatasetMeta(datasetIndex);
4010 if (!meta.type) {
4011 meta.type = dataset.type || me.config.type;
4012 }
Jian Li46770fc2016-08-03 02:32:45 +09004013
Steven Burrows96ee21e2017-07-11 19:49:45 +01004014 types.push(meta.type);
Jian Li46770fc2016-08-03 02:32:45 +09004015
Steven Burrows96ee21e2017-07-11 19:49:45 +01004016 if (meta.controller) {
4017 meta.controller.updateIndex(datasetIndex);
4018 } else {
4019 var ControllerClass = Chart.controllers[meta.type];
4020 if (ControllerClass === undefined) {
4021 throw new Error('"' + meta.type + '" is not a chart type.');
4022 }
Jian Li46770fc2016-08-03 02:32:45 +09004023
Steven Burrows96ee21e2017-07-11 19:49:45 +01004024 meta.controller = new ControllerClass(me, datasetIndex);
4025 newControllers.push(meta.controller);
4026 }
4027 }, me);
Jian Li46770fc2016-08-03 02:32:45 +09004028
Steven Burrows96ee21e2017-07-11 19:49:45 +01004029 if (types.length > 1) {
4030 for (var i = 1; i < types.length; i++) {
4031 if (types[i] !== types[i - 1]) {
4032 me.isCombo = true;
4033 break;
4034 }
4035 }
4036 }
Jian Li46770fc2016-08-03 02:32:45 +09004037
Steven Burrows96ee21e2017-07-11 19:49:45 +01004038 return newControllers;
4039 },
4040
4041 /**
4042 * Reset the elements of all datasets
4043 * @private
4044 */
4045 resetElements: function() {
4046 var me = this;
4047 helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4048 me.getDatasetMeta(datasetIndex).controller.reset();
4049 }, me);
4050 },
4051
4052 /**
4053 * Resets the chart back to it's state before the initial animation
4054 */
4055 reset: function() {
4056 this.resetElements();
4057 this.tooltip.initialize();
4058 },
4059
4060 update: function(animationDuration, lazy) {
4061 var me = this;
4062
4063 updateConfig(me);
4064
4065 if (plugins.notify(me, 'beforeUpdate') === false) {
4066 return;
4067 }
4068
4069 // In case the entire data object changed
4070 me.tooltip._data = me.data;
4071
4072 // Make sure dataset controllers are updated and new controllers are reset
4073 var newControllers = me.buildOrUpdateControllers();
4074
4075 // Make sure all dataset controllers have correct meta data counts
4076 helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4077 me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
4078 }, me);
4079
4080 me.updateLayout();
4081
4082 // Can only reset the new controllers after the scales have been updated
4083 helpers.each(newControllers, function(controller) {
4084 controller.reset();
4085 });
4086
4087 me.updateDatasets();
4088
4089 // Do this before render so that any plugins that need final scale updates can use it
4090 plugins.notify(me, 'afterUpdate');
4091
4092 if (me._bufferedRender) {
4093 me._bufferedRequest = {
4094 lazy: lazy,
4095 duration: animationDuration
4096 };
4097 } else {
4098 me.render(animationDuration, lazy);
4099 }
4100 },
4101
4102 /**
4103 * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
4104 * hook, in which case, plugins will not be called on `afterLayout`.
4105 * @private
4106 */
4107 updateLayout: function() {
4108 var me = this;
4109
4110 if (plugins.notify(me, 'beforeLayout') === false) {
4111 return;
4112 }
4113
4114 Chart.layoutService.update(this, this.width, this.height);
4115
4116 /**
4117 * Provided for backward compatibility, use `afterLayout` instead.
4118 * @method IPlugin#afterScaleUpdate
4119 * @deprecated since version 2.5.0
4120 * @todo remove at version 3
4121 * @private
4122 */
4123 plugins.notify(me, 'afterScaleUpdate');
4124 plugins.notify(me, 'afterLayout');
4125 },
4126
4127 /**
4128 * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
4129 * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
4130 * @private
4131 */
4132 updateDatasets: function() {
4133 var me = this;
4134
4135 if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
4136 return;
4137 }
4138
4139 for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
4140 me.updateDataset(i);
4141 }
4142
4143 plugins.notify(me, 'afterDatasetsUpdate');
4144 },
4145
4146 /**
4147 * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
4148 * hook, in which case, plugins will not be called on `afterDatasetUpdate`.
4149 * @private
4150 */
4151 updateDataset: function(index) {
4152 var me = this;
4153 var meta = me.getDatasetMeta(index);
4154 var args = {
4155 meta: meta,
4156 index: index
4157 };
4158
4159 if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
4160 return;
4161 }
4162
4163 meta.controller.update();
4164
4165 plugins.notify(me, 'afterDatasetUpdate', [args]);
4166 },
4167
4168 render: function(duration, lazy) {
4169 var me = this;
4170
4171 if (plugins.notify(me, 'beforeRender') === false) {
4172 return;
4173 }
4174
4175 var animationOptions = me.options.animation;
4176 var onComplete = function(animation) {
4177 plugins.notify(me, 'afterRender');
4178 helpers.callback(animationOptions && animationOptions.onComplete, [animation], me);
4179 };
4180
4181 if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {
4182 var animation = new Chart.Animation({
4183 numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps
4184 easing: animationOptions.easing,
4185
4186 render: function(chart, animationObject) {
4187 var easingFunction = helpers.easingEffects[animationObject.easing];
4188 var currentStep = animationObject.currentStep;
4189 var stepDecimal = currentStep / animationObject.numSteps;
4190
4191 chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
4192 },
4193
4194 onAnimationProgress: animationOptions.onProgress,
4195 onAnimationComplete: onComplete
4196 });
4197
4198 Chart.animationService.addAnimation(me, animation, duration, lazy);
4199 } else {
4200 me.draw();
4201
4202 // See https://github.com/chartjs/Chart.js/issues/3781
4203 onComplete(new Chart.Animation({numSteps: 0, chart: me}));
4204 }
4205
4206 return me;
4207 },
4208
4209 draw: function(easingValue) {
4210 var me = this;
4211
4212 me.clear();
4213
4214 if (easingValue === undefined || easingValue === null) {
4215 easingValue = 1;
4216 }
Jian Li46770fc2016-08-03 02:32:45 +09004217
Steven Burrows96ee21e2017-07-11 19:49:45 +01004218 me.transition(easingValue);
Jian Li46770fc2016-08-03 02:32:45 +09004219
Steven Burrows96ee21e2017-07-11 19:49:45 +01004220 if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
4221 return;
4222 }
Jian Li46770fc2016-08-03 02:32:45 +09004223
Steven Burrows96ee21e2017-07-11 19:49:45 +01004224 // Draw all the scales
4225 helpers.each(me.boxes, function(box) {
4226 box.draw(me.chartArea);
4227 }, me);
Jian Li46770fc2016-08-03 02:32:45 +09004228
Steven Burrows96ee21e2017-07-11 19:49:45 +01004229 if (me.scale) {
4230 me.scale.draw();
4231 }
Jian Li46770fc2016-08-03 02:32:45 +09004232
Steven Burrows96ee21e2017-07-11 19:49:45 +01004233 me.drawDatasets(easingValue);
Jian Li46770fc2016-08-03 02:32:45 +09004234
Steven Burrows96ee21e2017-07-11 19:49:45 +01004235 // Finally draw the tooltip
4236 me.tooltip.draw();
Jian Li46770fc2016-08-03 02:32:45 +09004237
Steven Burrows96ee21e2017-07-11 19:49:45 +01004238 plugins.notify(me, 'afterDraw', [easingValue]);
4239 },
Jian Li46770fc2016-08-03 02:32:45 +09004240
Steven Burrows96ee21e2017-07-11 19:49:45 +01004241 /**
4242 * @private
4243 */
4244 transition: function(easingValue) {
4245 var me = this;
Jian Li46770fc2016-08-03 02:32:45 +09004246
Steven Burrows96ee21e2017-07-11 19:49:45 +01004247 for (var i=0, ilen=(me.data.datasets || []).length; i<ilen; ++i) {
4248 if (me.isDatasetVisible(i)) {
4249 me.getDatasetMeta(i).controller.transition(easingValue);
4250 }
4251 }
Jian Li46770fc2016-08-03 02:32:45 +09004252
Steven Burrows96ee21e2017-07-11 19:49:45 +01004253 me.tooltip.transition(easingValue);
4254 },
Jian Li46770fc2016-08-03 02:32:45 +09004255
Steven Burrows96ee21e2017-07-11 19:49:45 +01004256 /**
4257 * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
4258 * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
4259 * @private
4260 */
4261 drawDatasets: function(easingValue) {
4262 var me = this;
Jian Li46770fc2016-08-03 02:32:45 +09004263
Steven Burrows96ee21e2017-07-11 19:49:45 +01004264 if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
4265 return;
4266 }
Jian Li46770fc2016-08-03 02:32:45 +09004267
Steven Burrows96ee21e2017-07-11 19:49:45 +01004268 // Draw datasets reversed to support proper line stacking
4269 for (var i=(me.data.datasets || []).length - 1; i >= 0; --i) {
4270 if (me.isDatasetVisible(i)) {
4271 me.drawDataset(i, easingValue);
4272 }
4273 }
Jian Li46770fc2016-08-03 02:32:45 +09004274
Steven Burrows96ee21e2017-07-11 19:49:45 +01004275 plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
4276 },
Jian Li46770fc2016-08-03 02:32:45 +09004277
Steven Burrows96ee21e2017-07-11 19:49:45 +01004278 /**
4279 * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
4280 * hook, in which case, plugins will not be called on `afterDatasetDraw`.
4281 * @private
4282 */
4283 drawDataset: function(index, easingValue) {
4284 var me = this;
4285 var meta = me.getDatasetMeta(index);
4286 var args = {
4287 meta: meta,
4288 index: index,
4289 easingValue: easingValue
4290 };
Jian Li46770fc2016-08-03 02:32:45 +09004291
Steven Burrows96ee21e2017-07-11 19:49:45 +01004292 if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
4293 return;
4294 }
Jian Li46770fc2016-08-03 02:32:45 +09004295
Steven Burrows96ee21e2017-07-11 19:49:45 +01004296 meta.controller.draw(easingValue);
Jian Li46770fc2016-08-03 02:32:45 +09004297
Steven Burrows96ee21e2017-07-11 19:49:45 +01004298 plugins.notify(me, 'afterDatasetDraw', [args]);
4299 },
Jian Li46770fc2016-08-03 02:32:45 +09004300
Steven Burrows96ee21e2017-07-11 19:49:45 +01004301 // Get the single element that was clicked on
4302 // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
4303 getElementAtEvent: function(e) {
4304 return Chart.Interaction.modes.single(this, e);
4305 },
Jian Li46770fc2016-08-03 02:32:45 +09004306
Steven Burrows96ee21e2017-07-11 19:49:45 +01004307 getElementsAtEvent: function(e) {
4308 return Chart.Interaction.modes.label(this, e, {intersect: true});
4309 },
Jian Li46770fc2016-08-03 02:32:45 +09004310
Steven Burrows96ee21e2017-07-11 19:49:45 +01004311 getElementsAtXAxis: function(e) {
4312 return Chart.Interaction.modes['x-axis'](this, e, {intersect: true});
4313 },
Jian Li46770fc2016-08-03 02:32:45 +09004314
Steven Burrows96ee21e2017-07-11 19:49:45 +01004315 getElementsAtEventForMode: function(e, mode, options) {
4316 var method = Chart.Interaction.modes[mode];
4317 if (typeof method === 'function') {
4318 return method(this, e, options);
4319 }
Jian Li46770fc2016-08-03 02:32:45 +09004320
Steven Burrows96ee21e2017-07-11 19:49:45 +01004321 return [];
4322 },
Jian Li46770fc2016-08-03 02:32:45 +09004323
Steven Burrows96ee21e2017-07-11 19:49:45 +01004324 getDatasetAtEvent: function(e) {
4325 return Chart.Interaction.modes.dataset(this, e, {intersect: true});
4326 },
Jian Li46770fc2016-08-03 02:32:45 +09004327
Steven Burrows96ee21e2017-07-11 19:49:45 +01004328 getDatasetMeta: function(datasetIndex) {
4329 var me = this;
4330 var dataset = me.data.datasets[datasetIndex];
4331 if (!dataset._meta) {
4332 dataset._meta = {};
4333 }
Jian Li46770fc2016-08-03 02:32:45 +09004334
Steven Burrows96ee21e2017-07-11 19:49:45 +01004335 var meta = dataset._meta[me.id];
4336 if (!meta) {
4337 meta = dataset._meta[me.id] = {
4338 type: null,
4339 data: [],
4340 dataset: null,
4341 controller: null,
4342 hidden: null, // See isDatasetVisible() comment
4343 xAxisID: null,
4344 yAxisID: null
4345 };
4346 }
Jian Li46770fc2016-08-03 02:32:45 +09004347
Steven Burrows96ee21e2017-07-11 19:49:45 +01004348 return meta;
4349 },
Jian Li46770fc2016-08-03 02:32:45 +09004350
Steven Burrows96ee21e2017-07-11 19:49:45 +01004351 getVisibleDatasetCount: function() {
4352 var count = 0;
4353 for (var i = 0, ilen = this.data.datasets.length; i<ilen; ++i) {
4354 if (this.isDatasetVisible(i)) {
4355 count++;
4356 }
4357 }
4358 return count;
4359 },
Jian Li46770fc2016-08-03 02:32:45 +09004360
Steven Burrows96ee21e2017-07-11 19:49:45 +01004361 isDatasetVisible: function(datasetIndex) {
4362 var meta = this.getDatasetMeta(datasetIndex);
Jian Li46770fc2016-08-03 02:32:45 +09004363
Steven Burrows96ee21e2017-07-11 19:49:45 +01004364 // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
4365 // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
4366 return typeof meta.hidden === 'boolean'? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
4367 },
Jian Li46770fc2016-08-03 02:32:45 +09004368
Steven Burrows96ee21e2017-07-11 19:49:45 +01004369 generateLegend: function() {
4370 return this.options.legendCallback(this);
4371 },
Jian Li46770fc2016-08-03 02:32:45 +09004372
Steven Burrows96ee21e2017-07-11 19:49:45 +01004373 destroy: function() {
4374 var me = this;
4375 var canvas = me.canvas;
4376 var meta, i, ilen;
Jian Li46770fc2016-08-03 02:32:45 +09004377
Steven Burrows96ee21e2017-07-11 19:49:45 +01004378 me.stop();
Jian Li46770fc2016-08-03 02:32:45 +09004379
Steven Burrows96ee21e2017-07-11 19:49:45 +01004380 // dataset controllers need to cleanup associated data
4381 for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
4382 meta = me.getDatasetMeta(i);
4383 if (meta.controller) {
4384 meta.controller.destroy();
4385 meta.controller = null;
4386 }
4387 }
Jian Li46770fc2016-08-03 02:32:45 +09004388
Steven Burrows96ee21e2017-07-11 19:49:45 +01004389 if (canvas) {
4390 me.unbindEvents();
4391 helpers.clear(me);
4392 platform.releaseContext(me.ctx);
4393 me.canvas = null;
4394 me.ctx = null;
4395 }
Jian Li46770fc2016-08-03 02:32:45 +09004396
Steven Burrows96ee21e2017-07-11 19:49:45 +01004397 plugins.notify(me, 'destroy');
Jian Li46770fc2016-08-03 02:32:45 +09004398
Steven Burrows96ee21e2017-07-11 19:49:45 +01004399 delete Chart.instances[me.id];
4400 },
Jian Li46770fc2016-08-03 02:32:45 +09004401
Steven Burrows96ee21e2017-07-11 19:49:45 +01004402 toBase64Image: function() {
4403 return this.canvas.toDataURL.apply(this.canvas, arguments);
4404 },
Jian Li46770fc2016-08-03 02:32:45 +09004405
Steven Burrows96ee21e2017-07-11 19:49:45 +01004406 initToolTip: function() {
4407 var me = this;
4408 me.tooltip = new Chart.Tooltip({
4409 _chart: me,
4410 _chartInstance: me, // deprecated, backward compatibility
4411 _data: me.data,
4412 _options: me.options.tooltips
4413 }, me);
4414 me.tooltip.initialize();
4415 },
Jian Li46770fc2016-08-03 02:32:45 +09004416
Steven Burrows96ee21e2017-07-11 19:49:45 +01004417 /**
4418 * @private
4419 */
4420 bindEvents: function() {
4421 var me = this;
4422 var listeners = me._listeners = {};
4423 var listener = function() {
4424 me.eventHandler.apply(me, arguments);
4425 };
Jian Li46770fc2016-08-03 02:32:45 +09004426
Steven Burrows96ee21e2017-07-11 19:49:45 +01004427 helpers.each(me.options.events, function(type) {
4428 platform.addEventListener(me, type, listener);
4429 listeners[type] = listener;
4430 });
Jian Li46770fc2016-08-03 02:32:45 +09004431
Steven Burrows96ee21e2017-07-11 19:49:45 +01004432 // Responsiveness is currently based on the use of an iframe, however this method causes
4433 // performance issues and could be troublesome when used with ad blockers. So make sure
4434 // that the user is still able to create a chart without iframe when responsive is false.
4435 // See https://github.com/chartjs/Chart.js/issues/2210
4436 if (me.options.responsive) {
4437 listener = function() {
4438 me.resize();
4439 };
Jian Li46770fc2016-08-03 02:32:45 +09004440
Steven Burrows96ee21e2017-07-11 19:49:45 +01004441 platform.addEventListener(me, 'resize', listener);
4442 listeners.resize = listener;
4443 }
4444 },
Jian Li46770fc2016-08-03 02:32:45 +09004445
Steven Burrows96ee21e2017-07-11 19:49:45 +01004446 /**
4447 * @private
4448 */
4449 unbindEvents: function() {
4450 var me = this;
4451 var listeners = me._listeners;
4452 if (!listeners) {
4453 return;
4454 }
Jian Li46770fc2016-08-03 02:32:45 +09004455
Steven Burrows96ee21e2017-07-11 19:49:45 +01004456 delete me._listeners;
4457 helpers.each(listeners, function(listener, type) {
4458 platform.removeEventListener(me, type, listener);
4459 });
4460 },
Jian Li46770fc2016-08-03 02:32:45 +09004461
Steven Burrows96ee21e2017-07-11 19:49:45 +01004462 updateHoverStyle: function(elements, mode, enabled) {
4463 var method = enabled? 'setHoverStyle' : 'removeHoverStyle';
4464 var element, i, ilen;
Jian Li46770fc2016-08-03 02:32:45 +09004465
Steven Burrows96ee21e2017-07-11 19:49:45 +01004466 for (i=0, ilen=elements.length; i<ilen; ++i) {
4467 element = elements[i];
4468 if (element) {
4469 this.getDatasetMeta(element._datasetIndex).controller[method](element);
4470 }
4471 }
4472 },
Jian Li46770fc2016-08-03 02:32:45 +09004473
Steven Burrows96ee21e2017-07-11 19:49:45 +01004474 /**
4475 * @private
4476 */
4477 eventHandler: function(e) {
4478 var me = this;
4479 var tooltip = me.tooltip;
Jian Li46770fc2016-08-03 02:32:45 +09004480
Steven Burrows96ee21e2017-07-11 19:49:45 +01004481 if (plugins.notify(me, 'beforeEvent', [e]) === false) {
4482 return;
4483 }
Jian Li46770fc2016-08-03 02:32:45 +09004484
Steven Burrows96ee21e2017-07-11 19:49:45 +01004485 // Buffer any update calls so that renders do not occur
4486 me._bufferedRender = true;
4487 me._bufferedRequest = null;
Jian Li46770fc2016-08-03 02:32:45 +09004488
Steven Burrows96ee21e2017-07-11 19:49:45 +01004489 var changed = me.handleEvent(e);
4490 changed |= tooltip && tooltip.handleEvent(e);
Jian Li46770fc2016-08-03 02:32:45 +09004491
Steven Burrows96ee21e2017-07-11 19:49:45 +01004492 plugins.notify(me, 'afterEvent', [e]);
Jian Li46770fc2016-08-03 02:32:45 +09004493
Steven Burrows96ee21e2017-07-11 19:49:45 +01004494 var bufferedRequest = me._bufferedRequest;
4495 if (bufferedRequest) {
4496 // If we have an update that was triggered, we need to do a normal render
4497 me.render(bufferedRequest.duration, bufferedRequest.lazy);
4498 } else if (changed && !me.animating) {
4499 // If entering, leaving, or changing elements, animate the change via pivot
4500 me.stop();
Jian Li46770fc2016-08-03 02:32:45 +09004501
Steven Burrows96ee21e2017-07-11 19:49:45 +01004502 // We only need to render at this point. Updating will cause scales to be
4503 // recomputed generating flicker & using more memory than necessary.
4504 me.render(me.options.hover.animationDuration, true);
4505 }
Jian Li46770fc2016-08-03 02:32:45 +09004506
Steven Burrows96ee21e2017-07-11 19:49:45 +01004507 me._bufferedRender = false;
4508 me._bufferedRequest = null;
Jian Li46770fc2016-08-03 02:32:45 +09004509
Steven Burrows96ee21e2017-07-11 19:49:45 +01004510 return me;
4511 },
Jian Li46770fc2016-08-03 02:32:45 +09004512
Steven Burrows96ee21e2017-07-11 19:49:45 +01004513 /**
4514 * Handle an event
4515 * @private
4516 * @param {IEvent} event the event to handle
4517 * @return {Boolean} true if the chart needs to re-render
4518 */
4519 handleEvent: function(e) {
4520 var me = this;
4521 var options = me.options || {};
4522 var hoverOptions = options.hover;
4523 var changed = false;
Jian Li46770fc2016-08-03 02:32:45 +09004524
Steven Burrows96ee21e2017-07-11 19:49:45 +01004525 me.lastActive = me.lastActive || [];
Jian Li46770fc2016-08-03 02:32:45 +09004526
Steven Burrows96ee21e2017-07-11 19:49:45 +01004527 // Find Active Elements for hover and tooltips
4528 if (e.type === 'mouseout') {
4529 me.active = [];
4530 } else {
4531 me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
4532 }
Jian Li46770fc2016-08-03 02:32:45 +09004533
Steven Burrows96ee21e2017-07-11 19:49:45 +01004534 // On Hover hook
4535 if (hoverOptions.onHover) {
4536 // Need to call with native event here to not break backwards compatibility
4537 hoverOptions.onHover.call(me, e.native, me.active);
4538 }
Jian Li46770fc2016-08-03 02:32:45 +09004539
Steven Burrows96ee21e2017-07-11 19:49:45 +01004540 if (e.type === 'mouseup' || e.type === 'click') {
4541 if (options.onClick) {
4542 // Use e.native here for backwards compatibility
4543 options.onClick.call(me, e.native, me.active);
4544 }
4545 }
Jian Li46770fc2016-08-03 02:32:45 +09004546
Steven Burrows96ee21e2017-07-11 19:49:45 +01004547 // Remove styling for last active (even if it may still be active)
4548 if (me.lastActive.length) {
4549 me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
4550 }
Jian Li46770fc2016-08-03 02:32:45 +09004551
Steven Burrows96ee21e2017-07-11 19:49:45 +01004552 // Built in hover styling
4553 if (me.active.length && hoverOptions.mode) {
4554 me.updateHoverStyle(me.active, hoverOptions.mode, true);
4555 }
Jian Li46770fc2016-08-03 02:32:45 +09004556
Steven Burrows96ee21e2017-07-11 19:49:45 +01004557 changed = !helpers.arrayEquals(me.active, me.lastActive);
Jian Li46770fc2016-08-03 02:32:45 +09004558
Steven Burrows96ee21e2017-07-11 19:49:45 +01004559 // Remember Last Actives
4560 me.lastActive = me.active;
Jian Li46770fc2016-08-03 02:32:45 +09004561
Steven Burrows96ee21e2017-07-11 19:49:45 +01004562 return changed;
4563 }
4564 });
Jian Li46770fc2016-08-03 02:32:45 +09004565
Steven Burrows96ee21e2017-07-11 19:49:45 +01004566 /**
4567 * Provided for backward compatibility, use Chart instead.
4568 * @class Chart.Controller
4569 * @deprecated since version 2.6.0
4570 * @todo remove at version 3
4571 * @private
4572 */
4573 Chart.Controller = Chart;
4574 };
Jian Li46770fc2016-08-03 02:32:45 +09004575
4576},{}],24:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01004577 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09004578
Steven Burrows96ee21e2017-07-11 19:49:45 +01004579 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09004580
Steven Burrows96ee21e2017-07-11 19:49:45 +01004581 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09004582
Steven Burrows96ee21e2017-07-11 19:49:45 +01004583 var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
Jian Li46770fc2016-08-03 02:32:45 +09004584
Steven Burrows96ee21e2017-07-11 19:49:45 +01004585 /**
4586 * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
4587 * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
4588 * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
4589 */
4590 function listenArrayEvents(array, listener) {
4591 if (array._chartjs) {
4592 array._chartjs.listeners.push(listener);
4593 return;
4594 }
Jian Li46770fc2016-08-03 02:32:45 +09004595
Steven Burrows96ee21e2017-07-11 19:49:45 +01004596 Object.defineProperty(array, '_chartjs', {
4597 configurable: true,
4598 enumerable: false,
4599 value: {
4600 listeners: [listener]
4601 }
4602 });
Jian Li46770fc2016-08-03 02:32:45 +09004603
Steven Burrows96ee21e2017-07-11 19:49:45 +01004604 arrayEvents.forEach(function(key) {
4605 var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
4606 var base = array[key];
Jian Li46770fc2016-08-03 02:32:45 +09004607
Steven Burrows96ee21e2017-07-11 19:49:45 +01004608 Object.defineProperty(array, key, {
4609 configurable: true,
4610 enumerable: false,
4611 value: function() {
4612 var args = Array.prototype.slice.call(arguments);
4613 var res = base.apply(this, args);
Jian Li46770fc2016-08-03 02:32:45 +09004614
Steven Burrows96ee21e2017-07-11 19:49:45 +01004615 helpers.each(array._chartjs.listeners, function(object) {
4616 if (typeof object[method] === 'function') {
4617 object[method].apply(object, args);
4618 }
4619 });
Jian Li46770fc2016-08-03 02:32:45 +09004620
Steven Burrows96ee21e2017-07-11 19:49:45 +01004621 return res;
4622 }
4623 });
4624 });
4625 }
Jian Li46770fc2016-08-03 02:32:45 +09004626
Steven Burrows96ee21e2017-07-11 19:49:45 +01004627 /**
4628 * Removes the given array event listener and cleanup extra attached properties (such as
4629 * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
4630 */
4631 function unlistenArrayEvents(array, listener) {
4632 var stub = array._chartjs;
4633 if (!stub) {
4634 return;
4635 }
Jian Li46770fc2016-08-03 02:32:45 +09004636
Steven Burrows96ee21e2017-07-11 19:49:45 +01004637 var listeners = stub.listeners;
4638 var index = listeners.indexOf(listener);
4639 if (index !== -1) {
4640 listeners.splice(index, 1);
4641 }
Jian Li46770fc2016-08-03 02:32:45 +09004642
Steven Burrows96ee21e2017-07-11 19:49:45 +01004643 if (listeners.length > 0) {
4644 return;
4645 }
Jian Li46770fc2016-08-03 02:32:45 +09004646
Steven Burrows96ee21e2017-07-11 19:49:45 +01004647 arrayEvents.forEach(function(key) {
4648 delete array[key];
4649 });
Jian Li46770fc2016-08-03 02:32:45 +09004650
Steven Burrows96ee21e2017-07-11 19:49:45 +01004651 delete array._chartjs;
4652 }
Jian Li46770fc2016-08-03 02:32:45 +09004653
Steven Burrows96ee21e2017-07-11 19:49:45 +01004654 // Base class for all dataset controllers (line, bar, etc)
4655 Chart.DatasetController = function(chart, datasetIndex) {
4656 this.initialize(chart, datasetIndex);
4657 };
Jian Li46770fc2016-08-03 02:32:45 +09004658
Steven Burrows96ee21e2017-07-11 19:49:45 +01004659 helpers.extend(Chart.DatasetController.prototype, {
Jian Li46770fc2016-08-03 02:32:45 +09004660
Steven Burrows96ee21e2017-07-11 19:49:45 +01004661 /**
4662 * Element type used to generate a meta dataset (e.g. Chart.element.Line).
4663 * @type {Chart.core.element}
4664 */
4665 datasetElementType: null,
Jian Li46770fc2016-08-03 02:32:45 +09004666
Steven Burrows96ee21e2017-07-11 19:49:45 +01004667 /**
4668 * Element type used to generate a meta data (e.g. Chart.element.Point).
4669 * @type {Chart.core.element}
4670 */
4671 dataElementType: null,
Jian Li46770fc2016-08-03 02:32:45 +09004672
Steven Burrows96ee21e2017-07-11 19:49:45 +01004673 initialize: function(chart, datasetIndex) {
4674 var me = this;
4675 me.chart = chart;
4676 me.index = datasetIndex;
4677 me.linkScales();
4678 me.addElements();
4679 },
Jian Li46770fc2016-08-03 02:32:45 +09004680
Steven Burrows96ee21e2017-07-11 19:49:45 +01004681 updateIndex: function(datasetIndex) {
4682 this.index = datasetIndex;
4683 },
Jian Li46770fc2016-08-03 02:32:45 +09004684
Steven Burrows96ee21e2017-07-11 19:49:45 +01004685 linkScales: function() {
4686 var me = this;
4687 var meta = me.getMeta();
4688 var dataset = me.getDataset();
Jian Li46770fc2016-08-03 02:32:45 +09004689
Steven Burrows96ee21e2017-07-11 19:49:45 +01004690 if (meta.xAxisID === null) {
4691 meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
4692 }
4693 if (meta.yAxisID === null) {
4694 meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
4695 }
4696 },
Jian Li46770fc2016-08-03 02:32:45 +09004697
Steven Burrows96ee21e2017-07-11 19:49:45 +01004698 getDataset: function() {
4699 return this.chart.data.datasets[this.index];
4700 },
Jian Li46770fc2016-08-03 02:32:45 +09004701
Steven Burrows96ee21e2017-07-11 19:49:45 +01004702 getMeta: function() {
4703 return this.chart.getDatasetMeta(this.index);
4704 },
Jian Li46770fc2016-08-03 02:32:45 +09004705
Steven Burrows96ee21e2017-07-11 19:49:45 +01004706 getScaleForId: function(scaleID) {
4707 return this.chart.scales[scaleID];
4708 },
Jian Li46770fc2016-08-03 02:32:45 +09004709
Steven Burrows96ee21e2017-07-11 19:49:45 +01004710 reset: function() {
4711 this.update(true);
4712 },
Jian Li46770fc2016-08-03 02:32:45 +09004713
Steven Burrows96ee21e2017-07-11 19:49:45 +01004714 /**
4715 * @private
4716 */
4717 destroy: function() {
4718 if (this._data) {
4719 unlistenArrayEvents(this._data, this);
4720 }
4721 },
Jian Li46770fc2016-08-03 02:32:45 +09004722
Steven Burrows96ee21e2017-07-11 19:49:45 +01004723 createMetaDataset: function() {
4724 var me = this;
4725 var type = me.datasetElementType;
4726 return type && new type({
4727 _chart: me.chart,
4728 _datasetIndex: me.index
4729 });
4730 },
Jian Lid7a5a742016-02-12 13:51:18 -08004731
Steven Burrows96ee21e2017-07-11 19:49:45 +01004732 createMetaData: function(index) {
4733 var me = this;
4734 var type = me.dataElementType;
4735 return type && new type({
4736 _chart: me.chart,
4737 _datasetIndex: me.index,
4738 _index: index
4739 });
4740 },
4741
4742 addElements: function() {
4743 var me = this;
4744 var meta = me.getMeta();
4745 var data = me.getDataset().data || [];
4746 var metaData = meta.data;
4747 var i, ilen;
4748
4749 for (i=0, ilen=data.length; i<ilen; ++i) {
4750 metaData[i] = metaData[i] || me.createMetaData(i);
4751 }
4752
4753 meta.dataset = meta.dataset || me.createMetaDataset();
4754 },
4755
4756 addElementAndReset: function(index) {
4757 var element = this.createMetaData(index);
4758 this.getMeta().data.splice(index, 0, element);
4759 this.updateElement(element, index, true);
4760 },
4761
4762 buildOrUpdateElements: function() {
4763 var me = this;
4764 var dataset = me.getDataset();
4765 var data = dataset.data || (dataset.data = []);
4766
4767 // In order to correctly handle data addition/deletion animation (an thus simulate
4768 // real-time charts), we need to monitor these data modifications and synchronize
4769 // the internal meta data accordingly.
4770 if (me._data !== data) {
4771 if (me._data) {
4772 // This case happens when the user replaced the data array instance.
4773 unlistenArrayEvents(me._data, me);
4774 }
4775
4776 listenArrayEvents(data, me);
4777 me._data = data;
4778 }
4779
4780 // Re-sync meta data in case the user replaced the data array or if we missed
4781 // any updates and so make sure that we handle number of datapoints changing.
4782 me.resyncElements();
4783 },
4784
4785 update: helpers.noop,
4786
4787 transition: function(easingValue) {
4788 var meta = this.getMeta();
4789 var elements = meta.data || [];
4790 var ilen = elements.length;
4791 var i = 0;
4792
4793 for (; i<ilen; ++i) {
4794 elements[i].transition(easingValue);
4795 }
4796
4797 if (meta.dataset) {
4798 meta.dataset.transition(easingValue);
4799 }
4800 },
4801
4802 draw: function() {
4803 var meta = this.getMeta();
4804 var elements = meta.data || [];
4805 var ilen = elements.length;
4806 var i = 0;
4807
4808 if (meta.dataset) {
4809 meta.dataset.draw();
4810 }
4811
4812 for (; i<ilen; ++i) {
4813 elements[i].draw();
4814 }
4815 },
4816
4817 removeHoverStyle: function(element, elementOpts) {
4818 var dataset = this.chart.data.datasets[element._datasetIndex],
4819 index = element._index,
4820 custom = element.custom || {},
4821 valueOrDefault = helpers.getValueAtIndexOrDefault,
4822 model = element._model;
4823
4824 model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
4825 model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
4826 model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
4827 },
4828
4829 setHoverStyle: function(element) {
4830 var dataset = this.chart.data.datasets[element._datasetIndex],
4831 index = element._index,
4832 custom = element.custom || {},
4833 valueOrDefault = helpers.getValueAtIndexOrDefault,
4834 getHoverColor = helpers.getHoverColor,
4835 model = element._model;
4836
4837 model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
4838 model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
4839 model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
4840 },
4841
4842 /**
4843 * @private
4844 */
4845 resyncElements: function() {
4846 var me = this;
4847 var meta = me.getMeta();
4848 var data = me.getDataset().data;
4849 var numMeta = meta.data.length;
4850 var numData = data.length;
4851
4852 if (numData < numMeta) {
4853 meta.data.splice(numData, numMeta - numData);
4854 } else if (numData > numMeta) {
4855 me.insertElements(numMeta, numData - numMeta);
4856 }
4857 },
4858
4859 /**
4860 * @private
4861 */
4862 insertElements: function(start, count) {
4863 for (var i=0; i<count; ++i) {
4864 this.addElementAndReset(start + i);
4865 }
4866 },
4867
4868 /**
4869 * @private
4870 */
4871 onDataPush: function() {
4872 this.insertElements(this.getDataset().data.length-1, arguments.length);
4873 },
4874
4875 /**
4876 * @private
4877 */
4878 onDataPop: function() {
4879 this.getMeta().data.pop();
4880 },
4881
4882 /**
4883 * @private
4884 */
4885 onDataShift: function() {
4886 this.getMeta().data.shift();
4887 },
4888
4889 /**
4890 * @private
4891 */
4892 onDataSplice: function(start, count) {
4893 this.getMeta().data.splice(start, count);
4894 this.insertElements(start, arguments.length - 2);
4895 },
4896
4897 /**
4898 * @private
4899 */
4900 onDataUnshift: function() {
4901 this.insertElements(0, arguments.length);
4902 }
4903 });
4904
4905 Chart.DatasetController.extend = helpers.inherits;
4906 };
4907
Jian Li46770fc2016-08-03 02:32:45 +09004908},{}],25:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01004909 'use strict';
Jian Lid7a5a742016-02-12 13:51:18 -08004910
Steven Burrows96ee21e2017-07-11 19:49:45 +01004911 var color = require(3);
Jian Lid7a5a742016-02-12 13:51:18 -08004912
Steven Burrows96ee21e2017-07-11 19:49:45 +01004913 module.exports = function(Chart) {
Jian Lid7a5a742016-02-12 13:51:18 -08004914
Steven Burrows96ee21e2017-07-11 19:49:45 +01004915 var helpers = Chart.helpers;
Jian Lid7a5a742016-02-12 13:51:18 -08004916
Steven Burrows96ee21e2017-07-11 19:49:45 +01004917 function interpolate(start, view, model, ease) {
4918 var keys = Object.keys(model);
4919 var i, ilen, key, actual, origin, target, type, c0, c1;
Jian Lid7a5a742016-02-12 13:51:18 -08004920
Steven Burrows96ee21e2017-07-11 19:49:45 +01004921 for (i=0, ilen=keys.length; i<ilen; ++i) {
4922 key = keys[i];
Jian Lid7a5a742016-02-12 13:51:18 -08004923
Steven Burrows96ee21e2017-07-11 19:49:45 +01004924 target = model[key];
Jian Lid7a5a742016-02-12 13:51:18 -08004925
Steven Burrows96ee21e2017-07-11 19:49:45 +01004926 // if a value is added to the model after pivot() has been called, the view
4927 // doesn't contain it, so let's initialize the view to the target value.
4928 if (!view.hasOwnProperty(key)) {
4929 view[key] = target;
4930 }
Jian Lid7a5a742016-02-12 13:51:18 -08004931
Steven Burrows96ee21e2017-07-11 19:49:45 +01004932 actual = view[key];
Jian Lid7a5a742016-02-12 13:51:18 -08004933
Steven Burrows96ee21e2017-07-11 19:49:45 +01004934 if (actual === target || key[0] === '_') {
4935 continue;
4936 }
Jian Lid7a5a742016-02-12 13:51:18 -08004937
Steven Burrows96ee21e2017-07-11 19:49:45 +01004938 if (!start.hasOwnProperty(key)) {
4939 start[key] = actual;
4940 }
Jian Lid7a5a742016-02-12 13:51:18 -08004941
Steven Burrows96ee21e2017-07-11 19:49:45 +01004942 origin = start[key];
Jian Lid7a5a742016-02-12 13:51:18 -08004943
Steven Burrows96ee21e2017-07-11 19:49:45 +01004944 type = typeof(target);
4945
4946 if (type === typeof(origin)) {
4947 if (type === 'string') {
4948 c0 = color(origin);
4949 if (c0.valid) {
4950 c1 = color(target);
4951 if (c1.valid) {
4952 view[key] = c1.mix(c0, ease).rgbString();
4953 continue;
4954 }
4955 }
4956 } else if (type === 'number' && isFinite(origin) && isFinite(target)) {
4957 view[key] = origin + (target - origin) * ease;
4958 continue;
4959 }
4960 }
4961
4962 view[key] = target;
4963 }
Jian Lid7a5a742016-02-12 13:51:18 -08004964 }
4965
Steven Burrows96ee21e2017-07-11 19:49:45 +01004966 Chart.elements = {};
4967
4968 Chart.Element = function(configuration) {
4969 helpers.extend(this, configuration);
4970 this.initialize.apply(this, arguments);
4971 };
4972
4973 helpers.extend(Chart.Element.prototype, {
4974
4975 initialize: function() {
4976 this.hidden = false;
4977 },
4978
4979 pivot: function() {
4980 var me = this;
4981 if (!me._view) {
4982 me._view = helpers.clone(me._model);
4983 }
4984 me._start = {};
4985 return me;
4986 },
4987
4988 transition: function(ease) {
4989 var me = this;
4990 var model = me._model;
4991 var start = me._start;
4992 var view = me._view;
4993
4994 // No animation -> No Transition
4995 if (!model || ease === 1) {
4996 me._view = model;
4997 me._start = null;
4998 return me;
4999 }
5000
5001 if (!view) {
5002 view = me._view = {};
5003 }
5004
5005 if (!start) {
5006 start = me._start = {};
5007 }
5008
5009 interpolate(start, view, model, ease);
5010
5011 return me;
5012 },
5013
5014 tooltipPosition: function() {
5015 return {
5016 x: this._model.x,
5017 y: this._model.y
5018 };
5019 },
5020
5021 hasValue: function() {
5022 return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
5023 }
5024 });
5025
5026 Chart.Element.extend = helpers.inherits;
5027 };
5028
5029},{"3":3}],26:[function(require,module,exports){
5030 /* global window: false */
5031 /* global document: false */
5032 'use strict';
5033
5034 var color = require(3);
5035
5036 module.exports = function(Chart) {
5037 // Global Chart helpers object for utility methods and classes
5038 var helpers = Chart.helpers = {};
5039
5040 // -- Basic js utility methods
5041 helpers.each = function(loopable, callback, self, reverse) {
5042 // Check to see if null or undefined firstly.
5043 var i, len;
5044 if (helpers.isArray(loopable)) {
5045 len = loopable.length;
5046 if (reverse) {
5047 for (i = len - 1; i >= 0; i--) {
5048 callback.call(self, loopable[i], i);
5049 }
5050 } else {
5051 for (i = 0; i < len; i++) {
5052 callback.call(self, loopable[i], i);
5053 }
5054 }
5055 } else if (typeof loopable === 'object') {
5056 var keys = Object.keys(loopable);
5057 len = keys.length;
5058 for (i = 0; i < len; i++) {
5059 callback.call(self, loopable[keys[i]], keys[i]);
5060 }
5061 }
5062 };
5063 helpers.clone = function(obj) {
5064 var objClone = {};
5065 helpers.each(obj, function(value, key) {
5066 if (helpers.isArray(value)) {
5067 objClone[key] = value.slice(0);
5068 } else if (typeof value === 'object' && value !== null) {
5069 objClone[key] = helpers.clone(value);
5070 } else {
5071 objClone[key] = value;
5072 }
5073 });
5074 return objClone;
5075 };
5076 helpers.extend = function(base) {
5077 var setFn = function(value, key) {
5078 base[key] = value;
5079 };
5080 for (var i = 1, ilen = arguments.length; i < ilen; i++) {
5081 helpers.each(arguments[i], setFn);
5082 }
5083 return base;
5084 };
5085 // Need a special merge function to chart configs since they are now grouped
5086 helpers.configMerge = function(_base) {
5087 var base = helpers.clone(_base);
5088 helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) {
5089 helpers.each(extension, function(value, key) {
5090 var baseHasProperty = base.hasOwnProperty(key);
5091 var baseVal = baseHasProperty ? base[key] : {};
5092
5093 if (key === 'scales') {
5094 // Scale config merging is complex. Add our own function here for that
5095 base[key] = helpers.scaleMerge(baseVal, value);
5096 } else if (key === 'scale') {
5097 // Used in polar area & radar charts since there is only one scale
5098 base[key] = helpers.configMerge(baseVal, Chart.scaleService.getScaleDefaults(value.type), value);
5099 } else if (baseHasProperty
5100 && typeof baseVal === 'object'
5101 && !helpers.isArray(baseVal)
5102 && baseVal !== null
5103 && typeof value === 'object'
5104 && !helpers.isArray(value)) {
5105 // If we are overwriting an object with an object, do a merge of the properties.
5106 base[key] = helpers.configMerge(baseVal, value);
5107 } else {
5108 // can just overwrite the value in this case
5109 base[key] = value;
5110 }
5111 });
5112 });
5113
5114 return base;
5115 };
5116 helpers.scaleMerge = function(_base, extension) {
5117 var base = helpers.clone(_base);
5118
5119 helpers.each(extension, function(value, key) {
5120 if (key === 'xAxes' || key === 'yAxes') {
5121 // These properties are arrays of items
5122 if (base.hasOwnProperty(key)) {
5123 helpers.each(value, function(valueObj, index) {
5124 var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear');
5125 var axisDefaults = Chart.scaleService.getScaleDefaults(axisType);
5126 if (index >= base[key].length || !base[key][index].type) {
5127 base[key].push(helpers.configMerge(axisDefaults, valueObj));
5128 } else if (valueObj.type && valueObj.type !== base[key][index].type) {
5129 // Type changed. Bring in the new defaults before we bring in valueObj so that valueObj can override the correct scale defaults
5130 base[key][index] = helpers.configMerge(base[key][index], axisDefaults, valueObj);
5131 } else {
5132 // Type is the same
5133 base[key][index] = helpers.configMerge(base[key][index], valueObj);
5134 }
5135 });
5136 } else {
5137 base[key] = [];
5138 helpers.each(value, function(valueObj) {
5139 var axisType = helpers.getValueOrDefault(valueObj.type, key === 'xAxes' ? 'category' : 'linear');
5140 base[key].push(helpers.configMerge(Chart.scaleService.getScaleDefaults(axisType), valueObj));
5141 });
5142 }
5143 } else if (base.hasOwnProperty(key) && typeof base[key] === 'object' && base[key] !== null && typeof value === 'object') {
5144 // If we are overwriting an object with an object, do a merge of the properties.
5145 base[key] = helpers.configMerge(base[key], value);
5146
5147 } else {
5148 // can just overwrite the value in this case
5149 base[key] = value;
5150 }
5151 });
5152
5153 return base;
5154 };
5155 helpers.getValueAtIndexOrDefault = function(value, index, defaultValue) {
5156 if (value === undefined || value === null) {
5157 return defaultValue;
5158 }
5159
5160 if (helpers.isArray(value)) {
5161 return index < value.length ? value[index] : defaultValue;
5162 }
5163
5164 return value;
5165 };
5166 helpers.getValueOrDefault = function(value, defaultValue) {
5167 return value === undefined ? defaultValue : value;
5168 };
5169 helpers.indexOf = Array.prototype.indexOf?
5170 function(array, item) {
5171 return array.indexOf(item);
5172 }:
5173 function(array, item) {
5174 for (var i = 0, ilen = array.length; i < ilen; ++i) {
5175 if (array[i] === item) {
5176 return i;
5177 }
5178 }
5179 return -1;
5180 };
5181 helpers.where = function(collection, filterCallback) {
5182 if (helpers.isArray(collection) && Array.prototype.filter) {
5183 return collection.filter(filterCallback);
5184 }
5185 var filtered = [];
5186
5187 helpers.each(collection, function(item) {
5188 if (filterCallback(item)) {
5189 filtered.push(item);
5190 }
5191 });
5192
5193 return filtered;
5194 };
5195 helpers.findIndex = Array.prototype.findIndex?
5196 function(array, callback, scope) {
5197 return array.findIndex(callback, scope);
5198 } :
5199 function(array, callback, scope) {
5200 scope = scope === undefined? array : scope;
5201 for (var i = 0, ilen = array.length; i < ilen; ++i) {
5202 if (callback.call(scope, array[i], i, array)) {
5203 return i;
5204 }
5205 }
5206 return -1;
5207 };
5208 helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
5209 // Default to start of the array
5210 if (startIndex === undefined || startIndex === null) {
5211 startIndex = -1;
5212 }
5213 for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
5214 var currentItem = arrayToSearch[i];
5215 if (filterCallback(currentItem)) {
5216 return currentItem;
5217 }
5218 }
5219 };
5220 helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
5221 // Default to end of the array
5222 if (startIndex === undefined || startIndex === null) {
5223 startIndex = arrayToSearch.length;
5224 }
5225 for (var i = startIndex - 1; i >= 0; i--) {
5226 var currentItem = arrayToSearch[i];
5227 if (filterCallback(currentItem)) {
5228 return currentItem;
5229 }
5230 }
5231 };
5232 helpers.inherits = function(extensions) {
5233 // Basic javascript inheritance based on the model created in Backbone.js
5234 var me = this;
5235 var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
5236 return me.apply(this, arguments);
5237 };
5238
5239 var Surrogate = function() {
5240 this.constructor = ChartElement;
5241 };
5242 Surrogate.prototype = me.prototype;
5243 ChartElement.prototype = new Surrogate();
5244
5245 ChartElement.extend = helpers.inherits;
5246
5247 if (extensions) {
5248 helpers.extend(ChartElement.prototype, extensions);
5249 }
5250
5251 ChartElement.__super__ = me.prototype;
5252
5253 return ChartElement;
5254 };
5255 helpers.noop = function() {};
5256 helpers.uid = (function() {
5257 var id = 0;
5258 return function() {
5259 return id++;
5260 };
5261 }());
5262 // -- Math methods
5263 helpers.isNumber = function(n) {
5264 return !isNaN(parseFloat(n)) && isFinite(n);
5265 };
5266 helpers.almostEquals = function(x, y, epsilon) {
5267 return Math.abs(x - y) < epsilon;
5268 };
5269 helpers.almostWhole = function(x, epsilon) {
5270 var rounded = Math.round(x);
5271 return (((rounded - epsilon) < x) && ((rounded + epsilon) > x));
5272 };
5273 helpers.max = function(array) {
5274 return array.reduce(function(max, value) {
5275 if (!isNaN(value)) {
5276 return Math.max(max, value);
5277 }
5278 return max;
5279 }, Number.NEGATIVE_INFINITY);
5280 };
5281 helpers.min = function(array) {
5282 return array.reduce(function(min, value) {
5283 if (!isNaN(value)) {
5284 return Math.min(min, value);
5285 }
5286 return min;
5287 }, Number.POSITIVE_INFINITY);
5288 };
5289 helpers.sign = Math.sign?
5290 function(x) {
5291 return Math.sign(x);
5292 } :
5293 function(x) {
5294 x = +x; // convert to a number
5295 if (x === 0 || isNaN(x)) {
5296 return x;
5297 }
5298 return x > 0 ? 1 : -1;
5299 };
5300 helpers.log10 = Math.log10?
5301 function(x) {
5302 return Math.log10(x);
5303 } :
5304 function(x) {
5305 return Math.log(x) / Math.LN10;
5306 };
5307 helpers.toRadians = function(degrees) {
5308 return degrees * (Math.PI / 180);
5309 };
5310 helpers.toDegrees = function(radians) {
5311 return radians * (180 / Math.PI);
5312 };
5313 // Gets the angle from vertical upright to the point about a centre.
5314 helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
5315 var distanceFromXCenter = anglePoint.x - centrePoint.x,
5316 distanceFromYCenter = anglePoint.y - centrePoint.y,
5317 radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
5318
5319 var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
5320
5321 if (angle < (-0.5 * Math.PI)) {
5322 angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
5323 }
5324
5325 return {
5326 angle: angle,
5327 distance: radialDistanceFromCenter
5328 };
5329 };
5330 helpers.distanceBetweenPoints = function(pt1, pt2) {
5331 return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
5332 };
5333 helpers.aliasPixel = function(pixelWidth) {
5334 return (pixelWidth % 2 === 0) ? 0 : 0.5;
5335 };
5336 helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
5337 // Props to Rob Spencer at scaled innovation for his post on splining between points
5338 // http://scaledinnovation.com/analytics/splines/aboutSplines.html
5339
5340 // This function must also respect "skipped" points
5341
5342 var previous = firstPoint.skip ? middlePoint : firstPoint,
5343 current = middlePoint,
5344 next = afterPoint.skip ? middlePoint : afterPoint;
5345
5346 var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
5347 var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
5348
5349 var s01 = d01 / (d01 + d12);
5350 var s12 = d12 / (d01 + d12);
5351
5352 // If all points are the same, s01 & s02 will be inf
5353 s01 = isNaN(s01) ? 0 : s01;
5354 s12 = isNaN(s12) ? 0 : s12;
5355
5356 var fa = t * s01; // scaling factor for triangle Ta
5357 var fb = t * s12;
5358
5359 return {
5360 previous: {
5361 x: current.x - fa * (next.x - previous.x),
5362 y: current.y - fa * (next.y - previous.y)
5363 },
5364 next: {
5365 x: current.x + fb * (next.x - previous.x),
5366 y: current.y + fb * (next.y - previous.y)
5367 }
5368 };
5369 };
5370 helpers.EPSILON = Number.EPSILON || 1e-14;
5371 helpers.splineCurveMonotone = function(points) {
5372 // This function calculates Bézier control points in a similar way than |splineCurve|,
5373 // but preserves monotonicity of the provided data and ensures no local extremums are added
5374 // between the dataset discrete points due to the interpolation.
5375 // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
5376
5377 var pointsWithTangents = (points || []).map(function(point) {
5378 return {
5379 model: point._model,
5380 deltaK: 0,
5381 mK: 0
5382 };
5383 });
5384
5385 // Calculate slopes (deltaK) and initialize tangents (mK)
5386 var pointsLen = pointsWithTangents.length;
5387 var i, pointBefore, pointCurrent, pointAfter;
5388 for (i = 0; i < pointsLen; ++i) {
5389 pointCurrent = pointsWithTangents[i];
5390 if (pointCurrent.model.skip) {
5391 continue;
5392 }
5393
5394 pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
5395 pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
5396 if (pointAfter && !pointAfter.model.skip) {
5397 var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);
5398
5399 // In the case of two points that appear at the same x pixel, slopeDeltaX is 0
5400 pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;
5401 }
5402
5403 if (!pointBefore || pointBefore.model.skip) {
5404 pointCurrent.mK = pointCurrent.deltaK;
5405 } else if (!pointAfter || pointAfter.model.skip) {
5406 pointCurrent.mK = pointBefore.deltaK;
5407 } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
5408 pointCurrent.mK = 0;
5409 } else {
5410 pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
5411 }
5412 }
5413
5414 // Adjust tangents to ensure monotonic properties
5415 var alphaK, betaK, tauK, squaredMagnitude;
5416 for (i = 0; i < pointsLen - 1; ++i) {
5417 pointCurrent = pointsWithTangents[i];
5418 pointAfter = pointsWithTangents[i + 1];
5419 if (pointCurrent.model.skip || pointAfter.model.skip) {
5420 continue;
5421 }
5422
5423 if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
5424 pointCurrent.mK = pointAfter.mK = 0;
5425 continue;
5426 }
5427
5428 alphaK = pointCurrent.mK / pointCurrent.deltaK;
5429 betaK = pointAfter.mK / pointCurrent.deltaK;
5430 squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
5431 if (squaredMagnitude <= 9) {
5432 continue;
5433 }
5434
5435 tauK = 3 / Math.sqrt(squaredMagnitude);
5436 pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
5437 pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
5438 }
5439
5440 // Compute control points
5441 var deltaX;
5442 for (i = 0; i < pointsLen; ++i) {
5443 pointCurrent = pointsWithTangents[i];
5444 if (pointCurrent.model.skip) {
5445 continue;
5446 }
5447
5448 pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
5449 pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
5450 if (pointBefore && !pointBefore.model.skip) {
5451 deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
5452 pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
5453 pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
5454 }
5455 if (pointAfter && !pointAfter.model.skip) {
5456 deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
5457 pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
5458 pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
5459 }
5460 }
5461 };
5462 helpers.nextItem = function(collection, index, loop) {
5463 if (loop) {
5464 return index >= collection.length - 1 ? collection[0] : collection[index + 1];
5465 }
5466 return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
5467 };
5468 helpers.previousItem = function(collection, index, loop) {
5469 if (loop) {
5470 return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
5471 }
5472 return index <= 0 ? collection[0] : collection[index - 1];
5473 };
5474 // Implementation of the nice number algorithm used in determining where axis labels will go
5475 helpers.niceNum = function(range, round) {
5476 var exponent = Math.floor(helpers.log10(range));
5477 var fraction = range / Math.pow(10, exponent);
5478 var niceFraction;
5479
5480 if (round) {
5481 if (fraction < 1.5) {
5482 niceFraction = 1;
5483 } else if (fraction < 3) {
5484 niceFraction = 2;
5485 } else if (fraction < 7) {
5486 niceFraction = 5;
5487 } else {
5488 niceFraction = 10;
5489 }
5490 } else if (fraction <= 1.0) {
5491 niceFraction = 1;
5492 } else if (fraction <= 2) {
5493 niceFraction = 2;
5494 } else if (fraction <= 5) {
5495 niceFraction = 5;
5496 } else {
5497 niceFraction = 10;
5498 }
5499
5500 return niceFraction * Math.pow(10, exponent);
5501 };
5502 // Easing functions adapted from Robert Penner's easing equations
5503 // http://www.robertpenner.com/easing/
5504 var easingEffects = helpers.easingEffects = {
5505 linear: function(t) {
5506 return t;
5507 },
5508 easeInQuad: function(t) {
5509 return t * t;
5510 },
5511 easeOutQuad: function(t) {
5512 return -1 * t * (t - 2);
5513 },
5514 easeInOutQuad: function(t) {
5515 if ((t /= 1 / 2) < 1) {
5516 return 1 / 2 * t * t;
5517 }
5518 return -1 / 2 * ((--t) * (t - 2) - 1);
5519 },
5520 easeInCubic: function(t) {
5521 return t * t * t;
5522 },
5523 easeOutCubic: function(t) {
5524 return 1 * ((t = t / 1 - 1) * t * t + 1);
5525 },
5526 easeInOutCubic: function(t) {
5527 if ((t /= 1 / 2) < 1) {
5528 return 1 / 2 * t * t * t;
5529 }
5530 return 1 / 2 * ((t -= 2) * t * t + 2);
5531 },
5532 easeInQuart: function(t) {
5533 return t * t * t * t;
5534 },
5535 easeOutQuart: function(t) {
5536 return -1 * ((t = t / 1 - 1) * t * t * t - 1);
5537 },
5538 easeInOutQuart: function(t) {
5539 if ((t /= 1 / 2) < 1) {
5540 return 1 / 2 * t * t * t * t;
5541 }
5542 return -1 / 2 * ((t -= 2) * t * t * t - 2);
5543 },
5544 easeInQuint: function(t) {
5545 return 1 * (t /= 1) * t * t * t * t;
5546 },
5547 easeOutQuint: function(t) {
5548 return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
5549 },
5550 easeInOutQuint: function(t) {
5551 if ((t /= 1 / 2) < 1) {
5552 return 1 / 2 * t * t * t * t * t;
5553 }
5554 return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
5555 },
5556 easeInSine: function(t) {
5557 return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
5558 },
5559 easeOutSine: function(t) {
5560 return 1 * Math.sin(t / 1 * (Math.PI / 2));
5561 },
5562 easeInOutSine: function(t) {
5563 return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
5564 },
5565 easeInExpo: function(t) {
5566 return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
5567 },
5568 easeOutExpo: function(t) {
5569 return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
5570 },
5571 easeInOutExpo: function(t) {
5572 if (t === 0) {
5573 return 0;
5574 }
5575 if (t === 1) {
5576 return 1;
5577 }
5578 if ((t /= 1 / 2) < 1) {
5579 return 1 / 2 * Math.pow(2, 10 * (t - 1));
5580 }
5581 return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
5582 },
5583 easeInCirc: function(t) {
5584 if (t >= 1) {
5585 return t;
5586 }
5587 return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
5588 },
5589 easeOutCirc: function(t) {
5590 return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
5591 },
5592 easeInOutCirc: function(t) {
5593 if ((t /= 1 / 2) < 1) {
5594 return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
5595 }
5596 return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
5597 },
5598 easeInElastic: function(t) {
5599 var s = 1.70158;
5600 var p = 0;
5601 var a = 1;
5602 if (t === 0) {
5603 return 0;
5604 }
5605 if ((t /= 1) === 1) {
5606 return 1;
5607 }
5608 if (!p) {
5609 p = 1 * 0.3;
5610 }
5611 if (a < Math.abs(1)) {
5612 a = 1;
5613 s = p / 4;
5614 } else {
5615 s = p / (2 * Math.PI) * Math.asin(1 / a);
5616 }
5617 return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
5618 },
5619 easeOutElastic: function(t) {
5620 var s = 1.70158;
5621 var p = 0;
5622 var a = 1;
5623 if (t === 0) {
5624 return 0;
5625 }
5626 if ((t /= 1) === 1) {
5627 return 1;
5628 }
5629 if (!p) {
5630 p = 1 * 0.3;
5631 }
5632 if (a < Math.abs(1)) {
5633 a = 1;
5634 s = p / 4;
5635 } else {
5636 s = p / (2 * Math.PI) * Math.asin(1 / a);
5637 }
5638 return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
5639 },
5640 easeInOutElastic: function(t) {
5641 var s = 1.70158;
5642 var p = 0;
5643 var a = 1;
5644 if (t === 0) {
5645 return 0;
5646 }
5647 if ((t /= 1 / 2) === 2) {
5648 return 1;
5649 }
5650 if (!p) {
5651 p = 1 * (0.3 * 1.5);
5652 }
5653 if (a < Math.abs(1)) {
5654 a = 1;
5655 s = p / 4;
5656 } else {
5657 s = p / (2 * Math.PI) * Math.asin(1 / a);
5658 }
5659 if (t < 1) {
5660 return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
5661 }
5662 return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;
5663 },
5664 easeInBack: function(t) {
5665 var s = 1.70158;
5666 return 1 * (t /= 1) * t * ((s + 1) * t - s);
5667 },
5668 easeOutBack: function(t) {
5669 var s = 1.70158;
5670 return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
5671 },
5672 easeInOutBack: function(t) {
5673 var s = 1.70158;
5674 if ((t /= 1 / 2) < 1) {
5675 return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
5676 }
5677 return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
5678 },
5679 easeInBounce: function(t) {
5680 return 1 - easingEffects.easeOutBounce(1 - t);
5681 },
5682 easeOutBounce: function(t) {
5683 if ((t /= 1) < (1 / 2.75)) {
5684 return 1 * (7.5625 * t * t);
5685 } else if (t < (2 / 2.75)) {
5686 return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
5687 } else if (t < (2.5 / 2.75)) {
5688 return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
5689 }
5690 return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
5691 },
5692 easeInOutBounce: function(t) {
5693 if (t < 1 / 2) {
5694 return easingEffects.easeInBounce(t * 2) * 0.5;
5695 }
5696 return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
5697 }
5698 };
5699 // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
5700 helpers.requestAnimFrame = (function() {
5701 if (typeof window === 'undefined') {
5702 return function(callback) {
5703 callback();
5704 };
5705 }
5706 return window.requestAnimationFrame ||
5707 window.webkitRequestAnimationFrame ||
5708 window.mozRequestAnimationFrame ||
5709 window.oRequestAnimationFrame ||
5710 window.msRequestAnimationFrame ||
5711 function(callback) {
5712 return window.setTimeout(callback, 1000 / 60);
5713 };
5714 }());
5715 // -- DOM methods
5716 helpers.getRelativePosition = function(evt, chart) {
5717 var mouseX, mouseY;
5718 var e = evt.originalEvent || evt,
5719 canvas = evt.currentTarget || evt.srcElement,
5720 boundingRect = canvas.getBoundingClientRect();
5721
5722 var touches = e.touches;
5723 if (touches && touches.length > 0) {
5724 mouseX = touches[0].clientX;
5725 mouseY = touches[0].clientY;
5726
5727 } else {
5728 mouseX = e.clientX;
5729 mouseY = e.clientY;
5730 }
5731
5732 // Scale mouse coordinates into canvas coordinates
5733 // by following the pattern laid out by 'jerryj' in the comments of
5734 // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
5735 var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left'));
5736 var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top'));
5737 var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right'));
5738 var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom'));
5739 var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
5740 var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
5741
5742 // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
5743 // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
5744 mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
5745 mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);
5746
5747 return {
5748 x: mouseX,
5749 y: mouseY
5750 };
5751
5752 };
5753 helpers.addEvent = function(node, eventType, method) {
5754 if (node.addEventListener) {
5755 node.addEventListener(eventType, method);
5756 } else if (node.attachEvent) {
5757 node.attachEvent('on' + eventType, method);
5758 } else {
5759 node['on' + eventType] = method;
5760 }
5761 };
5762 helpers.removeEvent = function(node, eventType, handler) {
5763 if (node.removeEventListener) {
5764 node.removeEventListener(eventType, handler, false);
5765 } else if (node.detachEvent) {
5766 node.detachEvent('on' + eventType, handler);
5767 } else {
5768 node['on' + eventType] = helpers.noop;
5769 }
5770 };
5771
5772 // Private helper function to convert max-width/max-height values that may be percentages into a number
5773 function parseMaxStyle(styleValue, node, parentProperty) {
5774 var valueInPixels;
5775 if (typeof(styleValue) === 'string') {
5776 valueInPixels = parseInt(styleValue, 10);
5777
5778 if (styleValue.indexOf('%') !== -1) {
5779 // percentage * size in dimension
5780 valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
5781 }
5782 } else {
5783 valueInPixels = styleValue;
5784 }
5785
5786 return valueInPixels;
Jian Li46770fc2016-08-03 02:32:45 +09005787 }
5788
Steven Burrows96ee21e2017-07-11 19:49:45 +01005789 /**
5790 * Returns if the given value contains an effective constraint.
5791 * @private
5792 */
5793 function isConstrainedValue(value) {
5794 return value !== undefined && value !== null && value !== 'none';
Jian Li46770fc2016-08-03 02:32:45 +09005795 }
5796
Steven Burrows96ee21e2017-07-11 19:49:45 +01005797 // Private helper to get a constraint dimension
5798 // @param domNode : the node to check the constraint on
5799 // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight)
5800 // @param percentageProperty : property of parent to use when calculating width as a percentage
5801 // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser
5802 function getConstraintDimension(domNode, maxStyle, percentageProperty) {
5803 var view = document.defaultView;
5804 var parentNode = domNode.parentNode;
5805 var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
5806 var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
5807 var hasCNode = isConstrainedValue(constrainedNode);
5808 var hasCContainer = isConstrainedValue(constrainedContainer);
5809 var infinity = Number.POSITIVE_INFINITY;
5810
5811 if (hasCNode || hasCContainer) {
5812 return Math.min(
5813 hasCNode? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
5814 hasCContainer? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
5815 }
5816
5817 return 'none';
Jian Li46770fc2016-08-03 02:32:45 +09005818 }
Steven Burrows96ee21e2017-07-11 19:49:45 +01005819 // returns Number or undefined if no constraint
5820 helpers.getConstraintWidth = function(domNode) {
5821 return getConstraintDimension(domNode, 'max-width', 'clientWidth');
5822 };
5823 // returns Number or undefined if no constraint
5824 helpers.getConstraintHeight = function(domNode) {
5825 return getConstraintDimension(domNode, 'max-height', 'clientHeight');
5826 };
5827 helpers.getMaximumWidth = function(domNode) {
5828 var container = domNode.parentNode;
5829 var paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10);
5830 var paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10);
5831 var w = container.clientWidth - paddingLeft - paddingRight;
5832 var cw = helpers.getConstraintWidth(domNode);
5833 return isNaN(cw)? w : Math.min(w, cw);
5834 };
5835 helpers.getMaximumHeight = function(domNode) {
5836 var container = domNode.parentNode;
5837 var paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10);
5838 var paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10);
5839 var h = container.clientHeight - paddingTop - paddingBottom;
5840 var ch = helpers.getConstraintHeight(domNode);
5841 return isNaN(ch)? h : Math.min(h, ch);
5842 };
5843 helpers.getStyle = function(el, property) {
5844 return el.currentStyle ?
5845 el.currentStyle[property] :
5846 document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
5847 };
5848 helpers.retinaScale = function(chart) {
5849 var pixelRatio = chart.currentDevicePixelRatio = window.devicePixelRatio || 1;
5850 if (pixelRatio === 1) {
5851 return;
5852 }
Jian Li46770fc2016-08-03 02:32:45 +09005853
Steven Burrows96ee21e2017-07-11 19:49:45 +01005854 var canvas = chart.canvas;
5855 var height = chart.height;
5856 var width = chart.width;
Jian Li46770fc2016-08-03 02:32:45 +09005857
Steven Burrows96ee21e2017-07-11 19:49:45 +01005858 canvas.height = height * pixelRatio;
5859 canvas.width = width * pixelRatio;
5860 chart.ctx.scale(pixelRatio, pixelRatio);
Jian Li46770fc2016-08-03 02:32:45 +09005861
Steven Burrows96ee21e2017-07-11 19:49:45 +01005862 // If no style has been set on the canvas, the render size is used as display size,
5863 // making the chart visually bigger, so let's enforce it to the "correct" values.
5864 // See https://github.com/chartjs/Chart.js/issues/3575
5865 canvas.style.height = height + 'px';
5866 canvas.style.width = width + 'px';
5867 };
5868 // -- Canvas methods
5869 helpers.clear = function(chart) {
5870 chart.ctx.clearRect(0, 0, chart.width, chart.height);
5871 };
5872 helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
5873 return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
5874 };
5875 helpers.longestText = function(ctx, font, arrayOfThings, cache) {
5876 cache = cache || {};
5877 var data = cache.data = cache.data || {};
5878 var gc = cache.garbageCollect = cache.garbageCollect || [];
Jian Li46770fc2016-08-03 02:32:45 +09005879
Steven Burrows96ee21e2017-07-11 19:49:45 +01005880 if (cache.font !== font) {
5881 data = cache.data = {};
5882 gc = cache.garbageCollect = [];
5883 cache.font = font;
5884 }
Jian Li46770fc2016-08-03 02:32:45 +09005885
Steven Burrows96ee21e2017-07-11 19:49:45 +01005886 ctx.font = font;
5887 var longest = 0;
5888 helpers.each(arrayOfThings, function(thing) {
5889 // Undefined strings and arrays should not be measured
5890 if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) {
5891 longest = helpers.measureText(ctx, data, gc, longest, thing);
5892 } else if (helpers.isArray(thing)) {
5893 // if it is an array lets measure each element
5894 // to do maybe simplify this function a bit so we can do this more recursively?
5895 helpers.each(thing, function(nestedThing) {
5896 // Undefined strings and arrays should not be measured
5897 if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) {
5898 longest = helpers.measureText(ctx, data, gc, longest, nestedThing);
5899 }
5900 });
5901 }
5902 });
Jian Li46770fc2016-08-03 02:32:45 +09005903
Steven Burrows96ee21e2017-07-11 19:49:45 +01005904 var gcLen = gc.length / 2;
5905 if (gcLen > arrayOfThings.length) {
5906 for (var i = 0; i < gcLen; i++) {
5907 delete data[gc[i]];
5908 }
5909 gc.splice(0, gcLen);
5910 }
5911 return longest;
5912 };
5913 helpers.measureText = function(ctx, data, gc, longest, string) {
5914 var textWidth = data[string];
5915 if (!textWidth) {
5916 textWidth = data[string] = ctx.measureText(string).width;
5917 gc.push(string);
5918 }
5919 if (textWidth > longest) {
5920 longest = textWidth;
5921 }
5922 return longest;
5923 };
5924 helpers.numberOfLabelLines = function(arrayOfThings) {
5925 var numberOfLines = 1;
5926 helpers.each(arrayOfThings, function(thing) {
5927 if (helpers.isArray(thing)) {
5928 if (thing.length > numberOfLines) {
5929 numberOfLines = thing.length;
5930 }
5931 }
5932 });
5933 return numberOfLines;
5934 };
5935 helpers.drawRoundedRectangle = function(ctx, x, y, width, height, radius) {
5936 ctx.beginPath();
5937 ctx.moveTo(x + radius, y);
5938 ctx.lineTo(x + width - radius, y);
5939 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
5940 ctx.lineTo(x + width, y + height - radius);
5941 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
5942 ctx.lineTo(x + radius, y + height);
5943 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
5944 ctx.lineTo(x, y + radius);
5945 ctx.quadraticCurveTo(x, y, x + radius, y);
5946 ctx.closePath();
5947 };
Jian Li46770fc2016-08-03 02:32:45 +09005948
Steven Burrows96ee21e2017-07-11 19:49:45 +01005949 helpers.color = !color?
5950 function(value) {
5951 console.error('Color.js not found!');
5952 return value;
5953 } :
5954 function(value) {
5955 /* global CanvasGradient */
5956 if (value instanceof CanvasGradient) {
5957 value = Chart.defaults.global.defaultColor;
5958 }
Jian Li46770fc2016-08-03 02:32:45 +09005959
Steven Burrows96ee21e2017-07-11 19:49:45 +01005960 return color(value);
5961 };
Jian Li46770fc2016-08-03 02:32:45 +09005962
Steven Burrows96ee21e2017-07-11 19:49:45 +01005963 helpers.isArray = Array.isArray?
5964 function(obj) {
5965 return Array.isArray(obj);
5966 } :
5967 function(obj) {
5968 return Object.prototype.toString.call(obj) === '[object Array]';
5969 };
5970 // ! @see http://stackoverflow.com/a/14853974
5971 helpers.arrayEquals = function(a0, a1) {
5972 var i, ilen, v0, v1;
Jian Li46770fc2016-08-03 02:32:45 +09005973
Steven Burrows96ee21e2017-07-11 19:49:45 +01005974 if (!a0 || !a1 || a0.length !== a1.length) {
5975 return false;
5976 }
Jian Li46770fc2016-08-03 02:32:45 +09005977
Steven Burrows96ee21e2017-07-11 19:49:45 +01005978 for (i = 0, ilen=a0.length; i < ilen; ++i) {
5979 v0 = a0[i];
5980 v1 = a1[i];
Jian Li46770fc2016-08-03 02:32:45 +09005981
Steven Burrows96ee21e2017-07-11 19:49:45 +01005982 if (v0 instanceof Array && v1 instanceof Array) {
5983 if (!helpers.arrayEquals(v0, v1)) {
5984 return false;
5985 }
5986 } else if (v0 !== v1) {
5987 // NOTE: two different object instances will never be equal: {x:20} != {x:20}
5988 return false;
5989 }
5990 }
Jian Li46770fc2016-08-03 02:32:45 +09005991
Steven Burrows96ee21e2017-07-11 19:49:45 +01005992 return true;
5993 };
5994 helpers.callback = function(fn, args, thisArg) {
5995 if (fn && typeof fn.call === 'function') {
5996 fn.apply(thisArg, args);
5997 }
5998 };
5999 helpers.getHoverColor = function(colorValue) {
6000 /* global CanvasPattern */
6001 return (colorValue instanceof CanvasPattern) ?
6002 colorValue :
6003 helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
6004 };
Jian Li46770fc2016-08-03 02:32:45 +09006005
Steven Burrows96ee21e2017-07-11 19:49:45 +01006006 /**
6007 * Provided for backward compatibility, use Chart.helpers#callback instead.
6008 * @function Chart.helpers#callCallback
6009 * @deprecated since version 2.6.0
6010 * @todo remove at version 3
6011 */
6012 helpers.callCallback = helpers.callback;
6013 };
Jian Li46770fc2016-08-03 02:32:45 +09006014
6015},{"3":3}],27:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01006016 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09006017
Steven Burrows96ee21e2017-07-11 19:49:45 +01006018 module.exports = function(Chart) {
6019 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09006020
Steven Burrows96ee21e2017-07-11 19:49:45 +01006021 /**
6022 * Helper function to get relative position for an event
6023 * @param {Event|IEvent} event - The event to get the position for
6024 * @param {Chart} chart - The chart
6025 * @returns {Point} the event position
6026 */
6027 function getRelativePosition(e, chart) {
6028 if (e.native) {
6029 return {
6030 x: e.x,
6031 y: e.y
6032 };
6033 }
Jian Li46770fc2016-08-03 02:32:45 +09006034
Steven Burrows96ee21e2017-07-11 19:49:45 +01006035 return helpers.getRelativePosition(e, chart);
6036 }
Jian Li46770fc2016-08-03 02:32:45 +09006037
Steven Burrows96ee21e2017-07-11 19:49:45 +01006038 /**
6039 * Helper function to traverse all of the visible elements in the chart
6040 * @param chart {chart} the chart
6041 * @param handler {Function} the callback to execute for each visible item
6042 */
6043 function parseVisibleItems(chart, handler) {
6044 var datasets = chart.data.datasets;
6045 var meta, i, j, ilen, jlen;
Jian Li46770fc2016-08-03 02:32:45 +09006046
Steven Burrows96ee21e2017-07-11 19:49:45 +01006047 for (i = 0, ilen = datasets.length; i < ilen; ++i) {
6048 if (!chart.isDatasetVisible(i)) {
6049 continue;
6050 }
Jian Li46770fc2016-08-03 02:32:45 +09006051
Steven Burrows96ee21e2017-07-11 19:49:45 +01006052 meta = chart.getDatasetMeta(i);
6053 for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
6054 var element = meta.data[j];
6055 if (!element._view.skip) {
6056 handler(element);
6057 }
6058 }
6059 }
6060 }
Jian Li46770fc2016-08-03 02:32:45 +09006061
Steven Burrows96ee21e2017-07-11 19:49:45 +01006062 /**
6063 * Helper function to get the items that intersect the event position
6064 * @param items {ChartElement[]} elements to filter
6065 * @param position {Point} the point to be nearest to
6066 * @return {ChartElement[]} the nearest items
6067 */
6068 function getIntersectItems(chart, position) {
6069 var elements = [];
Jian Li46770fc2016-08-03 02:32:45 +09006070
Steven Burrows96ee21e2017-07-11 19:49:45 +01006071 parseVisibleItems(chart, function(element) {
6072 if (element.inRange(position.x, position.y)) {
6073 elements.push(element);
6074 }
6075 });
Jian Li46770fc2016-08-03 02:32:45 +09006076
Steven Burrows96ee21e2017-07-11 19:49:45 +01006077 return elements;
6078 }
Jian Li46770fc2016-08-03 02:32:45 +09006079
Steven Burrows96ee21e2017-07-11 19:49:45 +01006080 /**
6081 * Helper function to get the items nearest to the event position considering all visible items in teh chart
6082 * @param chart {Chart} the chart to look at elements from
6083 * @param position {Point} the point to be nearest to
6084 * @param intersect {Boolean} if true, only consider items that intersect the position
6085 * @param distanceMetric {Function} Optional function to provide the distance between
6086 * @return {ChartElement[]} the nearest items
6087 */
6088 function getNearestItems(chart, position, intersect, distanceMetric) {
6089 var minDistance = Number.POSITIVE_INFINITY;
6090 var nearestItems = [];
Jian Li46770fc2016-08-03 02:32:45 +09006091
Steven Burrows96ee21e2017-07-11 19:49:45 +01006092 if (!distanceMetric) {
6093 distanceMetric = helpers.distanceBetweenPoints;
6094 }
Jian Li46770fc2016-08-03 02:32:45 +09006095
Steven Burrows96ee21e2017-07-11 19:49:45 +01006096 parseVisibleItems(chart, function(element) {
6097 if (intersect && !element.inRange(position.x, position.y)) {
6098 return;
6099 }
Jian Li46770fc2016-08-03 02:32:45 +09006100
Steven Burrows96ee21e2017-07-11 19:49:45 +01006101 var center = element.getCenterPoint();
6102 var distance = distanceMetric(position, center);
Jian Li46770fc2016-08-03 02:32:45 +09006103
Steven Burrows96ee21e2017-07-11 19:49:45 +01006104 if (distance < minDistance) {
6105 nearestItems = [element];
6106 minDistance = distance;
6107 } else if (distance === minDistance) {
6108 // Can have multiple items at the same distance in which case we sort by size
6109 nearestItems.push(element);
6110 }
6111 });
Jian Li46770fc2016-08-03 02:32:45 +09006112
Steven Burrows96ee21e2017-07-11 19:49:45 +01006113 return nearestItems;
6114 }
Jian Li46770fc2016-08-03 02:32:45 +09006115
Steven Burrows96ee21e2017-07-11 19:49:45 +01006116 function indexMode(chart, e, options) {
6117 var position = getRelativePosition(e, chart);
6118 var distanceMetric = function(pt1, pt2) {
6119 return Math.abs(pt1.x - pt2.x);
6120 };
6121 var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
6122 var elements = [];
Jian Li46770fc2016-08-03 02:32:45 +09006123
Steven Burrows96ee21e2017-07-11 19:49:45 +01006124 if (!items.length) {
6125 return [];
6126 }
Jian Li46770fc2016-08-03 02:32:45 +09006127
Steven Burrows96ee21e2017-07-11 19:49:45 +01006128 chart.data.datasets.forEach(function(dataset, datasetIndex) {
6129 if (chart.isDatasetVisible(datasetIndex)) {
6130 var meta = chart.getDatasetMeta(datasetIndex),
6131 element = meta.data[items[0]._index];
Jian Li46770fc2016-08-03 02:32:45 +09006132
Steven Burrows96ee21e2017-07-11 19:49:45 +01006133 // don't count items that are skipped (null data)
6134 if (element && !element._view.skip) {
6135 elements.push(element);
6136 }
6137 }
6138 });
Jian Li46770fc2016-08-03 02:32:45 +09006139
Steven Burrows96ee21e2017-07-11 19:49:45 +01006140 return elements;
6141 }
Jian Li46770fc2016-08-03 02:32:45 +09006142
Steven Burrows96ee21e2017-07-11 19:49:45 +01006143 /**
6144 * @interface IInteractionOptions
6145 */
6146 /**
6147 * If true, only consider items that intersect the point
6148 * @name IInterfaceOptions#boolean
6149 * @type Boolean
6150 */
6151
6152 /**
6153 * Contains interaction related functions
6154 * @namespace Chart.Interaction
6155 */
6156 Chart.Interaction = {
6157 // Helper function for different modes
6158 modes: {
6159 single: function(chart, e) {
6160 var position = getRelativePosition(e, chart);
6161 var elements = [];
6162
6163 parseVisibleItems(chart, function(element) {
6164 if (element.inRange(position.x, position.y)) {
6165 elements.push(element);
6166 return elements;
6167 }
6168 });
6169
6170 return elements.slice(0, 1);
6171 },
6172
6173 /**
6174 * @function Chart.Interaction.modes.label
6175 * @deprecated since version 2.4.0
6176 * @todo remove at version 3
6177 * @private
6178 */
6179 label: indexMode,
6180
6181 /**
6182 * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
6183 * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
6184 * @function Chart.Interaction.modes.index
6185 * @since v2.4.0
6186 * @param chart {chart} the chart we are returning items from
6187 * @param e {Event} the event we are find things at
6188 * @param options {IInteractionOptions} options to use during interaction
6189 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6190 */
6191 index: indexMode,
6192
6193 /**
6194 * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
6195 * If the options.intersect is false, we find the nearest item and return the items in that dataset
6196 * @function Chart.Interaction.modes.dataset
6197 * @param chart {chart} the chart we are returning items from
6198 * @param e {Event} the event we are find things at
6199 * @param options {IInteractionOptions} options to use during interaction
6200 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6201 */
6202 dataset: function(chart, e, options) {
6203 var position = getRelativePosition(e, chart);
6204 var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false);
6205
6206 if (items.length > 0) {
6207 items = chart.getDatasetMeta(items[0]._datasetIndex).data;
6208 }
6209
6210 return items;
6211 },
6212
6213 /**
6214 * @function Chart.Interaction.modes.x-axis
6215 * @deprecated since version 2.4.0. Use index mode and intersect == true
6216 * @todo remove at version 3
6217 * @private
6218 */
6219 'x-axis': function(chart, e) {
6220 return indexMode(chart, e, true);
6221 },
6222
6223 /**
6224 * Point mode returns all elements that hit test based on the event position
6225 * of the event
6226 * @function Chart.Interaction.modes.intersect
6227 * @param chart {chart} the chart we are returning items from
6228 * @param e {Event} the event we are find things at
6229 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6230 */
6231 point: function(chart, e) {
6232 var position = getRelativePosition(e, chart);
6233 return getIntersectItems(chart, position);
6234 },
6235
6236 /**
6237 * nearest mode returns the element closest to the point
6238 * @function Chart.Interaction.modes.intersect
6239 * @param chart {chart} the chart we are returning items from
6240 * @param e {Event} the event we are find things at
6241 * @param options {IInteractionOptions} options to use
6242 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6243 */
6244 nearest: function(chart, e, options) {
6245 var position = getRelativePosition(e, chart);
6246 var nearestItems = getNearestItems(chart, position, options.intersect);
6247
6248 // We have multiple items at the same distance from the event. Now sort by smallest
6249 if (nearestItems.length > 1) {
6250 nearestItems.sort(function(a, b) {
6251 var sizeA = a.getArea();
6252 var sizeB = b.getArea();
6253 var ret = sizeA - sizeB;
6254
6255 if (ret === 0) {
6256 // if equal sort by dataset index
6257 ret = a._datasetIndex - b._datasetIndex;
6258 }
6259
6260 return ret;
6261 });
6262 }
6263
6264 // Return only 1 item
6265 return nearestItems.slice(0, 1);
6266 },
6267
6268 /**
6269 * x mode returns the elements that hit-test at the current x coordinate
6270 * @function Chart.Interaction.modes.x
6271 * @param chart {chart} the chart we are returning items from
6272 * @param e {Event} the event we are find things at
6273 * @param options {IInteractionOptions} options to use
6274 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6275 */
6276 x: function(chart, e, options) {
6277 var position = getRelativePosition(e, chart);
6278 var items = [];
6279 var intersectsItem = false;
6280
6281 parseVisibleItems(chart, function(element) {
6282 if (element.inXRange(position.x)) {
6283 items.push(element);
6284 }
6285
6286 if (element.inRange(position.x, position.y)) {
6287 intersectsItem = true;
6288 }
6289 });
6290
6291 // If we want to trigger on an intersect and we don't have any items
6292 // that intersect the position, return nothing
6293 if (options.intersect && !intersectsItem) {
6294 items = [];
6295 }
6296 return items;
6297 },
6298
6299 /**
6300 * y mode returns the elements that hit-test at the current y coordinate
6301 * @function Chart.Interaction.modes.y
6302 * @param chart {chart} the chart we are returning items from
6303 * @param e {Event} the event we are find things at
6304 * @param options {IInteractionOptions} options to use
6305 * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6306 */
6307 y: function(chart, e, options) {
6308 var position = getRelativePosition(e, chart);
6309 var items = [];
6310 var intersectsItem = false;
6311
6312 parseVisibleItems(chart, function(element) {
6313 if (element.inYRange(position.y)) {
6314 items.push(element);
6315 }
6316
6317 if (element.inRange(position.x, position.y)) {
6318 intersectsItem = true;
6319 }
6320 });
6321
6322 // If we want to trigger on an intersect and we don't have any items
6323 // that intersect the position, return nothing
6324 if (options.intersect && !intersectsItem) {
6325 items = [];
6326 }
6327 return items;
6328 }
6329 }
6330 };
6331 };
Jian Li46770fc2016-08-03 02:32:45 +09006332
6333},{}],28:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01006334 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09006335
Steven Burrows96ee21e2017-07-11 19:49:45 +01006336 module.exports = function() {
Jian Li46770fc2016-08-03 02:32:45 +09006337
Steven Burrows96ee21e2017-07-11 19:49:45 +01006338 // Occupy the global variable of Chart, and create a simple base class
6339 var Chart = function(item, config) {
6340 this.construct(item, config);
6341 return this;
6342 };
Jian Li46770fc2016-08-03 02:32:45 +09006343
Steven Burrows96ee21e2017-07-11 19:49:45 +01006344 // Globally expose the defaults to allow for user updating/changing
6345 Chart.defaults = {
6346 global: {
6347 responsive: true,
6348 responsiveAnimationDuration: 0,
6349 maintainAspectRatio: true,
6350 events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
6351 hover: {
6352 onHover: null,
6353 mode: 'nearest',
6354 intersect: true,
6355 animationDuration: 400
6356 },
6357 onClick: null,
6358 defaultColor: 'rgba(0,0,0,0.1)',
6359 defaultFontColor: '#666',
6360 defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
6361 defaultFontSize: 12,
6362 defaultFontStyle: 'normal',
6363 showLines: true,
Jian Li46770fc2016-08-03 02:32:45 +09006364
Steven Burrows96ee21e2017-07-11 19:49:45 +01006365 // Element defaults defined in element extensions
6366 elements: {},
Jian Li46770fc2016-08-03 02:32:45 +09006367
Steven Burrows96ee21e2017-07-11 19:49:45 +01006368 // Legend callback string
6369 legendCallback: function(chart) {
6370 var text = [];
6371 text.push('<ul class="' + chart.id + '-legend">');
6372 for (var i = 0; i < chart.data.datasets.length; i++) {
6373 text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
6374 if (chart.data.datasets[i].label) {
6375 text.push(chart.data.datasets[i].label);
6376 }
6377 text.push('</li>');
6378 }
6379 text.push('</ul>');
Jian Li46770fc2016-08-03 02:32:45 +09006380
Steven Burrows96ee21e2017-07-11 19:49:45 +01006381 return text.join('');
6382 }
6383 }
6384 };
Jian Li46770fc2016-08-03 02:32:45 +09006385
Steven Burrows96ee21e2017-07-11 19:49:45 +01006386 Chart.Chart = Chart;
Jian Li46770fc2016-08-03 02:32:45 +09006387
Steven Burrows96ee21e2017-07-11 19:49:45 +01006388 return Chart;
6389 };
Jian Li46770fc2016-08-03 02:32:45 +09006390
6391},{}],29:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01006392 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09006393
Steven Burrows96ee21e2017-07-11 19:49:45 +01006394 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09006395
Steven Burrows96ee21e2017-07-11 19:49:45 +01006396 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09006397
Steven Burrows96ee21e2017-07-11 19:49:45 +01006398 function filterByPosition(array, position) {
6399 return helpers.where(array, function(v) {
6400 return v.position === position;
6401 });
6402 }
Jian Li46770fc2016-08-03 02:32:45 +09006403
Steven Burrows96ee21e2017-07-11 19:49:45 +01006404 function sortByWeight(array, reverse) {
6405 array.forEach(function(v, i) {
6406 v._tmpIndex_ = i;
6407 return v;
6408 });
6409 array.sort(function(a, b) {
6410 var v0 = reverse ? b : a;
6411 var v1 = reverse ? a : b;
6412 return v0.weight === v1.weight ?
6413 v0._tmpIndex_ - v1._tmpIndex_ :
6414 v0.weight - v1.weight;
6415 });
6416 array.forEach(function(v) {
6417 delete v._tmpIndex_;
6418 });
6419 }
Jian Li46770fc2016-08-03 02:32:45 +09006420
Steven Burrows96ee21e2017-07-11 19:49:45 +01006421 /**
6422 * @interface ILayoutItem
6423 * @prop {String} position - The position of the item in the chart layout. Possible values are
6424 * 'left', 'top', 'right', 'bottom', and 'chartArea'
6425 * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area
6426 * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down
6427 * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)
6428 * @prop {Function} update - Takes two parameters: width and height. Returns size of item
6429 * @prop {Function} getPadding - Returns an object with padding on the edges
6430 * @prop {Number} width - Width of item. Must be valid after update()
6431 * @prop {Number} height - Height of item. Must be valid after update()
6432 * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update
6433 * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update
6434 * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update
6435 * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update
6436 */
Jian Li46770fc2016-08-03 02:32:45 +09006437
Steven Burrows96ee21e2017-07-11 19:49:45 +01006438 // The layout service is very self explanatory. It's responsible for the layout within a chart.
6439 // Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
6440 // It is this service's responsibility of carrying out that layout.
6441 Chart.layoutService = {
6442 defaults: {},
Jian Li46770fc2016-08-03 02:32:45 +09006443
Steven Burrows96ee21e2017-07-11 19:49:45 +01006444 /**
6445 * Register a box to a chart.
6446 * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
6447 * @param {Chart} chart - the chart to use
6448 * @param {ILayoutItem} item - the item to add to be layed out
6449 */
6450 addBox: function(chart, item) {
6451 if (!chart.boxes) {
6452 chart.boxes = [];
6453 }
Jian Li46770fc2016-08-03 02:32:45 +09006454
Steven Burrows96ee21e2017-07-11 19:49:45 +01006455 // initialize item with default values
6456 item.fullWidth = item.fullWidth || false;
6457 item.position = item.position || 'top';
6458 item.weight = item.weight || 0;
Jian Li46770fc2016-08-03 02:32:45 +09006459
Steven Burrows96ee21e2017-07-11 19:49:45 +01006460 chart.boxes.push(item);
6461 },
Jian Li46770fc2016-08-03 02:32:45 +09006462
Steven Burrows96ee21e2017-07-11 19:49:45 +01006463 /**
6464 * Remove a layoutItem from a chart
6465 * @param {Chart} chart - the chart to remove the box from
6466 * @param {Object} layoutItem - the item to remove from the layout
6467 */
6468 removeBox: function(chart, layoutItem) {
6469 var index = chart.boxes? chart.boxes.indexOf(layoutItem) : -1;
6470 if (index !== -1) {
6471 chart.boxes.splice(index, 1);
6472 }
6473 },
Jian Li46770fc2016-08-03 02:32:45 +09006474
Steven Burrows96ee21e2017-07-11 19:49:45 +01006475 /**
6476 * Sets (or updates) options on the given `item`.
6477 * @param {Chart} chart - the chart in which the item lives (or will be added to)
6478 * @param {Object} item - the item to configure with the given options
6479 * @param {Object} options - the new item options.
6480 */
6481 configure: function(chart, item, options) {
6482 var props = ['fullWidth', 'position', 'weight'];
6483 var ilen = props.length;
6484 var i = 0;
6485 var prop;
Jian Li46770fc2016-08-03 02:32:45 +09006486
Steven Burrows96ee21e2017-07-11 19:49:45 +01006487 for (; i<ilen; ++i) {
6488 prop = props[i];
6489 if (options.hasOwnProperty(prop)) {
6490 item[prop] = options[prop];
6491 }
6492 }
6493 },
Jian Li46770fc2016-08-03 02:32:45 +09006494
Steven Burrows96ee21e2017-07-11 19:49:45 +01006495 /**
6496 * Fits boxes of the given chart into the given size by having each box measure itself
6497 * then running a fitting algorithm
6498 * @param {Chart} chart - the chart
6499 * @param {Number} width - the width to fit into
6500 * @param {Number} height - the height to fit into
6501 */
6502 update: function(chart, width, height) {
6503 if (!chart) {
6504 return;
6505 }
Jian Li46770fc2016-08-03 02:32:45 +09006506
Steven Burrows96ee21e2017-07-11 19:49:45 +01006507 var layoutOptions = chart.options.layout;
6508 var padding = layoutOptions ? layoutOptions.padding : null;
Jian Li46770fc2016-08-03 02:32:45 +09006509
Steven Burrows96ee21e2017-07-11 19:49:45 +01006510 var leftPadding = 0;
6511 var rightPadding = 0;
6512 var topPadding = 0;
6513 var bottomPadding = 0;
Jian Li46770fc2016-08-03 02:32:45 +09006514
Steven Burrows96ee21e2017-07-11 19:49:45 +01006515 if (!isNaN(padding)) {
6516 // options.layout.padding is a number. assign to all
6517 leftPadding = padding;
6518 rightPadding = padding;
6519 topPadding = padding;
6520 bottomPadding = padding;
6521 } else {
6522 leftPadding = padding.left || 0;
6523 rightPadding = padding.right || 0;
6524 topPadding = padding.top || 0;
6525 bottomPadding = padding.bottom || 0;
6526 }
Jian Li46770fc2016-08-03 02:32:45 +09006527
Steven Burrows96ee21e2017-07-11 19:49:45 +01006528 var leftBoxes = filterByPosition(chart.boxes, 'left');
6529 var rightBoxes = filterByPosition(chart.boxes, 'right');
6530 var topBoxes = filterByPosition(chart.boxes, 'top');
6531 var bottomBoxes = filterByPosition(chart.boxes, 'bottom');
6532 var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea');
Jian Li46770fc2016-08-03 02:32:45 +09006533
Steven Burrows96ee21e2017-07-11 19:49:45 +01006534 // Sort boxes by weight. A higher weight is further away from the chart area
6535 sortByWeight(leftBoxes, true);
6536 sortByWeight(rightBoxes, false);
6537 sortByWeight(topBoxes, true);
6538 sortByWeight(bottomBoxes, false);
Jian Li46770fc2016-08-03 02:32:45 +09006539
Steven Burrows96ee21e2017-07-11 19:49:45 +01006540 // Essentially we now have any number of boxes on each of the 4 sides.
6541 // Our canvas looks like the following.
6542 // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
6543 // B1 is the bottom axis
6544 // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
6545 // These locations are single-box locations only, when trying to register a chartArea location that is already taken,
6546 // an error will be thrown.
6547 //
6548 // |----------------------------------------------------|
6549 // | T1 (Full Width) |
6550 // |----------------------------------------------------|
6551 // | | | T2 | |
6552 // | |----|-------------------------------------|----|
6553 // | | | C1 | | C2 | |
6554 // | | |----| |----| |
6555 // | | | | |
6556 // | L1 | L2 | ChartArea (C0) | R1 |
6557 // | | | | |
6558 // | | |----| |----| |
6559 // | | | C3 | | C4 | |
6560 // | |----|-------------------------------------|----|
6561 // | | | B1 | |
6562 // |----------------------------------------------------|
6563 // | B2 (Full Width) |
6564 // |----------------------------------------------------|
6565 //
6566 // What we do to find the best sizing, we do the following
6567 // 1. Determine the minimum size of the chart area.
6568 // 2. Split the remaining width equally between each vertical axis
6569 // 3. Split the remaining height equally between each horizontal axis
6570 // 4. Give each layout the maximum size it can be. The layout will return it's minimum size
6571 // 5. Adjust the sizes of each axis based on it's minimum reported size.
6572 // 6. Refit each axis
6573 // 7. Position each axis in the final location
6574 // 8. Tell the chart the final location of the chart area
6575 // 9. Tell any axes that overlay the chart area the positions of the chart area
Jian Li46770fc2016-08-03 02:32:45 +09006576
Steven Burrows96ee21e2017-07-11 19:49:45 +01006577 // Step 1
6578 var chartWidth = width - leftPadding - rightPadding;
6579 var chartHeight = height - topPadding - bottomPadding;
6580 var chartAreaWidth = chartWidth / 2; // min 50%
6581 var chartAreaHeight = chartHeight / 2; // min 50%
Jian Li46770fc2016-08-03 02:32:45 +09006582
Steven Burrows96ee21e2017-07-11 19:49:45 +01006583 // Step 2
6584 var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);
Jian Li46770fc2016-08-03 02:32:45 +09006585
Steven Burrows96ee21e2017-07-11 19:49:45 +01006586 // Step 3
6587 var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);
Jian Li46770fc2016-08-03 02:32:45 +09006588
Steven Burrows96ee21e2017-07-11 19:49:45 +01006589 // Step 4
6590 var maxChartAreaWidth = chartWidth;
6591 var maxChartAreaHeight = chartHeight;
6592 var minBoxSizes = [];
Jian Li46770fc2016-08-03 02:32:45 +09006593
Steven Burrows96ee21e2017-07-11 19:49:45 +01006594 function getMinimumBoxSize(box) {
6595 var minSize;
6596 var isHorizontal = box.isHorizontal();
Jian Li46770fc2016-08-03 02:32:45 +09006597
Steven Burrows96ee21e2017-07-11 19:49:45 +01006598 if (isHorizontal) {
6599 minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
6600 maxChartAreaHeight -= minSize.height;
6601 } else {
6602 minSize = box.update(verticalBoxWidth, chartAreaHeight);
6603 maxChartAreaWidth -= minSize.width;
6604 }
Jian Li46770fc2016-08-03 02:32:45 +09006605
Steven Burrows96ee21e2017-07-11 19:49:45 +01006606 minBoxSizes.push({
6607 horizontal: isHorizontal,
6608 minSize: minSize,
6609 box: box,
6610 });
6611 }
Jian Li46770fc2016-08-03 02:32:45 +09006612
Steven Burrows96ee21e2017-07-11 19:49:45 +01006613 helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);
Jian Li46770fc2016-08-03 02:32:45 +09006614
Steven Burrows96ee21e2017-07-11 19:49:45 +01006615 // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478)
6616 var maxHorizontalLeftPadding = 0;
6617 var maxHorizontalRightPadding = 0;
6618 var maxVerticalTopPadding = 0;
6619 var maxVerticalBottomPadding = 0;
Jian Li46770fc2016-08-03 02:32:45 +09006620
Steven Burrows96ee21e2017-07-11 19:49:45 +01006621 helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) {
6622 if (horizontalBox.getPadding) {
6623 var boxPadding = horizontalBox.getPadding();
6624 maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left);
6625 maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right);
6626 }
6627 });
Jian Li46770fc2016-08-03 02:32:45 +09006628
Steven Burrows96ee21e2017-07-11 19:49:45 +01006629 helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) {
6630 if (verticalBox.getPadding) {
6631 var boxPadding = verticalBox.getPadding();
6632 maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top);
6633 maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom);
6634 }
6635 });
Jian Li46770fc2016-08-03 02:32:45 +09006636
Steven Burrows96ee21e2017-07-11 19:49:45 +01006637 // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
6638 // be if the axes are drawn at their minimum sizes.
6639 // Steps 5 & 6
6640 var totalLeftBoxesWidth = leftPadding;
6641 var totalRightBoxesWidth = rightPadding;
6642 var totalTopBoxesHeight = topPadding;
6643 var totalBottomBoxesHeight = bottomPadding;
Jian Li46770fc2016-08-03 02:32:45 +09006644
Steven Burrows96ee21e2017-07-11 19:49:45 +01006645 // Function to fit a box
6646 function fitBox(box) {
6647 var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) {
6648 return minBox.box === box;
6649 });
Jian Li46770fc2016-08-03 02:32:45 +09006650
Steven Burrows96ee21e2017-07-11 19:49:45 +01006651 if (minBoxSize) {
6652 if (box.isHorizontal()) {
6653 var scaleMargin = {
6654 left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding),
6655 right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding),
6656 top: 0,
6657 bottom: 0
6658 };
Jian Li46770fc2016-08-03 02:32:45 +09006659
Steven Burrows96ee21e2017-07-11 19:49:45 +01006660 // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
6661 // on the margin. Sometimes they need to increase in size slightly
6662 box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
6663 } else {
6664 box.update(minBoxSize.minSize.width, maxChartAreaHeight);
6665 }
6666 }
6667 }
Jian Li46770fc2016-08-03 02:32:45 +09006668
Steven Burrows96ee21e2017-07-11 19:49:45 +01006669 // Update, and calculate the left and right margins for the horizontal boxes
6670 helpers.each(leftBoxes.concat(rightBoxes), fitBox);
Jian Li46770fc2016-08-03 02:32:45 +09006671
Steven Burrows96ee21e2017-07-11 19:49:45 +01006672 helpers.each(leftBoxes, function(box) {
6673 totalLeftBoxesWidth += box.width;
6674 });
Jian Li46770fc2016-08-03 02:32:45 +09006675
Steven Burrows96ee21e2017-07-11 19:49:45 +01006676 helpers.each(rightBoxes, function(box) {
6677 totalRightBoxesWidth += box.width;
6678 });
Jian Li46770fc2016-08-03 02:32:45 +09006679
Steven Burrows96ee21e2017-07-11 19:49:45 +01006680 // Set the Left and Right margins for the horizontal boxes
6681 helpers.each(topBoxes.concat(bottomBoxes), fitBox);
Jian Li46770fc2016-08-03 02:32:45 +09006682
Steven Burrows96ee21e2017-07-11 19:49:45 +01006683 // Figure out how much margin is on the top and bottom of the vertical boxes
6684 helpers.each(topBoxes, function(box) {
6685 totalTopBoxesHeight += box.height;
6686 });
Jian Li46770fc2016-08-03 02:32:45 +09006687
Steven Burrows96ee21e2017-07-11 19:49:45 +01006688 helpers.each(bottomBoxes, function(box) {
6689 totalBottomBoxesHeight += box.height;
6690 });
Jian Li46770fc2016-08-03 02:32:45 +09006691
Steven Burrows96ee21e2017-07-11 19:49:45 +01006692 function finalFitVerticalBox(box) {
6693 var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) {
6694 return minSize.box === box;
6695 });
Jian Li46770fc2016-08-03 02:32:45 +09006696
Steven Burrows96ee21e2017-07-11 19:49:45 +01006697 var scaleMargin = {
6698 left: 0,
6699 right: 0,
6700 top: totalTopBoxesHeight,
6701 bottom: totalBottomBoxesHeight
6702 };
Jian Li46770fc2016-08-03 02:32:45 +09006703
Steven Burrows96ee21e2017-07-11 19:49:45 +01006704 if (minBoxSize) {
6705 box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
6706 }
6707 }
Jian Li46770fc2016-08-03 02:32:45 +09006708
Steven Burrows96ee21e2017-07-11 19:49:45 +01006709 // Let the left layout know the final margin
6710 helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);
Jian Li46770fc2016-08-03 02:32:45 +09006711
Steven Burrows96ee21e2017-07-11 19:49:45 +01006712 // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
6713 totalLeftBoxesWidth = leftPadding;
6714 totalRightBoxesWidth = rightPadding;
6715 totalTopBoxesHeight = topPadding;
6716 totalBottomBoxesHeight = bottomPadding;
Jian Li46770fc2016-08-03 02:32:45 +09006717
Steven Burrows96ee21e2017-07-11 19:49:45 +01006718 helpers.each(leftBoxes, function(box) {
6719 totalLeftBoxesWidth += box.width;
6720 });
Jian Li46770fc2016-08-03 02:32:45 +09006721
Steven Burrows96ee21e2017-07-11 19:49:45 +01006722 helpers.each(rightBoxes, function(box) {
6723 totalRightBoxesWidth += box.width;
6724 });
Jian Li46770fc2016-08-03 02:32:45 +09006725
Steven Burrows96ee21e2017-07-11 19:49:45 +01006726 helpers.each(topBoxes, function(box) {
6727 totalTopBoxesHeight += box.height;
6728 });
6729 helpers.each(bottomBoxes, function(box) {
6730 totalBottomBoxesHeight += box.height;
6731 });
Jian Li46770fc2016-08-03 02:32:45 +09006732
Steven Burrows96ee21e2017-07-11 19:49:45 +01006733 // We may be adding some padding to account for rotated x axis labels
6734 var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0);
6735 totalLeftBoxesWidth += leftPaddingAddition;
6736 totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0);
Jian Li46770fc2016-08-03 02:32:45 +09006737
Steven Burrows96ee21e2017-07-11 19:49:45 +01006738 var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0);
6739 totalTopBoxesHeight += topPaddingAddition;
6740 totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0);
Jian Li46770fc2016-08-03 02:32:45 +09006741
Steven Burrows96ee21e2017-07-11 19:49:45 +01006742 // Figure out if our chart area changed. This would occur if the dataset layout label rotation
6743 // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
6744 // without calling `fit` again
6745 var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
6746 var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;
Jian Li46770fc2016-08-03 02:32:45 +09006747
Steven Burrows96ee21e2017-07-11 19:49:45 +01006748 if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
6749 helpers.each(leftBoxes, function(box) {
6750 box.height = newMaxChartAreaHeight;
6751 });
Jian Li46770fc2016-08-03 02:32:45 +09006752
Steven Burrows96ee21e2017-07-11 19:49:45 +01006753 helpers.each(rightBoxes, function(box) {
6754 box.height = newMaxChartAreaHeight;
6755 });
Jian Li46770fc2016-08-03 02:32:45 +09006756
Steven Burrows96ee21e2017-07-11 19:49:45 +01006757 helpers.each(topBoxes, function(box) {
6758 if (!box.fullWidth) {
6759 box.width = newMaxChartAreaWidth;
6760 }
6761 });
Jian Li46770fc2016-08-03 02:32:45 +09006762
Steven Burrows96ee21e2017-07-11 19:49:45 +01006763 helpers.each(bottomBoxes, function(box) {
6764 if (!box.fullWidth) {
6765 box.width = newMaxChartAreaWidth;
6766 }
6767 });
Jian Li46770fc2016-08-03 02:32:45 +09006768
Steven Burrows96ee21e2017-07-11 19:49:45 +01006769 maxChartAreaHeight = newMaxChartAreaHeight;
6770 maxChartAreaWidth = newMaxChartAreaWidth;
6771 }
Jian Li46770fc2016-08-03 02:32:45 +09006772
Steven Burrows96ee21e2017-07-11 19:49:45 +01006773 // Step 7 - Position the boxes
6774 var left = leftPadding + leftPaddingAddition;
6775 var top = topPadding + topPaddingAddition;
Jian Li46770fc2016-08-03 02:32:45 +09006776
Steven Burrows96ee21e2017-07-11 19:49:45 +01006777 function placeBox(box) {
6778 if (box.isHorizontal()) {
6779 box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth;
6780 box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth;
6781 box.top = top;
6782 box.bottom = top + box.height;
Jian Li46770fc2016-08-03 02:32:45 +09006783
Steven Burrows96ee21e2017-07-11 19:49:45 +01006784 // Move to next point
6785 top = box.bottom;
Jian Li46770fc2016-08-03 02:32:45 +09006786
Steven Burrows96ee21e2017-07-11 19:49:45 +01006787 } else {
Jian Li46770fc2016-08-03 02:32:45 +09006788
Steven Burrows96ee21e2017-07-11 19:49:45 +01006789 box.left = left;
6790 box.right = left + box.width;
6791 box.top = totalTopBoxesHeight;
6792 box.bottom = totalTopBoxesHeight + maxChartAreaHeight;
Jian Li46770fc2016-08-03 02:32:45 +09006793
Steven Burrows96ee21e2017-07-11 19:49:45 +01006794 // Move to next point
6795 left = box.right;
6796 }
6797 }
Jian Li46770fc2016-08-03 02:32:45 +09006798
Steven Burrows96ee21e2017-07-11 19:49:45 +01006799 helpers.each(leftBoxes.concat(topBoxes), placeBox);
Jian Li46770fc2016-08-03 02:32:45 +09006800
Steven Burrows96ee21e2017-07-11 19:49:45 +01006801 // Account for chart width and height
6802 left += maxChartAreaWidth;
6803 top += maxChartAreaHeight;
Jian Li46770fc2016-08-03 02:32:45 +09006804
Steven Burrows96ee21e2017-07-11 19:49:45 +01006805 helpers.each(rightBoxes, placeBox);
6806 helpers.each(bottomBoxes, placeBox);
Jian Li46770fc2016-08-03 02:32:45 +09006807
Steven Burrows96ee21e2017-07-11 19:49:45 +01006808 // Step 8
6809 chart.chartArea = {
6810 left: totalLeftBoxesWidth,
6811 top: totalTopBoxesHeight,
6812 right: totalLeftBoxesWidth + maxChartAreaWidth,
6813 bottom: totalTopBoxesHeight + maxChartAreaHeight
6814 };
Jian Li46770fc2016-08-03 02:32:45 +09006815
Steven Burrows96ee21e2017-07-11 19:49:45 +01006816 // Step 9
6817 helpers.each(chartAreaBoxes, function(box) {
6818 box.left = chart.chartArea.left;
6819 box.top = chart.chartArea.top;
6820 box.right = chart.chartArea.right;
6821 box.bottom = chart.chartArea.bottom;
Jian Li46770fc2016-08-03 02:32:45 +09006822
Steven Burrows96ee21e2017-07-11 19:49:45 +01006823 box.update(maxChartAreaWidth, maxChartAreaHeight);
6824 });
6825 }
6826 };
6827 };
Jian Li46770fc2016-08-03 02:32:45 +09006828
6829},{}],30:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01006830 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09006831
Steven Burrows96ee21e2017-07-11 19:49:45 +01006832 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09006833
Steven Burrows96ee21e2017-07-11 19:49:45 +01006834 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09006835
Steven Burrows96ee21e2017-07-11 19:49:45 +01006836 Chart.defaults.global.plugins = {};
Jian Li46770fc2016-08-03 02:32:45 +09006837
Steven Burrows96ee21e2017-07-11 19:49:45 +01006838 /**
6839 * The plugin service singleton
6840 * @namespace Chart.plugins
6841 * @since 2.1.0
6842 */
6843 Chart.plugins = {
6844 /**
6845 * Globally registered plugins.
6846 * @private
6847 */
6848 _plugins: [],
Jian Li46770fc2016-08-03 02:32:45 +09006849
Steven Burrows96ee21e2017-07-11 19:49:45 +01006850 /**
6851 * This identifier is used to invalidate the descriptors cache attached to each chart
6852 * when a global plugin is registered or unregistered. In this case, the cache ID is
6853 * incremented and descriptors are regenerated during following API calls.
6854 * @private
6855 */
6856 _cacheId: 0,
Jian Li46770fc2016-08-03 02:32:45 +09006857
Steven Burrows96ee21e2017-07-11 19:49:45 +01006858 /**
6859 * Registers the given plugin(s) if not already registered.
6860 * @param {Array|Object} plugins plugin instance(s).
6861 */
6862 register: function(plugins) {
6863 var p = this._plugins;
6864 ([]).concat(plugins).forEach(function(plugin) {
6865 if (p.indexOf(plugin) === -1) {
6866 p.push(plugin);
6867 }
6868 });
Jian Li46770fc2016-08-03 02:32:45 +09006869
Steven Burrows96ee21e2017-07-11 19:49:45 +01006870 this._cacheId++;
6871 },
Jian Li46770fc2016-08-03 02:32:45 +09006872
Steven Burrows96ee21e2017-07-11 19:49:45 +01006873 /**
6874 * Unregisters the given plugin(s) only if registered.
6875 * @param {Array|Object} plugins plugin instance(s).
6876 */
6877 unregister: function(plugins) {
6878 var p = this._plugins;
6879 ([]).concat(plugins).forEach(function(plugin) {
6880 var idx = p.indexOf(plugin);
6881 if (idx !== -1) {
6882 p.splice(idx, 1);
6883 }
6884 });
Jian Li46770fc2016-08-03 02:32:45 +09006885
Steven Burrows96ee21e2017-07-11 19:49:45 +01006886 this._cacheId++;
6887 },
Jian Li46770fc2016-08-03 02:32:45 +09006888
Steven Burrows96ee21e2017-07-11 19:49:45 +01006889 /**
6890 * Remove all registered plugins.
6891 * @since 2.1.5
6892 */
6893 clear: function() {
6894 this._plugins = [];
6895 this._cacheId++;
6896 },
Jian Li46770fc2016-08-03 02:32:45 +09006897
Steven Burrows96ee21e2017-07-11 19:49:45 +01006898 /**
6899 * Returns the number of registered plugins?
6900 * @returns {Number}
6901 * @since 2.1.5
6902 */
6903 count: function() {
6904 return this._plugins.length;
6905 },
Jian Li46770fc2016-08-03 02:32:45 +09006906
Steven Burrows96ee21e2017-07-11 19:49:45 +01006907 /**
6908 * Returns all registered plugin instances.
6909 * @returns {Array} array of plugin objects.
6910 * @since 2.1.5
6911 */
6912 getAll: function() {
6913 return this._plugins;
6914 },
Jian Li46770fc2016-08-03 02:32:45 +09006915
Steven Burrows96ee21e2017-07-11 19:49:45 +01006916 /**
6917 * Calls enabled plugins for `chart` on the specified hook and with the given args.
6918 * This method immediately returns as soon as a plugin explicitly returns false. The
6919 * returned value can be used, for instance, to interrupt the current action.
6920 * @param {Object} chart - The chart instance for which plugins should be called.
6921 * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
6922 * @param {Array} [args] - Extra arguments to apply to the hook call.
6923 * @returns {Boolean} false if any of the plugins return false, else returns true.
6924 */
6925 notify: function(chart, hook, args) {
6926 var descriptors = this.descriptors(chart);
6927 var ilen = descriptors.length;
6928 var i, descriptor, plugin, params, method;
Jian Li46770fc2016-08-03 02:32:45 +09006929
Steven Burrows96ee21e2017-07-11 19:49:45 +01006930 for (i=0; i<ilen; ++i) {
6931 descriptor = descriptors[i];
6932 plugin = descriptor.plugin;
6933 method = plugin[hook];
6934 if (typeof method === 'function') {
6935 params = [chart].concat(args || []);
6936 params.push(descriptor.options);
6937 if (method.apply(plugin, params) === false) {
6938 return false;
6939 }
6940 }
6941 }
Jian Li46770fc2016-08-03 02:32:45 +09006942
Steven Burrows96ee21e2017-07-11 19:49:45 +01006943 return true;
6944 },
Jian Li46770fc2016-08-03 02:32:45 +09006945
Steven Burrows96ee21e2017-07-11 19:49:45 +01006946 /**
6947 * Returns descriptors of enabled plugins for the given chart.
6948 * @returns {Array} [{ plugin, options }]
6949 * @private
6950 */
6951 descriptors: function(chart) {
6952 var cache = chart._plugins || (chart._plugins = {});
6953 if (cache.id === this._cacheId) {
6954 return cache.descriptors;
6955 }
Jian Li46770fc2016-08-03 02:32:45 +09006956
Steven Burrows96ee21e2017-07-11 19:49:45 +01006957 var plugins = [];
6958 var descriptors = [];
6959 var config = (chart && chart.config) || {};
6960 var defaults = Chart.defaults.global.plugins;
6961 var options = (config.options && config.options.plugins) || {};
Jian Li46770fc2016-08-03 02:32:45 +09006962
Steven Burrows96ee21e2017-07-11 19:49:45 +01006963 this._plugins.concat(config.plugins || []).forEach(function(plugin) {
6964 var idx = plugins.indexOf(plugin);
6965 if (idx !== -1) {
6966 return;
6967 }
Jian Li46770fc2016-08-03 02:32:45 +09006968
Steven Burrows96ee21e2017-07-11 19:49:45 +01006969 var id = plugin.id;
6970 var opts = options[id];
6971 if (opts === false) {
6972 return;
6973 }
6974
6975 if (opts === true) {
6976 opts = helpers.clone(defaults[id]);
6977 }
6978
6979 plugins.push(plugin);
6980 descriptors.push({
6981 plugin: plugin,
6982 options: opts || {}
6983 });
6984 });
6985
6986 cache.descriptors = descriptors;
6987 cache.id = this._cacheId;
6988 return descriptors;
6989 }
6990 };
6991
6992 /**
6993 * Plugin extension hooks.
6994 * @interface IPlugin
6995 * @since 2.1.0
6996 */
6997 /**
6998 * @method IPlugin#beforeInit
6999 * @desc Called before initializing `chart`.
7000 * @param {Chart.Controller} chart - The chart instance.
7001 * @param {Object} options - The plugin options.
7002 */
7003 /**
7004 * @method IPlugin#afterInit
7005 * @desc Called after `chart` has been initialized and before the first update.
7006 * @param {Chart.Controller} chart - The chart instance.
7007 * @param {Object} options - The plugin options.
7008 */
7009 /**
7010 * @method IPlugin#beforeUpdate
7011 * @desc Called before updating `chart`. If any plugin returns `false`, the update
7012 * is cancelled (and thus subsequent render(s)) until another `update` is triggered.
7013 * @param {Chart.Controller} chart - The chart instance.
7014 * @param {Object} options - The plugin options.
7015 * @returns {Boolean} `false` to cancel the chart update.
7016 */
7017 /**
7018 * @method IPlugin#afterUpdate
7019 * @desc Called after `chart` has been updated and before rendering. Note that this
7020 * hook will not be called if the chart update has been previously cancelled.
7021 * @param {Chart.Controller} chart - The chart instance.
7022 * @param {Object} options - The plugin options.
7023 */
7024 /**
7025 * @method IPlugin#beforeDatasetsUpdate
7026 * @desc Called before updating the `chart` datasets. If any plugin returns `false`,
7027 * the datasets update is cancelled until another `update` is triggered.
7028 * @param {Chart.Controller} chart - The chart instance.
7029 * @param {Object} options - The plugin options.
7030 * @returns {Boolean} false to cancel the datasets update.
7031 * @since version 2.1.5
7032 */
7033 /**
7034 * @method IPlugin#afterDatasetsUpdate
7035 * @desc Called after the `chart` datasets have been updated. Note that this hook
7036 * will not be called if the datasets update has been previously cancelled.
7037 * @param {Chart.Controller} chart - The chart instance.
7038 * @param {Object} options - The plugin options.
7039 * @since version 2.1.5
7040 */
7041 /**
7042 * @method IPlugin#beforeDatasetUpdate
7043 * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin
7044 * returns `false`, the datasets update is cancelled until another `update` is triggered.
7045 * @param {Chart} chart - The chart instance.
7046 * @param {Object} args - The call arguments.
7047 * @param {Object} args.index - The dataset index.
7048 * @param {Number} args.meta - The dataset metadata.
7049 * @param {Object} options - The plugin options.
7050 * @returns {Boolean} `false` to cancel the chart datasets drawing.
7051 */
7052 /**
7053 * @method IPlugin#afterDatasetUpdate
7054 * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note
7055 * that this hook will not be called if the datasets update has been previously cancelled.
7056 * @param {Chart} chart - The chart instance.
7057 * @param {Object} args - The call arguments.
7058 * @param {Object} args.index - The dataset index.
7059 * @param {Number} args.meta - The dataset metadata.
7060 * @param {Object} options - The plugin options.
7061 */
7062 /**
7063 * @method IPlugin#beforeLayout
7064 * @desc Called before laying out `chart`. If any plugin returns `false`,
7065 * the layout update is cancelled until another `update` is triggered.
7066 * @param {Chart.Controller} chart - The chart instance.
7067 * @param {Object} options - The plugin options.
7068 * @returns {Boolean} `false` to cancel the chart layout.
7069 */
7070 /**
7071 * @method IPlugin#afterLayout
7072 * @desc Called after the `chart` has been layed out. Note that this hook will not
7073 * be called if the layout update has been previously cancelled.
7074 * @param {Chart.Controller} chart - The chart instance.
7075 * @param {Object} options - The plugin options.
7076 */
7077 /**
7078 * @method IPlugin#beforeRender
7079 * @desc Called before rendering `chart`. If any plugin returns `false`,
7080 * the rendering is cancelled until another `render` is triggered.
7081 * @param {Chart.Controller} chart - The chart instance.
7082 * @param {Object} options - The plugin options.
7083 * @returns {Boolean} `false` to cancel the chart rendering.
7084 */
7085 /**
7086 * @method IPlugin#afterRender
7087 * @desc Called after the `chart` has been fully rendered (and animation completed). Note
7088 * that this hook will not be called if the rendering has been previously cancelled.
7089 * @param {Chart.Controller} chart - The chart instance.
7090 * @param {Object} options - The plugin options.
7091 */
7092 /**
7093 * @method IPlugin#beforeDraw
7094 * @desc Called before drawing `chart` at every animation frame specified by the given
7095 * easing value. If any plugin returns `false`, the frame drawing is cancelled until
7096 * another `render` is triggered.
7097 * @param {Chart.Controller} chart - The chart instance.
7098 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
7099 * @param {Object} options - The plugin options.
7100 * @returns {Boolean} `false` to cancel the chart drawing.
7101 */
7102 /**
7103 * @method IPlugin#afterDraw
7104 * @desc Called after the `chart` has been drawn for the specific easing value. Note
7105 * that this hook will not be called if the drawing has been previously cancelled.
7106 * @param {Chart.Controller} chart - The chart instance.
7107 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
7108 * @param {Object} options - The plugin options.
7109 */
7110 /**
7111 * @method IPlugin#beforeDatasetsDraw
7112 * @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
7113 * the datasets drawing is cancelled until another `render` is triggered.
7114 * @param {Chart.Controller} chart - The chart instance.
7115 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
7116 * @param {Object} options - The plugin options.
7117 * @returns {Boolean} `false` to cancel the chart datasets drawing.
7118 */
7119 /**
7120 * @method IPlugin#afterDatasetsDraw
7121 * @desc Called after the `chart` datasets have been drawn. Note that this hook
7122 * will not be called if the datasets drawing has been previously cancelled.
7123 * @param {Chart.Controller} chart - The chart instance.
7124 * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
7125 * @param {Object} options - The plugin options.
7126 */
7127 /**
7128 * @method IPlugin#beforeDatasetDraw
7129 * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets
7130 * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing
7131 * is cancelled until another `render` is triggered.
7132 * @param {Chart} chart - The chart instance.
7133 * @param {Object} args - The call arguments.
7134 * @param {Object} args.index - The dataset index.
7135 * @param {Number} args.meta - The dataset metadata.
7136 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7137 * @param {Object} options - The plugin options.
7138 * @returns {Boolean} `false` to cancel the chart datasets drawing.
7139 */
7140 /**
7141 * @method IPlugin#afterDatasetDraw
7142 * @desc Called after the `chart` datasets at the given `args.index` have been drawn
7143 * (datasets are drawn in the reverse order). Note that this hook will not be called
7144 * if the datasets drawing has been previously cancelled.
7145 * @param {Chart} chart - The chart instance.
7146 * @param {Object} args - The call arguments.
7147 * @param {Object} args.index - The dataset index.
7148 * @param {Number} args.meta - The dataset metadata.
7149 * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7150 * @param {Object} options - The plugin options.
7151 */
7152 /**
7153 * @method IPlugin#beforeEvent
7154 * @desc Called before processing the specified `event`. If any plugin returns `false`,
7155 * the event will be discarded.
7156 * @param {Chart.Controller} chart - The chart instance.
7157 * @param {IEvent} event - The event object.
7158 * @param {Object} options - The plugin options.
7159 */
7160 /**
7161 * @method IPlugin#afterEvent
7162 * @desc Called after the `event` has been consumed. Note that this hook
7163 * will not be called if the `event` has been previously discarded.
7164 * @param {Chart.Controller} chart - The chart instance.
7165 * @param {IEvent} event - The event object.
7166 * @param {Object} options - The plugin options.
7167 */
7168 /**
7169 * @method IPlugin#resize
7170 * @desc Called after the chart as been resized.
7171 * @param {Chart.Controller} chart - The chart instance.
7172 * @param {Number} size - The new canvas display size (eq. canvas.style width & height).
7173 * @param {Object} options - The plugin options.
7174 */
7175 /**
7176 * @method IPlugin#destroy
7177 * @desc Called after the chart as been destroyed.
7178 * @param {Chart.Controller} chart - The chart instance.
7179 * @param {Object} options - The plugin options.
7180 */
7181
7182 /**
7183 * Provided for backward compatibility, use Chart.plugins instead
7184 * @namespace Chart.pluginService
7185 * @deprecated since version 2.1.5
7186 * @todo remove at version 3
7187 * @private
7188 */
7189 Chart.pluginService = Chart.plugins;
7190
7191 /**
7192 * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
7193 * effect, instead simply create/register plugins via plain JavaScript objects.
7194 * @interface Chart.PluginBase
7195 * @deprecated since version 2.5.0
7196 * @todo remove at version 3
7197 * @private
7198 */
7199 Chart.PluginBase = Chart.Element.extend({});
7200 };
Jian Li46770fc2016-08-03 02:32:45 +09007201
7202},{}],31:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01007203 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09007204
Steven Burrows96ee21e2017-07-11 19:49:45 +01007205 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09007206
Steven Burrows96ee21e2017-07-11 19:49:45 +01007207 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09007208
Steven Burrows96ee21e2017-07-11 19:49:45 +01007209 Chart.defaults.scale = {
7210 display: true,
7211 position: 'left',
Jian Li46770fc2016-08-03 02:32:45 +09007212
Steven Burrows96ee21e2017-07-11 19:49:45 +01007213 // grid line settings
7214 gridLines: {
7215 display: true,
7216 color: 'rgba(0, 0, 0, 0.1)',
7217 lineWidth: 1,
7218 drawBorder: true,
7219 drawOnChartArea: true,
7220 drawTicks: true,
7221 tickMarkLength: 10,
7222 zeroLineWidth: 1,
7223 zeroLineColor: 'rgba(0,0,0,0.25)',
7224 zeroLineBorderDash: [],
7225 zeroLineBorderDashOffset: 0.0,
7226 offsetGridLines: false,
7227 borderDash: [],
7228 borderDashOffset: 0.0
7229 },
Jian Li46770fc2016-08-03 02:32:45 +09007230
Steven Burrows96ee21e2017-07-11 19:49:45 +01007231 // scale label
7232 scaleLabel: {
7233 // actual label
7234 labelString: '',
Jian Li46770fc2016-08-03 02:32:45 +09007235
Steven Burrows96ee21e2017-07-11 19:49:45 +01007236 // display property
7237 display: false
7238 },
Jian Li46770fc2016-08-03 02:32:45 +09007239
Steven Burrows96ee21e2017-07-11 19:49:45 +01007240 // label settings
7241 ticks: {
7242 beginAtZero: false,
7243 minRotation: 0,
7244 maxRotation: 50,
7245 mirror: false,
7246 padding: 0,
7247 reverse: false,
7248 display: true,
7249 autoSkip: true,
7250 autoSkipPadding: 0,
7251 labelOffset: 0,
7252 // We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
7253 callback: Chart.Ticks.formatters.values
7254 }
7255 };
Jian Li46770fc2016-08-03 02:32:45 +09007256
Steven Burrows96ee21e2017-07-11 19:49:45 +01007257 function computeTextSize(context, tick, font) {
7258 return helpers.isArray(tick) ?
7259 helpers.longestText(context, font, tick) :
7260 context.measureText(tick).width;
7261 }
Jian Li46770fc2016-08-03 02:32:45 +09007262
Steven Burrows96ee21e2017-07-11 19:49:45 +01007263 function parseFontOptions(options) {
7264 var getValueOrDefault = helpers.getValueOrDefault;
7265 var globalDefaults = Chart.defaults.global;
7266 var size = getValueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
7267 var style = getValueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle);
7268 var family = getValueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily);
Jian Li46770fc2016-08-03 02:32:45 +09007269
Steven Burrows96ee21e2017-07-11 19:49:45 +01007270 return {
7271 size: size,
7272 style: style,
7273 family: family,
7274 font: helpers.fontString(size, style, family)
7275 };
7276 }
Jian Li46770fc2016-08-03 02:32:45 +09007277
Steven Burrows96ee21e2017-07-11 19:49:45 +01007278 Chart.Scale = Chart.Element.extend({
7279 /**
7280 * Get the padding needed for the scale
7281 * @method getPadding
7282 * @private
7283 * @returns {Padding} the necessary padding
7284 */
7285 getPadding: function() {
7286 var me = this;
7287 return {
7288 left: me.paddingLeft || 0,
7289 top: me.paddingTop || 0,
7290 right: me.paddingRight || 0,
7291 bottom: me.paddingBottom || 0
7292 };
7293 },
Jian Li46770fc2016-08-03 02:32:45 +09007294
Steven Burrows96ee21e2017-07-11 19:49:45 +01007295 // These methods are ordered by lifecyle. Utilities then follow.
7296 // Any function defined here is inherited by all scale types.
7297 // Any function can be extended by the scale type
Jian Li46770fc2016-08-03 02:32:45 +09007298
Steven Burrows96ee21e2017-07-11 19:49:45 +01007299 beforeUpdate: function() {
7300 helpers.callback(this.options.beforeUpdate, [this]);
7301 },
7302 update: function(maxWidth, maxHeight, margins) {
7303 var me = this;
Jian Li46770fc2016-08-03 02:32:45 +09007304
Steven Burrows96ee21e2017-07-11 19:49:45 +01007305 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
7306 me.beforeUpdate();
Jian Li46770fc2016-08-03 02:32:45 +09007307
Steven Burrows96ee21e2017-07-11 19:49:45 +01007308 // Absorb the master measurements
7309 me.maxWidth = maxWidth;
7310 me.maxHeight = maxHeight;
7311 me.margins = helpers.extend({
7312 left: 0,
7313 right: 0,
7314 top: 0,
7315 bottom: 0
7316 }, margins);
7317 me.longestTextCache = me.longestTextCache || {};
Jian Li46770fc2016-08-03 02:32:45 +09007318
Steven Burrows96ee21e2017-07-11 19:49:45 +01007319 // Dimensions
7320 me.beforeSetDimensions();
7321 me.setDimensions();
7322 me.afterSetDimensions();
Jian Li46770fc2016-08-03 02:32:45 +09007323
Steven Burrows96ee21e2017-07-11 19:49:45 +01007324 // Data min/max
7325 me.beforeDataLimits();
7326 me.determineDataLimits();
7327 me.afterDataLimits();
Jian Li46770fc2016-08-03 02:32:45 +09007328
Steven Burrows96ee21e2017-07-11 19:49:45 +01007329 // Ticks
7330 me.beforeBuildTicks();
7331 me.buildTicks();
7332 me.afterBuildTicks();
Jian Li46770fc2016-08-03 02:32:45 +09007333
Steven Burrows96ee21e2017-07-11 19:49:45 +01007334 me.beforeTickToLabelConversion();
7335 me.convertTicksToLabels();
7336 me.afterTickToLabelConversion();
Jian Li46770fc2016-08-03 02:32:45 +09007337
Steven Burrows96ee21e2017-07-11 19:49:45 +01007338 // Tick Rotation
7339 me.beforeCalculateTickRotation();
7340 me.calculateTickRotation();
7341 me.afterCalculateTickRotation();
7342 // Fit
7343 me.beforeFit();
7344 me.fit();
7345 me.afterFit();
7346 //
7347 me.afterUpdate();
Jian Li46770fc2016-08-03 02:32:45 +09007348
Steven Burrows96ee21e2017-07-11 19:49:45 +01007349 return me.minSize;
Jian Li46770fc2016-08-03 02:32:45 +09007350
Steven Burrows96ee21e2017-07-11 19:49:45 +01007351 },
7352 afterUpdate: function() {
7353 helpers.callback(this.options.afterUpdate, [this]);
7354 },
Jian Li46770fc2016-08-03 02:32:45 +09007355
Steven Burrows96ee21e2017-07-11 19:49:45 +01007356 //
Jian Li46770fc2016-08-03 02:32:45 +09007357
Steven Burrows96ee21e2017-07-11 19:49:45 +01007358 beforeSetDimensions: function() {
7359 helpers.callback(this.options.beforeSetDimensions, [this]);
7360 },
7361 setDimensions: function() {
7362 var me = this;
7363 // Set the unconstrained dimension before label rotation
7364 if (me.isHorizontal()) {
7365 // Reset position before calculating rotation
7366 me.width = me.maxWidth;
7367 me.left = 0;
7368 me.right = me.width;
7369 } else {
7370 me.height = me.maxHeight;
Jian Li46770fc2016-08-03 02:32:45 +09007371
Steven Burrows96ee21e2017-07-11 19:49:45 +01007372 // Reset position before calculating rotation
7373 me.top = 0;
7374 me.bottom = me.height;
7375 }
Jian Li46770fc2016-08-03 02:32:45 +09007376
Steven Burrows96ee21e2017-07-11 19:49:45 +01007377 // Reset padding
7378 me.paddingLeft = 0;
7379 me.paddingTop = 0;
7380 me.paddingRight = 0;
7381 me.paddingBottom = 0;
7382 },
7383 afterSetDimensions: function() {
7384 helpers.callback(this.options.afterSetDimensions, [this]);
7385 },
Jian Li46770fc2016-08-03 02:32:45 +09007386
Steven Burrows96ee21e2017-07-11 19:49:45 +01007387 // Data limits
7388 beforeDataLimits: function() {
7389 helpers.callback(this.options.beforeDataLimits, [this]);
7390 },
7391 determineDataLimits: helpers.noop,
7392 afterDataLimits: function() {
7393 helpers.callback(this.options.afterDataLimits, [this]);
7394 },
Jian Li46770fc2016-08-03 02:32:45 +09007395
Steven Burrows96ee21e2017-07-11 19:49:45 +01007396 //
7397 beforeBuildTicks: function() {
7398 helpers.callback(this.options.beforeBuildTicks, [this]);
7399 },
7400 buildTicks: helpers.noop,
7401 afterBuildTicks: function() {
7402 helpers.callback(this.options.afterBuildTicks, [this]);
7403 },
Jian Li46770fc2016-08-03 02:32:45 +09007404
Steven Burrows96ee21e2017-07-11 19:49:45 +01007405 beforeTickToLabelConversion: function() {
7406 helpers.callback(this.options.beforeTickToLabelConversion, [this]);
7407 },
7408 convertTicksToLabels: function() {
7409 var me = this;
7410 // Convert ticks to strings
7411 var tickOpts = me.options.ticks;
7412 me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback);
7413 },
7414 afterTickToLabelConversion: function() {
7415 helpers.callback(this.options.afterTickToLabelConversion, [this]);
7416 },
Jian Li46770fc2016-08-03 02:32:45 +09007417
Steven Burrows96ee21e2017-07-11 19:49:45 +01007418 //
Jian Li46770fc2016-08-03 02:32:45 +09007419
Steven Burrows96ee21e2017-07-11 19:49:45 +01007420 beforeCalculateTickRotation: function() {
7421 helpers.callback(this.options.beforeCalculateTickRotation, [this]);
7422 },
7423 calculateTickRotation: function() {
7424 var me = this;
7425 var context = me.ctx;
7426 var tickOpts = me.options.ticks;
Jian Li46770fc2016-08-03 02:32:45 +09007427
Steven Burrows96ee21e2017-07-11 19:49:45 +01007428 // Get the width of each grid by calculating the difference
7429 // between x offsets between 0 and 1.
7430 var tickFont = parseFontOptions(tickOpts);
7431 context.font = tickFont.font;
Jian Li46770fc2016-08-03 02:32:45 +09007432
Steven Burrows96ee21e2017-07-11 19:49:45 +01007433 var labelRotation = tickOpts.minRotation || 0;
Jian Li46770fc2016-08-03 02:32:45 +09007434
Steven Burrows96ee21e2017-07-11 19:49:45 +01007435 if (me.options.display && me.isHorizontal()) {
7436 var originalLabelWidth = helpers.longestText(context, tickFont.font, me.ticks, me.longestTextCache);
7437 var labelWidth = originalLabelWidth;
7438 var cosRotation;
7439 var sinRotation;
Jian Li46770fc2016-08-03 02:32:45 +09007440
Steven Burrows96ee21e2017-07-11 19:49:45 +01007441 // Allow 3 pixels x2 padding either side for label readability
7442 var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;
Jian Li46770fc2016-08-03 02:32:45 +09007443
Steven Burrows96ee21e2017-07-11 19:49:45 +01007444 // Max label rotation can be set or default to 90 - also act as a loop counter
7445 while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) {
7446 var angleRadians = helpers.toRadians(labelRotation);
7447 cosRotation = Math.cos(angleRadians);
7448 sinRotation = Math.sin(angleRadians);
Jian Li46770fc2016-08-03 02:32:45 +09007449
Steven Burrows96ee21e2017-07-11 19:49:45 +01007450 if (sinRotation * originalLabelWidth > me.maxHeight) {
7451 // go back one step
7452 labelRotation--;
7453 break;
7454 }
Jian Li46770fc2016-08-03 02:32:45 +09007455
Steven Burrows96ee21e2017-07-11 19:49:45 +01007456 labelRotation++;
7457 labelWidth = cosRotation * originalLabelWidth;
7458 }
7459 }
Jian Li46770fc2016-08-03 02:32:45 +09007460
Steven Burrows96ee21e2017-07-11 19:49:45 +01007461 me.labelRotation = labelRotation;
7462 },
7463 afterCalculateTickRotation: function() {
7464 helpers.callback(this.options.afterCalculateTickRotation, [this]);
7465 },
Jian Li46770fc2016-08-03 02:32:45 +09007466
Steven Burrows96ee21e2017-07-11 19:49:45 +01007467 //
Jian Li46770fc2016-08-03 02:32:45 +09007468
Steven Burrows96ee21e2017-07-11 19:49:45 +01007469 beforeFit: function() {
7470 helpers.callback(this.options.beforeFit, [this]);
7471 },
7472 fit: function() {
7473 var me = this;
7474 // Reset
7475 var minSize = me.minSize = {
7476 width: 0,
7477 height: 0
7478 };
Jian Li46770fc2016-08-03 02:32:45 +09007479
Steven Burrows96ee21e2017-07-11 19:49:45 +01007480 var opts = me.options;
7481 var tickOpts = opts.ticks;
7482 var scaleLabelOpts = opts.scaleLabel;
7483 var gridLineOpts = opts.gridLines;
7484 var display = opts.display;
7485 var isHorizontal = me.isHorizontal();
Jian Li46770fc2016-08-03 02:32:45 +09007486
Steven Burrows96ee21e2017-07-11 19:49:45 +01007487 var tickFont = parseFontOptions(tickOpts);
7488 var scaleLabelFontSize = parseFontOptions(scaleLabelOpts).size * 1.5;
7489 var tickMarkLength = opts.gridLines.tickMarkLength;
Jian Li46770fc2016-08-03 02:32:45 +09007490
Steven Burrows96ee21e2017-07-11 19:49:45 +01007491 // Width
7492 if (isHorizontal) {
7493 // subtract the margins to line up with the chartArea if we are a full width scale
7494 minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
7495 } else {
7496 minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
7497 }
Jian Li46770fc2016-08-03 02:32:45 +09007498
Steven Burrows96ee21e2017-07-11 19:49:45 +01007499 // height
7500 if (isHorizontal) {
7501 minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
7502 } else {
7503 minSize.height = me.maxHeight; // fill all the height
7504 }
Jian Li46770fc2016-08-03 02:32:45 +09007505
Steven Burrows96ee21e2017-07-11 19:49:45 +01007506 // Are we showing a title for the scale?
7507 if (scaleLabelOpts.display && display) {
7508 if (isHorizontal) {
7509 minSize.height += scaleLabelFontSize;
7510 } else {
7511 minSize.width += scaleLabelFontSize;
7512 }
7513 }
Jian Li46770fc2016-08-03 02:32:45 +09007514
Steven Burrows96ee21e2017-07-11 19:49:45 +01007515 // Don't bother fitting the ticks if we are not showing them
7516 if (tickOpts.display && display) {
7517 var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, me.ticks, me.longestTextCache);
7518 var tallestLabelHeightInLines = helpers.numberOfLabelLines(me.ticks);
7519 var lineSpace = tickFont.size * 0.5;
Jian Li46770fc2016-08-03 02:32:45 +09007520
Steven Burrows96ee21e2017-07-11 19:49:45 +01007521 if (isHorizontal) {
7522 // A horizontal axis is more constrained by the height.
7523 me.longestLabelWidth = largestTextWidth;
Jian Li46770fc2016-08-03 02:32:45 +09007524
Steven Burrows96ee21e2017-07-11 19:49:45 +01007525 var angleRadians = helpers.toRadians(me.labelRotation);
7526 var cosRotation = Math.cos(angleRadians);
7527 var sinRotation = Math.sin(angleRadians);
Jian Li46770fc2016-08-03 02:32:45 +09007528
Steven Burrows96ee21e2017-07-11 19:49:45 +01007529 // TODO - improve this calculation
7530 var labelHeight = (sinRotation * largestTextWidth)
7531 + (tickFont.size * tallestLabelHeightInLines)
7532 + (lineSpace * tallestLabelHeightInLines);
Jian Li46770fc2016-08-03 02:32:45 +09007533
Steven Burrows96ee21e2017-07-11 19:49:45 +01007534 minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight);
7535 me.ctx.font = tickFont.font;
Jian Li46770fc2016-08-03 02:32:45 +09007536
Steven Burrows96ee21e2017-07-11 19:49:45 +01007537 var firstTick = me.ticks[0];
7538 var firstLabelWidth = computeTextSize(me.ctx, firstTick, tickFont.font);
Jian Li46770fc2016-08-03 02:32:45 +09007539
Steven Burrows96ee21e2017-07-11 19:49:45 +01007540 var lastTick = me.ticks[me.ticks.length - 1];
7541 var lastLabelWidth = computeTextSize(me.ctx, lastTick, tickFont.font);
Jian Li46770fc2016-08-03 02:32:45 +09007542
Steven Burrows96ee21e2017-07-11 19:49:45 +01007543 // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned which means that the right padding is dominated
7544 // by the font height
7545 if (me.labelRotation !== 0) {
7546 me.paddingLeft = opts.position === 'bottom'? (cosRotation * firstLabelWidth) + 3: (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges
7547 me.paddingRight = opts.position === 'bottom'? (cosRotation * lineSpace) + 3: (cosRotation * lastLabelWidth) + 3;
7548 } else {
7549 me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
7550 me.paddingRight = lastLabelWidth / 2 + 3;
7551 }
7552 } else {
7553 // A vertical axis is more constrained by the width. Labels are the dominant factor here, so get that length first
7554 // Account for padding
Jian Li46770fc2016-08-03 02:32:45 +09007555
Steven Burrows96ee21e2017-07-11 19:49:45 +01007556 if (tickOpts.mirror) {
7557 largestTextWidth = 0;
7558 } else {
7559 largestTextWidth += me.options.ticks.padding;
7560 }
7561 minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth);
7562 me.paddingTop = tickFont.size / 2;
7563 me.paddingBottom = tickFont.size / 2;
7564 }
7565 }
Jian Li46770fc2016-08-03 02:32:45 +09007566
Steven Burrows96ee21e2017-07-11 19:49:45 +01007567 me.handleMargins();
Jian Li46770fc2016-08-03 02:32:45 +09007568
Steven Burrows96ee21e2017-07-11 19:49:45 +01007569 me.width = minSize.width;
7570 me.height = minSize.height;
7571 },
Jian Li46770fc2016-08-03 02:32:45 +09007572
Steven Burrows96ee21e2017-07-11 19:49:45 +01007573 /**
7574 * Handle margins and padding interactions
7575 * @private
7576 */
7577 handleMargins: function() {
7578 var me = this;
7579 if (me.margins) {
7580 me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
7581 me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
7582 me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
7583 me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
7584 }
7585 },
Jian Li46770fc2016-08-03 02:32:45 +09007586
Steven Burrows96ee21e2017-07-11 19:49:45 +01007587 afterFit: function() {
7588 helpers.callback(this.options.afterFit, [this]);
7589 },
Jian Li46770fc2016-08-03 02:32:45 +09007590
Steven Burrows96ee21e2017-07-11 19:49:45 +01007591 // Shared Methods
7592 isHorizontal: function() {
7593 return this.options.position === 'top' || this.options.position === 'bottom';
7594 },
7595 isFullWidth: function() {
7596 return (this.options.fullWidth);
7597 },
Jian Li46770fc2016-08-03 02:32:45 +09007598
Steven Burrows96ee21e2017-07-11 19:49:45 +01007599 // 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
7600 getRightValue: function(rawValue) {
7601 // Null and undefined values first
7602 if (rawValue === null || typeof(rawValue) === 'undefined') {
7603 return NaN;
7604 }
7605 // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
7606 if (typeof(rawValue) === 'number' && !isFinite(rawValue)) {
7607 return NaN;
7608 }
7609 // If it is in fact an object, dive in one more level
7610 if (typeof(rawValue) === 'object') {
7611 if ((rawValue instanceof Date) || (rawValue.isValid)) {
7612 return rawValue;
7613 }
7614 return this.getRightValue(this.isHorizontal() ? rawValue.x : rawValue.y);
7615 }
Jian Li46770fc2016-08-03 02:32:45 +09007616
Steven Burrows96ee21e2017-07-11 19:49:45 +01007617 // Value is good, return it
7618 return rawValue;
7619 },
Jian Li46770fc2016-08-03 02:32:45 +09007620
Steven Burrows96ee21e2017-07-11 19:49:45 +01007621 // Used to get the value to display in the tooltip for the data at the given index
7622 // function getLabelForIndex(index, datasetIndex)
7623 getLabelForIndex: helpers.noop,
Jian Li46770fc2016-08-03 02:32:45 +09007624
Steven Burrows96ee21e2017-07-11 19:49:45 +01007625 // Used to get data value locations. Value can either be an index or a numerical value
7626 getPixelForValue: helpers.noop,
Jian Li46770fc2016-08-03 02:32:45 +09007627
Steven Burrows96ee21e2017-07-11 19:49:45 +01007628 // Used to get the data value from a given pixel. This is the inverse of getPixelForValue
7629 getValueForPixel: helpers.noop,
Jian Li46770fc2016-08-03 02:32:45 +09007630
Steven Burrows96ee21e2017-07-11 19:49:45 +01007631 // Used for tick location, should
7632 getPixelForTick: function(index, includeOffset) {
7633 var me = this;
7634 if (me.isHorizontal()) {
7635 var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
7636 var tickWidth = innerWidth / Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
7637 var pixel = (tickWidth * index) + me.paddingLeft;
Jian Li46770fc2016-08-03 02:32:45 +09007638
Steven Burrows96ee21e2017-07-11 19:49:45 +01007639 if (includeOffset) {
7640 pixel += tickWidth / 2;
7641 }
Jian Li46770fc2016-08-03 02:32:45 +09007642
Steven Burrows96ee21e2017-07-11 19:49:45 +01007643 var finalVal = me.left + Math.round(pixel);
7644 finalVal += me.isFullWidth() ? me.margins.left : 0;
7645 return finalVal;
7646 }
7647 var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
7648 return me.top + (index * (innerHeight / (me.ticks.length - 1)));
7649 },
Jian Li46770fc2016-08-03 02:32:45 +09007650
Steven Burrows96ee21e2017-07-11 19:49:45 +01007651 // Utility for getting the pixel location of a percentage of scale
7652 getPixelForDecimal: function(decimal /* , includeOffset*/) {
7653 var me = this;
7654 if (me.isHorizontal()) {
7655 var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
7656 var valueOffset = (innerWidth * decimal) + me.paddingLeft;
Jian Li46770fc2016-08-03 02:32:45 +09007657
Steven Burrows96ee21e2017-07-11 19:49:45 +01007658 var finalVal = me.left + Math.round(valueOffset);
7659 finalVal += me.isFullWidth() ? me.margins.left : 0;
7660 return finalVal;
7661 }
7662 return me.top + (decimal * me.height);
7663 },
Jian Li46770fc2016-08-03 02:32:45 +09007664
Steven Burrows96ee21e2017-07-11 19:49:45 +01007665 getBasePixel: function() {
7666 return this.getPixelForValue(this.getBaseValue());
7667 },
Jian Li46770fc2016-08-03 02:32:45 +09007668
Steven Burrows96ee21e2017-07-11 19:49:45 +01007669 getBaseValue: function() {
7670 var me = this;
7671 var min = me.min;
7672 var max = me.max;
Jian Li46770fc2016-08-03 02:32:45 +09007673
Steven Burrows96ee21e2017-07-11 19:49:45 +01007674 return me.beginAtZero ? 0:
7675 min < 0 && max < 0? max :
7676 min > 0 && max > 0? min :
7677 0;
7678 },
Jian Li46770fc2016-08-03 02:32:45 +09007679
Steven Burrows96ee21e2017-07-11 19:49:45 +01007680 // Actually draw the scale on the canvas
7681 // @param {rectangle} chartArea : the area of the chart to draw full grid lines on
7682 draw: function(chartArea) {
7683 var me = this;
7684 var options = me.options;
7685 if (!options.display) {
7686 return;
7687 }
Jian Li46770fc2016-08-03 02:32:45 +09007688
Steven Burrows96ee21e2017-07-11 19:49:45 +01007689 var context = me.ctx;
7690 var globalDefaults = Chart.defaults.global;
7691 var optionTicks = options.ticks;
7692 var gridLines = options.gridLines;
7693 var scaleLabel = options.scaleLabel;
Jian Li46770fc2016-08-03 02:32:45 +09007694
Steven Burrows96ee21e2017-07-11 19:49:45 +01007695 var isRotated = me.labelRotation !== 0;
7696 var skipRatio;
7697 var useAutoskipper = optionTicks.autoSkip;
7698 var isHorizontal = me.isHorizontal();
Jian Li46770fc2016-08-03 02:32:45 +09007699
Steven Burrows96ee21e2017-07-11 19:49:45 +01007700 // figure out the maximum number of gridlines to show
7701 var maxTicks;
7702 if (optionTicks.maxTicksLimit) {
7703 maxTicks = optionTicks.maxTicksLimit;
7704 }
Jian Li46770fc2016-08-03 02:32:45 +09007705
Steven Burrows96ee21e2017-07-11 19:49:45 +01007706 var tickFontColor = helpers.getValueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
7707 var tickFont = parseFontOptions(optionTicks);
Jian Li46770fc2016-08-03 02:32:45 +09007708
Steven Burrows96ee21e2017-07-11 19:49:45 +01007709 var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;
Jian Li46770fc2016-08-03 02:32:45 +09007710
Steven Burrows96ee21e2017-07-11 19:49:45 +01007711 var scaleLabelFontColor = helpers.getValueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
7712 var scaleLabelFont = parseFontOptions(scaleLabel);
Jian Li46770fc2016-08-03 02:32:45 +09007713
Steven Burrows96ee21e2017-07-11 19:49:45 +01007714 var labelRotationRadians = helpers.toRadians(me.labelRotation);
7715 var cosRotation = Math.cos(labelRotationRadians);
7716 var longestRotatedLabel = me.longestLabelWidth * cosRotation;
Jian Li46770fc2016-08-03 02:32:45 +09007717
Steven Burrows96ee21e2017-07-11 19:49:45 +01007718 // Make sure we draw text in the correct color and font
7719 context.fillStyle = tickFontColor;
Jian Li46770fc2016-08-03 02:32:45 +09007720
Steven Burrows96ee21e2017-07-11 19:49:45 +01007721 var itemsToDraw = [];
Jian Li46770fc2016-08-03 02:32:45 +09007722
Steven Burrows96ee21e2017-07-11 19:49:45 +01007723 if (isHorizontal) {
7724 skipRatio = false;
Jian Li46770fc2016-08-03 02:32:45 +09007725
Steven Burrows96ee21e2017-07-11 19:49:45 +01007726 if ((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length > (me.width - (me.paddingLeft + me.paddingRight))) {
7727 skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * me.ticks.length) / (me.width - (me.paddingLeft + me.paddingRight)));
7728 }
Jian Li46770fc2016-08-03 02:32:45 +09007729
Steven Burrows96ee21e2017-07-11 19:49:45 +01007730 // if they defined a max number of optionTicks,
7731 // increase skipRatio until that number is met
7732 if (maxTicks && me.ticks.length > maxTicks) {
7733 while (!skipRatio || me.ticks.length / (skipRatio || 1) > maxTicks) {
7734 if (!skipRatio) {
7735 skipRatio = 1;
7736 }
7737 skipRatio += 1;
7738 }
7739 }
Jian Li46770fc2016-08-03 02:32:45 +09007740
Steven Burrows96ee21e2017-07-11 19:49:45 +01007741 if (!useAutoskipper) {
7742 skipRatio = false;
7743 }
7744 }
Jian Li46770fc2016-08-03 02:32:45 +09007745
Jian Li46770fc2016-08-03 02:32:45 +09007746
Steven Burrows96ee21e2017-07-11 19:49:45 +01007747 var xTickStart = options.position === 'right' ? me.left : me.right - tl;
7748 var xTickEnd = options.position === 'right' ? me.left + tl : me.right;
7749 var yTickStart = options.position === 'bottom' ? me.top : me.bottom - tl;
7750 var yTickEnd = options.position === 'bottom' ? me.top + tl : me.bottom;
Jian Lid7a5a742016-02-12 13:51:18 -08007751
Steven Burrows96ee21e2017-07-11 19:49:45 +01007752 helpers.each(me.ticks, function(label, index) {
7753 // If the callback returned a null or undefined value, do not draw this line
7754 if (label === undefined || label === null) {
7755 return;
7756 }
Jian Lid7a5a742016-02-12 13:51:18 -08007757
Steven Burrows96ee21e2017-07-11 19:49:45 +01007758 var isLastTick = me.ticks.length === index + 1;
Jian Li46770fc2016-08-03 02:32:45 +09007759
Steven Burrows96ee21e2017-07-11 19:49:45 +01007760 // Since we always show the last tick,we need may need to hide the last shown one before
7761 var shouldSkip = (skipRatio > 1 && index % skipRatio > 0) || (index % skipRatio === 0 && index + skipRatio >= me.ticks.length);
7762 if (shouldSkip && !isLastTick || (label === undefined || label === null)) {
7763 return;
7764 }
Jian Li46770fc2016-08-03 02:32:45 +09007765
Steven Burrows96ee21e2017-07-11 19:49:45 +01007766 var lineWidth, lineColor, borderDash, borderDashOffset;
7767 if (index === (typeof me.zeroLineIndex !== 'undefined' ? me.zeroLineIndex : 0)) {
7768 // Draw the first index specially
7769 lineWidth = gridLines.zeroLineWidth;
7770 lineColor = gridLines.zeroLineColor;
7771 borderDash = gridLines.zeroLineBorderDash;
7772 borderDashOffset = gridLines.zeroLineBorderDashOffset;
7773 } else {
7774 lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, index);
7775 lineColor = helpers.getValueAtIndexOrDefault(gridLines.color, index);
7776 borderDash = helpers.getValueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
7777 borderDashOffset = helpers.getValueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
7778 }
Jian Li46770fc2016-08-03 02:32:45 +09007779
Steven Burrows96ee21e2017-07-11 19:49:45 +01007780 // Common properties
7781 var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;
7782 var textAlign = 'middle';
7783 var textBaseline = 'middle';
Jian Li46770fc2016-08-03 02:32:45 +09007784
Steven Burrows96ee21e2017-07-11 19:49:45 +01007785 if (isHorizontal) {
Jian Li46770fc2016-08-03 02:32:45 +09007786
Steven Burrows96ee21e2017-07-11 19:49:45 +01007787 if (options.position === 'bottom') {
7788 // bottom
7789 textBaseline = !isRotated? 'top':'middle';
7790 textAlign = !isRotated? 'center': 'right';
7791 labelY = me.top + tl;
7792 } else {
7793 // top
7794 textBaseline = !isRotated? 'bottom':'middle';
7795 textAlign = !isRotated? 'center': 'left';
7796 labelY = me.bottom - tl;
7797 }
Jian Li46770fc2016-08-03 02:32:45 +09007798
Steven Burrows96ee21e2017-07-11 19:49:45 +01007799 var xLineValue = me.getPixelForTick(index) + helpers.aliasPixel(lineWidth); // xvalues for grid lines
7800 labelX = me.getPixelForTick(index, gridLines.offsetGridLines) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)
Jian Li46770fc2016-08-03 02:32:45 +09007801
Steven Burrows96ee21e2017-07-11 19:49:45 +01007802 tx1 = tx2 = x1 = x2 = xLineValue;
7803 ty1 = yTickStart;
7804 ty2 = yTickEnd;
7805 y1 = chartArea.top;
7806 y2 = chartArea.bottom;
7807 } else {
7808 var isLeft = options.position === 'left';
7809 var tickPadding = optionTicks.padding;
7810 var labelXOffset;
Jian Li46770fc2016-08-03 02:32:45 +09007811
Steven Burrows96ee21e2017-07-11 19:49:45 +01007812 if (optionTicks.mirror) {
7813 textAlign = isLeft ? 'left' : 'right';
7814 labelXOffset = tickPadding;
7815 } else {
7816 textAlign = isLeft ? 'right' : 'left';
7817 labelXOffset = tl + tickPadding;
7818 }
Jian Li46770fc2016-08-03 02:32:45 +09007819
Steven Burrows96ee21e2017-07-11 19:49:45 +01007820 labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset;
Jian Li46770fc2016-08-03 02:32:45 +09007821
Steven Burrows96ee21e2017-07-11 19:49:45 +01007822 var yLineValue = me.getPixelForTick(index); // xvalues for grid lines
7823 yLineValue += helpers.aliasPixel(lineWidth);
7824 labelY = me.getPixelForTick(index, gridLines.offsetGridLines);
Jian Li46770fc2016-08-03 02:32:45 +09007825
Steven Burrows96ee21e2017-07-11 19:49:45 +01007826 tx1 = xTickStart;
7827 tx2 = xTickEnd;
7828 x1 = chartArea.left;
7829 x2 = chartArea.right;
7830 ty1 = ty2 = y1 = y2 = yLineValue;
7831 }
Jian Li46770fc2016-08-03 02:32:45 +09007832
Steven Burrows96ee21e2017-07-11 19:49:45 +01007833 itemsToDraw.push({
7834 tx1: tx1,
7835 ty1: ty1,
7836 tx2: tx2,
7837 ty2: ty2,
7838 x1: x1,
7839 y1: y1,
7840 x2: x2,
7841 y2: y2,
7842 labelX: labelX,
7843 labelY: labelY,
7844 glWidth: lineWidth,
7845 glColor: lineColor,
7846 glBorderDash: borderDash,
7847 glBorderDashOffset: borderDashOffset,
7848 rotation: -1 * labelRotationRadians,
7849 label: label,
7850 textBaseline: textBaseline,
7851 textAlign: textAlign
7852 });
7853 });
Jian Li46770fc2016-08-03 02:32:45 +09007854
Steven Burrows96ee21e2017-07-11 19:49:45 +01007855 // Draw all of the tick labels, tick marks, and grid lines at the correct places
7856 helpers.each(itemsToDraw, function(itemToDraw) {
7857 if (gridLines.display) {
7858 context.save();
7859 context.lineWidth = itemToDraw.glWidth;
7860 context.strokeStyle = itemToDraw.glColor;
7861 if (context.setLineDash) {
7862 context.setLineDash(itemToDraw.glBorderDash);
7863 context.lineDashOffset = itemToDraw.glBorderDashOffset;
7864 }
Jian Li46770fc2016-08-03 02:32:45 +09007865
Steven Burrows96ee21e2017-07-11 19:49:45 +01007866 context.beginPath();
Jian Li46770fc2016-08-03 02:32:45 +09007867
Steven Burrows96ee21e2017-07-11 19:49:45 +01007868 if (gridLines.drawTicks) {
7869 context.moveTo(itemToDraw.tx1, itemToDraw.ty1);
7870 context.lineTo(itemToDraw.tx2, itemToDraw.ty2);
7871 }
Jian Li46770fc2016-08-03 02:32:45 +09007872
Steven Burrows96ee21e2017-07-11 19:49:45 +01007873 if (gridLines.drawOnChartArea) {
7874 context.moveTo(itemToDraw.x1, itemToDraw.y1);
7875 context.lineTo(itemToDraw.x2, itemToDraw.y2);
7876 }
Jian Li46770fc2016-08-03 02:32:45 +09007877
Steven Burrows96ee21e2017-07-11 19:49:45 +01007878 context.stroke();
7879 context.restore();
7880 }
Jian Li46770fc2016-08-03 02:32:45 +09007881
Steven Burrows96ee21e2017-07-11 19:49:45 +01007882 if (optionTicks.display) {
7883 context.save();
7884 context.translate(itemToDraw.labelX, itemToDraw.labelY);
7885 context.rotate(itemToDraw.rotation);
7886 context.font = tickFont.font;
7887 context.textBaseline = itemToDraw.textBaseline;
7888 context.textAlign = itemToDraw.textAlign;
Jian Li46770fc2016-08-03 02:32:45 +09007889
Steven Burrows96ee21e2017-07-11 19:49:45 +01007890 var label = itemToDraw.label;
7891 if (helpers.isArray(label)) {
7892 for (var i = 0, y = 0; i < label.length; ++i) {
7893 // We just make sure the multiline element is a string here..
7894 context.fillText('' + label[i], 0, y);
7895 // apply same lineSpacing as calculated @ L#320
7896 y += (tickFont.size * 1.5);
7897 }
7898 } else {
7899 context.fillText(label, 0, 0);
7900 }
7901 context.restore();
7902 }
7903 });
Jian Li46770fc2016-08-03 02:32:45 +09007904
Steven Burrows96ee21e2017-07-11 19:49:45 +01007905 if (scaleLabel.display) {
7906 // Draw the scale label
7907 var scaleLabelX;
7908 var scaleLabelY;
7909 var rotation = 0;
Jian Li46770fc2016-08-03 02:32:45 +09007910
Steven Burrows96ee21e2017-07-11 19:49:45 +01007911 if (isHorizontal) {
7912 scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
7913 scaleLabelY = options.position === 'bottom' ? me.bottom - (scaleLabelFont.size / 2) : me.top + (scaleLabelFont.size / 2);
7914 } else {
7915 var isLeft = options.position === 'left';
7916 scaleLabelX = isLeft ? me.left + (scaleLabelFont.size / 2) : me.right - (scaleLabelFont.size / 2);
7917 scaleLabelY = me.top + ((me.bottom - me.top) / 2);
7918 rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
7919 }
Jian Li46770fc2016-08-03 02:32:45 +09007920
Steven Burrows96ee21e2017-07-11 19:49:45 +01007921 context.save();
7922 context.translate(scaleLabelX, scaleLabelY);
7923 context.rotate(rotation);
7924 context.textAlign = 'center';
7925 context.textBaseline = 'middle';
7926 context.fillStyle = scaleLabelFontColor; // render in correct colour
7927 context.font = scaleLabelFont.font;
7928 context.fillText(scaleLabel.labelString, 0, 0);
7929 context.restore();
7930 }
Jian Li46770fc2016-08-03 02:32:45 +09007931
Steven Burrows96ee21e2017-07-11 19:49:45 +01007932 if (gridLines.drawBorder) {
7933 // Draw the line at the edge of the axis
7934 context.lineWidth = helpers.getValueAtIndexOrDefault(gridLines.lineWidth, 0);
7935 context.strokeStyle = helpers.getValueAtIndexOrDefault(gridLines.color, 0);
7936 var x1 = me.left,
7937 x2 = me.right,
7938 y1 = me.top,
7939 y2 = me.bottom;
Jian Li46770fc2016-08-03 02:32:45 +09007940
Steven Burrows96ee21e2017-07-11 19:49:45 +01007941 var aliasPixel = helpers.aliasPixel(context.lineWidth);
7942 if (isHorizontal) {
7943 y1 = y2 = options.position === 'top' ? me.bottom : me.top;
7944 y1 += aliasPixel;
7945 y2 += aliasPixel;
7946 } else {
7947 x1 = x2 = options.position === 'left' ? me.right : me.left;
7948 x1 += aliasPixel;
7949 x2 += aliasPixel;
7950 }
Jian Li46770fc2016-08-03 02:32:45 +09007951
Steven Burrows96ee21e2017-07-11 19:49:45 +01007952 context.beginPath();
7953 context.moveTo(x1, y1);
7954 context.lineTo(x2, y2);
7955 context.stroke();
7956 }
7957 }
7958 });
7959 };
Jian Li46770fc2016-08-03 02:32:45 +09007960
7961},{}],32:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01007962 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09007963
Steven Burrows96ee21e2017-07-11 19:49:45 +01007964 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09007965
Steven Burrows96ee21e2017-07-11 19:49:45 +01007966 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09007967
Steven Burrows96ee21e2017-07-11 19:49:45 +01007968 Chart.scaleService = {
7969 // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
7970 // use the new chart options to grab the correct scale
7971 constructors: {},
7972 // Use a registration function so that we can move to an ES6 map when we no longer need to support
7973 // old browsers
Jian Li46770fc2016-08-03 02:32:45 +09007974
Steven Burrows96ee21e2017-07-11 19:49:45 +01007975 // Scale config defaults
7976 defaults: {},
7977 registerScaleType: function(type, scaleConstructor, defaults) {
7978 this.constructors[type] = scaleConstructor;
7979 this.defaults[type] = helpers.clone(defaults);
7980 },
7981 getScaleConstructor: function(type) {
7982 return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
7983 },
7984 getScaleDefaults: function(type) {
7985 // Return the scale defaults merged with the global settings so that we always use the latest ones
7986 return this.defaults.hasOwnProperty(type) ? helpers.scaleMerge(Chart.defaults.scale, this.defaults[type]) : {};
7987 },
7988 updateScaleDefaults: function(type, additions) {
7989 var defaults = this.defaults;
7990 if (defaults.hasOwnProperty(type)) {
7991 defaults[type] = helpers.extend(defaults[type], additions);
7992 }
7993 },
7994 addScalesToLayout: function(chart) {
7995 // Adds each scale to the chart.boxes array to be sized accordingly
7996 helpers.each(chart.scales, function(scale) {
7997 // Set ILayoutItem parameters for backwards compatibility
7998 scale.fullWidth = scale.options.fullWidth;
7999 scale.position = scale.options.position;
8000 scale.weight = scale.options.weight;
8001 Chart.layoutService.addBox(chart, scale);
8002 });
8003 }
8004 };
8005 };
8006
Jian Li46770fc2016-08-03 02:32:45 +09008007},{}],33:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01008008 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09008009
Steven Burrows96ee21e2017-07-11 19:49:45 +01008010 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09008011
Steven Burrows96ee21e2017-07-11 19:49:45 +01008012 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09008013
Steven Burrows96ee21e2017-07-11 19:49:45 +01008014 /**
8015 * Namespace to hold static tick generation functions
8016 * @namespace Chart.Ticks
8017 */
8018 Chart.Ticks = {
8019 /**
8020 * Namespace to hold generators for different types of ticks
8021 * @namespace Chart.Ticks.generators
8022 */
8023 generators: {
8024 /**
8025 * Interface for the options provided to the numeric tick generator
8026 * @interface INumericTickGenerationOptions
8027 */
8028 /**
8029 * The maximum number of ticks to display
8030 * @name INumericTickGenerationOptions#maxTicks
8031 * @type Number
8032 */
8033 /**
8034 * The distance between each tick.
8035 * @name INumericTickGenerationOptions#stepSize
8036 * @type Number
8037 * @optional
8038 */
8039 /**
8040 * Forced minimum for the ticks. If not specified, the minimum of the data range is used to calculate the tick minimum
8041 * @name INumericTickGenerationOptions#min
8042 * @type Number
8043 * @optional
8044 */
8045 /**
8046 * The maximum value of the ticks. If not specified, the maximum of the data range is used to calculate the tick maximum
8047 * @name INumericTickGenerationOptions#max
8048 * @type Number
8049 * @optional
8050 */
Jian Li46770fc2016-08-03 02:32:45 +09008051
Steven Burrows96ee21e2017-07-11 19:49:45 +01008052 /**
8053 * Generate a set of linear ticks
8054 * @method Chart.Ticks.generators.linear
8055 * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
8056 * @param dataRange {IRange} the range of the data
8057 * @returns {Array<Number>} array of tick values
8058 */
8059 linear: function(generationOptions, dataRange) {
8060 var ticks = [];
8061 // To get a "nice" value for the tick spacing, we will use the appropriately named
8062 // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
8063 // for details.
Jian Li46770fc2016-08-03 02:32:45 +09008064
Steven Burrows96ee21e2017-07-11 19:49:45 +01008065 var spacing;
8066 if (generationOptions.stepSize && generationOptions.stepSize > 0) {
8067 spacing = generationOptions.stepSize;
8068 } else {
8069 var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
8070 spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
8071 }
8072 var niceMin = Math.floor(dataRange.min / spacing) * spacing;
8073 var niceMax = Math.ceil(dataRange.max / spacing) * spacing;
Jian Li46770fc2016-08-03 02:32:45 +09008074
Steven Burrows96ee21e2017-07-11 19:49:45 +01008075 // If min, max and stepSize is set and they make an evenly spaced scale use it.
8076 if (generationOptions.min && generationOptions.max && generationOptions.stepSize) {
8077 // If very close to our whole number, use it.
8078 if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
8079 niceMin = generationOptions.min;
8080 niceMax = generationOptions.max;
8081 }
8082 }
Jian Li46770fc2016-08-03 02:32:45 +09008083
Steven Burrows96ee21e2017-07-11 19:49:45 +01008084 var numSpaces = (niceMax - niceMin) / spacing;
8085 // If very close to our rounded value, use it.
8086 if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
8087 numSpaces = Math.round(numSpaces);
8088 } else {
8089 numSpaces = Math.ceil(numSpaces);
8090 }
Jian Li46770fc2016-08-03 02:32:45 +09008091
Steven Burrows96ee21e2017-07-11 19:49:45 +01008092 // Put the values into the ticks array
8093 ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
8094 for (var j = 1; j < numSpaces; ++j) {
8095 ticks.push(niceMin + (j * spacing));
8096 }
8097 ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);
Jian Li46770fc2016-08-03 02:32:45 +09008098
Steven Burrows96ee21e2017-07-11 19:49:45 +01008099 return ticks;
8100 },
Jian Li46770fc2016-08-03 02:32:45 +09008101
Steven Burrows96ee21e2017-07-11 19:49:45 +01008102 /**
8103 * Generate a set of logarithmic ticks
8104 * @method Chart.Ticks.generators.logarithmic
8105 * @param generationOptions {INumericTickGenerationOptions} the options used to generate the ticks
8106 * @param dataRange {IRange} the range of the data
8107 * @returns {Array<Number>} array of tick values
8108 */
8109 logarithmic: function(generationOptions, dataRange) {
8110 var ticks = [];
8111 var getValueOrDefault = helpers.getValueOrDefault;
Jian Li46770fc2016-08-03 02:32:45 +09008112
Steven Burrows96ee21e2017-07-11 19:49:45 +01008113 // Figure out what the max number of ticks we can support it is based on the size of
8114 // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
8115 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
8116 // the graph
8117 var tickVal = getValueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));
Jian Li46770fc2016-08-03 02:32:45 +09008118
Steven Burrows96ee21e2017-07-11 19:49:45 +01008119 var endExp = Math.floor(helpers.log10(dataRange.max));
8120 var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
8121 var exp;
8122 var significand;
Jian Li46770fc2016-08-03 02:32:45 +09008123
Steven Burrows96ee21e2017-07-11 19:49:45 +01008124 if (tickVal === 0) {
8125 exp = Math.floor(helpers.log10(dataRange.minNotZero));
8126 significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));
Jian Li46770fc2016-08-03 02:32:45 +09008127
Steven Burrows96ee21e2017-07-11 19:49:45 +01008128 ticks.push(tickVal);
8129 tickVal = significand * Math.pow(10, exp);
8130 } else {
8131 exp = Math.floor(helpers.log10(tickVal));
8132 significand = Math.floor(tickVal / Math.pow(10, exp));
8133 }
Jian Li46770fc2016-08-03 02:32:45 +09008134
Steven Burrows96ee21e2017-07-11 19:49:45 +01008135 do {
8136 ticks.push(tickVal);
Jian Li46770fc2016-08-03 02:32:45 +09008137
Steven Burrows96ee21e2017-07-11 19:49:45 +01008138 ++significand;
8139 if (significand === 10) {
8140 significand = 1;
8141 ++exp;
8142 }
Jian Li46770fc2016-08-03 02:32:45 +09008143
Steven Burrows96ee21e2017-07-11 19:49:45 +01008144 tickVal = significand * Math.pow(10, exp);
8145 } while (exp < endExp || (exp === endExp && significand < endSignificand));
Jian Li46770fc2016-08-03 02:32:45 +09008146
Steven Burrows96ee21e2017-07-11 19:49:45 +01008147 var lastTick = getValueOrDefault(generationOptions.max, tickVal);
8148 ticks.push(lastTick);
Jian Li46770fc2016-08-03 02:32:45 +09008149
Steven Burrows96ee21e2017-07-11 19:49:45 +01008150 return ticks;
8151 }
8152 },
Jian Li46770fc2016-08-03 02:32:45 +09008153
Steven Burrows96ee21e2017-07-11 19:49:45 +01008154 /**
8155 * Namespace to hold formatters for different types of ticks
8156 * @namespace Chart.Ticks.formatters
8157 */
8158 formatters: {
8159 /**
8160 * Formatter for value labels
8161 * @method Chart.Ticks.formatters.values
8162 * @param value the value to display
8163 * @return {String|Array} the label to display
8164 */
8165 values: function(value) {
8166 return helpers.isArray(value) ? value : '' + value;
8167 },
Jian Li46770fc2016-08-03 02:32:45 +09008168
Steven Burrows96ee21e2017-07-11 19:49:45 +01008169 /**
8170 * Formatter for linear numeric ticks
8171 * @method Chart.Ticks.formatters.linear
8172 * @param tickValue {Number} the value to be formatted
8173 * @param index {Number} the position of the tickValue parameter in the ticks array
8174 * @param ticks {Array<Number>} the list of ticks being converted
8175 * @return {String} string representation of the tickValue parameter
8176 */
8177 linear: function(tickValue, index, ticks) {
8178 // If we have lots of ticks, don't use the ones
8179 var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
Jian Li46770fc2016-08-03 02:32:45 +09008180
Steven Burrows96ee21e2017-07-11 19:49:45 +01008181 // If we have a number like 2.5 as the delta, figure out how many decimal places we need
8182 if (Math.abs(delta) > 1) {
8183 if (tickValue !== Math.floor(tickValue)) {
8184 // not an integer
8185 delta = tickValue - Math.floor(tickValue);
8186 }
8187 }
Jian Li46770fc2016-08-03 02:32:45 +09008188
Steven Burrows96ee21e2017-07-11 19:49:45 +01008189 var logDelta = helpers.log10(Math.abs(delta));
8190 var tickString = '';
Jian Li46770fc2016-08-03 02:32:45 +09008191
Steven Burrows96ee21e2017-07-11 19:49:45 +01008192 if (tickValue !== 0) {
8193 var numDecimal = -1 * Math.floor(logDelta);
8194 numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
8195 tickString = tickValue.toFixed(numDecimal);
8196 } else {
8197 tickString = '0'; // never show decimal places for 0
8198 }
Jian Li46770fc2016-08-03 02:32:45 +09008199
Steven Burrows96ee21e2017-07-11 19:49:45 +01008200 return tickString;
8201 },
Jian Li46770fc2016-08-03 02:32:45 +09008202
Steven Burrows96ee21e2017-07-11 19:49:45 +01008203 logarithmic: function(tickValue, index, ticks) {
8204 var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));
Jian Li46770fc2016-08-03 02:32:45 +09008205
Steven Burrows96ee21e2017-07-11 19:49:45 +01008206 if (tickValue === 0) {
8207 return '0';
8208 } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
8209 return tickValue.toExponential();
8210 }
8211 return '';
8212 }
8213 }
8214 };
8215 };
Jian Li46770fc2016-08-03 02:32:45 +09008216
8217},{}],34:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01008218 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09008219
Steven Burrows96ee21e2017-07-11 19:49:45 +01008220 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09008221
Steven Burrows96ee21e2017-07-11 19:49:45 +01008222 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09008223
Steven Burrows96ee21e2017-07-11 19:49:45 +01008224 /**
8225 * Helper method to merge the opacity into a color
8226 */
8227 function mergeOpacity(colorString, opacity) {
8228 var color = helpers.color(colorString);
8229 return color.alpha(opacity * color.alpha()).rgbaString();
8230 }
Jian Li46770fc2016-08-03 02:32:45 +09008231
Steven Burrows96ee21e2017-07-11 19:49:45 +01008232 Chart.defaults.global.tooltips = {
8233 enabled: true,
8234 custom: null,
8235 mode: 'nearest',
8236 position: 'average',
8237 intersect: true,
8238 backgroundColor: 'rgba(0,0,0,0.8)',
8239 titleFontStyle: 'bold',
8240 titleSpacing: 2,
8241 titleMarginBottom: 6,
8242 titleFontColor: '#fff',
8243 titleAlign: 'left',
8244 bodySpacing: 2,
8245 bodyFontColor: '#fff',
8246 bodyAlign: 'left',
8247 footerFontStyle: 'bold',
8248 footerSpacing: 2,
8249 footerMarginTop: 6,
8250 footerFontColor: '#fff',
8251 footerAlign: 'left',
8252 yPadding: 6,
8253 xPadding: 6,
8254 caretPadding: 2,
8255 caretSize: 5,
8256 cornerRadius: 6,
8257 multiKeyBackground: '#fff',
8258 displayColors: true,
8259 borderColor: 'rgba(0,0,0,0)',
8260 borderWidth: 0,
8261 callbacks: {
8262 // Args are: (tooltipItems, data)
8263 beforeTitle: helpers.noop,
8264 title: function(tooltipItems, data) {
8265 // Pick first xLabel for now
8266 var title = '';
8267 var labels = data.labels;
8268 var labelCount = labels ? labels.length : 0;
Jian Li46770fc2016-08-03 02:32:45 +09008269
Steven Burrows96ee21e2017-07-11 19:49:45 +01008270 if (tooltipItems.length > 0) {
8271 var item = tooltipItems[0];
Jian Li46770fc2016-08-03 02:32:45 +09008272
Steven Burrows96ee21e2017-07-11 19:49:45 +01008273 if (item.xLabel) {
8274 title = item.xLabel;
8275 } else if (labelCount > 0 && item.index < labelCount) {
8276 title = labels[item.index];
8277 }
8278 }
Jian Li46770fc2016-08-03 02:32:45 +09008279
Steven Burrows96ee21e2017-07-11 19:49:45 +01008280 return title;
8281 },
8282 afterTitle: helpers.noop,
Jian Li46770fc2016-08-03 02:32:45 +09008283
Steven Burrows96ee21e2017-07-11 19:49:45 +01008284 // Args are: (tooltipItems, data)
8285 beforeBody: helpers.noop,
Jian Li46770fc2016-08-03 02:32:45 +09008286
Steven Burrows96ee21e2017-07-11 19:49:45 +01008287 // Args are: (tooltipItem, data)
8288 beforeLabel: helpers.noop,
8289 label: function(tooltipItem, data) {
8290 var label = data.datasets[tooltipItem.datasetIndex].label || '';
Jian Li46770fc2016-08-03 02:32:45 +09008291
Steven Burrows96ee21e2017-07-11 19:49:45 +01008292 if (label) {
8293 label += ': ';
8294 }
8295 label += tooltipItem.yLabel;
8296 return label;
8297 },
8298 labelColor: function(tooltipItem, chart) {
8299 var meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
8300 var activeElement = meta.data[tooltipItem.index];
8301 var view = activeElement._view;
8302 return {
8303 borderColor: view.borderColor,
8304 backgroundColor: view.backgroundColor
8305 };
8306 },
8307 afterLabel: helpers.noop,
Jian Li46770fc2016-08-03 02:32:45 +09008308
Steven Burrows96ee21e2017-07-11 19:49:45 +01008309 // Args are: (tooltipItems, data)
8310 afterBody: helpers.noop,
Jian Li46770fc2016-08-03 02:32:45 +09008311
Steven Burrows96ee21e2017-07-11 19:49:45 +01008312 // Args are: (tooltipItems, data)
8313 beforeFooter: helpers.noop,
8314 footer: helpers.noop,
8315 afterFooter: helpers.noop
8316 }
8317 };
Jian Li46770fc2016-08-03 02:32:45 +09008318
Steven Burrows96ee21e2017-07-11 19:49:45 +01008319 // Helper to push or concat based on if the 2nd parameter is an array or not
8320 function pushOrConcat(base, toPush) {
8321 if (toPush) {
8322 if (helpers.isArray(toPush)) {
8323 // base = base.concat(toPush);
8324 Array.prototype.push.apply(base, toPush);
8325 } else {
8326 base.push(toPush);
8327 }
8328 }
Jian Li46770fc2016-08-03 02:32:45 +09008329
Steven Burrows96ee21e2017-07-11 19:49:45 +01008330 return base;
8331 }
Jian Li46770fc2016-08-03 02:32:45 +09008332
Steven Burrows96ee21e2017-07-11 19:49:45 +01008333 // Private helper to create a tooltip item model
8334 // @param element : the chart element (point, arc, bar) to create the tooltip item for
8335 // @return : new tooltip item
8336 function createTooltipItem(element) {
8337 var xScale = element._xScale;
8338 var yScale = element._yScale || element._scale; // handle radar || polarArea charts
8339 var index = element._index,
8340 datasetIndex = element._datasetIndex;
Jian Li46770fc2016-08-03 02:32:45 +09008341
Steven Burrows96ee21e2017-07-11 19:49:45 +01008342 return {
8343 xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
8344 yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
8345 index: index,
8346 datasetIndex: datasetIndex,
8347 x: element._model.x,
8348 y: element._model.y
8349 };
8350 }
Jian Li46770fc2016-08-03 02:32:45 +09008351
Steven Burrows96ee21e2017-07-11 19:49:45 +01008352 /**
8353 * Helper to get the reset model for the tooltip
8354 * @param tooltipOpts {Object} the tooltip options
8355 */
8356 function getBaseModel(tooltipOpts) {
8357 var globalDefaults = Chart.defaults.global;
8358 var getValueOrDefault = helpers.getValueOrDefault;
Jian Li46770fc2016-08-03 02:32:45 +09008359
Steven Burrows96ee21e2017-07-11 19:49:45 +01008360 return {
8361 // Positioning
8362 xPadding: tooltipOpts.xPadding,
8363 yPadding: tooltipOpts.yPadding,
8364 xAlign: tooltipOpts.xAlign,
8365 yAlign: tooltipOpts.yAlign,
Jian Li46770fc2016-08-03 02:32:45 +09008366
Steven Burrows96ee21e2017-07-11 19:49:45 +01008367 // Body
8368 bodyFontColor: tooltipOpts.bodyFontColor,
8369 _bodyFontFamily: getValueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
8370 _bodyFontStyle: getValueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
8371 _bodyAlign: tooltipOpts.bodyAlign,
8372 bodyFontSize: getValueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
8373 bodySpacing: tooltipOpts.bodySpacing,
Jian Li46770fc2016-08-03 02:32:45 +09008374
Steven Burrows96ee21e2017-07-11 19:49:45 +01008375 // Title
8376 titleFontColor: tooltipOpts.titleFontColor,
8377 _titleFontFamily: getValueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
8378 _titleFontStyle: getValueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
8379 titleFontSize: getValueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
8380 _titleAlign: tooltipOpts.titleAlign,
8381 titleSpacing: tooltipOpts.titleSpacing,
8382 titleMarginBottom: tooltipOpts.titleMarginBottom,
Jian Li46770fc2016-08-03 02:32:45 +09008383
Steven Burrows96ee21e2017-07-11 19:49:45 +01008384 // Footer
8385 footerFontColor: tooltipOpts.footerFontColor,
8386 _footerFontFamily: getValueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
8387 _footerFontStyle: getValueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
8388 footerFontSize: getValueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
8389 _footerAlign: tooltipOpts.footerAlign,
8390 footerSpacing: tooltipOpts.footerSpacing,
8391 footerMarginTop: tooltipOpts.footerMarginTop,
Jian Li46770fc2016-08-03 02:32:45 +09008392
Steven Burrows96ee21e2017-07-11 19:49:45 +01008393 // Appearance
8394 caretSize: tooltipOpts.caretSize,
8395 cornerRadius: tooltipOpts.cornerRadius,
8396 backgroundColor: tooltipOpts.backgroundColor,
8397 opacity: 0,
8398 legendColorBackground: tooltipOpts.multiKeyBackground,
8399 displayColors: tooltipOpts.displayColors,
8400 borderColor: tooltipOpts.borderColor,
8401 borderWidth: tooltipOpts.borderWidth
8402 };
8403 }
Jian Li46770fc2016-08-03 02:32:45 +09008404
Steven Burrows96ee21e2017-07-11 19:49:45 +01008405 /**
8406 * Get the size of the tooltip
8407 */
8408 function getTooltipSize(tooltip, model) {
8409 var ctx = tooltip._chart.ctx;
Jian Li46770fc2016-08-03 02:32:45 +09008410
Steven Burrows96ee21e2017-07-11 19:49:45 +01008411 var height = model.yPadding * 2; // Tooltip Padding
8412 var width = 0;
Jian Li46770fc2016-08-03 02:32:45 +09008413
Steven Burrows96ee21e2017-07-11 19:49:45 +01008414 // Count of all lines in the body
8415 var body = model.body;
8416 var combinedBodyLength = body.reduce(function(count, bodyItem) {
8417 return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
8418 }, 0);
8419 combinedBodyLength += model.beforeBody.length + model.afterBody.length;
Jian Li46770fc2016-08-03 02:32:45 +09008420
Steven Burrows96ee21e2017-07-11 19:49:45 +01008421 var titleLineCount = model.title.length;
8422 var footerLineCount = model.footer.length;
8423 var titleFontSize = model.titleFontSize,
8424 bodyFontSize = model.bodyFontSize,
8425 footerFontSize = model.footerFontSize;
Jian Li46770fc2016-08-03 02:32:45 +09008426
Steven Burrows96ee21e2017-07-11 19:49:45 +01008427 height += titleLineCount * titleFontSize; // Title Lines
8428 height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
8429 height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
8430 height += combinedBodyLength * bodyFontSize; // Body Lines
8431 height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
8432 height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
8433 height += footerLineCount * (footerFontSize); // Footer Lines
8434 height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing
Jian Li46770fc2016-08-03 02:32:45 +09008435
Steven Burrows96ee21e2017-07-11 19:49:45 +01008436 // Title width
8437 var widthPadding = 0;
8438 var maxLineWidth = function(line) {
8439 width = Math.max(width, ctx.measureText(line).width + widthPadding);
8440 };
Jian Li46770fc2016-08-03 02:32:45 +09008441
Steven Burrows96ee21e2017-07-11 19:49:45 +01008442 ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
8443 helpers.each(model.title, maxLineWidth);
Jian Li46770fc2016-08-03 02:32:45 +09008444
Steven Burrows96ee21e2017-07-11 19:49:45 +01008445 // Body width
8446 ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
8447 helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth);
Jian Li46770fc2016-08-03 02:32:45 +09008448
Steven Burrows96ee21e2017-07-11 19:49:45 +01008449 // Body lines may include some extra width due to the color box
8450 widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
8451 helpers.each(body, function(bodyItem) {
8452 helpers.each(bodyItem.before, maxLineWidth);
8453 helpers.each(bodyItem.lines, maxLineWidth);
8454 helpers.each(bodyItem.after, maxLineWidth);
8455 });
Jian Li46770fc2016-08-03 02:32:45 +09008456
Steven Burrows96ee21e2017-07-11 19:49:45 +01008457 // Reset back to 0
8458 widthPadding = 0;
Jian Li46770fc2016-08-03 02:32:45 +09008459
Steven Burrows96ee21e2017-07-11 19:49:45 +01008460 // Footer width
8461 ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
8462 helpers.each(model.footer, maxLineWidth);
Jian Li46770fc2016-08-03 02:32:45 +09008463
Steven Burrows96ee21e2017-07-11 19:49:45 +01008464 // Add padding
8465 width += 2 * model.xPadding;
Jian Li46770fc2016-08-03 02:32:45 +09008466
Steven Burrows96ee21e2017-07-11 19:49:45 +01008467 return {
8468 width: width,
8469 height: height
8470 };
8471 }
Jian Li46770fc2016-08-03 02:32:45 +09008472
Steven Burrows96ee21e2017-07-11 19:49:45 +01008473 /**
8474 * Helper to get the alignment of a tooltip given the size
8475 */
8476 function determineAlignment(tooltip, size) {
8477 var model = tooltip._model;
8478 var chart = tooltip._chart;
8479 var chartArea = tooltip._chart.chartArea;
8480 var xAlign = 'center';
8481 var yAlign = 'center';
Jian Li46770fc2016-08-03 02:32:45 +09008482
Steven Burrows96ee21e2017-07-11 19:49:45 +01008483 if (model.y < size.height) {
8484 yAlign = 'top';
8485 } else if (model.y > (chart.height - size.height)) {
8486 yAlign = 'bottom';
8487 }
Jian Li46770fc2016-08-03 02:32:45 +09008488
Steven Burrows96ee21e2017-07-11 19:49:45 +01008489 var lf, rf; // functions to determine left, right alignment
8490 var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
8491 var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
8492 var midX = (chartArea.left + chartArea.right) / 2;
8493 var midY = (chartArea.top + chartArea.bottom) / 2;
Jian Li46770fc2016-08-03 02:32:45 +09008494
Steven Burrows96ee21e2017-07-11 19:49:45 +01008495 if (yAlign === 'center') {
8496 lf = function(x) {
8497 return x <= midX;
8498 };
8499 rf = function(x) {
8500 return x > midX;
8501 };
8502 } else {
8503 lf = function(x) {
8504 return x <= (size.width / 2);
8505 };
8506 rf = function(x) {
8507 return x >= (chart.width - (size.width / 2));
8508 };
8509 }
Jian Li46770fc2016-08-03 02:32:45 +09008510
Steven Burrows96ee21e2017-07-11 19:49:45 +01008511 olf = function(x) {
8512 return x + size.width > chart.width;
8513 };
8514 orf = function(x) {
8515 return x - size.width < 0;
8516 };
8517 yf = function(y) {
8518 return y <= midY ? 'top' : 'bottom';
8519 };
Jian Li46770fc2016-08-03 02:32:45 +09008520
Steven Burrows96ee21e2017-07-11 19:49:45 +01008521 if (lf(model.x)) {
8522 xAlign = 'left';
Jian Li46770fc2016-08-03 02:32:45 +09008523
Steven Burrows96ee21e2017-07-11 19:49:45 +01008524 // Is tooltip too wide and goes over the right side of the chart.?
8525 if (olf(model.x)) {
8526 xAlign = 'center';
8527 yAlign = yf(model.y);
8528 }
8529 } else if (rf(model.x)) {
8530 xAlign = 'right';
Jian Li46770fc2016-08-03 02:32:45 +09008531
Steven Burrows96ee21e2017-07-11 19:49:45 +01008532 // Is tooltip too wide and goes outside left edge of canvas?
8533 if (orf(model.x)) {
8534 xAlign = 'center';
8535 yAlign = yf(model.y);
8536 }
8537 }
Jian Li46770fc2016-08-03 02:32:45 +09008538
Steven Burrows96ee21e2017-07-11 19:49:45 +01008539 var opts = tooltip._options;
8540 return {
8541 xAlign: opts.xAlign ? opts.xAlign : xAlign,
8542 yAlign: opts.yAlign ? opts.yAlign : yAlign
8543 };
8544 }
Jian Li46770fc2016-08-03 02:32:45 +09008545
Steven Burrows96ee21e2017-07-11 19:49:45 +01008546 /**
8547 * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
8548 */
8549 function getBackgroundPoint(vm, size, alignment) {
8550 // Background Position
8551 var x = vm.x;
8552 var y = vm.y;
8553
8554 var caretSize = vm.caretSize,
8555 caretPadding = vm.caretPadding,
8556 cornerRadius = vm.cornerRadius,
8557 xAlign = alignment.xAlign,
8558 yAlign = alignment.yAlign,
8559 paddingAndSize = caretSize + caretPadding,
8560 radiusAndPadding = cornerRadius + caretPadding;
8561
8562 if (xAlign === 'right') {
8563 x -= size.width;
8564 } else if (xAlign === 'center') {
8565 x -= (size.width / 2);
8566 }
8567
8568 if (yAlign === 'top') {
8569 y += paddingAndSize;
8570 } else if (yAlign === 'bottom') {
8571 y -= size.height + paddingAndSize;
8572 } else {
8573 y -= (size.height / 2);
8574 }
8575
8576 if (yAlign === 'center') {
8577 if (xAlign === 'left') {
8578 x += paddingAndSize;
8579 } else if (xAlign === 'right') {
8580 x -= paddingAndSize;
8581 }
8582 } else if (xAlign === 'left') {
8583 x -= radiusAndPadding;
8584 } else if (xAlign === 'right') {
8585 x += radiusAndPadding;
8586 }
8587
8588 return {
8589 x: x,
8590 y: y
8591 };
8592 }
8593
8594 Chart.Tooltip = Chart.Element.extend({
8595 initialize: function() {
8596 this._model = getBaseModel(this._options);
8597 },
8598
8599 // Get the title
8600 // Args are: (tooltipItem, data)
8601 getTitle: function() {
8602 var me = this;
8603 var opts = me._options;
8604 var callbacks = opts.callbacks;
8605
8606 var beforeTitle = callbacks.beforeTitle.apply(me, arguments),
8607 title = callbacks.title.apply(me, arguments),
8608 afterTitle = callbacks.afterTitle.apply(me, arguments);
8609
8610 var lines = [];
8611 lines = pushOrConcat(lines, beforeTitle);
8612 lines = pushOrConcat(lines, title);
8613 lines = pushOrConcat(lines, afterTitle);
8614
8615 return lines;
8616 },
8617
8618 // Args are: (tooltipItem, data)
8619 getBeforeBody: function() {
8620 var lines = this._options.callbacks.beforeBody.apply(this, arguments);
8621 return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
8622 },
8623
8624 // Args are: (tooltipItem, data)
8625 getBody: function(tooltipItems, data) {
8626 var me = this;
8627 var callbacks = me._options.callbacks;
8628 var bodyItems = [];
8629
8630 helpers.each(tooltipItems, function(tooltipItem) {
8631 var bodyItem = {
8632 before: [],
8633 lines: [],
8634 after: []
8635 };
8636 pushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data));
8637 pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
8638 pushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data));
8639
8640 bodyItems.push(bodyItem);
8641 });
8642
8643 return bodyItems;
8644 },
8645
8646 // Args are: (tooltipItem, data)
8647 getAfterBody: function() {
8648 var lines = this._options.callbacks.afterBody.apply(this, arguments);
8649 return helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];
8650 },
8651
8652 // Get the footer and beforeFooter and afterFooter lines
8653 // Args are: (tooltipItem, data)
8654 getFooter: function() {
8655 var me = this;
8656 var callbacks = me._options.callbacks;
8657
8658 var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
8659 var footer = callbacks.footer.apply(me, arguments);
8660 var afterFooter = callbacks.afterFooter.apply(me, arguments);
8661
8662 var lines = [];
8663 lines = pushOrConcat(lines, beforeFooter);
8664 lines = pushOrConcat(lines, footer);
8665 lines = pushOrConcat(lines, afterFooter);
8666
8667 return lines;
8668 },
8669
8670 update: function(changed) {
8671 var me = this;
8672 var opts = me._options;
8673
8674 // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
8675 // 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
8676 // which breaks any animations.
8677 var existingModel = me._model;
8678 var model = me._model = getBaseModel(opts);
8679 var active = me._active;
8680
8681 var data = me._data;
8682
8683 // In the case where active.length === 0 we need to keep these at existing values for good animations
8684 var alignment = {
8685 xAlign: existingModel.xAlign,
8686 yAlign: existingModel.yAlign
8687 };
8688 var backgroundPoint = {
8689 x: existingModel.x,
8690 y: existingModel.y
8691 };
8692 var tooltipSize = {
8693 width: existingModel.width,
8694 height: existingModel.height
8695 };
8696 var tooltipPosition = {
8697 x: existingModel.caretX,
8698 y: existingModel.caretY
8699 };
8700
8701 var i, len;
8702
8703 if (active.length) {
8704 model.opacity = 1;
8705
8706 var labelColors = [];
8707 tooltipPosition = Chart.Tooltip.positioners[opts.position](active, me._eventPosition);
8708
8709 var tooltipItems = [];
8710 for (i = 0, len = active.length; i < len; ++i) {
8711 tooltipItems.push(createTooltipItem(active[i]));
8712 }
8713
8714 // If the user provided a filter function, use it to modify the tooltip items
8715 if (opts.filter) {
8716 tooltipItems = tooltipItems.filter(function(a) {
8717 return opts.filter(a, data);
8718 });
8719 }
8720
8721 // If the user provided a sorting function, use it to modify the tooltip items
8722 if (opts.itemSort) {
8723 tooltipItems = tooltipItems.sort(function(a, b) {
8724 return opts.itemSort(a, b, data);
8725 });
8726 }
8727
8728 // Determine colors for boxes
8729 helpers.each(tooltipItems, function(tooltipItem) {
8730 labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
8731 });
8732
8733 // Build the Text Lines
8734 model.title = me.getTitle(tooltipItems, data);
8735 model.beforeBody = me.getBeforeBody(tooltipItems, data);
8736 model.body = me.getBody(tooltipItems, data);
8737 model.afterBody = me.getAfterBody(tooltipItems, data);
8738 model.footer = me.getFooter(tooltipItems, data);
Jian Li46770fc2016-08-03 02:32:45 +09008739
Steven Burrows96ee21e2017-07-11 19:49:45 +01008740 // Initial positioning and colors
8741 model.x = Math.round(tooltipPosition.x);
8742 model.y = Math.round(tooltipPosition.y);
8743 model.caretPadding = opts.caretPadding;
8744 model.labelColors = labelColors;
Jian Li46770fc2016-08-03 02:32:45 +09008745
Steven Burrows96ee21e2017-07-11 19:49:45 +01008746 // data points
8747 model.dataPoints = tooltipItems;
Jian Li46770fc2016-08-03 02:32:45 +09008748
Steven Burrows96ee21e2017-07-11 19:49:45 +01008749 // We need to determine alignment of the tooltip
8750 tooltipSize = getTooltipSize(this, model);
8751 alignment = determineAlignment(this, tooltipSize);
8752 // Final Size and Position
8753 backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment);
8754 } else {
8755 model.opacity = 0;
8756 }
Jian Li46770fc2016-08-03 02:32:45 +09008757
Steven Burrows96ee21e2017-07-11 19:49:45 +01008758 model.xAlign = alignment.xAlign;
8759 model.yAlign = alignment.yAlign;
8760 model.x = backgroundPoint.x;
8761 model.y = backgroundPoint.y;
8762 model.width = tooltipSize.width;
8763 model.height = tooltipSize.height;
Jian Li46770fc2016-08-03 02:32:45 +09008764
Steven Burrows96ee21e2017-07-11 19:49:45 +01008765 // Point where the caret on the tooltip points to
8766 model.caretX = tooltipPosition.x;
8767 model.caretY = tooltipPosition.y;
Jian Li46770fc2016-08-03 02:32:45 +09008768
Steven Burrows96ee21e2017-07-11 19:49:45 +01008769 me._model = model;
Jian Li46770fc2016-08-03 02:32:45 +09008770
Steven Burrows96ee21e2017-07-11 19:49:45 +01008771 if (changed && opts.custom) {
8772 opts.custom.call(me, model);
8773 }
Jian Li46770fc2016-08-03 02:32:45 +09008774
Steven Burrows96ee21e2017-07-11 19:49:45 +01008775 return me;
8776 },
8777 drawCaret: function(tooltipPoint, size) {
8778 var ctx = this._chart.ctx;
8779 var vm = this._view;
8780 var caretPosition = this.getCaretPosition(tooltipPoint, size, vm);
Jian Li46770fc2016-08-03 02:32:45 +09008781
Steven Burrows96ee21e2017-07-11 19:49:45 +01008782 ctx.lineTo(caretPosition.x1, caretPosition.y1);
8783 ctx.lineTo(caretPosition.x2, caretPosition.y2);
8784 ctx.lineTo(caretPosition.x3, caretPosition.y3);
8785 },
8786 getCaretPosition: function(tooltipPoint, size, vm) {
8787 var x1, x2, x3;
8788 var y1, y2, y3;
8789 var caretSize = vm.caretSize;
8790 var cornerRadius = vm.cornerRadius;
8791 var xAlign = vm.xAlign,
8792 yAlign = vm.yAlign;
8793 var ptX = tooltipPoint.x,
8794 ptY = tooltipPoint.y;
8795 var width = size.width,
8796 height = size.height;
Jian Li46770fc2016-08-03 02:32:45 +09008797
Steven Burrows96ee21e2017-07-11 19:49:45 +01008798 if (yAlign === 'center') {
8799 y2 = ptY + (height / 2);
Jian Li46770fc2016-08-03 02:32:45 +09008800
Steven Burrows96ee21e2017-07-11 19:49:45 +01008801 if (xAlign === 'left') {
8802 x1 = ptX;
8803 x2 = x1 - caretSize;
8804 x3 = x1;
Jian Li46770fc2016-08-03 02:32:45 +09008805
Steven Burrows96ee21e2017-07-11 19:49:45 +01008806 y1 = y2 + caretSize;
8807 y3 = y2 - caretSize;
8808 } else {
8809 x1 = ptX + width;
8810 x2 = x1 + caretSize;
8811 x3 = x1;
Jian Li46770fc2016-08-03 02:32:45 +09008812
Steven Burrows96ee21e2017-07-11 19:49:45 +01008813 y1 = y2 - caretSize;
8814 y3 = y2 + caretSize;
8815 }
8816 } else {
8817 if (xAlign === 'left') {
8818 x2 = ptX + cornerRadius + (caretSize);
8819 x1 = x2 - caretSize;
8820 x3 = x2 + caretSize;
8821 } else if (xAlign === 'right') {
8822 x2 = ptX + width - cornerRadius - caretSize;
8823 x1 = x2 - caretSize;
8824 x3 = x2 + caretSize;
8825 } else {
8826 x2 = ptX + (width / 2);
8827 x1 = x2 - caretSize;
8828 x3 = x2 + caretSize;
8829 }
8830 if (yAlign === 'top') {
8831 y1 = ptY;
8832 y2 = y1 - caretSize;
8833 y3 = y1;
8834 } else {
8835 y1 = ptY + height;
8836 y2 = y1 + caretSize;
8837 y3 = y1;
8838 // invert drawing order
8839 var tmp = x3;
8840 x3 = x1;
8841 x1 = tmp;
8842 }
8843 }
8844 return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3};
8845 },
8846 drawTitle: function(pt, vm, ctx, opacity) {
8847 var title = vm.title;
Jian Li46770fc2016-08-03 02:32:45 +09008848
Steven Burrows96ee21e2017-07-11 19:49:45 +01008849 if (title.length) {
8850 ctx.textAlign = vm._titleAlign;
8851 ctx.textBaseline = 'top';
Jian Li46770fc2016-08-03 02:32:45 +09008852
Steven Burrows96ee21e2017-07-11 19:49:45 +01008853 var titleFontSize = vm.titleFontSize,
8854 titleSpacing = vm.titleSpacing;
Jian Li46770fc2016-08-03 02:32:45 +09008855
Steven Burrows96ee21e2017-07-11 19:49:45 +01008856 ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity);
8857 ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
Jian Li46770fc2016-08-03 02:32:45 +09008858
Steven Burrows96ee21e2017-07-11 19:49:45 +01008859 var i, len;
8860 for (i = 0, len = title.length; i < len; ++i) {
8861 ctx.fillText(title[i], pt.x, pt.y);
8862 pt.y += titleFontSize + titleSpacing; // Line Height and spacing
Jian Li46770fc2016-08-03 02:32:45 +09008863
Steven Burrows96ee21e2017-07-11 19:49:45 +01008864 if (i + 1 === title.length) {
8865 pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
8866 }
8867 }
8868 }
8869 },
8870 drawBody: function(pt, vm, ctx, opacity) {
8871 var bodyFontSize = vm.bodyFontSize;
8872 var bodySpacing = vm.bodySpacing;
8873 var body = vm.body;
Jian Li46770fc2016-08-03 02:32:45 +09008874
Steven Burrows96ee21e2017-07-11 19:49:45 +01008875 ctx.textAlign = vm._bodyAlign;
8876 ctx.textBaseline = 'top';
Jian Li46770fc2016-08-03 02:32:45 +09008877
Steven Burrows96ee21e2017-07-11 19:49:45 +01008878 var textColor = mergeOpacity(vm.bodyFontColor, opacity);
8879 ctx.fillStyle = textColor;
8880 ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
Jian Li46770fc2016-08-03 02:32:45 +09008881
Steven Burrows96ee21e2017-07-11 19:49:45 +01008882 // Before Body
8883 var xLinePadding = 0;
8884 var fillLineOfText = function(line) {
8885 ctx.fillText(line, pt.x + xLinePadding, pt.y);
8886 pt.y += bodyFontSize + bodySpacing;
8887 };
Jian Li46770fc2016-08-03 02:32:45 +09008888
Steven Burrows96ee21e2017-07-11 19:49:45 +01008889 // Before body lines
8890 helpers.each(vm.beforeBody, fillLineOfText);
Jian Li46770fc2016-08-03 02:32:45 +09008891
Steven Burrows96ee21e2017-07-11 19:49:45 +01008892 var drawColorBoxes = vm.displayColors;
8893 xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;
Jian Li46770fc2016-08-03 02:32:45 +09008894
Steven Burrows96ee21e2017-07-11 19:49:45 +01008895 // Draw body lines now
8896 helpers.each(body, function(bodyItem, i) {
8897 helpers.each(bodyItem.before, fillLineOfText);
Jian Li46770fc2016-08-03 02:32:45 +09008898
Steven Burrows96ee21e2017-07-11 19:49:45 +01008899 helpers.each(bodyItem.lines, function(line) {
8900 // Draw Legend-like boxes if needed
8901 if (drawColorBoxes) {
8902 // Fill a white rect so that colours merge nicely if the opacity is < 1
8903 ctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity);
8904 ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
Jian Li46770fc2016-08-03 02:32:45 +09008905
Steven Burrows96ee21e2017-07-11 19:49:45 +01008906 // Border
8907 ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity);
8908 ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
Jian Li46770fc2016-08-03 02:32:45 +09008909
Steven Burrows96ee21e2017-07-11 19:49:45 +01008910 // Inner square
8911 ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity);
8912 ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
Jian Li46770fc2016-08-03 02:32:45 +09008913
Steven Burrows96ee21e2017-07-11 19:49:45 +01008914 ctx.fillStyle = textColor;
8915 }
Jian Li46770fc2016-08-03 02:32:45 +09008916
Steven Burrows96ee21e2017-07-11 19:49:45 +01008917 fillLineOfText(line);
8918 });
Jian Li46770fc2016-08-03 02:32:45 +09008919
Steven Burrows96ee21e2017-07-11 19:49:45 +01008920 helpers.each(bodyItem.after, fillLineOfText);
8921 });
Jian Li46770fc2016-08-03 02:32:45 +09008922
Steven Burrows96ee21e2017-07-11 19:49:45 +01008923 // Reset back to 0 for after body
8924 xLinePadding = 0;
Jian Li46770fc2016-08-03 02:32:45 +09008925
Steven Burrows96ee21e2017-07-11 19:49:45 +01008926 // After body lines
8927 helpers.each(vm.afterBody, fillLineOfText);
8928 pt.y -= bodySpacing; // Remove last body spacing
8929 },
8930 drawFooter: function(pt, vm, ctx, opacity) {
8931 var footer = vm.footer;
Jian Li46770fc2016-08-03 02:32:45 +09008932
Steven Burrows96ee21e2017-07-11 19:49:45 +01008933 if (footer.length) {
8934 pt.y += vm.footerMarginTop;
Jian Li46770fc2016-08-03 02:32:45 +09008935
Steven Burrows96ee21e2017-07-11 19:49:45 +01008936 ctx.textAlign = vm._footerAlign;
8937 ctx.textBaseline = 'top';
Jian Li46770fc2016-08-03 02:32:45 +09008938
Steven Burrows96ee21e2017-07-11 19:49:45 +01008939 ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity);
8940 ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
Jian Li46770fc2016-08-03 02:32:45 +09008941
Steven Burrows96ee21e2017-07-11 19:49:45 +01008942 helpers.each(footer, function(line) {
8943 ctx.fillText(line, pt.x, pt.y);
8944 pt.y += vm.footerFontSize + vm.footerSpacing;
8945 });
8946 }
8947 },
8948 drawBackground: function(pt, vm, ctx, tooltipSize, opacity) {
8949 ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);
8950 ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity);
8951 ctx.lineWidth = vm.borderWidth;
8952 var xAlign = vm.xAlign;
8953 var yAlign = vm.yAlign;
8954 var x = pt.x;
8955 var y = pt.y;
8956 var width = tooltipSize.width;
8957 var height = tooltipSize.height;
8958 var radius = vm.cornerRadius;
Jian Li46770fc2016-08-03 02:32:45 +09008959
Steven Burrows96ee21e2017-07-11 19:49:45 +01008960 ctx.beginPath();
8961 ctx.moveTo(x + radius, y);
8962 if (yAlign === 'top') {
8963 this.drawCaret(pt, tooltipSize);
8964 }
8965 ctx.lineTo(x + width - radius, y);
8966 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
8967 if (yAlign === 'center' && xAlign === 'right') {
8968 this.drawCaret(pt, tooltipSize);
8969 }
8970 ctx.lineTo(x + width, y + height - radius);
8971 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
8972 if (yAlign === 'bottom') {
8973 this.drawCaret(pt, tooltipSize);
8974 }
8975 ctx.lineTo(x + radius, y + height);
8976 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
8977 if (yAlign === 'center' && xAlign === 'left') {
8978 this.drawCaret(pt, tooltipSize);
8979 }
8980 ctx.lineTo(x, y + radius);
8981 ctx.quadraticCurveTo(x, y, x + radius, y);
8982 ctx.closePath();
Jian Li46770fc2016-08-03 02:32:45 +09008983
Steven Burrows96ee21e2017-07-11 19:49:45 +01008984 ctx.fill();
Jian Li46770fc2016-08-03 02:32:45 +09008985
Steven Burrows96ee21e2017-07-11 19:49:45 +01008986 if (vm.borderWidth > 0) {
8987 ctx.stroke();
8988 }
8989 },
8990 draw: function() {
8991 var ctx = this._chart.ctx;
8992 var vm = this._view;
Jian Li46770fc2016-08-03 02:32:45 +09008993
Steven Burrows96ee21e2017-07-11 19:49:45 +01008994 if (vm.opacity === 0) {
8995 return;
8996 }
Jian Li46770fc2016-08-03 02:32:45 +09008997
Steven Burrows96ee21e2017-07-11 19:49:45 +01008998 var tooltipSize = {
8999 width: vm.width,
9000 height: vm.height
9001 };
9002 var pt = {
9003 x: vm.x,
9004 y: vm.y
9005 };
Jian Li46770fc2016-08-03 02:32:45 +09009006
Steven Burrows96ee21e2017-07-11 19:49:45 +01009007 // IE11/Edge does not like very small opacities, so snap to 0
9008 var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
Jian Li46770fc2016-08-03 02:32:45 +09009009
Steven Burrows96ee21e2017-07-11 19:49:45 +01009010 // Truthy/falsey value for empty tooltip
9011 var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;
Jian Li46770fc2016-08-03 02:32:45 +09009012
Steven Burrows96ee21e2017-07-11 19:49:45 +01009013 if (this._options.enabled && hasTooltipContent) {
9014 // Draw Background
9015 this.drawBackground(pt, vm, ctx, tooltipSize, opacity);
Jian Li46770fc2016-08-03 02:32:45 +09009016
Steven Burrows96ee21e2017-07-11 19:49:45 +01009017 // Draw Title, Body, and Footer
9018 pt.x += vm.xPadding;
9019 pt.y += vm.yPadding;
Jian Li46770fc2016-08-03 02:32:45 +09009020
Steven Burrows96ee21e2017-07-11 19:49:45 +01009021 // Titles
9022 this.drawTitle(pt, vm, ctx, opacity);
Jian Li46770fc2016-08-03 02:32:45 +09009023
Steven Burrows96ee21e2017-07-11 19:49:45 +01009024 // Body
9025 this.drawBody(pt, vm, ctx, opacity);
Jian Li46770fc2016-08-03 02:32:45 +09009026
Steven Burrows96ee21e2017-07-11 19:49:45 +01009027 // Footer
9028 this.drawFooter(pt, vm, ctx, opacity);
9029 }
9030 },
Jian Li46770fc2016-08-03 02:32:45 +09009031
Steven Burrows96ee21e2017-07-11 19:49:45 +01009032 /**
9033 * Handle an event
9034 * @private
9035 * @param {IEvent} event - The event to handle
9036 * @returns {Boolean} true if the tooltip changed
9037 */
9038 handleEvent: function(e) {
9039 var me = this;
9040 var options = me._options;
9041 var changed = false;
Jian Li46770fc2016-08-03 02:32:45 +09009042
Steven Burrows96ee21e2017-07-11 19:49:45 +01009043 me._lastActive = me._lastActive || [];
Jian Li46770fc2016-08-03 02:32:45 +09009044
Steven Burrows96ee21e2017-07-11 19:49:45 +01009045 // Find Active Elements for tooltips
9046 if (e.type === 'mouseout') {
9047 me._active = [];
9048 } else {
9049 me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
9050 }
Jian Li46770fc2016-08-03 02:32:45 +09009051
Steven Burrows96ee21e2017-07-11 19:49:45 +01009052 // Remember Last Actives
9053 changed = !helpers.arrayEquals(me._active, me._lastActive);
Jian Li46770fc2016-08-03 02:32:45 +09009054
Steven Burrows96ee21e2017-07-11 19:49:45 +01009055 // If tooltip didn't change, do not handle the target event
9056 if (!changed) {
9057 return false;
9058 }
Jian Li46770fc2016-08-03 02:32:45 +09009059
Steven Burrows96ee21e2017-07-11 19:49:45 +01009060 me._lastActive = me._active;
Jian Li46770fc2016-08-03 02:32:45 +09009061
Steven Burrows96ee21e2017-07-11 19:49:45 +01009062 if (options.enabled || options.custom) {
9063 me._eventPosition = {
9064 x: e.x,
9065 y: e.y
9066 };
Jian Li46770fc2016-08-03 02:32:45 +09009067
Steven Burrows96ee21e2017-07-11 19:49:45 +01009068 var model = me._model;
9069 me.update(true);
9070 me.pivot();
Jian Li46770fc2016-08-03 02:32:45 +09009071
Steven Burrows96ee21e2017-07-11 19:49:45 +01009072 // See if our tooltip position changed
9073 changed |= (model.x !== me._model.x) || (model.y !== me._model.y);
9074 }
Jian Li46770fc2016-08-03 02:32:45 +09009075
Steven Burrows96ee21e2017-07-11 19:49:45 +01009076 return changed;
9077 }
9078 });
Jian Li46770fc2016-08-03 02:32:45 +09009079
Steven Burrows96ee21e2017-07-11 19:49:45 +01009080 /**
9081 * @namespace Chart.Tooltip.positioners
9082 */
9083 Chart.Tooltip.positioners = {
9084 /**
9085 * Average mode places the tooltip at the average position of the elements shown
9086 * @function Chart.Tooltip.positioners.average
9087 * @param elements {ChartElement[]} the elements being displayed in the tooltip
9088 * @returns {Point} tooltip position
9089 */
9090 average: function(elements) {
9091 if (!elements.length) {
9092 return false;
9093 }
Jian Li46770fc2016-08-03 02:32:45 +09009094
Steven Burrows96ee21e2017-07-11 19:49:45 +01009095 var i, len;
9096 var x = 0;
9097 var y = 0;
9098 var count = 0;
Jian Li46770fc2016-08-03 02:32:45 +09009099
Steven Burrows96ee21e2017-07-11 19:49:45 +01009100 for (i = 0, len = elements.length; i < len; ++i) {
9101 var el = elements[i];
9102 if (el && el.hasValue()) {
9103 var pos = el.tooltipPosition();
9104 x += pos.x;
9105 y += pos.y;
9106 ++count;
9107 }
9108 }
Jian Li46770fc2016-08-03 02:32:45 +09009109
Steven Burrows96ee21e2017-07-11 19:49:45 +01009110 return {
9111 x: Math.round(x / count),
9112 y: Math.round(y / count)
9113 };
9114 },
Jian Li46770fc2016-08-03 02:32:45 +09009115
Steven Burrows96ee21e2017-07-11 19:49:45 +01009116 /**
9117 * Gets the tooltip position nearest of the item nearest to the event position
9118 * @function Chart.Tooltip.positioners.nearest
9119 * @param elements {Chart.Element[]} the tooltip elements
9120 * @param eventPosition {Point} the position of the event in canvas coordinates
9121 * @returns {Point} the tooltip position
9122 */
9123 nearest: function(elements, eventPosition) {
9124 var x = eventPosition.x;
9125 var y = eventPosition.y;
Jian Li46770fc2016-08-03 02:32:45 +09009126
Steven Burrows96ee21e2017-07-11 19:49:45 +01009127 var nearestElement;
9128 var minDistance = Number.POSITIVE_INFINITY;
9129 var i, len;
9130 for (i = 0, len = elements.length; i < len; ++i) {
9131 var el = elements[i];
9132 if (el && el.hasValue()) {
9133 var center = el.getCenterPoint();
9134 var d = helpers.distanceBetweenPoints(eventPosition, center);
Jian Li46770fc2016-08-03 02:32:45 +09009135
Steven Burrows96ee21e2017-07-11 19:49:45 +01009136 if (d < minDistance) {
9137 minDistance = d;
9138 nearestElement = el;
9139 }
9140 }
9141 }
Jian Li46770fc2016-08-03 02:32:45 +09009142
Steven Burrows96ee21e2017-07-11 19:49:45 +01009143 if (nearestElement) {
9144 var tp = nearestElement.tooltipPosition();
9145 x = tp.x;
9146 y = tp.y;
9147 }
Jian Li46770fc2016-08-03 02:32:45 +09009148
Steven Burrows96ee21e2017-07-11 19:49:45 +01009149 return {
9150 x: x,
9151 y: y
9152 };
9153 }
9154 };
9155 };
Jian Li46770fc2016-08-03 02:32:45 +09009156
9157},{}],35:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01009158 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09009159
Steven Burrows96ee21e2017-07-11 19:49:45 +01009160 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09009161
Steven Burrows96ee21e2017-07-11 19:49:45 +01009162 var helpers = Chart.helpers,
9163 globalOpts = Chart.defaults.global;
Jian Li46770fc2016-08-03 02:32:45 +09009164
Steven Burrows96ee21e2017-07-11 19:49:45 +01009165 globalOpts.elements.arc = {
9166 backgroundColor: globalOpts.defaultColor,
9167 borderColor: '#fff',
9168 borderWidth: 2
9169 };
Jian Li46770fc2016-08-03 02:32:45 +09009170
Steven Burrows96ee21e2017-07-11 19:49:45 +01009171 Chart.elements.Arc = Chart.Element.extend({
9172 inLabelRange: function(mouseX) {
9173 var vm = this._view;
Jian Li46770fc2016-08-03 02:32:45 +09009174
Steven Burrows96ee21e2017-07-11 19:49:45 +01009175 if (vm) {
9176 return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
9177 }
9178 return false;
9179 },
9180 inRange: function(chartX, chartY) {
9181 var vm = this._view;
Jian Li46770fc2016-08-03 02:32:45 +09009182
Steven Burrows96ee21e2017-07-11 19:49:45 +01009183 if (vm) {
9184 var pointRelativePosition = helpers.getAngleFromPoint(vm, {
9185 x: chartX,
9186 y: chartY
9187 }),
9188 angle = pointRelativePosition.angle,
9189 distance = pointRelativePosition.distance;
Jian Li46770fc2016-08-03 02:32:45 +09009190
Steven Burrows96ee21e2017-07-11 19:49:45 +01009191 // Sanitise angle range
9192 var startAngle = vm.startAngle;
9193 var endAngle = vm.endAngle;
9194 while (endAngle < startAngle) {
9195 endAngle += 2.0 * Math.PI;
9196 }
9197 while (angle > endAngle) {
9198 angle -= 2.0 * Math.PI;
9199 }
9200 while (angle < startAngle) {
9201 angle += 2.0 * Math.PI;
9202 }
Jian Li46770fc2016-08-03 02:32:45 +09009203
Steven Burrows96ee21e2017-07-11 19:49:45 +01009204 // Check if within the range of the open/close angle
9205 var betweenAngles = (angle >= startAngle && angle <= endAngle),
9206 withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
Jian Li46770fc2016-08-03 02:32:45 +09009207
Steven Burrows96ee21e2017-07-11 19:49:45 +01009208 return (betweenAngles && withinRadius);
9209 }
9210 return false;
9211 },
9212 getCenterPoint: function() {
9213 var vm = this._view;
9214 var halfAngle = (vm.startAngle + vm.endAngle) / 2;
9215 var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
9216 return {
9217 x: vm.x + Math.cos(halfAngle) * halfRadius,
9218 y: vm.y + Math.sin(halfAngle) * halfRadius
9219 };
9220 },
9221 getArea: function() {
9222 var vm = this._view;
9223 return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
9224 },
9225 tooltipPosition: function() {
9226 var vm = this._view;
Jian Li46770fc2016-08-03 02:32:45 +09009227
Steven Burrows96ee21e2017-07-11 19:49:45 +01009228 var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2),
9229 rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
9230 return {
9231 x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
9232 y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
9233 };
9234 },
9235 draw: function() {
Jian Li46770fc2016-08-03 02:32:45 +09009236
Steven Burrows96ee21e2017-07-11 19:49:45 +01009237 var ctx = this._chart.ctx,
9238 vm = this._view,
9239 sA = vm.startAngle,
9240 eA = vm.endAngle;
Jian Li46770fc2016-08-03 02:32:45 +09009241
Steven Burrows96ee21e2017-07-11 19:49:45 +01009242 ctx.beginPath();
Jian Li46770fc2016-08-03 02:32:45 +09009243
Steven Burrows96ee21e2017-07-11 19:49:45 +01009244 ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
9245 ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
Jian Li46770fc2016-08-03 02:32:45 +09009246
Steven Burrows96ee21e2017-07-11 19:49:45 +01009247 ctx.closePath();
9248 ctx.strokeStyle = vm.borderColor;
9249 ctx.lineWidth = vm.borderWidth;
Jian Li46770fc2016-08-03 02:32:45 +09009250
Steven Burrows96ee21e2017-07-11 19:49:45 +01009251 ctx.fillStyle = vm.backgroundColor;
Jian Li46770fc2016-08-03 02:32:45 +09009252
Steven Burrows96ee21e2017-07-11 19:49:45 +01009253 ctx.fill();
9254 ctx.lineJoin = 'bevel';
Jian Li46770fc2016-08-03 02:32:45 +09009255
Steven Burrows96ee21e2017-07-11 19:49:45 +01009256 if (vm.borderWidth) {
9257 ctx.stroke();
9258 }
9259 }
9260 });
9261 };
Jian Li46770fc2016-08-03 02:32:45 +09009262
9263},{}],36:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01009264 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09009265
Steven Burrows96ee21e2017-07-11 19:49:45 +01009266 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09009267
Steven Burrows96ee21e2017-07-11 19:49:45 +01009268 var helpers = Chart.helpers;
9269 var globalDefaults = Chart.defaults.global;
Jian Li46770fc2016-08-03 02:32:45 +09009270
Steven Burrows96ee21e2017-07-11 19:49:45 +01009271 Chart.defaults.global.elements.line = {
9272 tension: 0.4,
9273 backgroundColor: globalDefaults.defaultColor,
9274 borderWidth: 3,
9275 borderColor: globalDefaults.defaultColor,
9276 borderCapStyle: 'butt',
9277 borderDash: [],
9278 borderDashOffset: 0.0,
9279 borderJoinStyle: 'miter',
9280 capBezierPoints: true,
9281 fill: true, // do we fill in the area between the line and its base axis
9282 };
Jian Li46770fc2016-08-03 02:32:45 +09009283
Steven Burrows96ee21e2017-07-11 19:49:45 +01009284 Chart.elements.Line = Chart.Element.extend({
9285 draw: function() {
9286 var me = this;
9287 var vm = me._view;
9288 var ctx = me._chart.ctx;
9289 var spanGaps = vm.spanGaps;
9290 var points = me._children.slice(); // clone array
9291 var globalOptionLineElements = globalDefaults.elements.line;
9292 var lastDrawnIndex = -1;
9293 var index, current, previous, currentVM;
Jian Li46770fc2016-08-03 02:32:45 +09009294
Steven Burrows96ee21e2017-07-11 19:49:45 +01009295 // If we are looping, adding the first point again
9296 if (me._loop && points.length) {
9297 points.push(points[0]);
9298 }
Jian Li46770fc2016-08-03 02:32:45 +09009299
Steven Burrows96ee21e2017-07-11 19:49:45 +01009300 ctx.save();
Jian Li46770fc2016-08-03 02:32:45 +09009301
Steven Burrows96ee21e2017-07-11 19:49:45 +01009302 // Stroke Line Options
9303 ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
Jian Li46770fc2016-08-03 02:32:45 +09009304
Steven Burrows96ee21e2017-07-11 19:49:45 +01009305 // IE 9 and 10 do not support line dash
9306 if (ctx.setLineDash) {
9307 ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
9308 }
Jian Li46770fc2016-08-03 02:32:45 +09009309
Steven Burrows96ee21e2017-07-11 19:49:45 +01009310 ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
9311 ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
9312 ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
9313 ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
Jian Li46770fc2016-08-03 02:32:45 +09009314
Steven Burrows96ee21e2017-07-11 19:49:45 +01009315 // Stroke Line
9316 ctx.beginPath();
9317 lastDrawnIndex = -1;
Jian Li46770fc2016-08-03 02:32:45 +09009318
Steven Burrows96ee21e2017-07-11 19:49:45 +01009319 for (index = 0; index < points.length; ++index) {
9320 current = points[index];
9321 previous = helpers.previousItem(points, index);
9322 currentVM = current._view;
Jian Li46770fc2016-08-03 02:32:45 +09009323
Steven Burrows96ee21e2017-07-11 19:49:45 +01009324 // First point moves to it's starting position no matter what
9325 if (index === 0) {
9326 if (!currentVM.skip) {
9327 ctx.moveTo(currentVM.x, currentVM.y);
9328 lastDrawnIndex = index;
9329 }
9330 } else {
9331 previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];
Jian Li46770fc2016-08-03 02:32:45 +09009332
Steven Burrows96ee21e2017-07-11 19:49:45 +01009333 if (!currentVM.skip) {
9334 if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
9335 // There was a gap and this is the first point after the gap
9336 ctx.moveTo(currentVM.x, currentVM.y);
9337 } else {
9338 // Line to next point
9339 helpers.canvas.lineTo(ctx, previous._view, current._view);
9340 }
9341 lastDrawnIndex = index;
9342 }
9343 }
9344 }
Jian Li46770fc2016-08-03 02:32:45 +09009345
Steven Burrows96ee21e2017-07-11 19:49:45 +01009346 ctx.stroke();
9347 ctx.restore();
9348 }
9349 });
9350 };
Jian Li46770fc2016-08-03 02:32:45 +09009351
Jian Li46770fc2016-08-03 02:32:45 +09009352},{}],37:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01009353 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09009354
Steven Burrows96ee21e2017-07-11 19:49:45 +01009355 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09009356
Steven Burrows96ee21e2017-07-11 19:49:45 +01009357 var helpers = Chart.helpers,
9358 globalOpts = Chart.defaults.global,
9359 defaultColor = globalOpts.defaultColor;
Jian Li46770fc2016-08-03 02:32:45 +09009360
Steven Burrows96ee21e2017-07-11 19:49:45 +01009361 globalOpts.elements.point = {
9362 radius: 3,
9363 pointStyle: 'circle',
9364 backgroundColor: defaultColor,
9365 borderWidth: 1,
9366 borderColor: defaultColor,
9367 // Hover
9368 hitRadius: 1,
9369 hoverRadius: 4,
9370 hoverBorderWidth: 1
9371 };
Jian Li46770fc2016-08-03 02:32:45 +09009372
Steven Burrows96ee21e2017-07-11 19:49:45 +01009373 function xRange(mouseX) {
9374 var vm = this._view;
9375 return vm ? (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
9376 }
Jian Li46770fc2016-08-03 02:32:45 +09009377
Steven Burrows96ee21e2017-07-11 19:49:45 +01009378 function yRange(mouseY) {
9379 var vm = this._view;
9380 return vm ? (Math.pow(mouseY - vm.y, 2) < Math.pow(vm.radius + vm.hitRadius, 2)) : false;
9381 }
Jian Li46770fc2016-08-03 02:32:45 +09009382
Steven Burrows96ee21e2017-07-11 19:49:45 +01009383 Chart.elements.Point = Chart.Element.extend({
9384 inRange: function(mouseX, mouseY) {
9385 var vm = this._view;
9386 return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
9387 },
Jian Li46770fc2016-08-03 02:32:45 +09009388
Steven Burrows96ee21e2017-07-11 19:49:45 +01009389 inLabelRange: xRange,
9390 inXRange: xRange,
9391 inYRange: yRange,
9392
9393 getCenterPoint: function() {
9394 var vm = this._view;
9395 return {
9396 x: vm.x,
9397 y: vm.y
9398 };
9399 },
9400 getArea: function() {
9401 return Math.PI * Math.pow(this._view.radius, 2);
9402 },
9403 tooltipPosition: function() {
9404 var vm = this._view;
9405 return {
9406 x: vm.x,
9407 y: vm.y,
9408 padding: vm.radius + vm.borderWidth
9409 };
9410 },
9411 draw: function(chartArea) {
9412 var vm = this._view;
9413 var model = this._model;
9414 var ctx = this._chart.ctx;
9415 var pointStyle = vm.pointStyle;
9416 var radius = vm.radius;
9417 var x = vm.x;
9418 var y = vm.y;
9419 var color = Chart.helpers.color;
9420 var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)
9421 var ratio = 0;
9422
9423 if (vm.skip) {
9424 return;
9425 }
9426
9427 ctx.strokeStyle = vm.borderColor || defaultColor;
9428 ctx.lineWidth = helpers.getValueOrDefault(vm.borderWidth, globalOpts.elements.point.borderWidth);
9429 ctx.fillStyle = vm.backgroundColor || defaultColor;
9430
9431 // Cliping for Points.
9432 // going out from inner charArea?
9433 if ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right*errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom*errMargin < model.y))) {
9434 // Point fade out
9435 if (model.x < chartArea.left) {
9436 ratio = (x - model.x) / (chartArea.left - model.x);
9437 } else if (chartArea.right*errMargin < model.x) {
9438 ratio = (model.x - x) / (model.x - chartArea.right);
9439 } else if (model.y < chartArea.top) {
9440 ratio = (y - model.y) / (chartArea.top - model.y);
9441 } else if (chartArea.bottom*errMargin < model.y) {
9442 ratio = (model.y - y) / (model.y - chartArea.bottom);
9443 }
9444 ratio = Math.round(ratio*100) / 100;
9445 ctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString();
9446 ctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString();
9447 }
9448
9449 Chart.canvasHelpers.drawPoint(ctx, pointStyle, radius, x, y);
9450 }
9451 });
9452 };
Jian Li46770fc2016-08-03 02:32:45 +09009453
9454},{}],38:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01009455 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09009456
Steven Burrows96ee21e2017-07-11 19:49:45 +01009457 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +09009458
Steven Burrows96ee21e2017-07-11 19:49:45 +01009459 var globalOpts = Chart.defaults.global;
Jian Li46770fc2016-08-03 02:32:45 +09009460
Steven Burrows96ee21e2017-07-11 19:49:45 +01009461 globalOpts.elements.rectangle = {
9462 backgroundColor: globalOpts.defaultColor,
9463 borderWidth: 0,
9464 borderColor: globalOpts.defaultColor,
9465 borderSkipped: 'bottom'
9466 };
Jian Li46770fc2016-08-03 02:32:45 +09009467
Steven Burrows96ee21e2017-07-11 19:49:45 +01009468 function isVertical(bar) {
9469 return bar._view.width !== undefined;
9470 }
Jian Li46770fc2016-08-03 02:32:45 +09009471
Steven Burrows96ee21e2017-07-11 19:49:45 +01009472 /**
9473 * Helper function to get the bounds of the bar regardless of the orientation
9474 * @private
9475 * @param bar {Chart.Element.Rectangle} the bar
9476 * @return {Bounds} bounds of the bar
9477 */
9478 function getBarBounds(bar) {
9479 var vm = bar._view;
9480 var x1, x2, y1, y2;
Jian Li46770fc2016-08-03 02:32:45 +09009481
Steven Burrows96ee21e2017-07-11 19:49:45 +01009482 if (isVertical(bar)) {
9483 // vertical
9484 var halfWidth = vm.width / 2;
9485 x1 = vm.x - halfWidth;
9486 x2 = vm.x + halfWidth;
9487 y1 = Math.min(vm.y, vm.base);
9488 y2 = Math.max(vm.y, vm.base);
9489 } else {
9490 // horizontal bar
9491 var halfHeight = vm.height / 2;
9492 x1 = Math.min(vm.x, vm.base);
9493 x2 = Math.max(vm.x, vm.base);
9494 y1 = vm.y - halfHeight;
9495 y2 = vm.y + halfHeight;
9496 }
Jian Li46770fc2016-08-03 02:32:45 +09009497
Steven Burrows96ee21e2017-07-11 19:49:45 +01009498 return {
9499 left: x1,
9500 top: y1,
9501 right: x2,
9502 bottom: y2
9503 };
9504 }
Jian Li46770fc2016-08-03 02:32:45 +09009505
Steven Burrows96ee21e2017-07-11 19:49:45 +01009506 Chart.elements.Rectangle = Chart.Element.extend({
9507 draw: function() {
9508 var ctx = this._chart.ctx;
9509 var vm = this._view;
9510 var left, right, top, bottom, signX, signY, borderSkipped;
9511 var borderWidth = vm.borderWidth;
Jian Li46770fc2016-08-03 02:32:45 +09009512
Steven Burrows96ee21e2017-07-11 19:49:45 +01009513 if (!vm.horizontal) {
9514 // bar
9515 left = vm.x - vm.width / 2;
9516 right = vm.x + vm.width / 2;
9517 top = vm.y;
9518 bottom = vm.base;
9519 signX = 1;
9520 signY = bottom > top? 1: -1;
9521 borderSkipped = vm.borderSkipped || 'bottom';
9522 } else {
9523 // horizontal bar
9524 left = vm.base;
9525 right = vm.x;
9526 top = vm.y - vm.height / 2;
9527 bottom = vm.y + vm.height / 2;
9528 signX = right > left? 1: -1;
9529 signY = 1;
9530 borderSkipped = vm.borderSkipped || 'left';
9531 }
Jian Li46770fc2016-08-03 02:32:45 +09009532
Steven Burrows96ee21e2017-07-11 19:49:45 +01009533 // Canvas doesn't allow us to stroke inside the width so we can
9534 // adjust the sizes to fit if we're setting a stroke on the line
9535 if (borderWidth) {
9536 // borderWidth shold be less than bar width and bar height.
9537 var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
9538 borderWidth = borderWidth > barSize? barSize: borderWidth;
9539 var halfStroke = borderWidth / 2;
9540 // Adjust borderWidth when bar top position is near vm.base(zero).
9541 var borderLeft = left + (borderSkipped !== 'left'? halfStroke * signX: 0);
9542 var borderRight = right + (borderSkipped !== 'right'? -halfStroke * signX: 0);
9543 var borderTop = top + (borderSkipped !== 'top'? halfStroke * signY: 0);
9544 var borderBottom = bottom + (borderSkipped !== 'bottom'? -halfStroke * signY: 0);
9545 // not become a vertical line?
9546 if (borderLeft !== borderRight) {
9547 top = borderTop;
9548 bottom = borderBottom;
9549 }
9550 // not become a horizontal line?
9551 if (borderTop !== borderBottom) {
9552 left = borderLeft;
9553 right = borderRight;
9554 }
9555 }
Jian Li46770fc2016-08-03 02:32:45 +09009556
Steven Burrows96ee21e2017-07-11 19:49:45 +01009557 ctx.beginPath();
9558 ctx.fillStyle = vm.backgroundColor;
9559 ctx.strokeStyle = vm.borderColor;
9560 ctx.lineWidth = borderWidth;
Jian Li46770fc2016-08-03 02:32:45 +09009561
Steven Burrows96ee21e2017-07-11 19:49:45 +01009562 // Corner points, from bottom-left to bottom-right clockwise
9563 // | 1 2 |
9564 // | 0 3 |
9565 var corners = [
9566 [left, bottom],
9567 [left, top],
9568 [right, top],
9569 [right, bottom]
9570 ];
Jian Li46770fc2016-08-03 02:32:45 +09009571
Steven Burrows96ee21e2017-07-11 19:49:45 +01009572 // Find first (starting) corner with fallback to 'bottom'
9573 var borders = ['bottom', 'left', 'top', 'right'];
9574 var startCorner = borders.indexOf(borderSkipped, 0);
9575 if (startCorner === -1) {
9576 startCorner = 0;
9577 }
9578
9579 function cornerAt(index) {
9580 return corners[(startCorner + index) % 4];
9581 }
9582
9583 // Draw rectangle from 'startCorner'
9584 var corner = cornerAt(0);
9585 ctx.moveTo(corner[0], corner[1]);
9586
9587 for (var i = 1; i < 4; i++) {
9588 corner = cornerAt(i);
9589 ctx.lineTo(corner[0], corner[1]);
9590 }
9591
9592 ctx.fill();
9593 if (borderWidth) {
9594 ctx.stroke();
9595 }
9596 },
9597 height: function() {
9598 var vm = this._view;
9599 return vm.base - vm.y;
9600 },
9601 inRange: function(mouseX, mouseY) {
9602 var inRange = false;
9603
9604 if (this._view) {
9605 var bounds = getBarBounds(this);
9606 inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
9607 }
9608
9609 return inRange;
9610 },
9611 inLabelRange: function(mouseX, mouseY) {
9612 var me = this;
9613 if (!me._view) {
9614 return false;
9615 }
9616
9617 var inRange = false;
9618 var bounds = getBarBounds(me);
9619
9620 if (isVertical(me)) {
9621 inRange = mouseX >= bounds.left && mouseX <= bounds.right;
9622 } else {
9623 inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
9624 }
9625
9626 return inRange;
9627 },
9628 inXRange: function(mouseX) {
9629 var bounds = getBarBounds(this);
9630 return mouseX >= bounds.left && mouseX <= bounds.right;
9631 },
9632 inYRange: function(mouseY) {
9633 var bounds = getBarBounds(this);
9634 return mouseY >= bounds.top && mouseY <= bounds.bottom;
9635 },
9636 getCenterPoint: function() {
9637 var vm = this._view;
9638 var x, y;
9639 if (isVertical(this)) {
9640 x = vm.x;
9641 y = (vm.y + vm.base) / 2;
9642 } else {
9643 x = (vm.x + vm.base) / 2;
9644 y = vm.y;
9645 }
9646
9647 return {x: x, y: y};
9648 },
9649 getArea: function() {
9650 var vm = this._view;
9651 return vm.width * Math.abs(vm.y - vm.base);
9652 },
9653 tooltipPosition: function() {
9654 var vm = this._view;
9655 return {
9656 x: vm.x,
9657 y: vm.y
9658 };
9659 }
9660 });
9661
9662 };
9663
Jian Li46770fc2016-08-03 02:32:45 +09009664},{}],39:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01009665 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09009666
Steven Burrows96ee21e2017-07-11 19:49:45 +01009667// Chart.Platform implementation for targeting a web browser
9668 module.exports = function(Chart) {
9669 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +09009670
Steven Burrows96ee21e2017-07-11 19:49:45 +01009671 // DOM event types -> Chart.js event types.
9672 // Note: only events with different types are mapped.
9673 // https://developer.mozilla.org/en-US/docs/Web/Events
9674 var eventTypeMap = {
9675 // Touch events
9676 touchstart: 'mousedown',
9677 touchmove: 'mousemove',
9678 touchend: 'mouseup',
Jian Li46770fc2016-08-03 02:32:45 +09009679
Steven Burrows96ee21e2017-07-11 19:49:45 +01009680 // Pointer events
9681 pointerenter: 'mouseenter',
9682 pointerdown: 'mousedown',
9683 pointermove: 'mousemove',
9684 pointerup: 'mouseup',
9685 pointerleave: 'mouseout',
9686 pointerout: 'mouseout'
9687 };
Jian Li46770fc2016-08-03 02:32:45 +09009688
Steven Burrows96ee21e2017-07-11 19:49:45 +01009689 /**
9690 * The "used" size is the final value of a dimension property after all calculations have
9691 * been performed. This method uses the computed style of `element` but returns undefined
9692 * if the computed style is not expressed in pixels. That can happen in some cases where
9693 * `element` has a size relative to its parent and this last one is not yet displayed,
9694 * for example because of `display: none` on a parent node.
9695 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
9696 * @returns {Number} Size in pixels or undefined if unknown.
9697 */
9698 function readUsedSize(element, property) {
9699 var value = helpers.getStyle(element, property);
9700 var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
9701 return matches? Number(matches[1]) : undefined;
9702 }
Jian Li46770fc2016-08-03 02:32:45 +09009703
Steven Burrows96ee21e2017-07-11 19:49:45 +01009704 /**
9705 * Initializes the canvas style and render size without modifying the canvas display size,
9706 * since responsiveness is handled by the controller.resize() method. The config is used
9707 * to determine the aspect ratio to apply in case no explicit height has been specified.
9708 */
9709 function initCanvas(canvas, config) {
9710 var style = canvas.style;
Jian Li46770fc2016-08-03 02:32:45 +09009711
Steven Burrows96ee21e2017-07-11 19:49:45 +01009712 // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
9713 // returns null or '' if no explicit value has been set to the canvas attribute.
9714 var renderHeight = canvas.getAttribute('height');
9715 var renderWidth = canvas.getAttribute('width');
Jian Li46770fc2016-08-03 02:32:45 +09009716
Steven Burrows96ee21e2017-07-11 19:49:45 +01009717 // Chart.js modifies some canvas values that we want to restore on destroy
9718 canvas._chartjs = {
9719 initial: {
9720 height: renderHeight,
9721 width: renderWidth,
9722 style: {
9723 display: style.display,
9724 height: style.height,
9725 width: style.width
9726 }
9727 }
9728 };
Jian Li46770fc2016-08-03 02:32:45 +09009729
Steven Burrows96ee21e2017-07-11 19:49:45 +01009730 // Force canvas to display as block to avoid extra space caused by inline
9731 // elements, which would interfere with the responsive resize process.
9732 // https://github.com/chartjs/Chart.js/issues/2538
9733 style.display = style.display || 'block';
Jian Li46770fc2016-08-03 02:32:45 +09009734
Steven Burrows96ee21e2017-07-11 19:49:45 +01009735 if (renderWidth === null || renderWidth === '') {
9736 var displayWidth = readUsedSize(canvas, 'width');
9737 if (displayWidth !== undefined) {
9738 canvas.width = displayWidth;
9739 }
9740 }
Jian Li46770fc2016-08-03 02:32:45 +09009741
Steven Burrows96ee21e2017-07-11 19:49:45 +01009742 if (renderHeight === null || renderHeight === '') {
9743 if (canvas.style.height === '') {
9744 // If no explicit render height and style height, let's apply the aspect ratio,
9745 // which one can be specified by the user but also by charts as default option
9746 // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
9747 canvas.height = canvas.width / (config.options.aspectRatio || 2);
9748 } else {
9749 var displayHeight = readUsedSize(canvas, 'height');
9750 if (displayWidth !== undefined) {
9751 canvas.height = displayHeight;
9752 }
9753 }
9754 }
Jian Li46770fc2016-08-03 02:32:45 +09009755
Steven Burrows96ee21e2017-07-11 19:49:45 +01009756 return canvas;
9757 }
Jian Li46770fc2016-08-03 02:32:45 +09009758
Steven Burrows96ee21e2017-07-11 19:49:45 +01009759 function createEvent(type, chart, x, y, nativeEvent) {
9760 return {
9761 type: type,
9762 chart: chart,
9763 native: nativeEvent || null,
9764 x: x !== undefined? x : null,
9765 y: y !== undefined? y : null,
9766 };
9767 }
Jian Li46770fc2016-08-03 02:32:45 +09009768
Steven Burrows96ee21e2017-07-11 19:49:45 +01009769 function fromNativeEvent(event, chart) {
9770 var type = eventTypeMap[event.type] || event.type;
9771 var pos = helpers.getRelativePosition(event, chart);
9772 return createEvent(type, chart, pos.x, pos.y, event);
9773 }
Jian Li46770fc2016-08-03 02:32:45 +09009774
Steven Burrows96ee21e2017-07-11 19:49:45 +01009775 function createResizer(handler) {
9776 var iframe = document.createElement('iframe');
9777 iframe.className = 'chartjs-hidden-iframe';
9778 iframe.style.cssText =
9779 'display:block;'+
9780 'overflow:hidden;'+
9781 'border:0;'+
9782 'margin:0;'+
9783 'top:0;'+
9784 'left:0;'+
9785 'bottom:0;'+
9786 'right:0;'+
9787 'height:100%;'+
9788 'width:100%;'+
9789 'position:absolute;'+
9790 'pointer-events:none;'+
9791 'z-index:-1;';
Jian Li46770fc2016-08-03 02:32:45 +09009792
Steven Burrows96ee21e2017-07-11 19:49:45 +01009793 // Prevent the iframe to gain focus on tab.
9794 // https://github.com/chartjs/Chart.js/issues/3090
9795 iframe.tabIndex = -1;
Jian Li46770fc2016-08-03 02:32:45 +09009796
Steven Burrows96ee21e2017-07-11 19:49:45 +01009797 // If the iframe is re-attached to the DOM, the resize listener is removed because the
9798 // content is reloaded, so make sure to install the handler after the iframe is loaded.
9799 // https://github.com/chartjs/Chart.js/issues/3521
9800 helpers.addEvent(iframe, 'load', function() {
9801 helpers.addEvent(iframe.contentWindow || iframe, 'resize', handler);
Jian Li46770fc2016-08-03 02:32:45 +09009802
Steven Burrows96ee21e2017-07-11 19:49:45 +01009803 // The iframe size might have changed while loading, which can also
9804 // happen if the size has been changed while detached from the DOM.
9805 handler();
9806 });
Jian Li46770fc2016-08-03 02:32:45 +09009807
Steven Burrows96ee21e2017-07-11 19:49:45 +01009808 return iframe;
9809 }
Jian Li46770fc2016-08-03 02:32:45 +09009810
Steven Burrows96ee21e2017-07-11 19:49:45 +01009811 function addResizeListener(node, listener, chart) {
9812 var stub = node._chartjs = {
9813 ticking: false
9814 };
Jian Li46770fc2016-08-03 02:32:45 +09009815
Steven Burrows96ee21e2017-07-11 19:49:45 +01009816 // Throttle the callback notification until the next animation frame.
9817 var notify = function() {
9818 if (!stub.ticking) {
9819 stub.ticking = true;
9820 helpers.requestAnimFrame.call(window, function() {
9821 if (stub.resizer) {
9822 stub.ticking = false;
9823 return listener(createEvent('resize', chart));
9824 }
9825 });
9826 }
9827 };
Jian Li46770fc2016-08-03 02:32:45 +09009828
Steven Burrows96ee21e2017-07-11 19:49:45 +01009829 // Let's keep track of this added iframe and thus avoid DOM query when removing it.
9830 stub.resizer = createResizer(notify);
9831
9832 node.insertBefore(stub.resizer, node.firstChild);
9833 }
9834
9835 function removeResizeListener(node) {
9836 if (!node || !node._chartjs) {
9837 return;
9838 }
9839
9840 var resizer = node._chartjs.resizer;
9841 if (resizer) {
9842 resizer.parentNode.removeChild(resizer);
9843 node._chartjs.resizer = null;
9844 }
9845
9846 delete node._chartjs;
9847 }
9848
9849 return {
9850 acquireContext: function(item, config) {
9851 if (typeof item === 'string') {
9852 item = document.getElementById(item);
9853 } else if (item.length) {
9854 // Support for array based queries (such as jQuery)
9855 item = item[0];
9856 }
9857
9858 if (item && item.canvas) {
9859 // Support for any object associated to a canvas (including a context2d)
9860 item = item.canvas;
9861 }
9862
9863 // To prevent canvas fingerprinting, some add-ons undefine the getContext
9864 // method, for example: https://github.com/kkapsner/CanvasBlocker
9865 // https://github.com/chartjs/Chart.js/issues/2807
9866 var context = item && item.getContext && item.getContext('2d');
9867
9868 // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
9869 // inside an iframe or when running in a protected environment. We could guess the
9870 // types from their toString() value but let's keep things flexible and assume it's
9871 // a sufficient condition if the item has a context2D which has item as `canvas`.
9872 // https://github.com/chartjs/Chart.js/issues/3887
9873 // https://github.com/chartjs/Chart.js/issues/4102
9874 // https://github.com/chartjs/Chart.js/issues/4152
9875 if (context && context.canvas === item) {
9876 initCanvas(item, config);
9877 return context;
9878 }
9879
9880 return null;
9881 },
9882
9883 releaseContext: function(context) {
9884 var canvas = context.canvas;
9885 if (!canvas._chartjs) {
9886 return;
9887 }
9888
9889 var initial = canvas._chartjs.initial;
9890 ['height', 'width'].forEach(function(prop) {
9891 var value = initial[prop];
9892 if (value === undefined || value === null) {
9893 canvas.removeAttribute(prop);
9894 } else {
9895 canvas.setAttribute(prop, value);
9896 }
9897 });
9898
9899 helpers.each(initial.style || {}, function(value, key) {
9900 canvas.style[key] = value;
9901 });
9902
9903 // The canvas render size might have been changed (and thus the state stack discarded),
9904 // we can't use save() and restore() to restore the initial state. So make sure that at
9905 // least the canvas context is reset to the default state by setting the canvas width.
9906 // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
9907 canvas.width = canvas.width;
9908
9909 delete canvas._chartjs;
9910 },
9911
9912 addEventListener: function(chart, type, listener) {
9913 var canvas = chart.canvas;
9914 if (type === 'resize') {
9915 // Note: the resize event is not supported on all browsers.
9916 addResizeListener(canvas.parentNode, listener, chart);
9917 return;
9918 }
9919
9920 var stub = listener._chartjs || (listener._chartjs = {});
9921 var proxies = stub.proxies || (stub.proxies = {});
9922 var proxy = proxies[chart.id + '_' + type] = function(event) {
9923 listener(fromNativeEvent(event, chart));
9924 };
9925
9926 helpers.addEvent(canvas, type, proxy);
9927 },
9928
9929 removeEventListener: function(chart, type, listener) {
9930 var canvas = chart.canvas;
9931 if (type === 'resize') {
9932 // Note: the resize event is not supported on all browsers.
9933 removeResizeListener(canvas.parentNode, listener);
9934 return;
9935 }
9936
9937 var stub = listener._chartjs || {};
9938 var proxies = stub.proxies || {};
9939 var proxy = proxies[chart.id + '_' + type];
9940 if (!proxy) {
9941 return;
9942 }
9943
9944 helpers.removeEvent(canvas, type, proxy);
9945 }
9946 };
9947 };
9948
Jian Li46770fc2016-08-03 02:32:45 +09009949},{}],40:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +01009950 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +09009951
Steven Burrows96ee21e2017-07-11 19:49:45 +01009952// By default, select the browser (DOM) platform.
9953// @TODO Make possible to select another platform at build time.
9954 var implementation = require(39);
Jian Li46770fc2016-08-03 02:32:45 +09009955
Steven Burrows96ee21e2017-07-11 19:49:45 +01009956 module.exports = function(Chart) {
9957 /**
9958 * @namespace Chart.platform
9959 * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
9960 * @since 2.4.0
9961 */
9962 Chart.platform = {
9963 /**
9964 * Called at chart construction time, returns a context2d instance implementing
9965 * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
9966 * @param {*} item - The native item from which to acquire context (platform specific)
9967 * @param {Object} options - The chart options
9968 * @returns {CanvasRenderingContext2D} context2d instance
9969 */
9970 acquireContext: function() {},
Jian Li46770fc2016-08-03 02:32:45 +09009971
Steven Burrows96ee21e2017-07-11 19:49:45 +01009972 /**
9973 * Called at chart destruction time, releases any resources associated to the context
9974 * previously returned by the acquireContext() method.
9975 * @param {CanvasRenderingContext2D} context - The context2d instance
9976 * @returns {Boolean} true if the method succeeded, else false
9977 */
9978 releaseContext: function() {},
Jian Li46770fc2016-08-03 02:32:45 +09009979
Steven Burrows96ee21e2017-07-11 19:49:45 +01009980 /**
9981 * Registers the specified listener on the given chart.
9982 * @param {Chart} chart - Chart from which to listen for event
9983 * @param {String} type - The ({@link IEvent}) type to listen for
9984 * @param {Function} listener - Receives a notification (an object that implements
9985 * the {@link IEvent} interface) when an event of the specified type occurs.
9986 */
9987 addEventListener: function() {},
Jian Li46770fc2016-08-03 02:32:45 +09009988
Steven Burrows96ee21e2017-07-11 19:49:45 +01009989 /**
9990 * Removes the specified listener previously registered with addEventListener.
9991 * @param {Chart} chart -Chart from which to remove the listener
9992 * @param {String} type - The ({@link IEvent}) type to remove
9993 * @param {Function} listener - The listener function to remove from the event target.
9994 */
9995 removeEventListener: function() {}
9996 };
Jian Li46770fc2016-08-03 02:32:45 +09009997
Steven Burrows96ee21e2017-07-11 19:49:45 +01009998 /**
9999 * @interface IPlatform
10000 * Allows abstracting platform dependencies away from the chart
10001 * @borrows Chart.platform.acquireContext as acquireContext
10002 * @borrows Chart.platform.releaseContext as releaseContext
10003 * @borrows Chart.platform.addEventListener as addEventListener
10004 * @borrows Chart.platform.removeEventListener as removeEventListener
10005 */
Jian Li46770fc2016-08-03 02:32:45 +090010006
Steven Burrows96ee21e2017-07-11 19:49:45 +010010007 /**
10008 * @interface IEvent
10009 * @prop {String} type - The event type name, possible values are:
10010 * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
10011 * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize'
10012 * @prop {*} native - The original native event (null for emulated events, e.g. 'resize')
10013 * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events)
10014 * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)
10015 */
Jian Li46770fc2016-08-03 02:32:45 +090010016
Steven Burrows96ee21e2017-07-11 19:49:45 +010010017 Chart.helpers.extend(Chart.platform, implementation(Chart));
10018 };
Jian Li46770fc2016-08-03 02:32:45 +090010019
Steven Burrows96ee21e2017-07-11 19:49:45 +010010020},{"39":39}],41:[function(require,module,exports){
10021 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +090010022
Steven Burrows96ee21e2017-07-11 19:49:45 +010010023 module.exports = function(Chart) {
10024 /**
10025 * Plugin based on discussion from the following Chart.js issues:
10026 * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569
10027 * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897
10028 */
10029 Chart.defaults.global.plugins.filler = {
10030 propagate: true
10031 };
Jian Li46770fc2016-08-03 02:32:45 +090010032
Steven Burrows96ee21e2017-07-11 19:49:45 +010010033 var defaults = Chart.defaults;
10034 var helpers = Chart.helpers;
10035 var mappers = {
10036 dataset: function(source) {
10037 var index = source.fill;
10038 var chart = source.chart;
10039 var meta = chart.getDatasetMeta(index);
10040 var visible = meta && chart.isDatasetVisible(index);
10041 var points = (visible && meta.dataset._children) || [];
Jian Li46770fc2016-08-03 02:32:45 +090010042
Steven Burrows96ee21e2017-07-11 19:49:45 +010010043 return !points.length? null : function(point, i) {
10044 return points[i]._view || null;
10045 };
10046 },
Jian Li46770fc2016-08-03 02:32:45 +090010047
Steven Burrows96ee21e2017-07-11 19:49:45 +010010048 boundary: function(source) {
10049 var boundary = source.boundary;
10050 var x = boundary? boundary.x : null;
10051 var y = boundary? boundary.y : null;
Jian Li46770fc2016-08-03 02:32:45 +090010052
Steven Burrows96ee21e2017-07-11 19:49:45 +010010053 return function(point) {
10054 return {
10055 x: x === null? point.x : x,
10056 y: y === null? point.y : y,
10057 };
10058 };
10059 }
10060 };
Jian Li46770fc2016-08-03 02:32:45 +090010061
Steven Burrows96ee21e2017-07-11 19:49:45 +010010062 // @todo if (fill[0] === '#')
10063 function decodeFill(el, index, count) {
10064 var model = el._model || {};
10065 var fill = model.fill;
10066 var target;
Jian Li46770fc2016-08-03 02:32:45 +090010067
Steven Burrows96ee21e2017-07-11 19:49:45 +010010068 if (fill === undefined) {
10069 fill = !!model.backgroundColor;
10070 }
Jian Li46770fc2016-08-03 02:32:45 +090010071
Steven Burrows96ee21e2017-07-11 19:49:45 +010010072 if (fill === false || fill === null) {
10073 return false;
10074 }
Jian Li46770fc2016-08-03 02:32:45 +090010075
Steven Burrows96ee21e2017-07-11 19:49:45 +010010076 if (fill === true) {
10077 return 'origin';
10078 }
Jian Li46770fc2016-08-03 02:32:45 +090010079
Steven Burrows96ee21e2017-07-11 19:49:45 +010010080 target = parseFloat(fill, 10);
10081 if (isFinite(target) && Math.floor(target) === target) {
10082 if (fill[0] === '-' || fill[0] === '+') {
10083 target = index + target;
10084 }
Jian Li46770fc2016-08-03 02:32:45 +090010085
Steven Burrows96ee21e2017-07-11 19:49:45 +010010086 if (target === index || target < 0 || target >= count) {
10087 return false;
10088 }
Jian Li46770fc2016-08-03 02:32:45 +090010089
Steven Burrows96ee21e2017-07-11 19:49:45 +010010090 return target;
10091 }
Jian Li46770fc2016-08-03 02:32:45 +090010092
Steven Burrows96ee21e2017-07-11 19:49:45 +010010093 switch (fill) {
10094 // compatibility
10095 case 'bottom':
10096 return 'start';
10097 case 'top':
10098 return 'end';
10099 case 'zero':
10100 return 'origin';
10101 // supported boundaries
10102 case 'origin':
10103 case 'start':
10104 case 'end':
10105 return fill;
10106 // invalid fill values
10107 default:
10108 return false;
10109 }
10110 }
Jian Li46770fc2016-08-03 02:32:45 +090010111
Steven Burrows96ee21e2017-07-11 19:49:45 +010010112 function computeBoundary(source) {
10113 var model = source.el._model || {};
10114 var scale = source.el._scale || {};
10115 var fill = source.fill;
10116 var target = null;
10117 var horizontal;
Jian Li46770fc2016-08-03 02:32:45 +090010118
Steven Burrows96ee21e2017-07-11 19:49:45 +010010119 if (isFinite(fill)) {
10120 return null;
10121 }
Jian Li46770fc2016-08-03 02:32:45 +090010122
Steven Burrows96ee21e2017-07-11 19:49:45 +010010123 // Backward compatibility: until v3, we still need to support boundary values set on
10124 // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
10125 // controllers might still use it (e.g. the Smith chart).
Jian Li46770fc2016-08-03 02:32:45 +090010126
Steven Burrows96ee21e2017-07-11 19:49:45 +010010127 if (fill === 'start') {
10128 target = model.scaleBottom === undefined? scale.bottom : model.scaleBottom;
10129 } else if (fill === 'end') {
10130 target = model.scaleTop === undefined? scale.top : model.scaleTop;
10131 } else if (model.scaleZero !== undefined) {
10132 target = model.scaleZero;
10133 } else if (scale.getBasePosition) {
10134 target = scale.getBasePosition();
10135 } else if (scale.getBasePixel) {
10136 target = scale.getBasePixel();
10137 }
Jian Li46770fc2016-08-03 02:32:45 +090010138
Steven Burrows96ee21e2017-07-11 19:49:45 +010010139 if (target !== undefined && target !== null) {
10140 if (target.x !== undefined && target.y !== undefined) {
10141 return target;
10142 }
Jian Li46770fc2016-08-03 02:32:45 +090010143
Steven Burrows96ee21e2017-07-11 19:49:45 +010010144 if (typeof target === 'number' && isFinite(target)) {
10145 horizontal = scale.isHorizontal();
10146 return {
10147 x: horizontal? target : null,
10148 y: horizontal? null : target
10149 };
10150 }
10151 }
Jian Li46770fc2016-08-03 02:32:45 +090010152
Steven Burrows96ee21e2017-07-11 19:49:45 +010010153 return null;
10154 }
Jian Li46770fc2016-08-03 02:32:45 +090010155
Steven Burrows96ee21e2017-07-11 19:49:45 +010010156 function resolveTarget(sources, index, propagate) {
10157 var source = sources[index];
10158 var fill = source.fill;
10159 var visited = [index];
10160 var target;
Jian Li46770fc2016-08-03 02:32:45 +090010161
Steven Burrows96ee21e2017-07-11 19:49:45 +010010162 if (!propagate) {
10163 return fill;
10164 }
Jian Li46770fc2016-08-03 02:32:45 +090010165
Steven Burrows96ee21e2017-07-11 19:49:45 +010010166 while (fill !== false && visited.indexOf(fill) === -1) {
10167 if (!isFinite(fill)) {
10168 return fill;
10169 }
Jian Li46770fc2016-08-03 02:32:45 +090010170
Steven Burrows96ee21e2017-07-11 19:49:45 +010010171 target = sources[fill];
10172 if (!target) {
10173 return false;
10174 }
Jian Li46770fc2016-08-03 02:32:45 +090010175
Steven Burrows96ee21e2017-07-11 19:49:45 +010010176 if (target.visible) {
10177 return fill;
10178 }
Jian Li46770fc2016-08-03 02:32:45 +090010179
Steven Burrows96ee21e2017-07-11 19:49:45 +010010180 visited.push(fill);
10181 fill = target.fill;
10182 }
Jian Li46770fc2016-08-03 02:32:45 +090010183
Steven Burrows96ee21e2017-07-11 19:49:45 +010010184 return false;
10185 }
Jian Li46770fc2016-08-03 02:32:45 +090010186
Steven Burrows96ee21e2017-07-11 19:49:45 +010010187 function createMapper(source) {
10188 var fill = source.fill;
10189 var type = 'dataset';
Jian Li46770fc2016-08-03 02:32:45 +090010190
Steven Burrows96ee21e2017-07-11 19:49:45 +010010191 if (fill === false) {
10192 return null;
10193 }
Jian Li46770fc2016-08-03 02:32:45 +090010194
Steven Burrows96ee21e2017-07-11 19:49:45 +010010195 if (!isFinite(fill)) {
10196 type = 'boundary';
10197 }
Jian Li46770fc2016-08-03 02:32:45 +090010198
Steven Burrows96ee21e2017-07-11 19:49:45 +010010199 return mappers[type](source);
10200 }
Jian Li46770fc2016-08-03 02:32:45 +090010201
Steven Burrows96ee21e2017-07-11 19:49:45 +010010202 function isDrawable(point) {
10203 return point && !point.skip;
10204 }
Jian Li46770fc2016-08-03 02:32:45 +090010205
Steven Burrows96ee21e2017-07-11 19:49:45 +010010206 function drawArea(ctx, curve0, curve1, len0, len1) {
10207 var i;
Jian Li46770fc2016-08-03 02:32:45 +090010208
Steven Burrows96ee21e2017-07-11 19:49:45 +010010209 if (!len0 || !len1) {
10210 return;
10211 }
Jian Li46770fc2016-08-03 02:32:45 +090010212
Steven Burrows96ee21e2017-07-11 19:49:45 +010010213 // building first area curve (normal)
10214 ctx.moveTo(curve0[0].x, curve0[0].y);
10215 for (i=1; i<len0; ++i) {
10216 helpers.canvas.lineTo(ctx, curve0[i-1], curve0[i]);
10217 }
Jian Li46770fc2016-08-03 02:32:45 +090010218
Steven Burrows96ee21e2017-07-11 19:49:45 +010010219 // joining the two area curves
10220 ctx.lineTo(curve1[len1-1].x, curve1[len1-1].y);
Jian Li46770fc2016-08-03 02:32:45 +090010221
Steven Burrows96ee21e2017-07-11 19:49:45 +010010222 // building opposite area curve (reverse)
10223 for (i=len1-1; i>0; --i) {
10224 helpers.canvas.lineTo(ctx, curve1[i], curve1[i-1], true);
10225 }
10226 }
Jian Li46770fc2016-08-03 02:32:45 +090010227
Steven Burrows96ee21e2017-07-11 19:49:45 +010010228 function doFill(ctx, points, mapper, view, color, loop) {
10229 var count = points.length;
10230 var span = view.spanGaps;
10231 var curve0 = [];
10232 var curve1 = [];
10233 var len0 = 0;
10234 var len1 = 0;
10235 var i, ilen, index, p0, p1, d0, d1;
Jian Li46770fc2016-08-03 02:32:45 +090010236
Steven Burrows96ee21e2017-07-11 19:49:45 +010010237 ctx.beginPath();
10238
10239 for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
10240 index = i%count;
10241 p0 = points[index]._view;
10242 p1 = mapper(p0, index, view);
10243 d0 = isDrawable(p0);
10244 d1 = isDrawable(p1);
10245
10246 if (d0 && d1) {
10247 len0 = curve0.push(p0);
10248 len1 = curve1.push(p1);
10249 } else if (len0 && len1) {
10250 if (!span) {
10251 drawArea(ctx, curve0, curve1, len0, len1);
10252 len0 = len1 = 0;
10253 curve0 = [];
10254 curve1 = [];
10255 } else {
10256 if (d0) {
10257 curve0.push(p0);
10258 }
10259 if (d1) {
10260 curve1.push(p1);
10261 }
10262 }
10263 }
10264 }
10265
10266 drawArea(ctx, curve0, curve1, len0, len1);
10267
10268 ctx.closePath();
10269 ctx.fillStyle = color;
10270 ctx.fill();
10271 }
10272
10273 return {
10274 id: 'filler',
10275
10276 afterDatasetsUpdate: function(chart, options) {
10277 var count = (chart.data.datasets || []).length;
10278 var propagate = options.propagate;
10279 var sources = [];
10280 var meta, i, el, source;
10281
10282 for (i = 0; i < count; ++i) {
10283 meta = chart.getDatasetMeta(i);
10284 el = meta.dataset;
10285 source = null;
10286
10287 if (el && el._model && el instanceof Chart.elements.Line) {
10288 source = {
10289 visible: chart.isDatasetVisible(i),
10290 fill: decodeFill(el, i, count),
10291 chart: chart,
10292 el: el
10293 };
10294 }
10295
10296 meta.$filler = source;
10297 sources.push(source);
10298 }
10299
10300 for (i=0; i<count; ++i) {
10301 source = sources[i];
10302 if (!source) {
10303 continue;
10304 }
10305
10306 source.fill = resolveTarget(sources, i, propagate);
10307 source.boundary = computeBoundary(source);
10308 source.mapper = createMapper(source);
10309 }
10310 },
10311
10312 beforeDatasetDraw: function(chart, args) {
10313 var meta = args.meta.$filler;
10314 if (!meta) {
10315 return;
10316 }
10317
10318 var el = meta.el;
10319 var view = el._view;
10320 var points = el._children || [];
10321 var mapper = meta.mapper;
10322 var color = view.backgroundColor || defaults.global.defaultColor;
10323
10324 if (mapper && color && points.length) {
10325 doFill(chart.ctx, points, mapper, view, color, el._loop);
10326 }
10327 }
10328 };
10329 };
10330
Jian Li46770fc2016-08-03 02:32:45 +090010331},{}],42:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +010010332 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +090010333
Steven Burrows96ee21e2017-07-11 19:49:45 +010010334 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +090010335
Steven Burrows96ee21e2017-07-11 19:49:45 +010010336 var helpers = Chart.helpers;
10337 var layout = Chart.layoutService;
10338 var noop = helpers.noop;
Jian Li46770fc2016-08-03 02:32:45 +090010339
Steven Burrows96ee21e2017-07-11 19:49:45 +010010340 Chart.defaults.global.legend = {
10341 display: true,
10342 position: 'top',
10343 fullWidth: true,
10344 reverse: false,
10345 weight: 1000,
Jian Li46770fc2016-08-03 02:32:45 +090010346
Steven Burrows96ee21e2017-07-11 19:49:45 +010010347 // a callback that will handle
10348 onClick: function(e, legendItem) {
10349 var index = legendItem.datasetIndex;
10350 var ci = this.chart;
10351 var meta = ci.getDatasetMeta(index);
Jian Li46770fc2016-08-03 02:32:45 +090010352
Steven Burrows96ee21e2017-07-11 19:49:45 +010010353 // See controller.isDatasetVisible comment
10354 meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
Jian Li46770fc2016-08-03 02:32:45 +090010355
Steven Burrows96ee21e2017-07-11 19:49:45 +010010356 // We hid a dataset ... rerender the chart
10357 ci.update();
10358 },
Jian Li46770fc2016-08-03 02:32:45 +090010359
Steven Burrows96ee21e2017-07-11 19:49:45 +010010360 onHover: null,
Jian Li46770fc2016-08-03 02:32:45 +090010361
Steven Burrows96ee21e2017-07-11 19:49:45 +010010362 labels: {
10363 boxWidth: 40,
10364 padding: 10,
10365 // Generates labels shown in the legend
10366 // Valid properties to return:
10367 // text : text to display
10368 // fillStyle : fill of coloured box
10369 // strokeStyle: stroke of coloured box
10370 // hidden : if this legend item refers to a hidden item
10371 // lineCap : cap style for line
10372 // lineDash
10373 // lineDashOffset :
10374 // lineJoin :
10375 // lineWidth :
10376 generateLabels: function(chart) {
10377 var data = chart.data;
10378 return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
10379 return {
10380 text: dataset.label,
10381 fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
10382 hidden: !chart.isDatasetVisible(i),
10383 lineCap: dataset.borderCapStyle,
10384 lineDash: dataset.borderDash,
10385 lineDashOffset: dataset.borderDashOffset,
10386 lineJoin: dataset.borderJoinStyle,
10387 lineWidth: dataset.borderWidth,
10388 strokeStyle: dataset.borderColor,
10389 pointStyle: dataset.pointStyle,
Jian Li46770fc2016-08-03 02:32:45 +090010390
Steven Burrows96ee21e2017-07-11 19:49:45 +010010391 // Below is extra data used for toggling the datasets
10392 datasetIndex: i
10393 };
10394 }, this) : [];
10395 }
10396 }
10397 };
Jian Li46770fc2016-08-03 02:32:45 +090010398
Steven Burrows96ee21e2017-07-11 19:49:45 +010010399 /**
10400 * Helper function to get the box width based on the usePointStyle option
10401 * @param labelopts {Object} the label options on the legend
10402 * @param fontSize {Number} the label font size
10403 * @return {Number} width of the color box area
10404 */
10405 function getBoxWidth(labelOpts, fontSize) {
10406 return labelOpts.usePointStyle ?
10407 fontSize * Math.SQRT2 :
10408 labelOpts.boxWidth;
10409 }
Jian Li46770fc2016-08-03 02:32:45 +090010410
Steven Burrows96ee21e2017-07-11 19:49:45 +010010411 Chart.Legend = Chart.Element.extend({
Jian Li46770fc2016-08-03 02:32:45 +090010412
Steven Burrows96ee21e2017-07-11 19:49:45 +010010413 initialize: function(config) {
10414 helpers.extend(this, config);
Jian Li46770fc2016-08-03 02:32:45 +090010415
Steven Burrows96ee21e2017-07-11 19:49:45 +010010416 // Contains hit boxes for each dataset (in dataset order)
10417 this.legendHitBoxes = [];
Jian Li46770fc2016-08-03 02:32:45 +090010418
Steven Burrows96ee21e2017-07-11 19:49:45 +010010419 // Are we in doughnut mode which has a different data type
10420 this.doughnutMode = false;
10421 },
Jian Li46770fc2016-08-03 02:32:45 +090010422
Steven Burrows96ee21e2017-07-11 19:49:45 +010010423 // These methods are ordered by lifecycle. Utilities then follow.
10424 // Any function defined here is inherited by all legend types.
10425 // Any function can be extended by the legend type
Jian Li46770fc2016-08-03 02:32:45 +090010426
Steven Burrows96ee21e2017-07-11 19:49:45 +010010427 beforeUpdate: noop,
10428 update: function(maxWidth, maxHeight, margins) {
10429 var me = this;
Jian Li46770fc2016-08-03 02:32:45 +090010430
Steven Burrows96ee21e2017-07-11 19:49:45 +010010431 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
10432 me.beforeUpdate();
Jian Li46770fc2016-08-03 02:32:45 +090010433
Steven Burrows96ee21e2017-07-11 19:49:45 +010010434 // Absorb the master measurements
10435 me.maxWidth = maxWidth;
10436 me.maxHeight = maxHeight;
10437 me.margins = margins;
Jian Li46770fc2016-08-03 02:32:45 +090010438
Steven Burrows96ee21e2017-07-11 19:49:45 +010010439 // Dimensions
10440 me.beforeSetDimensions();
10441 me.setDimensions();
10442 me.afterSetDimensions();
10443 // Labels
10444 me.beforeBuildLabels();
10445 me.buildLabels();
10446 me.afterBuildLabels();
Jian Li46770fc2016-08-03 02:32:45 +090010447
Steven Burrows96ee21e2017-07-11 19:49:45 +010010448 // Fit
10449 me.beforeFit();
10450 me.fit();
10451 me.afterFit();
10452 //
10453 me.afterUpdate();
Jian Li46770fc2016-08-03 02:32:45 +090010454
Steven Burrows96ee21e2017-07-11 19:49:45 +010010455 return me.minSize;
10456 },
10457 afterUpdate: noop,
Jian Li46770fc2016-08-03 02:32:45 +090010458
Steven Burrows96ee21e2017-07-11 19:49:45 +010010459 //
Jian Li46770fc2016-08-03 02:32:45 +090010460
Steven Burrows96ee21e2017-07-11 19:49:45 +010010461 beforeSetDimensions: noop,
10462 setDimensions: function() {
10463 var me = this;
10464 // Set the unconstrained dimension before label rotation
10465 if (me.isHorizontal()) {
10466 // Reset position before calculating rotation
10467 me.width = me.maxWidth;
10468 me.left = 0;
10469 me.right = me.width;
10470 } else {
10471 me.height = me.maxHeight;
Jian Li46770fc2016-08-03 02:32:45 +090010472
Steven Burrows96ee21e2017-07-11 19:49:45 +010010473 // Reset position before calculating rotation
10474 me.top = 0;
10475 me.bottom = me.height;
10476 }
Jian Li46770fc2016-08-03 02:32:45 +090010477
Steven Burrows96ee21e2017-07-11 19:49:45 +010010478 // Reset padding
10479 me.paddingLeft = 0;
10480 me.paddingTop = 0;
10481 me.paddingRight = 0;
10482 me.paddingBottom = 0;
Jian Li46770fc2016-08-03 02:32:45 +090010483
Steven Burrows96ee21e2017-07-11 19:49:45 +010010484 // Reset minSize
10485 me.minSize = {
10486 width: 0,
10487 height: 0
10488 };
10489 },
10490 afterSetDimensions: noop,
Jian Li46770fc2016-08-03 02:32:45 +090010491
Steven Burrows96ee21e2017-07-11 19:49:45 +010010492 //
Jian Li46770fc2016-08-03 02:32:45 +090010493
Steven Burrows96ee21e2017-07-11 19:49:45 +010010494 beforeBuildLabels: noop,
10495 buildLabels: function() {
10496 var me = this;
10497 var labelOpts = me.options.labels;
10498 var legendItems = labelOpts.generateLabels.call(me, me.chart);
Jian Li46770fc2016-08-03 02:32:45 +090010499
Steven Burrows96ee21e2017-07-11 19:49:45 +010010500 if (labelOpts.filter) {
10501 legendItems = legendItems.filter(function(item) {
10502 return labelOpts.filter(item, me.chart.data);
10503 });
10504 }
Jian Li46770fc2016-08-03 02:32:45 +090010505
Steven Burrows96ee21e2017-07-11 19:49:45 +010010506 if (me.options.reverse) {
10507 legendItems.reverse();
10508 }
Jian Li46770fc2016-08-03 02:32:45 +090010509
Steven Burrows96ee21e2017-07-11 19:49:45 +010010510 me.legendItems = legendItems;
10511 },
10512 afterBuildLabels: noop,
Jian Li46770fc2016-08-03 02:32:45 +090010513
Steven Burrows96ee21e2017-07-11 19:49:45 +010010514 //
Jian Li46770fc2016-08-03 02:32:45 +090010515
Steven Burrows96ee21e2017-07-11 19:49:45 +010010516 beforeFit: noop,
10517 fit: function() {
10518 var me = this;
10519 var opts = me.options;
10520 var labelOpts = opts.labels;
10521 var display = opts.display;
Jian Li46770fc2016-08-03 02:32:45 +090010522
Steven Burrows96ee21e2017-07-11 19:49:45 +010010523 var ctx = me.ctx;
Jian Li46770fc2016-08-03 02:32:45 +090010524
Steven Burrows96ee21e2017-07-11 19:49:45 +010010525 var globalDefault = Chart.defaults.global,
10526 itemOrDefault = helpers.getValueOrDefault,
10527 fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize),
10528 fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle),
10529 fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily),
10530 labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
Jian Li46770fc2016-08-03 02:32:45 +090010531
Steven Burrows96ee21e2017-07-11 19:49:45 +010010532 // Reset hit boxes
10533 var hitboxes = me.legendHitBoxes = [];
Jian Li46770fc2016-08-03 02:32:45 +090010534
Steven Burrows96ee21e2017-07-11 19:49:45 +010010535 var minSize = me.minSize;
10536 var isHorizontal = me.isHorizontal();
Jian Li46770fc2016-08-03 02:32:45 +090010537
Steven Burrows96ee21e2017-07-11 19:49:45 +010010538 if (isHorizontal) {
10539 minSize.width = me.maxWidth; // fill all the width
10540 minSize.height = display ? 10 : 0;
10541 } else {
10542 minSize.width = display ? 10 : 0;
10543 minSize.height = me.maxHeight; // fill all the height
10544 }
10545
10546 // Increase sizes here
10547 if (display) {
10548 ctx.font = labelFont;
10549
10550 if (isHorizontal) {
10551 // Labels
10552
10553 // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
10554 var lineWidths = me.lineWidths = [0];
10555 var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;
10556
10557 ctx.textAlign = 'left';
10558 ctx.textBaseline = 'top';
10559
10560 helpers.each(me.legendItems, function(legendItem, i) {
10561 var boxWidth = getBoxWidth(labelOpts, fontSize);
10562 var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
10563
10564 if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
10565 totalHeight += fontSize + (labelOpts.padding);
10566 lineWidths[lineWidths.length] = me.left;
10567 }
10568
10569 // Store the hitbox width and height here. Final position will be updated in `draw`
10570 hitboxes[i] = {
10571 left: 0,
10572 top: 0,
10573 width: width,
10574 height: fontSize
10575 };
10576
10577 lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
10578 });
10579
10580 minSize.height += totalHeight;
10581
10582 } else {
10583 var vPadding = labelOpts.padding;
10584 var columnWidths = me.columnWidths = [];
10585 var totalWidth = labelOpts.padding;
10586 var currentColWidth = 0;
10587 var currentColHeight = 0;
10588 var itemHeight = fontSize + vPadding;
10589
10590 helpers.each(me.legendItems, function(legendItem, i) {
10591 var boxWidth = getBoxWidth(labelOpts, fontSize);
10592 var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
10593
10594 // If too tall, go to new column
10595 if (currentColHeight + itemHeight > minSize.height) {
10596 totalWidth += currentColWidth + labelOpts.padding;
10597 columnWidths.push(currentColWidth); // previous column width
10598
10599 currentColWidth = 0;
10600 currentColHeight = 0;
10601 }
10602
10603 // Get max width
10604 currentColWidth = Math.max(currentColWidth, itemWidth);
10605 currentColHeight += itemHeight;
10606
10607 // Store the hitbox width and height here. Final position will be updated in `draw`
10608 hitboxes[i] = {
10609 left: 0,
10610 top: 0,
10611 width: itemWidth,
10612 height: fontSize
10613 };
10614 });
10615
10616 totalWidth += currentColWidth;
10617 columnWidths.push(currentColWidth);
10618 minSize.width += totalWidth;
10619 }
10620 }
10621
10622 me.width = minSize.width;
10623 me.height = minSize.height;
10624 },
10625 afterFit: noop,
10626
10627 // Shared Methods
10628 isHorizontal: function() {
10629 return this.options.position === 'top' || this.options.position === 'bottom';
10630 },
10631
10632 // Actually draw the legend on the canvas
10633 draw: function() {
10634 var me = this;
10635 var opts = me.options;
10636 var labelOpts = opts.labels;
10637 var globalDefault = Chart.defaults.global,
10638 lineDefault = globalDefault.elements.line,
10639 legendWidth = me.width,
10640 lineWidths = me.lineWidths;
10641
10642 if (opts.display) {
10643 var ctx = me.ctx,
10644 cursor,
10645 itemOrDefault = helpers.getValueOrDefault,
10646 fontColor = itemOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor),
10647 fontSize = itemOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize),
10648 fontStyle = itemOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle),
10649 fontFamily = itemOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily),
10650 labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
10651
10652 // Canvas setup
10653 ctx.textAlign = 'left';
10654 ctx.textBaseline = 'top';
10655 ctx.lineWidth = 0.5;
10656 ctx.strokeStyle = fontColor; // for strikethrough effect
10657 ctx.fillStyle = fontColor; // render in correct colour
10658 ctx.font = labelFont;
10659
10660 var boxWidth = getBoxWidth(labelOpts, fontSize),
10661 hitboxes = me.legendHitBoxes;
10662
10663 // current position
10664 var drawLegendBox = function(x, y, legendItem) {
10665 if (isNaN(boxWidth) || boxWidth <= 0) {
10666 return;
10667 }
10668
10669 // Set the ctx for the box
10670 ctx.save();
10671
10672 ctx.fillStyle = itemOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
10673 ctx.lineCap = itemOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
10674 ctx.lineDashOffset = itemOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
10675 ctx.lineJoin = itemOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
10676 ctx.lineWidth = itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
10677 ctx.strokeStyle = itemOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
10678 var isLineWidthZero = (itemOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);
10679
10680 if (ctx.setLineDash) {
10681 // IE 9 and 10 do not support line dash
10682 ctx.setLineDash(itemOrDefault(legendItem.lineDash, lineDefault.borderDash));
10683 }
10684
10685 if (opts.labels && opts.labels.usePointStyle) {
10686 // Recalculate x and y for drawPoint() because its expecting
10687 // x and y to be center of figure (instead of top left)
10688 var radius = fontSize * Math.SQRT2 / 2;
10689 var offSet = radius / Math.SQRT2;
10690 var centerX = x + offSet;
10691 var centerY = y + offSet;
10692
10693 // Draw pointStyle as legend symbol
10694 Chart.canvasHelpers.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
10695 } else {
10696 // Draw box as legend symbol
10697 if (!isLineWidthZero) {
10698 ctx.strokeRect(x, y, boxWidth, fontSize);
10699 }
10700 ctx.fillRect(x, y, boxWidth, fontSize);
10701 }
10702
10703 ctx.restore();
10704 };
10705 var fillText = function(x, y, legendItem, textWidth) {
10706 ctx.fillText(legendItem.text, boxWidth + (fontSize / 2) + x, y);
10707
10708 if (legendItem.hidden) {
10709 // Strikethrough the text if hidden
10710 ctx.beginPath();
10711 ctx.lineWidth = 2;
10712 ctx.moveTo(boxWidth + (fontSize / 2) + x, y + (fontSize / 2));
10713 ctx.lineTo(boxWidth + (fontSize / 2) + x + textWidth, y + (fontSize / 2));
10714 ctx.stroke();
10715 }
10716 };
10717
10718 // Horizontal
10719 var isHorizontal = me.isHorizontal();
10720 if (isHorizontal) {
10721 cursor = {
10722 x: me.left + ((legendWidth - lineWidths[0]) / 2),
10723 y: me.top + labelOpts.padding,
10724 line: 0
10725 };
10726 } else {
10727 cursor = {
10728 x: me.left + labelOpts.padding,
10729 y: me.top + labelOpts.padding,
10730 line: 0
10731 };
10732 }
10733
10734 var itemHeight = fontSize + labelOpts.padding;
10735 helpers.each(me.legendItems, function(legendItem, i) {
10736 var textWidth = ctx.measureText(legendItem.text).width,
10737 width = boxWidth + (fontSize / 2) + textWidth,
10738 x = cursor.x,
10739 y = cursor.y;
10740
10741 if (isHorizontal) {
10742 if (x + width >= legendWidth) {
10743 y = cursor.y += itemHeight;
10744 cursor.line++;
10745 x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
10746 }
10747 } else if (y + itemHeight > me.bottom) {
10748 x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
10749 y = cursor.y = me.top + labelOpts.padding;
10750 cursor.line++;
10751 }
10752
10753 drawLegendBox(x, y, legendItem);
10754
10755 hitboxes[i].left = x;
10756 hitboxes[i].top = y;
10757
10758 // Fill the actual label
10759 fillText(x, y, legendItem, textWidth);
10760
10761 if (isHorizontal) {
10762 cursor.x += width + (labelOpts.padding);
10763 } else {
10764 cursor.y += itemHeight;
10765 }
10766
10767 });
10768 }
10769 },
10770
10771 /**
10772 * Handle an event
10773 * @private
10774 * @param {IEvent} event - The event to handle
10775 * @return {Boolean} true if a change occured
10776 */
10777 handleEvent: function(e) {
10778 var me = this;
10779 var opts = me.options;
10780 var type = e.type === 'mouseup' ? 'click' : e.type;
10781 var changed = false;
10782
10783 if (type === 'mousemove') {
10784 if (!opts.onHover) {
10785 return;
10786 }
10787 } else if (type === 'click') {
10788 if (!opts.onClick) {
10789 return;
10790 }
10791 } else {
10792 return;
10793 }
10794
10795 // Chart event already has relative position in it
10796 var x = e.x,
10797 y = e.y;
10798
10799 if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
10800 // See if we are touching one of the dataset boxes
10801 var lh = me.legendHitBoxes;
10802 for (var i = 0; i < lh.length; ++i) {
10803 var hitBox = lh[i];
10804
10805 if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
10806 // Touching an element
10807 if (type === 'click') {
10808 // use e.native for backwards compatibility
10809 opts.onClick.call(me, e.native, me.legendItems[i]);
10810 changed = true;
10811 break;
10812 } else if (type === 'mousemove') {
10813 // use e.native for backwards compatibility
10814 opts.onHover.call(me, e.native, me.legendItems[i]);
10815 changed = true;
10816 break;
10817 }
10818 }
10819 }
10820 }
10821
10822 return changed;
10823 }
10824 });
10825
10826 function createNewLegendAndAttach(chart, legendOpts) {
10827 var legend = new Chart.Legend({
10828 ctx: chart.ctx,
10829 options: legendOpts,
10830 chart: chart
10831 });
10832
10833 layout.configure(chart, legend, legendOpts);
10834 layout.addBox(chart, legend);
10835 chart.legend = legend;
10836 }
10837
10838 return {
10839 id: 'legend',
10840
10841 beforeInit: function(chart) {
10842 var legendOpts = chart.options.legend;
10843
10844 if (legendOpts) {
10845 createNewLegendAndAttach(chart, legendOpts);
10846 }
10847 },
10848
10849 beforeUpdate: function(chart) {
10850 var legendOpts = chart.options.legend;
10851 var legend = chart.legend;
10852
10853 if (legendOpts) {
10854 legendOpts = helpers.configMerge(Chart.defaults.global.legend, legendOpts);
10855
10856 if (legend) {
10857 layout.configure(chart, legend, legendOpts);
10858 legend.options = legendOpts;
10859 } else {
10860 createNewLegendAndAttach(chart, legendOpts);
10861 }
10862 } else if (legend) {
10863 layout.removeBox(chart, legend);
10864 delete chart.legend;
10865 }
10866 },
10867
10868 afterEvent: function(chart, e) {
10869 var legend = chart.legend;
10870 if (legend) {
10871 legend.handleEvent(e);
10872 }
10873 }
10874 };
10875 };
10876
Jian Li46770fc2016-08-03 02:32:45 +090010877},{}],43:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +010010878 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +090010879
Steven Burrows96ee21e2017-07-11 19:49:45 +010010880 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +090010881
Steven Burrows96ee21e2017-07-11 19:49:45 +010010882 var helpers = Chart.helpers;
10883 var layout = Chart.layoutService;
10884 var noop = helpers.noop;
Jian Li46770fc2016-08-03 02:32:45 +090010885
Steven Burrows96ee21e2017-07-11 19:49:45 +010010886 Chart.defaults.global.title = {
10887 display: false,
10888 position: 'top',
10889 fullWidth: true,
10890 weight: 2000, // by default greater than legend (1000) to be above
10891 fontStyle: 'bold',
10892 padding: 10,
Jian Li46770fc2016-08-03 02:32:45 +090010893
Steven Burrows96ee21e2017-07-11 19:49:45 +010010894 // actual title
10895 text: ''
10896 };
Jian Li46770fc2016-08-03 02:32:45 +090010897
Steven Burrows96ee21e2017-07-11 19:49:45 +010010898 Chart.Title = Chart.Element.extend({
10899 initialize: function(config) {
10900 var me = this;
10901 helpers.extend(me, config);
Jian Li46770fc2016-08-03 02:32:45 +090010902
Steven Burrows96ee21e2017-07-11 19:49:45 +010010903 // Contains hit boxes for each dataset (in dataset order)
10904 me.legendHitBoxes = [];
10905 },
Jian Li46770fc2016-08-03 02:32:45 +090010906
Steven Burrows96ee21e2017-07-11 19:49:45 +010010907 // These methods are ordered by lifecycle. Utilities then follow.
Jian Li46770fc2016-08-03 02:32:45 +090010908
Steven Burrows96ee21e2017-07-11 19:49:45 +010010909 beforeUpdate: noop,
10910 update: function(maxWidth, maxHeight, margins) {
10911 var me = this;
Jian Li46770fc2016-08-03 02:32:45 +090010912
Steven Burrows96ee21e2017-07-11 19:49:45 +010010913 // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
10914 me.beforeUpdate();
Jian Li46770fc2016-08-03 02:32:45 +090010915
Steven Burrows96ee21e2017-07-11 19:49:45 +010010916 // Absorb the master measurements
10917 me.maxWidth = maxWidth;
10918 me.maxHeight = maxHeight;
10919 me.margins = margins;
Jian Li46770fc2016-08-03 02:32:45 +090010920
Steven Burrows96ee21e2017-07-11 19:49:45 +010010921 // Dimensions
10922 me.beforeSetDimensions();
10923 me.setDimensions();
10924 me.afterSetDimensions();
10925 // Labels
10926 me.beforeBuildLabels();
10927 me.buildLabels();
10928 me.afterBuildLabels();
Jian Li46770fc2016-08-03 02:32:45 +090010929
Steven Burrows96ee21e2017-07-11 19:49:45 +010010930 // Fit
10931 me.beforeFit();
10932 me.fit();
10933 me.afterFit();
10934 //
10935 me.afterUpdate();
Jian Li46770fc2016-08-03 02:32:45 +090010936
Steven Burrows96ee21e2017-07-11 19:49:45 +010010937 return me.minSize;
Jian Lid7a5a742016-02-12 13:51:18 -080010938
Steven Burrows96ee21e2017-07-11 19:49:45 +010010939 },
10940 afterUpdate: noop,
Jian Lid7a5a742016-02-12 13:51:18 -080010941
Steven Burrows96ee21e2017-07-11 19:49:45 +010010942 //
Jian Lid7a5a742016-02-12 13:51:18 -080010943
Steven Burrows96ee21e2017-07-11 19:49:45 +010010944 beforeSetDimensions: noop,
10945 setDimensions: function() {
10946 var me = this;
10947 // Set the unconstrained dimension before label rotation
10948 if (me.isHorizontal()) {
10949 // Reset position before calculating rotation
10950 me.width = me.maxWidth;
10951 me.left = 0;
10952 me.right = me.width;
10953 } else {
10954 me.height = me.maxHeight;
Jian Li46770fc2016-08-03 02:32:45 +090010955
Steven Burrows96ee21e2017-07-11 19:49:45 +010010956 // Reset position before calculating rotation
10957 me.top = 0;
10958 me.bottom = me.height;
10959 }
Jian Li46770fc2016-08-03 02:32:45 +090010960
Steven Burrows96ee21e2017-07-11 19:49:45 +010010961 // Reset padding
10962 me.paddingLeft = 0;
10963 me.paddingTop = 0;
10964 me.paddingRight = 0;
10965 me.paddingBottom = 0;
Jian Li46770fc2016-08-03 02:32:45 +090010966
Steven Burrows96ee21e2017-07-11 19:49:45 +010010967 // Reset minSize
10968 me.minSize = {
10969 width: 0,
10970 height: 0
10971 };
10972 },
10973 afterSetDimensions: noop,
Jian Li46770fc2016-08-03 02:32:45 +090010974
Steven Burrows96ee21e2017-07-11 19:49:45 +010010975 //
Jian Li46770fc2016-08-03 02:32:45 +090010976
Steven Burrows96ee21e2017-07-11 19:49:45 +010010977 beforeBuildLabels: noop,
10978 buildLabels: noop,
10979 afterBuildLabels: noop,
Jian Li46770fc2016-08-03 02:32:45 +090010980
Steven Burrows96ee21e2017-07-11 19:49:45 +010010981 //
Jian Li46770fc2016-08-03 02:32:45 +090010982
Steven Burrows96ee21e2017-07-11 19:49:45 +010010983 beforeFit: noop,
10984 fit: function() {
10985 var me = this,
10986 valueOrDefault = helpers.getValueOrDefault,
10987 opts = me.options,
10988 globalDefaults = Chart.defaults.global,
10989 display = opts.display,
10990 fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize),
10991 minSize = me.minSize;
Jian Li46770fc2016-08-03 02:32:45 +090010992
Steven Burrows96ee21e2017-07-11 19:49:45 +010010993 if (me.isHorizontal()) {
10994 minSize.width = me.maxWidth; // fill all the width
10995 minSize.height = display ? fontSize + (opts.padding * 2) : 0;
10996 } else {
10997 minSize.width = display ? fontSize + (opts.padding * 2) : 0;
10998 minSize.height = me.maxHeight; // fill all the height
10999 }
Jian Li46770fc2016-08-03 02:32:45 +090011000
Steven Burrows96ee21e2017-07-11 19:49:45 +010011001 me.width = minSize.width;
11002 me.height = minSize.height;
Jian Li46770fc2016-08-03 02:32:45 +090011003
Steven Burrows96ee21e2017-07-11 19:49:45 +010011004 },
11005 afterFit: noop,
Jian Li46770fc2016-08-03 02:32:45 +090011006
Steven Burrows96ee21e2017-07-11 19:49:45 +010011007 // Shared Methods
11008 isHorizontal: function() {
11009 var pos = this.options.position;
11010 return pos === 'top' || pos === 'bottom';
11011 },
Jian Li46770fc2016-08-03 02:32:45 +090011012
Steven Burrows96ee21e2017-07-11 19:49:45 +010011013 // Actually draw the title block on the canvas
11014 draw: function() {
11015 var me = this,
11016 ctx = me.ctx,
11017 valueOrDefault = helpers.getValueOrDefault,
11018 opts = me.options,
11019 globalDefaults = Chart.defaults.global;
Jian Li46770fc2016-08-03 02:32:45 +090011020
Steven Burrows96ee21e2017-07-11 19:49:45 +010011021 if (opts.display) {
11022 var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize),
11023 fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle),
11024 fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily),
11025 titleFont = helpers.fontString(fontSize, fontStyle, fontFamily),
11026 rotation = 0,
11027 titleX,
11028 titleY,
11029 top = me.top,
11030 left = me.left,
11031 bottom = me.bottom,
11032 right = me.right,
11033 maxWidth;
Jian Li46770fc2016-08-03 02:32:45 +090011034
Steven Burrows96ee21e2017-07-11 19:49:45 +010011035 ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
11036 ctx.font = titleFont;
Jian Li46770fc2016-08-03 02:32:45 +090011037
Steven Burrows96ee21e2017-07-11 19:49:45 +010011038 // Horizontal
11039 if (me.isHorizontal()) {
11040 titleX = left + ((right - left) / 2); // midpoint of the width
11041 titleY = top + ((bottom - top) / 2); // midpoint of the height
11042 maxWidth = right - left;
11043 } else {
11044 titleX = opts.position === 'left' ? left + (fontSize / 2) : right - (fontSize / 2);
11045 titleY = top + ((bottom - top) / 2);
11046 maxWidth = bottom - top;
11047 rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
11048 }
Jian Li46770fc2016-08-03 02:32:45 +090011049
Steven Burrows96ee21e2017-07-11 19:49:45 +010011050 ctx.save();
11051 ctx.translate(titleX, titleY);
11052 ctx.rotate(rotation);
11053 ctx.textAlign = 'center';
11054 ctx.textBaseline = 'middle';
11055 ctx.fillText(opts.text, 0, 0, maxWidth);
11056 ctx.restore();
11057 }
11058 }
11059 });
Jian Li46770fc2016-08-03 02:32:45 +090011060
Steven Burrows96ee21e2017-07-11 19:49:45 +010011061 function createNewTitleBlockAndAttach(chart, titleOpts) {
11062 var title = new Chart.Title({
11063 ctx: chart.ctx,
11064 options: titleOpts,
11065 chart: chart
11066 });
Jian Li46770fc2016-08-03 02:32:45 +090011067
Steven Burrows96ee21e2017-07-11 19:49:45 +010011068 layout.configure(chart, title, titleOpts);
11069 layout.addBox(chart, title);
11070 chart.titleBlock = title;
11071 }
Jian Li46770fc2016-08-03 02:32:45 +090011072
Steven Burrows96ee21e2017-07-11 19:49:45 +010011073 return {
11074 id: 'title',
Jian Li46770fc2016-08-03 02:32:45 +090011075
Steven Burrows96ee21e2017-07-11 19:49:45 +010011076 beforeInit: function(chart) {
11077 var titleOpts = chart.options.title;
Jian Li46770fc2016-08-03 02:32:45 +090011078
Steven Burrows96ee21e2017-07-11 19:49:45 +010011079 if (titleOpts) {
11080 createNewTitleBlockAndAttach(chart, titleOpts);
11081 }
11082 },
Jian Li46770fc2016-08-03 02:32:45 +090011083
Steven Burrows96ee21e2017-07-11 19:49:45 +010011084 beforeUpdate: function(chart) {
11085 var titleOpts = chart.options.title;
11086 var titleBlock = chart.titleBlock;
Jian Li46770fc2016-08-03 02:32:45 +090011087
Steven Burrows96ee21e2017-07-11 19:49:45 +010011088 if (titleOpts) {
11089 titleOpts = helpers.configMerge(Chart.defaults.global.title, titleOpts);
Jian Li46770fc2016-08-03 02:32:45 +090011090
Steven Burrows96ee21e2017-07-11 19:49:45 +010011091 if (titleBlock) {
11092 layout.configure(chart, titleBlock, titleOpts);
11093 titleBlock.options = titleOpts;
11094 } else {
11095 createNewTitleBlockAndAttach(chart, titleOpts);
11096 }
11097 } else if (titleBlock) {
11098 Chart.layoutService.removeBox(chart, titleBlock);
11099 delete chart.titleBlock;
11100 }
11101 }
11102 };
11103 };
Jian Li46770fc2016-08-03 02:32:45 +090011104
11105},{}],44:[function(require,module,exports){
Steven Burrows96ee21e2017-07-11 19:49:45 +010011106 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +090011107
Steven Burrows96ee21e2017-07-11 19:49:45 +010011108 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +090011109
Steven Burrows96ee21e2017-07-11 19:49:45 +010011110 var helpers = Chart.helpers;
11111 // Default config for a category scale
11112 var defaultConfig = {
11113 position: 'bottom'
11114 };
Jian Li46770fc2016-08-03 02:32:45 +090011115
Steven Burrows96ee21e2017-07-11 19:49:45 +010011116 var DatasetScale = Chart.Scale.extend({
11117 /**
11118 * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
11119 * else fall back to data.labels
11120 * @private
11121 */
11122 getLabels: function() {
11123 var data = this.chart.data;
11124 return (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
11125 },
Jian Li46770fc2016-08-03 02:32:45 +090011126
Steven Burrows96ee21e2017-07-11 19:49:45 +010011127 determineDataLimits: function() {
11128 var me = this;
11129 var labels = me.getLabels();
11130 me.minIndex = 0;
11131 me.maxIndex = labels.length - 1;
11132 var findIndex;
Jian Li46770fc2016-08-03 02:32:45 +090011133
Steven Burrows96ee21e2017-07-11 19:49:45 +010011134 if (me.options.ticks.min !== undefined) {
11135 // user specified min value
11136 findIndex = helpers.indexOf(labels, me.options.ticks.min);
11137 me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
11138 }
Jian Li46770fc2016-08-03 02:32:45 +090011139
Steven Burrows96ee21e2017-07-11 19:49:45 +010011140 if (me.options.ticks.max !== undefined) {
11141 // user specified max value
11142 findIndex = helpers.indexOf(labels, me.options.ticks.max);
11143 me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
11144 }
Jian Li46770fc2016-08-03 02:32:45 +090011145
Steven Burrows96ee21e2017-07-11 19:49:45 +010011146 me.min = labels[me.minIndex];
11147 me.max = labels[me.maxIndex];
11148 },
Jian Li46770fc2016-08-03 02:32:45 +090011149
Steven Burrows96ee21e2017-07-11 19:49:45 +010011150 buildTicks: function() {
11151 var me = this;
11152 var labels = me.getLabels();
11153 // If we are viewing some subset of labels, slice the original array
11154 me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
11155 },
Jian Li46770fc2016-08-03 02:32:45 +090011156
Steven Burrows96ee21e2017-07-11 19:49:45 +010011157 getLabelForIndex: function(index, datasetIndex) {
11158 var me = this;
11159 var data = me.chart.data;
11160 var isHorizontal = me.isHorizontal();
Jian Li46770fc2016-08-03 02:32:45 +090011161
Steven Burrows96ee21e2017-07-11 19:49:45 +010011162 if (data.yLabels && !isHorizontal) {
11163 return me.getRightValue(data.datasets[datasetIndex].data[index]);
11164 }
11165 return me.ticks[index - me.minIndex];
11166 },
Jian Li46770fc2016-08-03 02:32:45 +090011167
Steven Burrows96ee21e2017-07-11 19:49:45 +010011168 // Used to get data value locations. Value can either be an index or a numerical value
11169 getPixelForValue: function(value, index, datasetIndex, includeOffset) {
11170 var me = this;
11171 // 1 is added because we need the length but we have the indexes
11172 var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
Jian Li46770fc2016-08-03 02:32:45 +090011173
Steven Burrows96ee21e2017-07-11 19:49:45 +010011174 // If value is a data object, then index is the index in the data array,
11175 // not the index of the scale. We need to change that.
11176 var valueCategory;
11177 if (value !== undefined && value !== null) {
11178 valueCategory = me.isHorizontal() ? value.x : value.y;
11179 }
11180 if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
11181 var labels = me.getLabels();
11182 value = valueCategory || value;
11183 var idx = labels.indexOf(value);
11184 index = idx !== -1 ? idx : index;
11185 }
Jian Li46770fc2016-08-03 02:32:45 +090011186
Steven Burrows96ee21e2017-07-11 19:49:45 +010011187 if (me.isHorizontal()) {
11188 var valueWidth = me.width / offsetAmt;
11189 var widthOffset = (valueWidth * (index - me.minIndex));
Jian Li46770fc2016-08-03 02:32:45 +090011190
Steven Burrows96ee21e2017-07-11 19:49:45 +010011191 if (me.options.gridLines.offsetGridLines && includeOffset || me.maxIndex === me.minIndex && includeOffset) {
11192 widthOffset += (valueWidth / 2);
11193 }
Jian Li46770fc2016-08-03 02:32:45 +090011194
Steven Burrows96ee21e2017-07-11 19:49:45 +010011195 return me.left + Math.round(widthOffset);
11196 }
11197 var valueHeight = me.height / offsetAmt;
11198 var heightOffset = (valueHeight * (index - me.minIndex));
Jian Li46770fc2016-08-03 02:32:45 +090011199
Steven Burrows96ee21e2017-07-11 19:49:45 +010011200 if (me.options.gridLines.offsetGridLines && includeOffset) {
11201 heightOffset += (valueHeight / 2);
11202 }
Jian Li46770fc2016-08-03 02:32:45 +090011203
Steven Burrows96ee21e2017-07-11 19:49:45 +010011204 return me.top + Math.round(heightOffset);
11205 },
11206 getPixelForTick: function(index, includeOffset) {
11207 return this.getPixelForValue(this.ticks[index], index + this.minIndex, null, includeOffset);
11208 },
11209 getValueForPixel: function(pixel) {
11210 var me = this;
11211 var value;
11212 var offsetAmt = Math.max((me.ticks.length - ((me.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
11213 var horz = me.isHorizontal();
11214 var valueDimension = (horz ? me.width : me.height) / offsetAmt;
Jian Li46770fc2016-08-03 02:32:45 +090011215
Steven Burrows96ee21e2017-07-11 19:49:45 +010011216 pixel -= horz ? me.left : me.top;
Jian Li46770fc2016-08-03 02:32:45 +090011217
Steven Burrows96ee21e2017-07-11 19:49:45 +010011218 if (me.options.gridLines.offsetGridLines) {
11219 pixel -= (valueDimension / 2);
11220 }
Jian Li46770fc2016-08-03 02:32:45 +090011221
Steven Burrows96ee21e2017-07-11 19:49:45 +010011222 if (pixel <= 0) {
11223 value = 0;
11224 } else {
11225 value = Math.round(pixel / valueDimension);
11226 }
Jian Li46770fc2016-08-03 02:32:45 +090011227
Steven Burrows96ee21e2017-07-11 19:49:45 +010011228 return value;
11229 },
11230 getBasePixel: function() {
11231 return this.bottom;
11232 }
11233 });
Jian Li46770fc2016-08-03 02:32:45 +090011234
Steven Burrows96ee21e2017-07-11 19:49:45 +010011235 Chart.scaleService.registerScaleType('category', DatasetScale, defaultConfig);
Jian Li46770fc2016-08-03 02:32:45 +090011236
Steven Burrows96ee21e2017-07-11 19:49:45 +010011237 };
Jian Li46770fc2016-08-03 02:32:45 +090011238
Steven Burrows96ee21e2017-07-11 19:49:45 +010011239},{}],45:[function(require,module,exports){
11240 'use strict';
Jian Li46770fc2016-08-03 02:32:45 +090011241
Steven Burrows96ee21e2017-07-11 19:49:45 +010011242 module.exports = function(Chart) {
Jian Li46770fc2016-08-03 02:32:45 +090011243
Steven Burrows96ee21e2017-07-11 19:49:45 +010011244 var helpers = Chart.helpers;
Jian Li46770fc2016-08-03 02:32:45 +090011245
Steven Burrows96ee21e2017-07-11 19:49:45 +010011246 var defaultConfig = {
11247 position: 'left',
11248 ticks: {
11249 callback: Chart.Ticks.formatters.linear
11250 }
11251 };
Jian Li46770fc2016-08-03 02:32:45 +090011252
Steven Burrows96ee21e2017-07-11 19:49:45 +010011253 var LinearScale = Chart.LinearScaleBase.extend({
Jian Li46770fc2016-08-03 02:32:45 +090011254
Steven Burrows96ee21e2017-07-11 19:49:45 +010011255 determineDataLimits: function() {
11256 var me = this;
11257 var opts = me.options;
11258 var chart = me.chart;
11259 var data = chart.data;
11260 var datasets = data.datasets;
11261 var isHorizontal = me.isHorizontal();
11262 var DEFAULT_MIN = 0;
11263 var DEFAULT_MAX = 1;
Jian Li46770fc2016-08-03 02:32:45 +090011264
Steven Burrows96ee21e2017-07-11 19:49:45 +010011265 function IDMatches(meta) {
11266 return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
11267 }
Jian Li46770fc2016-08-03 02:32:45 +090011268
Steven Burrows96ee21e2017-07-11 19:49:45 +010011269 // First Calculate the range
11270 me.min = null;
11271 me.max = null;
Jian Li46770fc2016-08-03 02:32:45 +090011272
Steven Burrows96ee21e2017-07-11 19:49:45 +010011273 var hasStacks = opts.stacked;
11274 if (hasStacks === undefined) {
11275 helpers.each(datasets, function(dataset, datasetIndex) {
11276 if (hasStacks) {
11277 return;
11278 }
Jian Li46770fc2016-08-03 02:32:45 +090011279
Steven Burrows96ee21e2017-07-11 19:49:45 +010011280 var meta = chart.getDatasetMeta(datasetIndex);
11281 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
11282 meta.stack !== undefined) {
11283 hasStacks = true;
11284 }
11285 });
11286 }
Jian Li46770fc2016-08-03 02:32:45 +090011287
Steven Burrows96ee21e2017-07-11 19:49:45 +010011288 if (opts.stacked || hasStacks) {
11289 var valuesPerStack = {};
Jian Li46770fc2016-08-03 02:32:45 +090011290
Steven Burrows96ee21e2017-07-11 19:49:45 +010011291 helpers.each(datasets, function(dataset, datasetIndex) {
11292 var meta = chart.getDatasetMeta(datasetIndex);
11293 var key = [
11294 meta.type,
11295 // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
11296 ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
11297 meta.stack
11298 ].join('.');
Jian Li46770fc2016-08-03 02:32:45 +090011299
Steven Burrows96ee21e2017-07-11 19:49:45 +010011300 if (valuesPerStack[key] === undefined) {
11301 valuesPerStack[key] = {
11302 positiveValues: [],
11303 negativeValues: []
11304 };
11305 }
Jian Li46770fc2016-08-03 02:32:45 +090011306
Steven Burrows96ee21e2017-07-11 19:49:45 +010011307 // Store these per type
11308 var positiveValues = valuesPerStack[key].positiveValues;
11309 var negativeValues = valuesPerStack[key].negativeValues;
Jian Li46770fc2016-08-03 02:32:45 +090011310
Steven Burrows96ee21e2017-07-11 19:49:45 +010011311 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
11312 helpers.each(dataset.data, function(rawValue, index) {
11313 var value = +me.getRightValue(rawValue);
11314 if (isNaN(value) || meta.data[index].hidden) {
11315 return;
11316 }
Jian Li46770fc2016-08-03 02:32:45 +090011317
Steven Burrows96ee21e2017-07-11 19:49:45 +010011318 positiveValues[index] = positiveValues[index] || 0;
11319 negativeValues[index] = negativeValues[index] || 0;
Jian Li46770fc2016-08-03 02:32:45 +090011320
Steven Burrows96ee21e2017-07-11 19:49:45 +010011321 if (opts.relativePoints) {
11322 positiveValues[index] = 100;
11323 } else if (value < 0) {
11324 negativeValues[index] += value;
11325 } else {
11326 positiveValues[index] += value;
11327 }
11328 });
11329 }
11330 });
Jian Li46770fc2016-08-03 02:32:45 +090011331
Steven Burrows96ee21e2017-07-11 19:49:45 +010011332 helpers.each(valuesPerStack, function(valuesForType) {
11333 var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
11334 var minVal = helpers.min(values);
11335 var maxVal = helpers.max(values);
11336 me.min = me.min === null ? minVal : Math.min(me.min, minVal);
11337 me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
11338 });
Jian Li46770fc2016-08-03 02:32:45 +090011339
Steven Burrows96ee21e2017-07-11 19:49:45 +010011340 } else {
11341 helpers.each(datasets, function(dataset, datasetIndex) {
11342 var meta = chart.getDatasetMeta(datasetIndex);
11343 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
11344 helpers.each(dataset.data, function(rawValue, index) {
11345 var value = +me.getRightValue(rawValue);
11346 if (isNaN(value) || meta.data[index].hidden) {
11347 return;
11348 }
Jian Li46770fc2016-08-03 02:32:45 +090011349
Steven Burrows96ee21e2017-07-11 19:49:45 +010011350 if (me.min === null) {
11351 me.min = value;
11352 } else if (value < me.min) {
11353 me.min = value;
11354 }
Jian Li46770fc2016-08-03 02:32:45 +090011355
Steven Burrows96ee21e2017-07-11 19:49:45 +010011356 if (me.max === null) {
11357 me.max = value;
11358 } else if (value > me.max) {
11359 me.max = value;
11360 }
11361 });
11362 }
11363 });
11364 }
Jian Li46770fc2016-08-03 02:32:45 +090011365
Steven Burrows96ee21e2017-07-11 19:49:45 +010011366 me.min = isFinite(me.min) ? me.min : DEFAULT_MIN;
11367 me.max = isFinite(me.max) ? me.max : DEFAULT_MAX;
Jian Li46770fc2016-08-03 02:32:45 +090011368
Steven Burrows96ee21e2017-07-11 19:49:45 +010011369 // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
11370 this.handleTickRangeOptions();
11371 },
11372 getTickLimit: function() {
11373 var maxTicks;
11374 var me = this;
11375 var tickOpts = me.options.ticks;
Jian Li46770fc2016-08-03 02:32:45 +090011376
Steven Burrows96ee21e2017-07-11 19:49:45 +010011377 if (me.isHorizontal()) {
11378 maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));
11379 } else {
11380 // The factor of 2 used to scale the font size has been experimentally determined.
11381 var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, Chart.defaults.global.defaultFontSize);
11382 maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));
11383 }
Jian Li46770fc2016-08-03 02:32:45 +090011384
Steven Burrows96ee21e2017-07-11 19:49:45 +010011385 return maxTicks;
11386 },
11387 // Called after the ticks are built. We need
11388 handleDirectionalChanges: function() {
11389 if (!this.isHorizontal()) {
11390 // We are in a vertical orientation. The top value is the highest. So reverse the array
11391 this.ticks.reverse();
11392 }
11393 },
11394 getLabelForIndex: function(index, datasetIndex) {
11395 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
11396 },
11397 // Utils
11398 getPixelForValue: function(value) {
11399 // This must be called after fit has been run so that
11400 // this.left, this.top, this.right, and this.bottom have been defined
11401 var me = this;
11402 var start = me.start;
Jian Li46770fc2016-08-03 02:32:45 +090011403
Steven Burrows96ee21e2017-07-11 19:49:45 +010011404 var rightValue = +me.getRightValue(value);
11405 var pixel;
11406 var range = me.end - start;
Jian Li46770fc2016-08-03 02:32:45 +090011407
Steven Burrows96ee21e2017-07-11 19:49:45 +010011408 if (me.isHorizontal()) {
11409 pixel = me.left + (me.width / range * (rightValue - start));
11410 return Math.round(pixel);
11411 }
Jian Li46770fc2016-08-03 02:32:45 +090011412
Steven Burrows96ee21e2017-07-11 19:49:45 +010011413 pixel = me.bottom - (me.height / range * (rightValue - start));
11414 return Math.round(pixel);
11415 },
11416 getValueForPixel: function(pixel) {
11417 var me = this;
11418 var isHorizontal = me.isHorizontal();
11419 var innerDimension = isHorizontal ? me.width : me.height;
11420 var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
11421 return me.start + ((me.end - me.start) * offset);
11422 },
11423 getPixelForTick: function(index) {
11424 return this.getPixelForValue(this.ticksAsNumbers[index]);
11425 }
11426 });
11427 Chart.scaleService.registerScaleType('linear', LinearScale, defaultConfig);
Jian Li46770fc2016-08-03 02:32:45 +090011428
Steven Burrows96ee21e2017-07-11 19:49:45 +010011429 };
Jian Li46770fc2016-08-03 02:32:45 +090011430
Steven Burrows96ee21e2017-07-11 19:49:45 +010011431},{}],46:[function(require,module,exports){
11432 'use strict';
11433
11434 module.exports = function(Chart) {
11435
11436 var helpers = Chart.helpers,
11437 noop = helpers.noop;
11438
11439 Chart.LinearScaleBase = Chart.Scale.extend({
11440 handleTickRangeOptions: function() {
11441 var me = this;
11442 var opts = me.options;
11443 var tickOpts = opts.ticks;
11444
11445 // If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
11446 // do nothing since that would make the chart weird. If the user really wants a weird chart
11447 // axis, they can manually override it
11448 if (tickOpts.beginAtZero) {
11449 var minSign = helpers.sign(me.min);
11450 var maxSign = helpers.sign(me.max);
11451
11452 if (minSign < 0 && maxSign < 0) {
11453 // move the top up to 0
11454 me.max = 0;
11455 } else if (minSign > 0 && maxSign > 0) {
11456 // move the bottom down to 0
11457 me.min = 0;
11458 }
11459 }
11460
11461 if (tickOpts.min !== undefined) {
11462 me.min = tickOpts.min;
11463 } else if (tickOpts.suggestedMin !== undefined) {
11464 if (me.min === null) {
11465 me.min = tickOpts.suggestedMin;
11466 } else {
11467 me.min = Math.min(me.min, tickOpts.suggestedMin);
11468 }
11469 }
11470
11471 if (tickOpts.max !== undefined) {
11472 me.max = tickOpts.max;
11473 } else if (tickOpts.suggestedMax !== undefined) {
11474 if (me.max === null) {
11475 me.max = tickOpts.suggestedMax;
11476 } else {
11477 me.max = Math.max(me.max, tickOpts.suggestedMax);
11478 }
11479 }
11480
11481 if (me.min === me.max) {
11482 me.max++;
11483
11484 if (!tickOpts.beginAtZero) {
11485 me.min--;
11486 }
11487 }
11488 },
11489 getTickLimit: noop,
11490 handleDirectionalChanges: noop,
11491
11492 buildTicks: function() {
11493 var me = this;
11494 var opts = me.options;
11495 var tickOpts = opts.ticks;
11496
11497 // Figure out what the max number of ticks we can support it is based on the size of
11498 // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
11499 // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
11500 // the graph. Make sure we always have at least 2 ticks
11501 var maxTicks = me.getTickLimit();
11502 maxTicks = Math.max(2, maxTicks);
11503
11504 var numericGeneratorOptions = {
11505 maxTicks: maxTicks,
11506 min: tickOpts.min,
11507 max: tickOpts.max,
11508 stepSize: helpers.getValueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
11509 };
11510 var ticks = me.ticks = Chart.Ticks.generators.linear(numericGeneratorOptions, me);
11511
11512 me.handleDirectionalChanges();
11513
11514 // At this point, we need to update our max and min given the tick values since we have expanded the
11515 // range of the scale
11516 me.max = helpers.max(ticks);
11517 me.min = helpers.min(ticks);
11518
11519 if (tickOpts.reverse) {
11520 ticks.reverse();
11521
11522 me.start = me.max;
11523 me.end = me.min;
11524 } else {
11525 me.start = me.min;
11526 me.end = me.max;
11527 }
11528 },
11529 convertTicksToLabels: function() {
11530 var me = this;
11531 me.ticksAsNumbers = me.ticks.slice();
11532 me.zeroLineIndex = me.ticks.indexOf(0);
11533
11534 Chart.Scale.prototype.convertTicksToLabels.call(me);
11535 }
11536 });
11537 };
11538
11539},{}],47:[function(require,module,exports){
11540 'use strict';
11541
11542 module.exports = function(Chart) {
11543
11544 var helpers = Chart.helpers;
11545
11546 var defaultConfig = {
11547 position: 'left',
11548
11549 // label settings
11550 ticks: {
11551 callback: Chart.Ticks.formatters.logarithmic
11552 }
11553 };
11554
11555 var LogarithmicScale = Chart.Scale.extend({
11556 determineDataLimits: function() {
11557 var me = this;
11558 var opts = me.options;
11559 var tickOpts = opts.ticks;
11560 var chart = me.chart;
11561 var data = chart.data;
11562 var datasets = data.datasets;
11563 var getValueOrDefault = helpers.getValueOrDefault;
11564 var isHorizontal = me.isHorizontal();
11565 function IDMatches(meta) {
11566 return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
11567 }
11568
11569 // Calculate Range
11570 me.min = null;
11571 me.max = null;
11572 me.minNotZero = null;
11573
11574 var hasStacks = opts.stacked;
11575 if (hasStacks === undefined) {
11576 helpers.each(datasets, function(dataset, datasetIndex) {
11577 if (hasStacks) {
11578 return;
11579 }
11580
11581 var meta = chart.getDatasetMeta(datasetIndex);
11582 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
11583 meta.stack !== undefined) {
11584 hasStacks = true;
11585 }
11586 });
11587 }
11588
11589 if (opts.stacked || hasStacks) {
11590 var valuesPerStack = {};
11591
11592 helpers.each(datasets, function(dataset, datasetIndex) {
11593 var meta = chart.getDatasetMeta(datasetIndex);
11594 var key = [
11595 meta.type,
11596 // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
11597 ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
11598 meta.stack
11599 ].join('.');
11600
11601 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
11602 if (valuesPerStack[key] === undefined) {
11603 valuesPerStack[key] = [];
11604 }
11605
11606 helpers.each(dataset.data, function(rawValue, index) {
11607 var values = valuesPerStack[key];
11608 var value = +me.getRightValue(rawValue);
11609 if (isNaN(value) || meta.data[index].hidden) {
11610 return;
11611 }
11612
11613 values[index] = values[index] || 0;
11614
11615 if (opts.relativePoints) {
11616 values[index] = 100;
11617 } else {
11618 // Don't need to split positive and negative since the log scale can't handle a 0 crossing
11619 values[index] += value;
11620 }
11621 });
11622 }
11623 });
11624
11625 helpers.each(valuesPerStack, function(valuesForType) {
11626 var minVal = helpers.min(valuesForType);
11627 var maxVal = helpers.max(valuesForType);
11628 me.min = me.min === null ? minVal : Math.min(me.min, minVal);
11629 me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
11630 });
11631
11632 } else {
11633 helpers.each(datasets, function(dataset, datasetIndex) {
11634 var meta = chart.getDatasetMeta(datasetIndex);
11635 if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
11636 helpers.each(dataset.data, function(rawValue, index) {
11637 var value = +me.getRightValue(rawValue);
11638 if (isNaN(value) || meta.data[index].hidden) {
11639 return;
11640 }
11641
11642 if (me.min === null) {
11643 me.min = value;
11644 } else if (value < me.min) {
11645 me.min = value;
11646 }
11647
11648 if (me.max === null) {
11649 me.max = value;
11650 } else if (value > me.max) {
11651 me.max = value;
11652 }
11653
11654 if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
11655 me.minNotZero = value;
11656 }
11657 });
11658 }
11659 });
11660 }
11661
11662 me.min = getValueOrDefault(tickOpts.min, me.min);
11663 me.max = getValueOrDefault(tickOpts.max, me.max);
11664
11665 if (me.min === me.max) {
11666 if (me.min !== 0 && me.min !== null) {
11667 me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);
11668 me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
11669 } else {
11670 me.min = 1;
11671 me.max = 10;
11672 }
11673 }
11674 },
11675 buildTicks: function() {
11676 var me = this;
11677 var opts = me.options;
11678 var tickOpts = opts.ticks;
11679
11680 var generationOptions = {
11681 min: tickOpts.min,
11682 max: tickOpts.max
11683 };
11684 var ticks = me.ticks = Chart.Ticks.generators.logarithmic(generationOptions, me);
11685
11686 if (!me.isHorizontal()) {
11687 // We are in a vertical orientation. The top value is the highest. So reverse the array
11688 ticks.reverse();
11689 }
11690
11691 // At this point, we need to update our max and min given the tick values since we have expanded the
11692 // range of the scale
11693 me.max = helpers.max(ticks);
11694 me.min = helpers.min(ticks);
11695
11696 if (tickOpts.reverse) {
11697 ticks.reverse();
11698
11699 me.start = me.max;
11700 me.end = me.min;
11701 } else {
11702 me.start = me.min;
11703 me.end = me.max;
11704 }
11705 },
11706 convertTicksToLabels: function() {
11707 this.tickValues = this.ticks.slice();
11708
11709 Chart.Scale.prototype.convertTicksToLabels.call(this);
11710 },
11711 // Get the correct tooltip label
11712 getLabelForIndex: function(index, datasetIndex) {
11713 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
11714 },
11715 getPixelForTick: function(index) {
11716 return this.getPixelForValue(this.tickValues[index]);
11717 },
11718 getPixelForValue: function(value) {
11719 var me = this;
11720 var innerDimension;
11721 var pixel;
11722
11723 var start = me.start;
11724 var newVal = +me.getRightValue(value);
11725 var range;
11726 var opts = me.options;
11727 var tickOpts = opts.ticks;
11728
11729 if (me.isHorizontal()) {
11730 range = helpers.log10(me.end) - helpers.log10(start); // todo: if start === 0
11731 if (newVal === 0) {
11732 pixel = me.left;
11733 } else {
11734 innerDimension = me.width;
11735 pixel = me.left + (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
11736 }
11737 } else {
11738 // Bottom - top since pixels increase downward on a screen
11739 innerDimension = me.height;
11740 if (start === 0 && !tickOpts.reverse) {
11741 range = helpers.log10(me.end) - helpers.log10(me.minNotZero);
11742 if (newVal === start) {
11743 pixel = me.bottom;
11744 } else if (newVal === me.minNotZero) {
11745 pixel = me.bottom - innerDimension * 0.02;
11746 } else {
11747 pixel = me.bottom - innerDimension * 0.02 - (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
11748 }
11749 } else if (me.end === 0 && tickOpts.reverse) {
11750 range = helpers.log10(me.start) - helpers.log10(me.minNotZero);
11751 if (newVal === me.end) {
11752 pixel = me.top;
11753 } else if (newVal === me.minNotZero) {
11754 pixel = me.top + innerDimension * 0.02;
11755 } else {
11756 pixel = me.top + innerDimension * 0.02 + (innerDimension * 0.98/ range * (helpers.log10(newVal)-helpers.log10(me.minNotZero)));
11757 }
11758 } else if (newVal === 0) {
11759 pixel = tickOpts.reverse ? me.top : me.bottom;
11760 } else {
11761 range = helpers.log10(me.end) - helpers.log10(start);
11762 innerDimension = me.height;
11763 pixel = me.bottom - (innerDimension / range * (helpers.log10(newVal) - helpers.log10(start)));
11764 }
11765 }
11766 return pixel;
11767 },
11768 getValueForPixel: function(pixel) {
11769 var me = this;
11770 var range = helpers.log10(me.end) - helpers.log10(me.start);
11771 var value, innerDimension;
11772
11773 if (me.isHorizontal()) {
11774 innerDimension = me.width;
11775 value = me.start * Math.pow(10, (pixel - me.left) * range / innerDimension);
11776 } else { // todo: if start === 0
11777 innerDimension = me.height;
11778 value = Math.pow(10, (me.bottom - pixel) * range / innerDimension) / me.start;
11779 }
11780 return value;
11781 }
11782 });
11783 Chart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);
11784
11785 };
11786
11787},{}],48:[function(require,module,exports){
11788 'use strict';
11789
11790 module.exports = function(Chart) {
11791
11792 var helpers = Chart.helpers;
11793 var globalDefaults = Chart.defaults.global;
11794
11795 var defaultConfig = {
11796 display: true,
11797
11798 // Boolean - Whether to animate scaling the chart from the centre
11799 animate: true,
11800 position: 'chartArea',
11801
11802 angleLines: {
11803 display: true,
11804 color: 'rgba(0, 0, 0, 0.1)',
11805 lineWidth: 1
11806 },
11807
11808 gridLines: {
11809 circular: false
11810 },
11811
11812 // label settings
11813 ticks: {
11814 // Boolean - Show a backdrop to the scale label
11815 showLabelBackdrop: true,
11816
11817 // String - The colour of the label backdrop
11818 backdropColor: 'rgba(255,255,255,0.75)',
11819
11820 // Number - The backdrop padding above & below the label in pixels
11821 backdropPaddingY: 2,
11822
11823 // Number - The backdrop padding to the side of the label in pixels
11824 backdropPaddingX: 2,
11825
11826 callback: Chart.Ticks.formatters.linear
11827 },
11828
11829 pointLabels: {
11830 // Boolean - if true, show point labels
11831 display: true,
11832
11833 // Number - Point label font size in pixels
11834 fontSize: 10,
11835
11836 // Function - Used to convert point labels
11837 callback: function(label) {
11838 return label;
11839 }
11840 }
11841 };
11842
11843 function getValueCount(scale) {
11844 var opts = scale.options;
11845 return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0;
11846 }
11847
11848 function getPointLabelFontOptions(scale) {
11849 var pointLabelOptions = scale.options.pointLabels;
11850 var fontSize = helpers.getValueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);
11851 var fontStyle = helpers.getValueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);
11852 var fontFamily = helpers.getValueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);
11853 var font = helpers.fontString(fontSize, fontStyle, fontFamily);
11854
11855 return {
11856 size: fontSize,
11857 style: fontStyle,
11858 family: fontFamily,
11859 font: font
11860 };
11861 }
11862
11863 function measureLabelSize(ctx, fontSize, label) {
11864 if (helpers.isArray(label)) {
11865 return {
11866 w: helpers.longestText(ctx, ctx.font, label),
11867 h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize)
11868 };
11869 }
11870
11871 return {
11872 w: ctx.measureText(label).width,
11873 h: fontSize
11874 };
11875 }
11876
11877 function determineLimits(angle, pos, size, min, max) {
11878 if (angle === min || angle === max) {
11879 return {
11880 start: pos - (size / 2),
11881 end: pos + (size / 2)
11882 };
11883 } else if (angle < min || angle > max) {
11884 return {
11885 start: pos - size - 5,
11886 end: pos
11887 };
11888 }
11889
11890 return {
11891 start: pos,
11892 end: pos + size + 5
11893 };
11894 }
11895
11896 /**
11897 * Helper function to fit a radial linear scale with point labels
11898 */
11899 function fitWithPointLabels(scale) {
11900 /*
11901 * Right, this is really confusing and there is a lot of maths going on here
11902 * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
11903 *
11904 * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
11905 *
11906 * Solution:
11907 *
11908 * We assume the radius of the polygon is half the size of the canvas at first
11909 * at each index we check if the text overlaps.
11910 *
11911 * Where it does, we store that angle and that index.
11912 *
11913 * After finding the largest index and angle we calculate how much we need to remove
11914 * from the shape radius to move the point inwards by that x.
11915 *
11916 * We average the left and right distances to get the maximum shape radius that can fit in the box
11917 * along with labels.
11918 *
11919 * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
11920 * on each side, removing that from the size, halving it and adding the left x protrusion width.
11921 *
11922 * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
11923 * and position it in the most space efficient manner
11924 *
11925 * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
11926 */
11927
11928 var plFont = getPointLabelFontOptions(scale);
11929
11930 // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
11931 // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
11932 var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
11933 var furthestLimits = {
11934 r: scale.width,
11935 l: 0,
11936 t: scale.height,
11937 b: 0
11938 };
11939 var furthestAngles = {};
11940 var i;
11941 var textSize;
11942 var pointPosition;
11943
11944 scale.ctx.font = plFont.font;
11945 scale._pointLabelSizes = [];
11946
11947 var valueCount = getValueCount(scale);
11948 for (i = 0; i < valueCount; i++) {
11949 pointPosition = scale.getPointPosition(i, largestPossibleRadius);
11950 textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || '');
11951 scale._pointLabelSizes[i] = textSize;
11952
11953 // Add quarter circle to make degree 0 mean top of circle
11954 var angleRadians = scale.getIndexAngle(i);
11955 var angle = helpers.toDegrees(angleRadians) % 360;
11956 var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
11957 var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
11958
11959 if (hLimits.start < furthestLimits.l) {
11960 furthestLimits.l = hLimits.start;
11961 furthestAngles.l = angleRadians;
11962 }
11963
11964 if (hLimits.end > furthestLimits.r) {
11965 furthestLimits.r = hLimits.end;
11966 furthestAngles.r = angleRadians;
11967 }
11968
11969 if (vLimits.start < furthestLimits.t) {
11970 furthestLimits.t = vLimits.start;
11971 furthestAngles.t = angleRadians;
11972 }
11973
11974 if (vLimits.end > furthestLimits.b) {
11975 furthestLimits.b = vLimits.end;
11976 furthestAngles.b = angleRadians;
11977 }
11978 }
11979
11980 scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles);
11981 }
11982
11983 /**
11984 * Helper function to fit a radial linear scale with no point labels
11985 */
11986 function fit(scale) {
11987 var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
11988 scale.drawingArea = Math.round(largestPossibleRadius);
11989 scale.setCenterPoint(0, 0, 0, 0);
11990 }
11991
11992 function getTextAlignForAngle(angle) {
11993 if (angle === 0 || angle === 180) {
11994 return 'center';
11995 } else if (angle < 180) {
11996 return 'left';
11997 }
11998
11999 return 'right';
12000 }
12001
12002 function fillText(ctx, text, position, fontSize) {
12003 if (helpers.isArray(text)) {
12004 var y = position.y;
12005 var spacing = 1.5 * fontSize;
12006
12007 for (var i = 0; i < text.length; ++i) {
12008 ctx.fillText(text[i], position.x, y);
12009 y+= spacing;
12010 }
12011 } else {
12012 ctx.fillText(text, position.x, position.y);
12013 }
12014 }
12015
12016 function adjustPointPositionForLabelHeight(angle, textSize, position) {
12017 if (angle === 90 || angle === 270) {
12018 position.y -= (textSize.h / 2);
12019 } else if (angle > 270 || angle < 90) {
12020 position.y -= textSize.h;
12021 }
12022 }
12023
12024 function drawPointLabels(scale) {
12025 var ctx = scale.ctx;
12026 var getValueOrDefault = helpers.getValueOrDefault;
12027 var opts = scale.options;
12028 var angleLineOpts = opts.angleLines;
12029 var pointLabelOpts = opts.pointLabels;
12030
12031 ctx.lineWidth = angleLineOpts.lineWidth;
12032 ctx.strokeStyle = angleLineOpts.color;
12033
12034 var outerDistance = scale.getDistanceFromCenterForValue(opts.reverse ? scale.min : scale.max);
12035
12036 // Point Label Font
12037 var plFont = getPointLabelFontOptions(scale);
12038
12039 ctx.textBaseline = 'top';
12040
12041 for (var i = getValueCount(scale) - 1; i >= 0; i--) {
12042 if (angleLineOpts.display) {
12043 var outerPosition = scale.getPointPosition(i, outerDistance);
12044 ctx.beginPath();
12045 ctx.moveTo(scale.xCenter, scale.yCenter);
12046 ctx.lineTo(outerPosition.x, outerPosition.y);
12047 ctx.stroke();
12048 ctx.closePath();
12049 }
12050
12051 if (pointLabelOpts.display) {
12052 // Extra 3px out for some label spacing
12053 var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5);
12054
12055 // Keep this in loop since we may support array properties here
12056 var pointLabelFontColor = getValueOrDefault(pointLabelOpts.fontColor, globalDefaults.defaultFontColor);
12057 ctx.font = plFont.font;
12058 ctx.fillStyle = pointLabelFontColor;
12059
12060 var angleRadians = scale.getIndexAngle(i);
12061 var angle = helpers.toDegrees(angleRadians);
12062 ctx.textAlign = getTextAlignForAngle(angle);
12063 adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
12064 fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size);
12065 }
12066 }
12067 }
12068
12069 function drawRadiusLine(scale, gridLineOpts, radius, index) {
12070 var ctx = scale.ctx;
12071 ctx.strokeStyle = helpers.getValueAtIndexOrDefault(gridLineOpts.color, index - 1);
12072 ctx.lineWidth = helpers.getValueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
12073
12074 if (scale.options.gridLines.circular) {
12075 // Draw circular arcs between the points
12076 ctx.beginPath();
12077 ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
12078 ctx.closePath();
12079 ctx.stroke();
12080 } else {
12081 // Draw straight lines connecting each index
12082 var valueCount = getValueCount(scale);
12083
12084 if (valueCount === 0) {
12085 return;
12086 }
12087
12088 ctx.beginPath();
12089 var pointPosition = scale.getPointPosition(0, radius);
12090 ctx.moveTo(pointPosition.x, pointPosition.y);
12091
12092 for (var i = 1; i < valueCount; i++) {
12093 pointPosition = scale.getPointPosition(i, radius);
12094 ctx.lineTo(pointPosition.x, pointPosition.y);
12095 }
12096
12097 ctx.closePath();
12098 ctx.stroke();
12099 }
12100 }
12101
12102 function numberOrZero(param) {
12103 return helpers.isNumber(param) ? param : 0;
12104 }
12105
12106 var LinearRadialScale = Chart.LinearScaleBase.extend({
12107 setDimensions: function() {
12108 var me = this;
12109 var opts = me.options;
12110 var tickOpts = opts.ticks;
12111 // Set the unconstrained dimension before label rotation
12112 me.width = me.maxWidth;
12113 me.height = me.maxHeight;
12114 me.xCenter = Math.round(me.width / 2);
12115 me.yCenter = Math.round(me.height / 2);
12116
12117 var minSize = helpers.min([me.height, me.width]);
12118 var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
12119 me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);
12120 },
12121 determineDataLimits: function() {
12122 var me = this;
12123 var chart = me.chart;
12124 var min = Number.POSITIVE_INFINITY;
12125 var max = Number.NEGATIVE_INFINITY;
12126
12127 helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
12128 if (chart.isDatasetVisible(datasetIndex)) {
12129 var meta = chart.getDatasetMeta(datasetIndex);
12130
12131 helpers.each(dataset.data, function(rawValue, index) {
12132 var value = +me.getRightValue(rawValue);
12133 if (isNaN(value) || meta.data[index].hidden) {
12134 return;
12135 }
12136
12137 min = Math.min(value, min);
12138 max = Math.max(value, max);
12139 });
12140 }
12141 });
12142
12143 me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
12144 me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);
12145
12146 // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
12147 me.handleTickRangeOptions();
12148 },
12149 getTickLimit: function() {
12150 var tickOpts = this.options.ticks;
12151 var tickFontSize = helpers.getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
12152 return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));
12153 },
12154 convertTicksToLabels: function() {
12155 var me = this;
12156 Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me);
12157
12158 // Point labels
12159 me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
12160 },
12161 getLabelForIndex: function(index, datasetIndex) {
12162 return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
12163 },
12164 fit: function() {
12165 if (this.options.pointLabels.display) {
12166 fitWithPointLabels(this);
12167 } else {
12168 fit(this);
12169 }
12170 },
12171 /**
12172 * Set radius reductions and determine new radius and center point
12173 * @private
12174 */
12175 setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
12176 var me = this;
12177 var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
12178 var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
12179 var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
12180 var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b);
12181
12182 radiusReductionLeft = numberOrZero(radiusReductionLeft);
12183 radiusReductionRight = numberOrZero(radiusReductionRight);
12184 radiusReductionTop = numberOrZero(radiusReductionTop);
12185 radiusReductionBottom = numberOrZero(radiusReductionBottom);
12186
12187 me.drawingArea = Math.min(
12188 Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
12189 Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
12190 me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
12191 },
12192 setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
12193 var me = this;
12194 var maxRight = me.width - rightMovement - me.drawingArea,
12195 maxLeft = leftMovement + me.drawingArea,
12196 maxTop = topMovement + me.drawingArea,
12197 maxBottom = me.height - bottomMovement - me.drawingArea;
12198
12199 me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
12200 me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top);
12201 },
12202
12203 getIndexAngle: function(index) {
12204 var angleMultiplier = (Math.PI * 2) / getValueCount(this);
12205 var startAngle = this.chart.options && this.chart.options.startAngle ?
12206 this.chart.options.startAngle :
12207 0;
12208
12209 var startAngleRadians = startAngle * Math.PI * 2 / 360;
12210
12211 // Start from the top instead of right, so remove a quarter of the circle
12212 return index * angleMultiplier + startAngleRadians;
12213 },
12214 getDistanceFromCenterForValue: function(value) {
12215 var me = this;
12216
12217 if (value === null) {
12218 return 0; // null always in center
12219 }
12220
12221 // Take into account half font size + the yPadding of the top value
12222 var scalingFactor = me.drawingArea / (me.max - me.min);
12223 if (me.options.reverse) {
12224 return (me.max - value) * scalingFactor;
12225 }
12226 return (value - me.min) * scalingFactor;
12227 },
12228 getPointPosition: function(index, distanceFromCenter) {
12229 var me = this;
12230 var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
12231 return {
12232 x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,
12233 y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter
12234 };
12235 },
12236 getPointPositionForValue: function(index, value) {
12237 return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
12238 },
12239
12240 getBasePosition: function() {
12241 var me = this;
12242 var min = me.min;
12243 var max = me.max;
12244
12245 return me.getPointPositionForValue(0,
12246 me.beginAtZero? 0:
12247 min < 0 && max < 0? max :
12248 min > 0 && max > 0? min :
12249 0);
12250 },
12251
12252 draw: function() {
12253 var me = this;
12254 var opts = me.options;
12255 var gridLineOpts = opts.gridLines;
12256 var tickOpts = opts.ticks;
12257 var getValueOrDefault = helpers.getValueOrDefault;
12258
12259 if (opts.display) {
12260 var ctx = me.ctx;
12261
12262 // Tick Font
12263 var tickFontSize = getValueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
12264 var tickFontStyle = getValueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
12265 var tickFontFamily = getValueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
12266 var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
12267
12268 helpers.each(me.ticks, function(label, index) {
12269 // Don't draw a centre value (if it is minimum)
12270 if (index > 0 || opts.reverse) {
12271 var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
12272 var yHeight = me.yCenter - yCenterOffset;
12273
12274 // Draw circular lines around the scale
12275 if (gridLineOpts.display && index !== 0) {
12276 drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
12277 }
12278
12279 if (tickOpts.display) {
12280 var tickFontColor = getValueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
12281 ctx.font = tickLabelFont;
12282
12283 if (tickOpts.showLabelBackdrop) {
12284 var labelWidth = ctx.measureText(label).width;
12285 ctx.fillStyle = tickOpts.backdropColor;
12286 ctx.fillRect(
12287 me.xCenter - labelWidth / 2 - tickOpts.backdropPaddingX,
12288 yHeight - tickFontSize / 2 - tickOpts.backdropPaddingY,
12289 labelWidth + tickOpts.backdropPaddingX * 2,
12290 tickFontSize + tickOpts.backdropPaddingY * 2
12291 );
12292 }
12293
12294 ctx.textAlign = 'center';
12295 ctx.textBaseline = 'middle';
12296 ctx.fillStyle = tickFontColor;
12297 ctx.fillText(label, me.xCenter, yHeight);
12298 }
12299 }
12300 });
12301
12302 if (opts.angleLines.display || opts.pointLabels.display) {
12303 drawPointLabels(me);
12304 }
12305 }
12306 }
12307 });
12308 Chart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig);
12309
12310 };
12311
12312},{}],49:[function(require,module,exports){
12313 /* global window: false */
12314 'use strict';
12315
12316 var moment = require(1);
12317 moment = typeof(moment) === 'function' ? moment : window.moment;
12318
12319 module.exports = function(Chart) {
12320
12321 var helpers = Chart.helpers;
12322 var interval = {
12323 millisecond: {
12324 size: 1,
12325 steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
12326 },
12327 second: {
12328 size: 1000,
12329 steps: [1, 2, 5, 10, 30]
12330 },
12331 minute: {
12332 size: 60000,
12333 steps: [1, 2, 5, 10, 30]
12334 },
12335 hour: {
12336 size: 3600000,
12337 steps: [1, 2, 3, 6, 12]
12338 },
12339 day: {
12340 size: 86400000,
12341 steps: [1, 2, 5]
12342 },
12343 week: {
12344 size: 604800000,
12345 maxStep: 4
12346 },
12347 month: {
12348 size: 2.628e9,
12349 maxStep: 3
12350 },
12351 quarter: {
12352 size: 7.884e9,
12353 maxStep: 4
12354 },
12355 year: {
12356 size: 3.154e10,
12357 maxStep: false
12358 }
12359 };
12360
12361 var defaultConfig = {
12362 position: 'bottom',
12363
12364 time: {
12365 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
12366 format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
12367 unit: false, // false == automatic or override with week, month, year, etc.
12368 round: false, // none, or override with week, month, year, etc.
12369 displayFormat: false, // DEPRECATED
12370 isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/
12371 minUnit: 'millisecond',
12372
12373 // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
12374 displayFormats: {
12375 millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
12376 second: 'h:mm:ss a', // 11:20:01 AM
12377 minute: 'h:mm:ss a', // 11:20:01 AM
12378 hour: 'MMM D, hA', // Sept 4, 5PM
12379 day: 'll', // Sep 4 2015
12380 week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
12381 month: 'MMM YYYY', // Sept 2015
12382 quarter: '[Q]Q - YYYY', // Q3
12383 year: 'YYYY' // 2015
12384 },
12385 },
12386 ticks: {
12387 autoSkip: false
12388 }
12389 };
12390
12391 /**
12392 * Helper function to parse time to a moment object
12393 * @param axis {TimeAxis} the time axis
12394 * @param label {Date|string|number|Moment} The thing to parse
12395 * @return {Moment} parsed time
12396 */
12397 function parseTime(axis, label) {
12398 var timeOpts = axis.options.time;
12399 if (typeof timeOpts.parser === 'string') {
12400 return moment(label, timeOpts.parser);
12401 }
12402 if (typeof timeOpts.parser === 'function') {
12403 return timeOpts.parser(label);
12404 }
12405 if (typeof label.getMonth === 'function' || typeof label === 'number') {
12406 // Date objects
12407 return moment(label);
12408 }
12409 if (label.isValid && label.isValid()) {
12410 // Moment support
12411 return label;
12412 }
12413 var format = timeOpts.format;
12414 if (typeof format !== 'string' && format.call) {
12415 // Custom parsing (return an instance of moment)
12416 console.warn('options.time.format is deprecated and replaced by options.time.parser.');
12417 return format(label);
12418 }
12419 // Moment format parsing
12420 return moment(label, format);
12421 }
12422
12423 /**
12424 * Figure out which is the best unit for the scale
12425 * @param minUnit {String} minimum unit to use
12426 * @param min {Number} scale minimum
12427 * @param max {Number} scale maximum
12428 * @return {String} the unit to use
12429 */
12430 function determineUnit(minUnit, min, max, maxTicks) {
12431 var units = Object.keys(interval);
12432 var unit;
12433 var numUnits = units.length;
12434
12435 for (var i = units.indexOf(minUnit); i < numUnits; i++) {
12436 unit = units[i];
12437 var unitDetails = interval[unit];
12438 var steps = (unitDetails.steps && unitDetails.steps[unitDetails.steps.length - 1]) || unitDetails.maxStep;
12439 if (steps === undefined || Math.ceil((max - min) / (steps * unitDetails.size)) <= maxTicks) {
12440 break;
12441 }
12442 }
12443
12444 return unit;
12445 }
12446
12447 /**
12448 * Determines how we scale the unit
12449 * @param min {Number} the scale minimum
12450 * @param max {Number} the scale maximum
12451 * @param unit {String} the unit determined by the {@see determineUnit} method
12452 * @return {Number} the axis step size as a multiple of unit
12453 */
12454 function determineStepSize(min, max, unit, maxTicks) {
12455 // Using our unit, figoure out what we need to scale as
12456 var unitDefinition = interval[unit];
12457 var unitSizeInMilliSeconds = unitDefinition.size;
12458 var sizeInUnits = Math.ceil((max - min) / unitSizeInMilliSeconds);
12459 var multiplier = 1;
12460 var range = max - min;
12461
12462 if (unitDefinition.steps) {
12463 // Have an array of steps
12464 var numSteps = unitDefinition.steps.length;
12465 for (var i = 0; i < numSteps && sizeInUnits > maxTicks; i++) {
12466 multiplier = unitDefinition.steps[i];
12467 sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier));
12468 }
12469 } else {
12470 while (sizeInUnits > maxTicks && maxTicks > 0) {
12471 ++multiplier;
12472 sizeInUnits = Math.ceil(range / (unitSizeInMilliSeconds * multiplier));
12473 }
12474 }
12475
12476 return multiplier;
12477 }
12478
12479 /**
12480 * Helper for generating axis labels.
12481 * @param options {ITimeGeneratorOptions} the options for generation
12482 * @param dataRange {IRange} the data range
12483 * @param niceRange {IRange} the pretty range to display
12484 * @return {Number[]} ticks
12485 */
12486 function generateTicks(options, dataRange, niceRange) {
12487 var ticks = [];
12488 if (options.maxTicks) {
12489 var stepSize = options.stepSize;
12490 ticks.push(options.min !== undefined ? options.min : niceRange.min);
12491 var cur = moment(niceRange.min);
12492 while (cur.add(stepSize, options.unit).valueOf() < niceRange.max) {
12493 ticks.push(cur.valueOf());
12494 }
12495 var realMax = options.max || niceRange.max;
12496 if (ticks[ticks.length - 1] !== realMax) {
12497 ticks.push(realMax);
12498 }
12499 }
12500 return ticks;
12501 }
12502
12503 /**
12504 * @function Chart.Ticks.generators.time
12505 * @param options {ITimeGeneratorOptions} the options for generation
12506 * @param dataRange {IRange} the data range
12507 * @return {Number[]} ticks
12508 */
12509 Chart.Ticks.generators.time = function(options, dataRange) {
12510 var niceMin;
12511 var niceMax;
12512 var isoWeekday = options.isoWeekday;
12513 if (options.unit === 'week' && isoWeekday !== false) {
12514 niceMin = moment(dataRange.min).startOf('isoWeek').isoWeekday(isoWeekday).valueOf();
12515 niceMax = moment(dataRange.max).startOf('isoWeek').isoWeekday(isoWeekday);
12516 if (dataRange.max - niceMax > 0) {
12517 niceMax.add(1, 'week');
12518 }
12519 niceMax = niceMax.valueOf();
12520 } else {
12521 niceMin = moment(dataRange.min).startOf(options.unit).valueOf();
12522 niceMax = moment(dataRange.max).startOf(options.unit);
12523 if (dataRange.max - niceMax > 0) {
12524 niceMax.add(1, options.unit);
12525 }
12526 niceMax = niceMax.valueOf();
12527 }
12528 return generateTicks(options, dataRange, {
12529 min: niceMin,
12530 max: niceMax
12531 });
12532 };
12533
12534 var TimeScale = Chart.Scale.extend({
12535 initialize: function() {
12536 if (!moment) {
12537 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');
12538 }
12539
12540 Chart.Scale.prototype.initialize.call(this);
12541 },
12542 determineDataLimits: function() {
12543 var me = this;
12544 var timeOpts = me.options.time;
12545
12546 // We store the data range as unix millisecond timestamps so dataMin and dataMax will always be integers.
12547 var dataMin = Number.MAX_SAFE_INTEGER;
12548 var dataMax = Number.MIN_SAFE_INTEGER;
12549
12550 var chartData = me.chart.data;
12551 var parsedData = {
12552 labels: [],
12553 datasets: []
12554 };
12555
12556 var timestamp;
12557
12558 helpers.each(chartData.labels, function(label, labelIndex) {
12559 var labelMoment = parseTime(me, label);
12560
12561 if (labelMoment.isValid()) {
12562 // We need to round the time
12563 if (timeOpts.round) {
12564 labelMoment.startOf(timeOpts.round);
12565 }
12566
12567 timestamp = labelMoment.valueOf();
12568 dataMin = Math.min(timestamp, dataMin);
12569 dataMax = Math.max(timestamp, dataMax);
12570
12571 // Store this value for later
12572 parsedData.labels[labelIndex] = timestamp;
12573 }
12574 });
12575
12576 helpers.each(chartData.datasets, function(dataset, datasetIndex) {
12577 var timestamps = [];
12578
12579 if (typeof dataset.data[0] === 'object' && dataset.data[0] !== null && me.chart.isDatasetVisible(datasetIndex)) {
12580 // We have potential point data, so we need to parse this
12581 helpers.each(dataset.data, function(value, dataIndex) {
12582 var dataMoment = parseTime(me, me.getRightValue(value));
12583
12584 if (dataMoment.isValid()) {
12585 if (timeOpts.round) {
12586 dataMoment.startOf(timeOpts.round);
12587 }
12588
12589 timestamp = dataMoment.valueOf();
12590 dataMin = Math.min(timestamp, dataMin);
12591 dataMax = Math.max(timestamp, dataMax);
12592 timestamps[dataIndex] = timestamp;
12593 }
12594 });
12595 } else {
12596 // We have no x coordinates, so use the ones from the labels
12597 timestamps = parsedData.labels.slice();
12598 }
12599
12600 parsedData.datasets[datasetIndex] = timestamps;
12601 });
12602
12603 me.dataMin = dataMin;
12604 me.dataMax = dataMax;
12605 me._parsedData = parsedData;
12606 },
12607 buildTicks: function() {
12608 var me = this;
12609 var timeOpts = me.options.time;
12610
12611 var minTimestamp;
12612 var maxTimestamp;
12613 var dataMin = me.dataMin;
12614 var dataMax = me.dataMax;
12615
12616 if (timeOpts.min) {
12617 var minMoment = parseTime(me, timeOpts.min);
12618 if (timeOpts.round) {
12619 minMoment.round(timeOpts.round);
12620 }
12621 minTimestamp = minMoment.valueOf();
12622 }
12623
12624 if (timeOpts.max) {
12625 maxTimestamp = parseTime(me, timeOpts.max).valueOf();
12626 }
12627
12628 var maxTicks = me.getLabelCapacity(minTimestamp || dataMin);
12629 var unit = timeOpts.unit || determineUnit(timeOpts.minUnit, minTimestamp || dataMin, maxTimestamp || dataMax, maxTicks);
12630 me.displayFormat = timeOpts.displayFormats[unit];
12631
12632 var stepSize = timeOpts.stepSize || determineStepSize(minTimestamp || dataMin, maxTimestamp || dataMax, unit, maxTicks);
12633 me.ticks = Chart.Ticks.generators.time({
12634 maxTicks: maxTicks,
12635 min: minTimestamp,
12636 max: maxTimestamp,
12637 stepSize: stepSize,
12638 unit: unit,
12639 isoWeekday: timeOpts.isoWeekday
12640 }, {
12641 min: dataMin,
12642 max: dataMax
12643 });
12644
12645 // At this point, we need to update our max and min given the tick values since we have expanded the
12646 // range of the scale
12647 me.max = helpers.max(me.ticks);
12648 me.min = helpers.min(me.ticks);
12649 },
12650 // Get tooltip label
12651 getLabelForIndex: function(index, datasetIndex) {
12652 var me = this;
12653 var label = me.chart.data.labels && index < me.chart.data.labels.length ? me.chart.data.labels[index] : '';
12654 var value = me.chart.data.datasets[datasetIndex].data[index];
12655
12656 if (value !== null && typeof value === 'object') {
12657 label = me.getRightValue(value);
12658 }
12659
12660 // Format nicely
12661 if (me.options.time.tooltipFormat) {
12662 label = parseTime(me, label).format(me.options.time.tooltipFormat);
12663 }
12664
12665 return label;
12666 },
12667 // Function to format an individual tick mark
12668 tickFormatFunction: function(tick, index, ticks) {
12669 var formattedTick = tick.format(this.displayFormat);
12670 var tickOpts = this.options.ticks;
12671 var callback = helpers.getValueOrDefault(tickOpts.callback, tickOpts.userCallback);
12672
12673 if (callback) {
12674 return callback(formattedTick, index, ticks);
12675 }
12676 return formattedTick;
12677 },
12678 convertTicksToLabels: function() {
12679 var me = this;
12680 me.ticksAsTimestamps = me.ticks;
12681 me.ticks = me.ticks.map(function(tick) {
12682 return moment(tick);
12683 }).map(me.tickFormatFunction, me);
12684 },
12685 getPixelForOffset: function(offset) {
12686 var me = this;
12687 var epochWidth = me.max - me.min;
12688 var decimal = epochWidth ? (offset - me.min) / epochWidth : 0;
12689
12690 if (me.isHorizontal()) {
12691 var valueOffset = (me.width * decimal);
12692 return me.left + Math.round(valueOffset);
12693 }
12694
12695 var heightOffset = (me.height * decimal);
12696 return me.top + Math.round(heightOffset);
12697 },
12698 getPixelForValue: function(value, index, datasetIndex) {
12699 var me = this;
12700 var offset = null;
12701 if (index !== undefined && datasetIndex !== undefined) {
12702 offset = me._parsedData.datasets[datasetIndex][index];
12703 }
12704
12705 if (offset === null) {
12706 if (!value || !value.isValid) {
12707 // not already a moment object
12708 value = parseTime(me, me.getRightValue(value));
12709 }
12710
12711 if (value && value.isValid && value.isValid()) {
12712 offset = value.valueOf();
12713 }
12714 }
12715
12716 if (offset !== null) {
12717 return me.getPixelForOffset(offset);
12718 }
12719 },
12720 getPixelForTick: function(index) {
12721 return this.getPixelForOffset(this.ticksAsTimestamps[index]);
12722 },
12723 getValueForPixel: function(pixel) {
12724 var me = this;
12725 var innerDimension = me.isHorizontal() ? me.width : me.height;
12726 var offset = (pixel - (me.isHorizontal() ? me.left : me.top)) / innerDimension;
12727 return moment(me.min + (offset * (me.max - me.min)));
12728 },
12729 // Crude approximation of what the label width might be
12730 getLabelWidth: function(label) {
12731 var me = this;
12732 var ticks = me.options.ticks;
12733
12734 var tickLabelWidth = me.ctx.measureText(label).width;
12735 var cosRotation = Math.cos(helpers.toRadians(ticks.maxRotation));
12736 var sinRotation = Math.sin(helpers.toRadians(ticks.maxRotation));
12737 var tickFontSize = helpers.getValueOrDefault(ticks.fontSize, Chart.defaults.global.defaultFontSize);
12738 return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
12739 },
12740 getLabelCapacity: function(exampleTime) {
12741 var me = this;
12742
12743 me.displayFormat = me.options.time.displayFormats.millisecond; // Pick the longest format for guestimation
12744 var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, []);
12745 var tickLabelWidth = me.getLabelWidth(exampleLabel);
12746
12747 var innerWidth = me.isHorizontal() ? me.width : me.height;
12748 var labelCapacity = innerWidth / tickLabelWidth;
12749 return labelCapacity;
12750 }
12751 });
12752 Chart.scaleService.registerScaleType('time', TimeScale, defaultConfig);
12753
12754 };
Jian Li46770fc2016-08-03 02:32:45 +090012755
12756},{"1":1}]},{},[7])(7)
12757});