PageSourceSearch

https://marsacsurlisle.fr/lib/chartjs/Chart.js

js marsacsurlisle.fr collected 2026-09-24 10:15:29 UTC 531,355 bytes, 14,456 lines download raw bytes

1/*!
2 * Chart.js
3 * http://chartjs.org/
4 * Version: 2.7.3
5 *
6 * Copyright 2018 Chart.js Contributors
7 * Released under the MIT license
8 * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
9 */
10(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
11
12},{}],2:[function(require,module,exports){
13    /* MIT license */
14    var colorNames = require(6);
15
16    module.exports = {
17        getRgba: getRgba,
18        getHsla: getHsla,
19        getRgb: getRgb,
20        getHsl: getHsl,
21        getHwb: getHwb,
22        getAlpha: getAlpha,
23
24        hexString: hexString,
25        rgbString: rgbString,
26        rgbaString: rgbaString,
27        percentString: percentString,
28        percentaString: percentaString,
29        hslString: hslString,
30        hslaString: hslaString,
31        hwbString: hwbString,
32        keyword: keyword
33    }
34
35    function getRgba(string) {
36        if (!string) {
37            return;
38        }
39        var abbr =  /^#([a-fA-F0-9]{3})$/i,
40            hex =  /^#([a-fA-F0-9]{6})$/i,
41            rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
42            per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
43            keyword = /(\w+)/;
44
45        var rgb = [0, 0, 0],
46            a = 1,
47            match = string.match(abbr);
48        if (match) {
49            match = match[1];
50            for (var i = 0; i < rgb.length; i++) {
51                rgb[i] = parseInt(match[i] + match[i], 16);
52            }
53        }
54        else if (match = string.match(hex)) {
55            match = match[1];
56            for (var i = 0; i < rgb.length; i++) {
57                rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
58            }
59        }
60        else if (match = string.match(rgba)) {
61            for (var i = 0; i < rgb.length; i++) {
62                rgb[i] = parseInt(match[i + 1]);
63            }
64            a = parseFloat(match[4]);
65        }
66        else if (match = string.match(per)) {
67            for (var i = 0; i < rgb.length; i++) {
68                rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
69            }
70            a = parseFloat(match[4]);
71        }
72        else if (match = string.match(keyword)) {
73            if (match[1] == "transparent") {
74                return [0, 0, 0, 0];
75            }
76            rgb = colorNames[match[1]];
77            if (!rgb) {
78                return;
79            }
80        }
81
82        for (var i = 0; i < rgb.length; i++) {
83            rgb[i] = scale(rgb[i], 0, 255);
84        }
85        if (!a && a != 0) {
86            a = 1;
87        }
88        else {
89            a = scale(a, 0, 1);
90        }
91        rgb[3] = a;
92        return rgb;
93    }
94
95    function getHsla(string) {
96        if (!string) {
97            return;
98        }
99        var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
100        var match = string.match(hsl);
101        if (match) {
102            var alpha = parseFloat(match[4]);
103            var h = scale(parseInt(match[1]), 0, 360),
104                s = scale(parseFloat(match[2]), 0, 100),
105                l = scale(parseFloat(match[3]), 0, 100),
106                a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
107            return [h, s, l, a];
108        }
109    }
110
111    function getHwb(string) {
112        if (!string) {
113            return;
114        }
115        var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
116        var match = string.match(hwb);
117        if (match) {
118            var alpha = parseFloat(match[4]);
119            var h = scale(parseInt(match[1]), 0, 360),
120                w = scale(parseFloat(match[2]), 0, 100),
121                b = scale(parseFloat(match[3]), 0, 100),
122                a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
123            return [h, w, b, a];
124        }
125    }
126
127    function getRgb(string) {
128        var rgba = getRgba(string);
129        return rgba && rgba.slice(0, 3);
130    }
131
132    function getHsl(string) {
133        var hsla = getHsla(string);
134        return hsla && hsla.slice(0, 3);
135    }
136
137    function getAlpha(string) {
138        var vals = getRgba(string);
139        if (vals) {
140            return vals[3];
141        }
142        else if (vals = getHsla(string)) {
143            return vals[3];
144        }
145        else if (vals = getHwb(string)) {
146            return vals[3];
147        }
148    }
149
150// generators
151    function hexString(rgb) {
152        return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1])
153            + hexDouble(rgb[2]);
154    }
155
156    function rgbString(rgba, alpha) {
157        if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
158            return rgbaString(rgba, alpha);
159        }
160        return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
161    }
162
163    function rgbaString(rgba, alpha) {
164        if (alpha === undefined) {
165            alpha = (rgba[3] !== undefined ? rgba[3] : 1);
166        }
167        return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
168            + ", " + alpha + ")";
169    }
170
vendor: 15,513 bytes, lines 171-661
171    function percentString(rgba, alpha) {
172        if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
173            return percentaString(rgba, alpha);
174        }
175        var r = Math.round(rgba[0]/255 * 100),
176            g = Math.round(rgba[1]/255 * 100),
177            b = Math.round(rgba[2]/255 * 100);
178
179        return "rgb(" + r + "%, " + g + "%, " + b + "%)";
180    }
181
182    function percentaString(rgba, alpha) {
183        var r = Math.round(rgba[0]/255 * 100),
184            g = Math.round(rgba[1]/255 * 100),
185            b = Math.round(rgba[2]/255 * 100);
186        return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
187    }
188
189    function hslString(hsla, alpha) {
190        if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
191            return hslaString(hsla, alpha);
192        }
193        return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
194    }
195
196    function hslaString(hsla, alpha) {
197        if (alpha === undefined) {
198            alpha = (hsla[3] !== undefined ? hsla[3] : 1);
199        }
200        return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
201            + alpha + ")";
202    }
203
204// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
205// (hwb have alpha optional & 1 is default value)
206    function hwbString(hwb, alpha) {
207        if (alpha === undefined) {
208            alpha = (hwb[3] !== undefined ? hwb[3] : 1);
209        }
210        return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
211            + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
212    }
213
214    function keyword(rgb) {
215        return reverseNames[rgb.slice(0, 3)];
216    }
217
218// helpers
219    function scale(num, min, max) {
220        return Math.min(Math.max(min, num), max);
221    }
222
223    function hexDouble(num) {
224        var str = num.toString(16).toUpperCase();
225        return (str.length < 2) ? "0" + str : str;
226    }
227
228
229//create a list of reverse color names
230    var reverseNames = {};
231    for (var name in colorNames) {
232        reverseNames[colorNames[name]] = name;
233    }
234
235},{"6":6}],3:[function(require,module,exports){
236    /* MIT license */
237    var convert = require(5);
238    var string = require(2);
239
240    var Color = function (obj) {
241        if (obj instanceof Color) {
242            return obj;
243        }
244        if (!(this instanceof Color)) {
245            return new Color(obj);
246        }
247
248        this.valid = false;
249        this.values = {
250            rgb: [0, 0, 0],
251            hsl: [0, 0, 0],
252            hsv: [0, 0, 0],
253            hwb: [0, 0, 0],
254            cmyk: [0, 0, 0, 0],
255            alpha: 1
256        };
257
258        // parse Color() argument
259        var vals;
260        if (typeof obj === 'string') {
261            vals = string.getRgba(obj);
262            if (vals) {
263                this.setValues('rgb', vals);
264            } else if (vals = string.getHsla(obj)) {
265                this.setValues('hsl', vals);
266            } else if (vals = string.getHwb(obj)) {
267                this.setValues('hwb', vals);
268            }
269        } else if (typeof obj === 'object') {
270            vals = obj;
271            if (vals.r !== undefined || vals.red !== undefined) {
272                this.setValues('rgb', vals);
273            } else if (vals.l !== undefined || vals.lightness !== undefined) {
274                this.setValues('hsl', vals);
275            } else if (vals.v !== undefined || vals.value !== undefined) {
276                this.setValues('hsv', vals);
277            } else if (vals.w !== undefined || vals.whiteness !== undefined) {
278                this.setValues('hwb', vals);
279            } else if (vals.c !== undefined || vals.cyan !== undefined) {
280                this.setValues('cmyk', vals);
281            }
282        }
283    };
284
285    Color.prototype = {
286        isValid: function () {
287            return this.valid;
288        },
289        rgb: function () {
290            return this.setSpace('rgb', arguments);
291        },
292        hsl: function () {
293            return this.setSpace('hsl', arguments);
294        },
295        hsv: function () {
296            return this.setSpace('hsv', arguments);
297        },
298        hwb: function () {
299            return this.setSpace('hwb', arguments);
300        },
301        cmyk: function () {
302            return this.setSpace('cmyk', arguments);
303        },
304
305        rgbArray: function () {
306            return this.values.rgb;
307        },
308        hslArray: function () {
309            return this.values.hsl;
310        },
311        hsvArray: function () {
312            return this.values.hsv;
313        },
314        hwbArray: function () {
315            var values = this.values;
316            if (values.alpha !== 1) {
317                return values.hwb.concat([values.alpha]);
318            }
319            return values.hwb;
320        },
321        cmykArray: function () {
322            return this.values.cmyk;
323        },
324        rgbaArray: function () {
325            var values = this.values;
326            return values.rgb.concat([values.alpha]);
327        },
328        hslaArray: function () {
329            var values = this.values;
330            return values.hsl.concat([values.alpha]);
331        },
332        alpha: function (val) {
333            if (val === undefined) {
334                return this.values.alpha;
335            }
336            this.setValues('alpha', val);
337            return this;
338        },
339
340        red: function (val) {
341            return this.setChannel('rgb', 0, val);
342        },
343        green: function (val) {
344            return this.setChannel('rgb', 1, val);
345        },
346        blue: function (val) {
347            return this.setChannel('rgb', 2, val);
348        },
349        hue: function (val) {
350            if (val) {
351                val %= 360;
352                val = val < 0 ? 360 + val : val;
353            }
354            return this.setChannel('hsl', 0, val);
355        },
356        saturation: function (val) {
357            return this.setChannel('hsl', 1, val);
358        },
359        lightness: function (val) {
360            return this.setChannel('hsl', 2, val);
361        },
362        saturationv: function (val) {
363            return this.setChannel('hsv', 1, val);
364        },
365        whiteness: function (val) {
366            return this.setChannel('hwb', 1, val);
367        },
368        blackness: function (val) {
369            return this.setChannel('hwb', 2, val);
370        },
371        value: function (val) {
372            return this.setChannel('hsv', 2, val);
373        },
374        cyan: function (val) {
375            return this.setChannel('cmyk', 0, val);
376        },
377        magenta: function (val) {
378            return this.setChannel('cmyk', 1, val);
379        },
380        yellow: function (val) {
381            return this.setChannel('cmyk', 2, val);
382        },
383        black: function (val) {
384            return this.setChannel('cmyk', 3, val);
385        },
386
387        hexString: function () {
388            return string.hexString(this.values.rgb);
389        },
390        rgbString: function () {
391            return string.rgbString(this.values.rgb, this.values.alpha);
392        },
393        rgbaString: function () {
394            return string.rgbaString(this.values.rgb, this.values.alpha);
395        },
396        percentString: function () {
397            return string.percentString(this.values.rgb, this.values.alpha);
398        },
399        hslString: function () {
400            return string.hslString(this.values.hsl, this.values.alpha);
401        },
402        hslaString: function () {
403            return string.hslaString(this.values.hsl, this.values.alpha);
404        },
405        hwbString: function () {
406            return string.hwbString(this.values.hwb, this.values.alpha);
407        },
408        keyword: function () {
409            return string.keyword(this.values.rgb, this.values.alpha);
410        },
411
412        rgbNumber: function () {
413            var rgb = this.values.rgb;
414            return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
415        },
416
417        luminosity: function () {
418            // http://www.w3.org/TR/WCAG20/#relativeluminancedef
419            var rgb = this.values.rgb;
420            var lum = [];
421            for (var i = 0; i < rgb.length; i++) {
422                var chan = rgb[i] / 255;
423                lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
424            }
425            return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
426        },
427
428        contrast: function (color2) {
429            // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
430            var lum1 = this.luminosity();
431            var lum2 = color2.luminosity();
432            if (lum1 > lum2) {
433                return (lum1 + 0.05) / (lum2 + 0.05);
434            }
435            return (lum2 + 0.05) / (lum1 + 0.05);
436        },
437
438        level: function (color2) {
439            var contrastRatio = this.contrast(color2);
440            if (contrastRatio >= 7.1) {
441                return 'AAA';
442            }
443
444            return (contrastRatio >= 4.5) ? 'AA' : '';
445        },
446
447        dark: function () {
448            // YIQ equation from http://24ways.org/2010/calculating-color-contrast
449            var rgb = this.values.rgb;
450            var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
451            return yiq < 128;
452        },
453
454        light: function () {
455            return !this.dark();
456        },
457
458        negate: function () {
459            var rgb = [];
460            for (var i = 0; i < 3; i++) {
461                rgb[i] = 255 - this.values.rgb[i];
462            }
463            this.setValues('rgb', rgb);
464            return this;
465        },
466
467        lighten: function (ratio) {
468            var hsl = this.values.hsl;
469            hsl[2] += hsl[2] * ratio;
470            this.setValues('hsl', hsl);
471            return this;
472        },
473
474        darken: function (ratio) {
475            var hsl = this.values.hsl;
476            hsl[2] -= hsl[2] * ratio;
477            this.setValues('hsl', hsl);
478            return this;
479        },
480
481        saturate: function (ratio) {
482            var hsl = this.values.hsl;
483            hsl[1] += hsl[1] * ratio;
484            this.setValues('hsl', hsl);
485            return this;
486        },
487
488        desaturate: function (ratio) {
489            var hsl = this.values.hsl;
490            hsl[1] -= hsl[1] * ratio;
491            this.setValues('hsl', hsl);
492            return this;
493        },
494
495        whiten: function (ratio) {
496            var hwb = this.values.hwb;
497            hwb[1] += hwb[1] * ratio;
498            this.setValues('hwb', hwb);
499            return this;
500        },
501
502        blacken: function (ratio) {
503            var hwb = this.values.hwb;
504            hwb[2] += hwb[2] * ratio;
505            this.setValues('hwb', hwb);
506            return this;
507        },
508
509        greyscale: function () {
510            var rgb = this.values.rgb;
511            // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
512            var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
513            this.setValues('rgb', [val, val, val]);
514            return this;
515        },
516
517        clearer: function (ratio) {
518            var alpha = this.values.alpha;
519            this.setValues('alpha', alpha - (alpha * ratio));
520            return this;
521        },
522
523        opaquer: function (ratio) {
524            var alpha = this.values.alpha;
525            this.setValues('alpha', alpha + (alpha * ratio));
526            return this;
527        },
528
529        rotate: function (degrees) {
530            var hsl = this.values.hsl;
531            var hue = (hsl[0] + degrees) % 360;
532            hsl[0] = hue < 0 ? 360 + hue : hue;
533            this.setValues('hsl', hsl);
534            return this;
535        },
536
537        /**
538         * Ported from sass implementation in C
539         * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
540         */
541        mix: function (mixinColor, weight) {
542            var color1 = this;
543            var color2 = mixinColor;
544            var p = weight === undefined ? 0.5 : weight;
545
546            var w = 2 * p - 1;
547            var a = color1.alpha() - color2.alpha();
548
549            var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
550            var w2 = 1 - w1;
551
552            return this
553                .rgb(
554                    w1 * color1.red() + w2 * color2.red(),
555                    w1 * color1.green() + w2 * color2.green(),
556                    w1 * color1.blue() + w2 * color2.blue()
557                )
558                .alpha(color1.alpha() * p + color2.alpha() * (1 - p));
559        },
560
561        toJSON: function () {
562            return this.rgb();
563        },
564
565        clone: function () {
566            // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
567            // making the final build way to big to embed in Chart.js. So let's do it manually,
568            // assuming that values to clone are 1 dimension arrays containing only numbers,
569            // except 'alpha' which is a number.
570            var result = new Color();
571            var source = this.values;
572            var target = result.values;
573            var value, type;
574
575            for (var prop in source) {
576                if (source.hasOwnProperty(prop)) {
577                    value = source[prop];
578                    type = ({}).toString.call(value);
579                    if (type === '[object Array]') {
580                        target[prop] = value.slice(0);
581                    } else if (type === '[object Number]') {
582                        target[prop] = value;
583                    } else {
584                        console.error('unexpected color value:', value);
585                    }
586                }
587            }
588
589            return result;
590        }
591    };
592
593    Color.prototype.spaces = {
594        rgb: ['red', 'green', 'blue'],
595        hsl: ['hue', 'saturation', 'lightness'],
596        hsv: ['hue', 'saturation', 'value'],
597        hwb: ['hue', 'whiteness', 'blackness'],
598        cmyk: ['cyan', 'magenta', 'yellow', 'black']
599    };
600
601    Color.prototype.maxes = {
602        rgb: [255, 255, 255],
603        hsl: [360, 100, 100],
604        hsv: [360, 100, 100],
605        hwb: [360, 100, 100],
606        cmyk: [100, 100, 100, 100]
607    };
608
609    Color.prototype.getValues = function (space) {
610        var values = this.values;
611        var vals = {};
612
613        for (var i = 0; i < space.length; i++) {
614            vals[space.charAt(i)] = values[space][i];
615        }
616
617        if (values.alpha !== 1) {
618            vals.a = values.alpha;
619        }
620
621        // {r: 255, g: 255, b: 255, a: 0.4}
622        return vals;
623    };
624
625    Color.prototype.setValues = function (space, vals) {
626        var values = this.values;
627        var spaces = this.spaces;
628        var maxes = this.maxes;
629        var alpha = 1;
630        var i;
631
632        this.valid = true;
633
634        if (space === 'alpha') {
635            alpha = vals;
636        } else if (vals.length) {
637            // [10, 10, 10]
638            values[space] = vals.slice(0, space.length);
639            alpha = vals[space.length];
640        } else if (vals[space.charAt(0)] !== undefined) {
641            // {r: 10, g: 10, b: 10}
642            for (i = 0; i < space.length; i++) {
643                values[space][i] = vals[space.charAt(i)];
644            }
645
646            alpha = vals.a;
647        } else if (vals[spaces[space][0]] !== undefined) {
648            // {red: 10, green: 10, blue: 10}
649            var chans = spaces[space];
650
651            for (i = 0; i < space.length; i++) {
652                values[space][i] = vals[chans[i]];
653            }
654
655            alpha = vals.alpha;
656        }
657
658        values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));
659
660        if (space === 'alpha') {
661            return false;
662        }
663
664        var capped;
665
666        // cap values of the space prior converting all values
667        for (i = 0; i < space.length; i++) {
668            capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
669            values[space][i] = Math.round(capped);
670        }
671
672        // convert to all the other color spaces
673        for (var sname in spaces) {
674            if (sname !== space) {
675                values[sname] = convert[space][sname](values[space]);
676            }
677        }
678
679        return true;
680    };
681
682    Color.prototype.setSpace = function (space, args) {
683        var vals = args[0];
684
685        if (vals === undefined) {
686            // color.rgb()
687            return this.getValues(space);
688        }
689
690        // color.rgb(10, 10, 10)
691        if (typeof vals === 'number') {
692            vals = Array.prototype.slice.call(args);
693        }
694
695        this.setValues(space, vals);
696        return this;
697    };
698
699    Color.prototype.setChannel = function (space, index, val) {
700        var svalues = this.values[space];
701        if (val === undefined) {
702            // color.red()
703            return svalues[index];
704        } else if (val === svalues[index]) {
705            // color.red(color.red())
706            return this;
707        }
708
709        // color.red(100)
710        svalues[index] = val;
711        this.setValues(space, svalues);
712
713        return this;
714    };
715
716    if (typeof window !== 'undefined') {
717        window.Color = Color;
718    }
719
720    module.exports = Color;
721
722},{"2":2,"5":5}],4:[function(require,module,exports){
723    /* MIT license */
724
725    module.exports = {
726        rgb2hsl: rgb2hsl,
727        rgb2hsv: rgb2hsv,
728        rgb2hwb: rgb2hwb,
729        rgb2cmyk: rgb2cmyk,
730        rgb2keyword: rgb2keyword,
731        rgb2xyz: rgb2xyz,
732        rgb2lab: rgb2lab,
733        rgb2lch: rgb2lch,
734
735        hsl2rgb: hsl2rgb,
736        hsl2hsv: hsl2hsv,
737        hsl2hwb: hsl2hwb,
738        hsl2cmyk: hsl2cmyk,
739        hsl2keyword: hsl2keyword,
740
741        hsv2rgb: hsv2rgb,
742        hsv2hsl: hsv2hsl,
743        hsv2hwb: hsv2hwb,
744        hsv2cmyk: hsv2cmyk,
745        hsv2keyword: hsv2keyword,
746
747        hwb2rgb: hwb2rgb,
748        hwb2hsl: hwb2hsl,
749        hwb2hsv: hwb2hsv,
750        hwb2cmyk: hwb2cmyk,
751        hwb2keyword: hwb2keyword,
752
753        cmyk2rgb: cmyk2rgb,
754        cmyk2hsl: cmyk2hsl,
755        cmyk2hsv: cmyk2hsv,
756        cmyk2hwb: cmyk2hwb,
757        cmyk2keyword: cmyk2keyword,
758
759        keyword2rgb: keyword2rgb,
760        keyword2hsl: keyword2hsl,
761        keyword2hsv: keyword2hsv,
762        keyword2hwb: keyword2hwb,
763        keyword2cmyk: keyword2cmyk,
764        keyword2lab: keyword2lab,
765        keyword2xyz: keyword2xyz,
766
767        xyz2rgb: xyz2rgb,
768        xyz2lab: xyz2lab,
769        xyz2lch: xyz2lch,
770
771        lab2xyz: lab2xyz,
772        lab2rgb: lab2rgb,
773        lab2lch: lab2lch,
774
775        lch2lab: lch2lab,
776        lch2xyz: lch2xyz,
777        lch2rgb: lch2rgb
778    }
779
780
781    function rgb2hsl(rgb) {
782        var r = rgb[0]/255,
783            g = rgb[1]/255,
784            b = rgb[2]/255,
785            min = Math.min(r, g, b),
786            max = Math.max(r, g, b),
787            delta = max - min,
788            h, s, l;
789
790        if (max == min)
791            h = 0;
792        else if (r == max)
793            h = (g - b) / delta;
794        else if (g == max)
795            h = 2 + (b - r) / delta;
796        else if (b == max)
797            h = 4 + (r - g)/ delta;
798
799        h = Math.min(h * 60, 360);
800
801        if (h < 0)
802            h += 360;
803
804        l = (min + max) / 2;
805
806        if (max == min)
807            s = 0;
808        else if (l <= 0.5)
809            s = delta / (max + min);
810        else
811            s = delta / (2 - max - min);
812
813        return [h, s * 100, l * 100];
814    }
815
816    function rgb2hsv(rgb) {
817        var r = rgb[0],
818            g = rgb[1],
819            b = rgb[2],
820            min = Math.min(r, g, b),
821            max = Math.max(r, g, b),
822            delta = max - min,
823            h, s, v;
824
825        if (max == 0)
826            s = 0;
827        else
828            s = (delta/max * 1000)/10;
829
830        if (max == min)
831            h = 0;
832        else if (r == max)
833            h = (g - b) / delta;
834        else if (g == max)
835            h = 2 + (b - r) / delta;
836        else if (b == max)
837            h = 4 + (r - g) / delta;
838
839        h = Math.min(h * 60, 360);
840
841        if (h < 0)
842            h += 360;
843
844        v = ((max / 255) * 1000) / 10;
845
846        return [h, s, v];
847    }
848
849    function rgb2hwb(rgb) {
850        var r = rgb[0],
851            g = rgb[1],
852            b = rgb[2],
853            h = rgb2hsl(rgb)[0],
854            w = 1/255 * Math.min(r, Math.min(g, b)),
855            b = 1 - 1/255 * Math.max(r, Math.max(g, b));
856
857        return [h, w * 100, b * 100];
858    }
859
860    function rgb2cmyk(rgb) {
861        var r = rgb[0] / 255,
862            g = rgb[1] / 255,
863            b = rgb[2] / 255,
864            c, m, y, k;
865
866        k = Math.min(1 - r, 1 - g, 1 - b);
867        c = (1 - r - k) / (1 - k) || 0;
868        m = (1 - g - k) / (1 - k) || 0;
869        y = (1 - b - k) / (1 - k) || 0;
870        return [c * 100, m * 100, y * 100, k * 100];
871    }
872
873    function rgb2keyword(rgb) {
874        return reverseKeywords[JSON.stringify(rgb)];
875    }
876
877    function rgb2xyz(rgb) {
878        var r = rgb[0] / 255,
879            g = rgb[1] / 255,
880            b = rgb[2] / 255;
881
882        // assume sRGB
883        r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
884        g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
885        b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
886
887        var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
888        var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
889        var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
890
891        return [x * 100, y *100, z * 100];
892    }
893
894    function rgb2lab(rgb) {
895        var xyz = rgb2xyz(rgb),
896            x = xyz[0],
897            y = xyz[1],
898            z = xyz[2],
899            l, a, b;
900
901        x /= 95.047;
902        y /= 100;
903        z /= 108.883;
904
905        x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
906        y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
907        z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
908
909        l = (116 * y) - 16;
910        a = 500 * (x - y);
911        b = 200 * (y - z);
912
913        return [l, a, b];
914    }
915
916    function rgb2lch(args) {
917        return lab2lch(rgb2lab(args));
918    }
919
920    function hsl2rgb(hsl) {
921        var h = hsl[0] / 360,
922            s = hsl[1] / 100,
923            l = hsl[2] / 100,
924            t1, t2, t3, rgb, val;
925
926        if (s == 0) {
927            val = l * 255;
928            return [val, val, val];
929        }
930
931        if (l < 0.5)
932            t2 = l * (1 + s);
933        else
934            t2 = l + s - l * s;
935        t1 = 2 * l - t2;
936
937        rgb = [0, 0, 0];
938        for (var i = 0; i < 3; i++) {
939            t3 = h + 1 / 3 * - (i - 1);
940            t3 < 0 && t3++;
941            t3 > 1 && t3--;
942
943            if (6 * t3 < 1)
944                val = t1 + (t2 - t1) * 6 * t3;
945            else if (2 * t3 < 1)
946                val = t2;
947            else if (3 * t3 < 2)
948                val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
949            else
950                val = t1;
951
952            rgb[i] = val * 255;
953        }
954
955        return rgb;
956    }
957
958    function hsl2hsv(hsl) {
959        var h = hsl[0],
960            s = hsl[1] / 100,
961            l = hsl[2] / 100,
962            sv, v;
963
964        if(l === 0) {
965            // no need to do calc on black
966            // also avoids divide by 0 error
967            return [0, 0, 0];
968        }
969
970        l *= 2;
971        s *= (l <= 1) ? l : 2 - l;
972        v = (l + s) / 2;
973        sv = (2 * s) / (l + s);
974        return [h, sv * 100, v * 100];
975    }
976
977    function hsl2hwb(args) {
978        return rgb2hwb(hsl2rgb(args));
979    }
980
981    function hsl2cmyk(args) {
982        return rgb2cmyk(hsl2rgb(args));
983    }
984
985    function hsl2keyword(args) {
986        return rgb2keyword(hsl2rgb(args));
987    }
988
989
990    function hsv2rgb(hsv) {
991        var h = hsv[0] / 60,
992            s = hsv[1] / 100,
993            v = hsv[2] / 100,
994            hi = Math.floor(h) % 6;
995
996        var f = h - Math.floor(h),
997            p = 255 * v * (1 - s),
998            q = 255 * v * (1 - (s * f)),
999            t = 255 * v * (1 - (s * (1 - f))),
1000            v = 255 * v;
1001
1002        switch(hi) {
1003            case 0:
1004                return [v, t, p];
1005            case 1:
1006                return [q, v, p];
1007            case 2:
1008                return [p, v, t];
1009            case 3:
1010                return [p, q, v];
1011            case 4:
1012                return [t, p, v];
1013            case 5:
1014                return [v, p, q];
1015        }
1016    }
1017
1018    function hsv2hsl(hsv) {
1019        var h = hsv[0],
1020            s = hsv[1] / 100,
1021            v = hsv[2] / 100,
1022            sl, l;
1023
1024        l = (2 - s) * v;
1025        sl = s * v;
1026        sl /= (l <= 1) ? l : 2 - l;
1027        sl = sl || 0;
1028        l /= 2;
1029        return [h, sl * 100, l * 100];
1030    }
1031
1032    function hsv2hwb(args) {
1033        return rgb2hwb(hsv2rgb(args))
1034    }
1035
1036    function hsv2cmyk(args) {
1037        return rgb2cmyk(hsv2rgb(args));
1038    }
1039
1040    function hsv2keyword(args) {
1041        return rgb2keyword(hsv2rgb(args));
1042    }
1043
1044// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
1045    function hwb2rgb(hwb) {
1046        var h = hwb[0] / 360,
1047            wh = hwb[1] / 100,
1048            bl = hwb[2] / 100,
1049            ratio = wh + bl,
1050            i, v, f, n;
1051
1052        // wh + bl cant be > 1
1053        if (ratio > 1) {
1054            wh /= ratio;
1055            bl /= ratio;
1056        }
1057
1058        i = Math.floor(6 * h);
1059        v = 1 - bl;
1060        f = 6 * h - i;
1061        if ((i & 0x01) != 0) {
1062            f = 1 - f;
1063        }
1064        n = wh + f * (v - wh);  // linear interpolation
1065
1066        switch (i) {
1067            default:
1068            case 6:
1069            case 0: r = v; g = n; b = wh; break;
1070            case 1: r = n; g = v; b = wh; break;
1071            case 2: r = wh; g = v; b = n; break;
1072            case 3: r = wh; g = n; b = v; break;
1073            case 4: r = n; g = wh; b = v; break;
1074            case 5: r = v; g = wh; b = n; break;
1075        }
1076
1077        return [r * 255, g * 255, b * 255];
1078    }
1079
1080    function hwb2hsl(args) {
1081        return rgb2hsl(hwb2rgb(args));
1082    }
1083
1084    function hwb2hsv(args) {
1085        return rgb2hsv(hwb2rgb(args));
1086    }
1087
1088    function hwb2cmyk(args) {
1089        return rgb2cmyk(hwb2rgb(args));
1090    }
1091
1092    function hwb2keyword(args) {
1093        return rgb2keyword(hwb2rgb(args));
1094    }
1095
1096    function cmyk2rgb(cmyk) {
1097        var c = cmyk[0] / 100,
1098            m = cmyk[1] / 100,
1099            y = cmyk[2] / 100,
1100            k = cmyk[3] / 100,
1101            r, g, b;
1102
1103        r = 1 - Math.min(1, c * (1 - k) + k);
1104        g = 1 - Math.min(1, m * (1 - k) + k);
1105        b = 1 - Math.min(1, y * (1 - k) + k);
1106        return [r * 255, g * 255, b * 255];
1107    }
1108
1109    function cmyk2hsl(args) {
1110        return rgb2hsl(cmyk2rgb(args));
1111    }
1112
1113    function cmyk2hsv(args) {
1114        return rgb2hsv(cmyk2rgb(args));
1115    }
1116
1117    function cmyk2hwb(args) {
1118        return rgb2hwb(cmyk2rgb(args));
1119    }
1120
1121    function cmyk2keyword(args) {
1122        return rgb2keyword(cmyk2rgb(args));
1123    }
1124
1125
1126    function xyz2rgb(xyz) {
1127        var x = xyz[0] / 100,
1128            y = xyz[1] / 100,
1129            z = xyz[2] / 100,
1130            r, g, b;
1131
1132        r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
1133        g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
1134        b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
1135
1136        // assume sRGB
1137        r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
1138            : r = (r * 12.92);
1139
1140        g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
1141            : g = (g * 12.92);
1142
1143        b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
1144            : b = (b * 12.92);
1145
1146        r = Math.min(Math.max(0, r), 1);
1147        g = Math.min(Math.max(0, g), 1);
1148        b = Math.min(Math.max(0, b), 1);
1149
1150        return [r * 255, g * 255, b * 255];
1151    }
1152
1153    function xyz2lab(xyz) {
1154        var x = xyz[0],
1155            y = xyz[1],
1156            z = xyz[2],
1157            l, a, b;
1158
1159        x /= 95.047;
1160        y /= 100;
1161        z /= 108.883;
1162
1163        x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
1164        y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
1165        z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
1166
1167        l = (116 * y) - 16;
1168        a = 500 * (x - y);
1169        b = 200 * (y - z);
1170
1171        return [l, a, b];
1172    }
1173
1174    function xyz2lch(args) {
1175        return lab2lch(xyz2lab(args));
1176    }
1177
1178    function lab2xyz(lab) {
1179        var l = lab[0],
1180            a = lab[1],
1181            b = lab[2],
1182            x, y, z, y2;
1183
1184        if (l <= 8) {
1185            y = (l * 100) / 903.3;
1186            y2 = (7.787 * (y / 100)) + (16 / 116);
1187        } else {
1188            y = 100 * Math.pow((l + 16) / 116, 3);
1189            y2 = Math.pow(y / 100, 1/3);
1190        }
1191
1192        x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);
1193
1194        z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);
1195
1196        return [x, y, z];
1197    }
1198
1199    function lab2lch(lab) {
1200        var l = lab[0],
1201            a = lab[1],
1202            b = lab[2],
1203            hr, h, c;
1204
1205        hr = Math.atan2(b, a);
1206        h = hr * 360 / 2 / Math.PI;
1207        if (h < 0) {
1208            h += 360;
1209        }
1210        c = Math.sqrt(a * a + b * b);
1211        return [l, c, h];
1212    }
1213
1214    function lab2rgb(args) {
1215        return xyz2rgb(lab2xyz(args));
1216    }
1217
1218    function lch2lab(lch) {
1219        var l = lch[0],
1220            c = lch[1],
1221            h = lch[2],
1222            a, b, hr;
1223
1224        hr = h / 360 * 2 * Math.PI;
1225        a = c * Math.cos(hr);
1226        b = c * Math.sin(hr);
1227        return [l, a, b];
1228    }
1229
1230    function lch2xyz(args) {
1231        return lab2xyz(lch2lab(args));
1232    }
1233
1234    function lch2rgb(args) {
1235        return lab2rgb(lch2lab(args));
1236    }
1237
1238    function keyword2rgb(keyword) {
1239        return cssKeywords[keyword];
1240    }
1241
1242    function keyword2hsl(args) {
1243        return rgb2hsl(keyword2rgb(args));
1244    }
1245
1246    function keyword2hsv(args) {
1247        return rgb2hsv(keyword2rgb(args));
1248    }
1249
1250    function keyword2hwb(args) {
1251        return rgb2hwb(keyword2rgb(args));
1252    }
1253
1254    function keyword2cmyk(args) {
1255        return rgb2cmyk(keyword2rgb(args));
1256    }
1257
1258    function keyword2lab(args) {
1259        return rgb2lab(keyword2rgb(args));
1260    }
1261
1262    function keyword2xyz(args) {
1263        return rgb2xyz(keyword2rgb(args));
1264    }
1265
1266    var cssKeywords = {
1267        aliceblue:  [240,248,255],
1268        antiquewhite: [250,235,215],
1269        aqua: [0,255,255],
1270        aquamarine: [127,255,212],
1271        azure:  [240,255,255],
1272        beige:  [245,245,220],
1273        bisque: [255,228,196],
1274        black:  [0,0,0],
1275        blanchedalmond: [255,235,205],
1276        blue: [0,0,255],
1277        blueviolet: [138,43,226],
1278        brown:  [165,42,42],
1279        burlywood:  [222,184,135],
1280        cadetblue:  [95,158,160],
1281        chartreuse: [127,255,0],
1282        chocolate:  [210,105,30],
1283        coral:  [255,127,80],
1284        cornflowerblue: [100,149,237],
1285        cornsilk: [255,248,220],
1286        crimson:  [220,20,60],
1287        cyan: [0,255,255],
1288        darkblue: [0,0,139],
1289        darkcyan: [0,139,139],
1290        darkgoldenrod:  [184,134,11],
1291        darkgray: [169,169,169],
1292        darkgreen:  [0,100,0],
1293        darkgrey: [169,169,169],
1294        darkkhaki:  [189,183,107],
1295        darkmagenta:  [139,0,139],
1296        darkolivegreen: [85,107,47],
1297        darkorange: [255,140,0],
1298        darkorchid: [153,50,204],
1299        darkred:  [139,0,0],
1300        darksalmon: [233,150,122],
1301        darkseagreen: [143,188,143],
1302        darkslateblue:  [72,61,139],
1303        darkslategray:  [47,79,79],
1304        darkslategrey:  [47,79,79],
1305        darkturquoise:  [0,206,209],
1306        darkviolet: [148,0,211],
1307        deeppink: [255,20,147],
1308        deepskyblue:  [0,191,255],
1309        dimgray:  [105,105,105],
1310        dimgrey:  [105,105,105],
1311        dodgerblue: [30,144,255],
1312        firebrick:  [178,34,34],
1313        floralwhite:  [255,250,240],
1314        forestgreen:  [34,139,34],
1315        fuchsia:  [255,0,255],
1316        gainsboro:  [220,220,220],
1317        ghostwhite: [248,248,255],
1318        gold: [255,215,0],
1319        goldenrod:  [218,165,32],
1320        gray: [128,128,128],
1321        green:  [0,128,0],
1322        greenyellow:  [173,255,47],
1323        grey: [128,128,128],
1324        honeydew: [240,255,240],
1325        hotpink:  [255,105,180],
1326        indianred:  [205,92,92],
1327        indigo: [75,0,130],
1328        ivory:  [255,255,240],
1329        khaki:  [240,230,140],
1330        lavender: [230,230,250],
1331        lavenderblush:  [255,240,245],
1332        lawngreen:  [124,252,0],
1333        lemonchiffon: [255,250,205],
1334        lightblue:  [173,216,230],
1335        lightcoral: [240,128,128],
1336        lightcyan:  [224,255,255],
1337        lightgoldenrodyellow: [250,250,210],
1338        lightgray:  [211,211,211],
1339        lightgreen: [144,238,144],
1340        lightgrey:  [211,211,211],
1341        lightpink:  [255,182,193],
1342        lightsalmon:  [255,160,122],
1343        lightseagreen:  [32,178,170],
1344        lightskyblue: [135,206,250],
1345        lightslategray: [119,136,153],
1346        lightslategrey: [119,136,153],
1347        lightsteelblue: [176,196,222],
1348        lightyellow:  [255,255,224],
1349        lime: [0,255,0],
1350        limegreen:  [50,205,50],
1351        linen:  [250,240,230],
1352        magenta:  [255,0,255],
1353        maroon: [128,0,0],
1354        mediumaquamarine: [102,205,170],
1355        mediumblue: [0,0,205],
1356        mediumorchid: [186,85,211],
1357        mediumpurple: [147,112,219],
1358        mediumseagreen: [60,179,113],
1359        mediumslateblue:  [123,104,238],
1360        mediumspringgreen:  [0,250,154],
1361        mediumturquoise:  [72,209,204],
1362        mediumvioletred:  [199,21,133],
1363        midnightblue: [25,25,112],
1364        mintcream:  [245,255,250],
1365        mistyrose:  [255,228,225],
1366        moccasin: [255,228,181],
1367        navajowhite:  [255,222,173],
1368        navy: [0,0,128],
1369        oldlace:  [253,245,230],
1370        olive:  [128,128,0],
1371        olivedrab:  [107,142,35],
1372        orange: [255,165,0],
1373        orangered:  [255,69,0],
1374        orchid: [218,112,214],
1375        palegoldenrod:  [238,232,170],
1376        palegreen:  [152,251,152],
1377        paleturquoise:  [175,238,238],
1378        palevioletred:  [219,112,147],
1379        papayawhip: [255,239,213],
1380        peachpuff:  [255,218,185],
1381        peru: [205,133,63],
1382        pink: [255,192,203],
1383        plum: [221,160,221],
1384        powderblue: [176,224,230],
1385        purple: [128,0,128],
1386        rebeccapurple: [102, 51, 153],
1387        red:  [255,0,0],
1388        rosybrown:  [188,143,143],
1389        royalblue:  [65,105,225],
1390        saddlebrown:  [139,69,19],
1391        salmon: [250,128,114],
1392        sandybrown: [244,164,96],
1393        seagreen: [46,139,87],
1394        seashell: [255,245,238],
1395        sienna: [160,82,45],
1396        silver: [192,192,192],
1397        skyblue:  [135,206,235],
1398        slateblue:  [106,90,205],
1399        slategray:  [112,128,144],
1400        slategrey:  [112,128,144],
1401        snow: [255,250,250],
1402        springgreen:  [0,255,127],
1403        steelblue:  [70,130,180],
1404        tan:  [210,180,140],
1405        teal: [0,128,128],
1406        thistle:  [216,191,216],
1407        tomato: [255,99,71],
1408        turquoise:  [64,224,208],
1409        violet: [238,130,238],
1410        wheat:  [245,222,179],
1411        white:  [255,255,255],
1412        whitesmoke: [245,245,245],
1413        yellow: [255,255,0],
1414        yellowgreen:  [154,205,50]
1415    };
1416
1417    var reverseKeywords = {};
1418    for (var key in cssKeywords) {
1419        reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
1420    }
1421
1422},{}],5:[function(require,module,exports){
1423    var conversions = require(4);
1424
1425    var convert = function() {
1426        return new Converter();
1427    }
1428
1429    for (var func in conversions) {
1430        // export Raw versions
1431        convert[func + "Raw"] =  (function(func) {
1432            // accept array or plain args
1433            return function(arg) {
1434                if (typeof arg == "number")
1435                    arg = Array.prototype.slice.call(arguments);
1436                return conversions[func](arg);
1437            }
1438        })(func);
1439
1440        var pair = /(\w+)2(\w+)/.exec(func),
1441            from = pair[1],
1442            to = pair[2];
1443
1444        // export rgb2hsl and ["rgb"]["hsl"]
1445        convert[from] = convert[from] || {};
1446
1447        convert[from][to] = convert[func] = (function(func) {
1448            return function(arg) {
1449                if (typeof arg == "number")
1450                    arg = Array.prototype.slice.call(arguments);
1451
1452                var val = conversions[func](arg);
1453                if (typeof val == "string" || val === undefined)
1454                    return val; // keyword
1455
1456                for (var i = 0; i < val.length; i++)
1457                    val[i] = Math.round(val[i]);
1458                return val;
1459            }
1460        })(func);
1461    }
1462
1463
1464    /* Converter does lazy conversion and caching */
1465    var Converter = function() {
1466        this.convs = {};
1467    };
1468
1469    /* Either get the values for a space or
1470  set the values for a space, depending on args */
1471    Converter.prototype.routeSpace = function(space, args) {
1472        var values = args[0];
1473        if (values === undefined) {
1474            // color.rgb()
1475            return this.getValues(space);
1476        }
1477        // color.rgb(10, 10, 10)
1478        if (typeof values == "number") {
1479            values = Array.prototype.slice.call(args);
1480        }
1481
1482        return this.setValues(space, values);
1483    };
1484
1485    /* Set the values for a space, invalidating cache */
1486    Converter.prototype.setValues = function(space, values) {
1487        this.space = space;
1488        this.convs = {};
1489        this.convs[space] = values;
1490        return this;
1491    };
1492
1493    /* Get the values for a space. If there's already
1494  a conversion for the space, fetch it, otherwise
1495  compute it */
1496    Converter.prototype.getValues = function(space) {
1497        var vals = this.convs[space];
1498        if (!vals) {
1499            var fspace = this.space,
1500                from = this.convs[fspace];
1501            vals = convert[fspace][space](from);
1502
1503            this.convs[space] = vals;
1504        }
1505        return vals;
1506    };
1507
1508    ["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) {
1509        Converter.prototype[space] = function(vals) {
1510            return this.routeSpace(space, arguments);
1511        }
1512    });
1513
1514    module.exports = convert;
1515},{"4":4}],6:[function(require,module,exports){
1516    'use strict'
1517
1518    module.exports = {
1519        "aliceblue": [240, 248, 255],
1520        "antiquewhite": [250, 235, 215],
1521        "aqua": [0, 255, 255],
1522        "aquamarine": [127, 255, 212],
1523        "azure": [240, 255, 255],
1524        "beige": [245, 245, 220],
1525        "bisque": [255, 228, 196],
1526        "black": [0, 0, 0],
1527        "blanchedalmond": [255, 235, 205],
1528        "blue": [0, 0, 255],
1529        "blueviolet": [138, 43, 226],
1530        "brown": [165, 42, 42],
1531        "burlywood": [222, 184, 135],
1532        "cadetblue": [95, 158, 160],
1533        "chartreuse": [127, 255, 0],
1534        "chocolate": [210, 105, 30],
1535        "coral": [255, 127, 80],
1536        "cornflowerblue": [100, 149, 237],
1537        "cornsilk": [255, 248, 220],
1538        "crimson": [220, 20, 60],
1539        "cyan": [0, 255, 255],
1540        "darkblue": [0, 0, 139],
1541        "darkcyan": [0, 139, 139],
1542        "darkgoldenrod": [184, 134, 11],
1543        "darkgray": [169, 169, 169],
1544        "darkgreen": [0, 100, 0],
1545        "darkgrey": [169, 169, 169],
1546        "darkkhaki": [189, 183, 107],
1547        "darkmagenta": [139, 0, 139],
1548        "darkolivegreen": [85, 107, 47],
1549        "darkorange": [255, 140, 0],
1550        "darkorchid": [153, 50, 204],
1551        "darkred": [139, 0, 0],
1552        "darksalmon": [233, 150, 122],
1553        "darkseagreen": [143, 188, 143],
1554        "darkslateblue": [72, 61, 139],
1555        "darkslategray": [47, 79, 79],
1556        "darkslategrey": [47, 79, 79],
1557        "darkturquoise": [0, 206, 209],
1558        "darkviolet": [148, 0, 211],
1559        "deeppink": [255, 20, 147],
1560        "deepskyblue": [0, 191, 255],
1561        "dimgray": [105, 105, 105],
1562        "dimgrey": [105, 105, 105],
1563        "dodgerblue": [30, 144, 255],
1564        "firebrick": [178, 34, 34],
1565        "floralwhite": [255, 250, 240],
1566        "forestgreen": [34, 139, 34],
1567        "fuchsia": [255, 0, 255],
1568        "gainsboro": [220, 220, 220],
1569        "ghostwhite": [248, 248, 255],
1570        "gold": [255, 215, 0],
1571        "goldenrod": [218, 165, 32],
1572        "gray": [128, 128, 128],
1573        "green": [0, 128, 0],
1574        "greenyellow": [173, 255, 47],
1575        "grey": [128, 128, 128],
1576        "honeydew": [240, 255, 240],
1577        "hotpink": [255, 105, 180],
1578        "indianred": [205, 92, 92],
1579        "indigo": [75, 0, 130],
1580        "ivory": [255, 255, 240],
1581        "khaki": [240, 230, 140],
1582        "lavender": [230, 230, 250],
1583        "lavenderblush": [255, 240, 245],
1584        "lawngreen": [124, 252, 0],
1585        "lemonchiffon": [255, 250, 205],
1586        "lightblue": [173, 216, 230],
1587        "lightcoral": [240, 128, 128],
1588        "lightcyan": [224, 255, 255],
1589        "lightgoldenrodyellow": [250, 250, 210],
1590        "lightgray": [211, 211, 211],
1591        "lightgreen": [144, 238, 144],
1592        "lightgrey": [211, 211, 211],
1593        "lightpink": [255, 182, 193],
1594        "lightsalmon": [255, 160, 122],
1595        "lightseagreen": [32, 178, 170],
1596        "lightskyblue": [135, 206, 250],
1597        "lightslategray": [119, 136, 153],
1598        "lightslategrey": [119, 136, 153],
1599        "lightsteelblue": [176, 196, 222],
1600        "lightyellow": [255, 255, 224],
1601        "lime": [0, 255, 0],
1602        "limegreen": [50, 205, 50],
1603        "linen": [250, 240, 230],
1604        "magenta": [255, 0, 255],
1605        "maroon": [128, 0, 0],
1606        "mediumaquamarine": [102, 205, 170],
1607        "mediumblue": [0, 0, 205],
1608        "mediumorchid": [186, 85, 211],
1609        "mediumpurple": [147, 112, 219],
1610        "mediumseagreen": [60, 179, 113],
1611        "mediumslateblue": [123, 104, 238],
1612        "mediumspringgreen": [0, 250, 154],
1613        "mediumturquoise": [72, 209, 204],
1614        "mediumvioletred": [199, 21, 133],
1615        "midnightblue": [25, 25, 112],
1616        "mintcream": [245, 255, 250],
1617        "mistyrose": [255, 228, 225],
1618        "moccasin": [255, 228, 181],
1619        "navajowhite": [255, 222, 173],
1620        "navy": [0, 0, 128],
1621        "oldlace": [253, 245, 230],
1622        "olive": [128, 128, 0],
1623        "olivedrab": [107, 142, 35],
1624        "orange": [255, 165, 0],
1625        "orangered": [255, 69, 0],
1626        "orchid": [218, 112, 214],
1627        "palegoldenrod": [238, 232, 170],
1628        "palegreen": [152, 251, 152],
1629        "paleturquoise": [175, 238, 238],
1630        "palevioletred": [219, 112, 147],
1631        "papayawhip": [255, 239, 213],
1632        "peachpuff": [255, 218, 185],
1633        "peru": [205, 133, 63],
1634        "pink": [255, 192, 203],
1635        "plum": [221, 160, 221],
1636        "powderblue": [176, 224, 230],
1637        "purple": [128, 0, 128],
1638        "rebeccapurple": [102, 51, 153],
1639        "red": [255, 0, 0],
1640        "rosybrown": [188, 143, 143],
1641        "royalblue": [65, 105, 225],
1642        "saddlebrown": [139, 69, 19],
1643        "salmon": [250, 128, 114],
1644        "sandybrown": [244, 164, 96],
1645        "seagreen": [46, 139, 87],
1646        "seashell": [255, 245, 238],
1647        "sienna": [160, 82, 45],
1648        "silver": [192, 192, 192],
1649        "skyblue": [135, 206, 235],
1650        "slateblue": [106, 90, 205],
1651        "slategray": [112, 128, 144],
1652        "slategrey": [112, 128, 144],
1653        "snow": [255, 250, 250],
1654        "springgreen": [0, 255, 127],
1655        "steelblue": [70, 130, 180],
1656        "tan": [210, 180, 140],
1657        "teal": [0, 128, 128],
1658        "thistle": [216, 191, 216],
1659        "tomato": [255, 99, 71],
1660        "turquoise": [64, 224, 208],
1661        "violet": [238, 130, 238],
1662        "wheat": [245, 222, 179],
1663        "white": [255, 255, 255],
1664        "whitesmoke": [245, 245, 245],
1665        "yellow": [255, 255, 0],
1666        "yellowgreen": [154, 205, 50]
1667    };
1668
1669},{}],7:[function(require,module,exports){
1670    /**
1671     * @namespace Chart
1672     */
1673    var Chart = require(30)();
1674
1675    Chart.helpers = require(46);
1676
1677// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
1678    require(28)(Chart);
1679
1680    Chart.Animation = require(22);
1681    Chart.animationService = require(23);
1682    Chart.defaults = require(26);
1683    Chart.Element = require(27);
1684    Chart.elements = require(41);
1685    Chart.Interaction = require(29);
1686    Chart.layouts = require(31);
1687    Chart.platform = require(49);
1688    Chart.plugins = require(32);
1689    Chart.Scale = require(33);
1690    Chart.scaleService = require(34);
1691    Chart.Ticks = require(35);
1692    Chart.Tooltip = require(36);
1693
1694    require(24)(Chart);
1695    require(25)(Chart);
1696
1697    require(56)(Chart);
1698    require(54)(Chart);
1699    require(55)(Chart);
1700    require(57)(Chart);
1701    require(58)(Chart);
1702    require(59)(Chart);
1703
1704// Controllers must be loaded after elements
1705// See Chart.core.datasetController.dataElementType
1706    require(15)(Chart);
1707    require(16)(Chart);
1708    require(17)(Chart);
1709    require(18)(Chart);
1710    require(19)(Chart);
1711    require(20)(Chart);
1712    require(21)(Chart);
1713
1714    require(8)(Chart);
1715    require(9)(Chart);
1716    require(10)(Chart);
1717    require(11)(Chart);
1718    require(12)(Chart);
1719    require(13)(Chart);
1720    require(14)(Chart);
1721
1722// Loading built-in plugins
1723    var plugins = require(50);
1724    for (var k in plugins) {
1725        if (plugins.hasOwnProperty(k)) {
1726            Chart.plugins.register(plugins[k]);
1727        }
1728    }
1729
1730    Chart.platform.initialize();
1731
1732    module.exports = Chart;
1733    if (typeof window !== 'undefined') {
1734        window.Chart = Chart;
1735    }
1736
1737// DEPRECATIONS
1738
1739    /**
1740     * Provided for backward compatibility, not available anymore
1741     * @namespace Chart.Legend
1742     * @deprecated since version 2.1.5
1743     * @todo remove at version 3
1744     * @private
1745     */
1746    Chart.Legend = plugins.legend._element;
1747
1748    /**
1749     * Provided for backward compatibility, not available anymore
1750     * @namespace Chart.Title
1751     * @deprecated since version 2.1.5
1752     * @todo remove at version 3
1753     * @private
1754     */
1755    Chart.Title = plugins.title._element;
1756
1757    /**
1758     * Provided for backward compatibility, use Chart.plugins instead
1759     * @namespace Chart.pluginService
1760     * @deprecated since version 2.1.5
1761     * @todo remove at version 3
1762     * @private
1763     */
1764    Chart.pluginService = Chart.plugins;
1765
1766    /**
1767     * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
1768     * effect, instead simply create/register plugins via plain JavaScript objects.
1769     * @interface Chart.PluginBase
1770     * @deprecated since version 2.5.0
1771     * @todo remove at version 3
1772     * @private
1773     */
1774    Chart.PluginBase = Chart.Element.extend({});
1775
1776    /**
1777     * Provided for backward compatibility, use Chart.helpers.canvas instead.
1778     * @namespace Chart.canvasHelpers
1779     * @deprecated since version 2.6.0
1780     * @todo remove at version 3
1781     * @private
1782     */
1783    Chart.canvasHelpers = Chart.helpers.canvas;
1784
1785    /**
1786     * Provided for backward compatibility, use Chart.layouts instead.
1787     * @namespace Chart.layoutService
1788     * @deprecated since version 2.8.0
1789     * @todo remove at version 3
1790     * @private
1791     */
1792    Chart.layoutService = Chart.layouts;
1793
1794},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"41":41,"46":46,"49":49,"50":50,"54":54,"55":55,"56":56,"57":57,"58":58,"59":59,"8":8,"9":9}],8:[function(require,module,exports){
1795    'use strict';
1796
1797    module.exports = function(Chart) {
1798
1799        Chart.Bar = function(context, config) {
1800            config.type = 'bar';
1801
1802            return new Chart(context, config);
1803        };
1804
1805    };
1806
1807},{}],9:[function(require,module,exports){
1808    'use strict';
1809
1810    module.exports = function(Chart) {
1811
1812        Chart.Bubble = function(context, config) {
1813            config.type = 'bubble';
1814            return new Chart(context, config);
1815        };
1816
1817    };
1818
1819},{}],10:[function(require,module,exports){
1820    'use strict';
1821
1822    module.exports = function(Chart) {
1823
1824        Chart.Doughnut = function(context, config) {
1825            config.type = 'doughnut';
1826
1827            return new Chart(context, config);
1828        };
1829
1830    };
1831
1832},{}],11:[function(require,module,exports){
1833    'use strict';
1834
1835    module.exports = function(Chart) {
1836
1837        Chart.Line = function(context, config) {
1838            config.type = 'line';
1839
1840            return new Chart(context, config);
1841        };
1842
1843    };
1844
1845},{}],12:[function(require,module,exports){
1846    'use strict';
1847
1848    module.exports = function(Chart) {
1849
1850        Chart.PolarArea = function(context, config) {
1851            config.type = 'polarArea';
1852
1853            return new Chart(context, config);
1854        };
1855
1856    };
1857
1858},{}],13:[function(require,module,exports){
1859    'use strict';
1860
1861    module.exports = function(Chart) {
1862
1863        Chart.Radar = function(context, config) {
1864            config.type = 'radar';
1865
1866            return new Chart(context, config);
1867        };
1868
1869    };
1870
1871},{}],14:[function(require,module,exports){
1872    'use strict';
1873
1874    module.exports = function(Chart) {
1875        Chart.Scatter = function(context, config) {
1876            config.type = 'scatter';
1877            return new Chart(context, config);
1878        };
1879    };
1880
1881},{}],15:[function(require,module,exports){
1882    'use strict';
1883
1884    var defaults = require(26);
1885    var elements = require(41);
1886    var helpers = require(46);
1887
1888    defaults._set('bar', {
1889        hover: {
1890            mode: 'label'
1891        },
1892
1893        scales: {
1894            xAxes: [{
1895                type: 'category',
1896
1897                // Specific to Bar Controller
1898                categoryPercentage: 0.8,
1899                barPercentage: 0.9,
1900
1901                // offset settings
1902                offset: true,
1903
1904                // grid line settings
1905                gridLines: {
1906                    offsetGridLines: true
1907                }
1908            }],
1909
1910            yAxes: [{
1911                type: 'linear'
1912            }]
1913        }
1914    });
1915
1916    defaults._set('horizontalBar', {
1917        hover: {
1918            mode: 'index',
1919            axis: 'y'
1920        },
1921
1922        scales: {
1923            xAxes: [{
1924                type: 'linear',
1925                position: 'bottom'
1926            }],
1927
1928            yAxes: [{
1929                position: 'left',
1930                type: 'category',
1931
1932                // Specific to Horizontal Bar Controller
1933                categoryPercentage: 0.8,
1934                barPercentage: 0.9,
1935
1936                // offset settings
1937                offset: true,
1938
1939                // grid line settings
1940                gridLines: {
1941                    offsetGridLines: true
1942                }
1943            }]
1944        },
1945
1946        elements: {
1947            rectangle: {
1948                borderSkipped: 'left'
1949            }
1950        },
1951
1952        tooltips: {
1953            callbacks: {
1954                title: function(item, data) {
1955                    // Pick first xLabel for now
1956                    var title = '';
1957
1958                    if (item.length > 0) {
1959                        if (item[0].yLabel) {
1960                            title = item[0].yLabel;
1961                        } else if (data.labels.length > 0 && item[0].index < data.labels.length) {
1962                            title = data.labels[item[0].index];
1963                        }
1964                    }
1965
1966                    return title;
1967                },
1968
1969                label: function(item, data) {
1970                    var datasetLabel = data.datasets[item.datasetIndex].label || '';
1971                    return datasetLabel + ': ' + item.xLabel;
1972                }
1973            },
1974            mode: 'index',
1975            axis: 'y'
1976        }
1977    });
1978
1979    /**
1980     * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
1981     * @private
1982     */
1983    function computeMinSampleSize(scale, pixels) {
1984        var min = scale.isHorizontal() ? scale.width : scale.height;
1985        var ticks = scale.getTicks();
1986        var prev, curr, i, ilen;
1987
1988        for (i = 1, ilen = pixels.length; i < ilen; ++i) {
1989            min = Math.min(min, pixels[i] - pixels[i - 1]);
1990        }
1991
1992        for (i = 0, ilen = ticks.length; i < ilen; ++i) {
1993            curr = scale.getPixelForTick(i);
1994            min = i > 0 ? Math.min(min, curr - prev) : min;
1995            prev = curr;
1996        }
1997
1998        return min;
1999    }
2000
2001    /**
2002     * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null,
2003     * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This
2004     * mode currently always generates bars equally sized (until we introduce scriptable options?).
2005     * @private
2006     */
2007    function computeFitCategoryTraits(index, ruler, options) {
2008        var thickness = options.barThickness;
2009        var count = ruler.stackCount;
2010        var curr = ruler.pixels[index];
2011        var size, ratio;
2012
2013        if (helpers.isNullOrUndef(thickness)) {
2014            size = ruler.min * options.categoryPercentage;
2015            ratio = options.barPercentage;
2016        } else {
2017            // When bar thickness is enforced, category and bar percentages are ignored.
2018            // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%')
2019            // and deprecate barPercentage since this value is ignored when thickness is absolute.
2020            size = thickness * count;
2021            ratio = 1;
2022        }
2023
2024        return {
2025            chunk: size / count,
2026            ratio: ratio,
2027            start: curr - (size / 2)
2028        };
2029    }
2030
2031    /**
2032     * Computes an "optimal" category that globally arranges bars side by side (no gap when
2033     * percentage options are 1), based on the previous and following categories. This mode
2034     * generates bars with different widths when data are not evenly spaced.
2035     * @private
2036     */
2037    function computeFlexCategoryTraits(index, ruler, options) {
2038        var pixels = ruler.pixels;
2039        var curr = pixels[index];
2040        var prev = index > 0 ? pixels[index - 1] : null;
2041        var next = index < pixels.length - 1 ? pixels[index + 1] : null;
2042        var percent = options.categoryPercentage;
2043        var start, size;
2044
2045        if (prev === null) {
2046            // first data: its size is double based on the next point or,
2047            // if it's also the last data, we use the scale end extremity.
2048            prev = curr - (next === null ? ruler.end - curr : next - curr);
2049        }
2050
2051        if (next === null) {
2052            // last data: its size is also double based on the previous point.
2053            next = curr + curr - prev;
2054        }
2055
2056        start = curr - ((curr - prev) / 2) * percent;
2057        size = ((next - prev) / 2) * percent;
2058
2059        return {
2060            chunk: size / ruler.stackCount,
2061            ratio: options.barPercentage,
2062            start: start
2063        };
2064    }
2065
2066    module.exports = function(Chart) {
2067
2068        Chart.controllers.bar = Chart.DatasetController.extend({
2069
2070            dataElementType: elements.Rectangle,
2071
2072            initialize: function() {
2073                var me = this;
2074                var meta;
2075
2076                Chart.DatasetController.prototype.initialize.apply(me, arguments);
2077
2078                meta = me.getMeta();
2079                meta.stack = me.getDataset().stack;
2080                meta.bar = true;
2081            },
2082
2083            update: function(reset) {
2084                var me = this;
2085                var rects = me.getMeta().data;
2086                var i, ilen;
2087
2088                me._ruler = me.getRuler();
2089
2090                for (i = 0, ilen = rects.length; i < ilen; ++i) {
2091                    me.updateElement(rects[i], i, reset);
2092                }
2093            },
2094
2095            updateElement: function(rectangle, index, reset) {
2096                var me = this;
2097                var chart = me.chart;
2098                var meta = me.getMeta();
2099                var dataset = me.getDataset();
2100                var custom = rectangle.custom || {};
2101                var rectangleOptions = chart.options.elements.rectangle;
2102
2103                rectangle._xScale = me.getScaleForId(meta.xAxisID);
2104                rectangle._yScale = me.getScale
2104ForId(meta.yAxisID);
2105                rectangle._datasetIndex = me.index;
2106                rectangle._index = index;
2107
2108                rectangle._model = {
2109                    datasetLabel: dataset.label,
2110                    label: chart.data.labels[index],
2111                    borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped,
2112                    backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor),
2113                    borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor),
2114                    borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth)
2115                };
2116
2117                me.updateElementGeometry(rectangle, index, reset);
2118
2119                rectangle.pivot();
2120            },
2121
2122            /**
2123             * @private
2124             */
2125            updateElementGeometry: function(rectangle, index, reset) {
2126                var me = this;
2127                var model = rectangle._model;
2128                var vscale = me.getValueScale();
2129                var base = vscale.getBasePixel();
2130                var horizontal = vscale.isHorizontal();
2131                var ruler = me._ruler || me.getRuler();
2132                var vpixels = me.calculateBarValuePixels(me.index, index);
2133                var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);
2134
2135                model.horizontal = horizontal;
2136                model.base = reset ? base : vpixels.base;
2137                model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
2138                model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
2139                model.height = horizontal ? ipixels.size : undefined;
2140                model.width = horizontal ? undefined : ipixels.size;
2141            },
2142
2143            /**
2144             * @private
2145             */
2146            getValueScaleId: function() {
2147                return this.getMeta().yAxisID;
2148            },
2149
2150            /**
2151             * @private
2152             */
2153            getIndexScaleId: function() {
2154                return this.getMeta().xAxisID;
2155            },
2156
2157            /**
2158             * @private
2159             */
2160            getValueScale: function() {
2161                return this.getScaleForId(this.getValueScaleId());
2162            },
2163
2164            /**
2165             * @private
2166             */
2167            getIndexScale: function() {
2168                return this.getScaleForId(this.getIndexScaleId());
2169            },
2170
2171            /**
2172             * Returns the stacks based on groups and bar visibility.
2173             * @param {Number} [last] - The dataset index
2174             * @returns {Array} The stack list
2175             * @private
2176             */
2177            _getStacks: function(last) {
2178                var me = this;
2179                var chart = me.chart;
2180                var scale = me.getIndexScale();
2181                var stacked = scale.options.stacked;
2182                var ilen = last === undefined ? chart.data.datasets.length : last + 1;
2183                var stacks = [];
2184                var i, meta;
2185
2186                for (i = 0; i < ilen; ++i) {
2187                    meta = chart.getDatasetMeta(i);
2188                    if (meta.bar && chart.isDatasetVisible(i) &&
2189                        (stacked === false ||
2190                            (stacked === true && stacks.indexOf(meta.stack) === -1) ||
2191                            (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {
2192                        stacks.push(meta.stack);
2193                    }
2194                }
2195
2196                return stacks;
2197            },
2198
2199            /**
2200             * Returns the effective number of stacks based on groups and bar visibility.
2201             * @private
2202             */
2203            getStackCount: function() {
2204                return this._getStacks().length;
2205            },
2206
2207            /**
2208             * Returns the stack index for the given dataset based on groups and bar visibility.
2209             * @param {Number} [datasetIndex] - The dataset index
2210             * @param {String} [name] - The stack name to find
2211             * @returns {Number} The stack index
2212             * @private
2213             */
2214            getStackIndex: function(datasetIndex, name) {
2215                var stacks = this._getStacks(datasetIndex);
2216                var index = (name !== undefined)
2217                    ? stacks.indexOf(name)
2218                    : -1; // indexOf returns -1 if element is not present
2219
2220                return (index === -1)
2221                    ? stacks.length - 1
2222                    : index;
2223            },
2224
2225            /**
2226             * @private
2227             */
2228            getRuler: function() {
2229                var me = this;
2230                var scale = me.getIndexScale();
2231                var stackCount = me.getStackCount();
2232                var datasetIndex = me.index;
2233                var isHorizontal = scale.isHorizontal();
2234                var start = isHorizontal ? scale.left : scale.top;
2235                var end = start + (isHorizontal ? scale.width : scale.height);
2236                var pixels = [];
2237                var i, ilen, min;
2238
2239                for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
2240                    pixels.push(scale.getPixelForValue(null, i, datasetIndex));
2241                }
2242
2243                min = helpers.isNullOrUndef(scale.options.barThickness)
2244                    ? computeMinSampleSize(scale, pixels)
2245                    : -1;
2246
2247                return {
2248                    min: min,
2249                    pixels: pixels,
2250                    start: start,
2251                    end: end,
2252                    stackCount: stackCount,
2253                    scale: scale
2254                };
2255            },
2256
2257            /**
2258             * Note: pixel values are not clamped to the scale area.
2259             * @private
2260             */
2261            calculateBarValuePixels: function(datasetIndex, index) {
2262                var me = this;
2263                var chart = me.chart;
2264                var meta = me.getMeta();
2265                var scale = me.getValueScale();
2266                var datasets = chart.data.datasets;
2267                var value = scale.getRightValue(datasets[datasetIndex].data[index]);
2268                var stacked = scale.options.stacked;
2269                var stack = meta.stack;
2270                var start = 0;
2271                var i, imeta, ivalue, base, head, size;
2272
2273                if (stacked || (stacked === undefined && stack !== undefined)) {
2274                    for (i = 0; i < datasetIndex; ++i) {
2275                        imeta = chart.getDatasetMeta(i);
2276
2277                        if (imeta.bar &&
2278                            imeta.stack === stack &&
2279                            imeta.controller.getValueScaleId() === scale.id &&
2280                            chart.isDatasetVisible(i)) {
2281
2282                            ivalue = scale.getRightValue(datasets[i].data[index]);
2283                            if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
2284                                start += ivalue;
2285                            }
2286                        }
2287                    }
2288                }
2289
2290                base = scale.getPixelForValue(start);
2291                head = scale.getPixelForValue(start + value);
2292                size = (head - base) / 2;
2293
2294                return {
2295                    size: size,
2296                    base: base,
2297                    head: head,
2298                    center: head + size / 2
2299                };
2300            },
2301
2302            /**
2303             * @private
2304             */
2305            calculateBarIndexPixels: function(datasetIndex, index, ruler) {
2306                var me = this;
2307                var options = ruler.scale.options;
2308                var range = options.barThickness === 'flex'
2309                    ? computeFlexCategoryTraits(index, ruler, options)
2310                    : computeFitCategoryTraits(index, ruler, options);
2311
2312                var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);
2313                var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
2314                var size = Math.min(
2315                    helpers.valueOrDefault(options.maxBarThickness, Infinity),
2316                    range.chunk * range.ratio);
2317
2318                return {
2319                    base: center - size / 2,
2320                    head: center + size / 2,
2321                    center: center,
2322                    size: size
2323                };
2324            },
2325
2326            draw: function() {
2327                var me = this;
2328                var chart = me.chart;
2329                var scale = me.getValueScale();
2330                var rects = me.getMeta().data;
2331                var dataset = me.getDataset();
2332                var ilen = rects.length;
2333                var i = 0;
2334
2335                helpers.canvas.clipArea(chart.ctx, chart.chartArea);
2336
2337                for (; i < ilen; ++i) {
2338                    if (!isNaN(scale.getRightValue(dataset.data[i]))) {
2339                        rects[i].draw();
2340                    }
2341                }
2342
2343                helpers.canvas.unclipArea(chart.ctx);
2344            },
2345        });
2346
2347        Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
2348            /**
2349             * @private
2350             */
2351            getValueScaleId: function() {
2352                return this.getMeta().xAxisID;
2353            },
2354
2355            /**
2356             * @private
2357             */
2358            getIndexScaleId: function() {
2359                return this.getMeta().yAxisID;
2360            }
2361        });
2362    };
2363
2364},{"26":26,"41":41,"46":46}],16:[function(require,module,exports){
2365    'use strict';
2366
2367    var defaults = require(26);
2368    var elements = require(41);
2369    var helpers = require(46);
2370
2371    defaults._set('bubble', {
2372        hover: {
vendor: 5,109 bytes, lines 2373-2508
2373            mode: 'single'
2374        },
2375
2376        scales: {
2377            xAxes: [{
2378                type: 'linear', // bubble should probably use a linear scale by default
2379                position: 'bottom',
2380                id: 'x-axis-0' // need an ID so datasets can reference the scale
2381            }],
2382            yAxes: [{
2383                type: 'linear',
2384                position: 'left',
2385                id: 'y-axis-0'
2386            }]
2387        },
2388
2389        tooltips: {
2390            callbacks: {
2391                title: function() {
2392                    // Title doesn't make sense for scatter since we format the data as a point
2393                    return '';
2394                },
2395                label: function(item, data) {
2396                    var datasetLabel = data.datasets[item.datasetIndex].label || '';
2397                    var dataPoint = data.datasets[item.datasetIndex].data[item.index];
2398                    return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';
2399                }
2400            }
2401        }
2402    });
2403
2404
2405    module.exports = function(Chart) {
2406
2407        Chart.controllers.bubble = Chart.DatasetController.extend({
2408            /**
2409             * @protected
2410             */
2411            dataElementType: elements.Point,
2412
2413            /**
2414             * @protected
2415             */
2416            update: function(reset) {
2417                var me = this;
2418                var meta = me.getMeta();
2419                var points = meta.data;
2420
2421                // Update Points
2422                helpers.each(points, function(point, index) {
2423                    me.updateElement(point, index, reset);
2424                });
2425            },
2426
2427            /**
2428             * @protected
2429             */
2430            updateElement: function(point, index, reset) {
2431                var me = this;
2432                var meta = me.getMeta();
2433                var custom = point.custom || {};
2434                var xScale = me.getScaleForId(meta.xAxisID);
2435                var yScale = me.getScaleForId(meta.yAxisID);
2436                var options = me._resolveElementOptions(point, index);
2437                var data = me.getDataset().data[index];
2438                var dsIndex = me.index;
2439
2440                var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
2441                var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);
2442
2443                point._xScale = xScale;
2444                point._yScale = yScale;
2445                point._options = options;
2446                point._datasetIndex = dsIndex;
2447                point._index = index;
2448                point._model = {
2449                    backgroundColor: options.backgroundColor,
2450                    borderColor: options.borderColor,
2451                    borderWidth: options.borderWidth,
2452                    hitRadius: options.hitRadius,
2453                    pointStyle: options.pointStyle,
2454                    rotation: options.rotation,
2455                    radius: reset ? 0 : options.radius,
2456                    skip: custom.skip || isNaN(x) || isNaN(y),
2457                    x: x,
2458                    y: y,
2459                };
2460
2461                point.pivot();
2462            },
2463
2464            /**
2465             * @protected
2466             */
2467            setHoverStyle: function(point) {
2468                var model = point._model;
2469                var options = point._options;
2470                point.$previousStyle = {
2471                    backgroundColor: model.backgroundColor,
2472                    borderColor: model.borderColor,
2473                    borderWidth: model.borderWidth,
2474                    radius: model.radius
2475                };
2476                model.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor));
2477                model.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor));
2478                model.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth);
2479                model.radius = options.radius + options.hoverRadius;
2480            },
2481
2482            /**
2483             * @private
2484             */
2485            _resolveElementOptions: function(point, index) {
2486                var me = this;
2487                var chart = me.chart;
2488                var datasets = chart.data.datasets;
2489                var dataset = datasets[me.index];
2490                var custom = point.custom || {};
2491                var options = chart.options.elements.point;
2492                var resolve = helpers.options.resolve;
2493                var data = dataset.data[index];
2494                var values = {};
2495                var i, ilen, key;
2496
2497                // Scriptable options
2498                var context = {
2499                    chart: chart,
2500                    dataIndex: index,
2501                    dataset: dataset,
2502                    datasetIndex: me.index
2503                };
2504
2505                var keys = [
2506                    'backgroundColor',
2507                    'borderColor',
2508                    'borderWidth',
2509                    'hoverBackgroundColor',
2510                    'hoverBorderColor',
2511                    'hoverBorderWidth',
2512                    'hoverRadius',
2513                    'hitRadius',
2514                    'pointStyle',
2515                    'rotation'
2516                ];
2517
2518                for (i = 0, ilen = keys.length; i < ilen; ++i) {
2519                    key = keys[i];
2520                    values[key] = resolve([
2521                        custom[key],
2522                        dataset[key],
2523                        options[key]
2524                    ], context, index);
2525                }
2526
2527                // Custom radius resolution
2528                values.radius = resolve([
2529                    custom.radius,
2530                    data ? data.r : undefined,
2531                    dataset.radius,
2532                    options.radius
2533                ], context, index);
2534                return values;
2535            }
2536        });
2537    };
2538
2539},{"26":26,"41":41,"46":46}],17:[function(require,module,exports){
2540    'use strict';
2541
2542    var defaults = require(26);
2543    var elements = require(41);
2544    var helpers = require(46);
2545
2546    defaults._set('doughnut', {
2547        animation: {
2548            // Boolean - Whether we animate the rotation of the Doughnut
2549            animateRotate: true,
2550            // Boolean - Whether we animate scaling the Doughnut from the centre
2551            animateScale: false
2552        },
2553        hover: {
2554            mode: 'single'
2555        },
2556        legendCallback: function(chart) {
2557            var text = [];
2558            text.push('<ul class="' + chart.id + '-legend">');
2559
2560            var data = chart.data;
2561            var datasets = data.datasets;
2562            var labels = data.labels;
2563
2564            if (datasets.length) {
2565                for (var i = 0; i < datasets[0].data.length; ++i) {
2566                    text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
2567                    if (labels[i]) {
2568                        text.push(labels[i]);
2569                    }
2570                    text.push('</li>');
2571                }
2572            }
2573
2574            text.push('</ul>');
2575            return text.join('');
2576        },
2577        legend: {
2578            labels: {
2579                generateLabels: function(chart) {
2580                    var data = chart.data;
2581                    if (data.labels.length && data.datasets.length) {
2582                        return data.labels.map(function(label, i) {
2583                            var meta = chart.getDatasetMeta(0);
2584                            var ds = data.datasets[0];
2585                            var arc = meta.data[i];
2586                            var custom = arc && arc.custom || {};
2587                            var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
2588                            var arcOpts = chart.options.elements.arc;
2589                            var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
2590                            var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
2591                            var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
2592
2593                            return {
2594                                text: label,
2595                                fillStyle: fill,
2596                                strokeStyle: stroke,
2597                                lineWidth: bw,
2598                                hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
2599
2600                                // Extra data used for toggling the correct item
2601                                index: i
2602                            };
2603                        });
2604                    }
2605                    return [];
2606                }
2607            },
2608
2609            onClick: function(e, legendItem) {
2610                var index = legendItem.index;
2611                var chart = this.chart;
2612                var i, ilen, meta;
2613
2614                for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
2615                    meta = chart.getDatasetMeta(i);
2616                    // toggle visibility of index if exists
2617                    if (meta.data[index]) {
2618                        meta.data[index].hidden = !meta.data[index].hidden;
2619                    }
2620                }
2621
2622                chart.update();
2623            }
2624        },
2625
2626        // The percentage of the chart that we cut out of the middle.
2627        cutoutPercentage: 50,
2628
2629        // The rotation of the chart, where the first data arc begins.
2630        rotation: Math.PI * -0.5,
2631
2632        // The total circumference of the chart.
2633        circumference: Math.PI * 2.0,
2634
2635        // Need to override these to give a nice default
2636        tooltips: {
2637            callbacks: {
2638                title: function() {
2639                    return '';
2640                },
2641                label: function(tooltipItem, data) {
2642                    var dataLabel = data.labels[tooltipItem.index];
2643                    var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
2644
2645                    if (helpers.isArray(dataLabel)) {
2646                        // show value on first line of multiline label
2647                        // need to clone because we are changing the value
2648                        dataLabel = dataLabel.slice();
2649                        dataLabel[0] += value;
2650                    } else {
2651                        dataLabel += value;
2652                    }
2653
2654                    return dataLabel;
2655                }
2656            }
2657        }
2658    });
2659
2660    defaults._set('pie', helpers.clone(defaults.doughnut));
2661    defaults._set('pie', {
2662        cutoutPercentage: 0
2663    });
2664
2665    module.exports = function(Chart) {
2666
2667        Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
2668
2669            dataElementType: elements.Arc,
2670
2671            linkScales: helpers.noop,
2672
2673            // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
2674            getRingIndex: function(datasetIndex) {
2675                var ringIndex = 0;
2676
2677                for (var j = 0; j < datasetIndex; ++j) {
2678                    if (this.chart.isDatasetVisible(j)) {
2679                        ++ringIndex;
2680                    }
2681                }
2682
2683                return ringIndex;
2684            },
2685
2686            update: function(reset) {
2687                var me = this;
2688                var chart = me.chart;
2689                var chartArea = chart.chartArea;
2690                var opts = chart.options;
2691                var arcOpts = opts.elements.arc;
2692                var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth;
2693                var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;
2694                var minSize = Math.min(availableWidth, availableHeight);
2695                var offset = {x: 0, y: 0};
2696                var meta = me.getMeta();
2697                var cutoutPercentage = opts.cutoutPercentage;
2698                var circumference = opts.circumference;
2699
2700                // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
2701                if (circumference < Math.PI * 2.0) {
2702                    var startAngle = opts.rotation % (Math.PI * 2.0);
2703                    startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
2704                    var endAngle = startAngle + circumference;
2705                    var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
2706                    var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
2707                    var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
2708                    var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
2709                    var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
2710                    var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
2711                    var cutout = cutoutPercentage / 100.0;
2712                    var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
2713                    var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
2714                    var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
2715                    minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
2716                    offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
2717                }
2718
2719                chart.borderWidth = me.getMaxBorderWidth(meta.data);
2720                chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
2721                chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
2722                chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
2723                chart.offsetX = offset.x * chart.outerRadius;
2724                chart.offsetY = offset.y * chart.outerRadius;
2725
2726                meta.total = me.calculateTotal();
2727
2728                me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
2729                me.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);
2730
2731                helpers.each(meta.data, function(arc, index) {
2732                    me.updateElement(arc, index, reset);
2733                });
2734            },
2735
2736            updateElement: function(arc, index, reset) {
2737                var me = this;
2738                var chart = me.chart;
2739                var chartArea = chart.chartArea;
2740                var opts = chart.options;
2741                var animationOpts = opts.animation;
2742                var centerX = (chartArea.left + chartArea.right) / 2;
2743                var centerY = (chartArea.top + chartArea.bottom) / 2;
2744                var startAngle = opts.rotation; // non reset case handled later
2745                var endAngle = opts.rotation; // non reset case handled later
2746                var dataset = me.getDataset();
2747                var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
2748                var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
2749                var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
2750                var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
2751
2752                helpers.extend(arc, {
2753                    // Utility
2754                    _datasetIndex: me.index,
2755                    _index: index,
2756
2757                    // Desired view properties
2758                    _model: {
2759                        x: centerX + chart.offsetX,
2760                        y: centerY + chart.offsetY,
2761                        startAngle: startAngle,
2762                        endAngle: endAngle,
2763                        circumference: circumference,
2764                        outerRadius: outerRadius,
2765                        innerRadius: innerRadius,
2766                        label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
2767                    }
2768                });
2769
2770                var model = arc._model;
2771
2772                // Resets the visual styles
2773                var custom = arc.custom || {};
2774                var valueOrDefault = helpers.valueAtIndexOrDefault;
2775                var elementOpts = this.chart.options.elements.arc;
2776                model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
2777                model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
2778                model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
2779
2780                // Set correct angles if not resetting
2781                if (!reset || !animationOpts.animateRotate) {
2782                    if (index === 0) {
2783                        model.startAngle = opts.rotation;
2784                    } else {
2785                        model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
2786                    }
2787
2788                    model.endAngle = model.startAngle + model.circumference;
2789                }
2790
2791                arc.pivot();
2792            },
2793
2794            calculateTotal: function() {
2795                var dataset = this.getDataset();
2796                var meta = this.getMeta();
2797                var total = 0;
2798                var value;
2799
2800                helpers.each(meta.data, function(element, index) {
2801                    value = dataset.data[index];
2802                    if (!isNaN(value) && !element.hidden) {
2803                        total += Math.abs(value);
2804                    }
2805                });
2806
2807                /* if (total === 0) {
2808				total = NaN;
2809			}*/
2810
2811                return total;
2812            },
2813
2814            calculateCircumference: function(value) {
2815                var total = this.getMeta().total;
2816                if (total > 0 && !isNaN(value)) {
2817                    return (Math.PI * 2.0) * (Math.abs(value) / total);
2818                }
2819                return 0;
2820            },
2821
2822            // gets the max border or hover width to properly scale pie charts
2823            getMaxBorderWidth: function(arcs) {
2824                var max = 0;
2825                var index = this.index;
2826                var length = arcs.length;
2827                var borderWidth;
2828                var hoverWidth;
2829
2830                for (var i = 0; i < length; i++) {
2831                    borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0;
2832                    hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
2833
2834                    max = borderWidth > max ? borderWidth : max;
2835                    max = hoverWidth > max ? hoverWidth : max;
2836                }
2837                return max;
2838            }
2839        });
2840    };
2841
2842},{"26":26,"41":41,"46":46}],18:[function(require,module,exports){
2843    'use strict';
2844
2845    var defaults = require(26);
2846    var elements = require(41);
2847    var helpers = require(46);
2848
2849    defaults._set('line', {
2850        showLines: true,
2851        spanGaps: false,
2852
2853        hover: {
2854            mode: 'label'
2855        },
2856
2857        scales: {
2858            xAxes: [{
2859                type: 'category',
2860                id: 'x-axis-0'
2861            }],
2862            yAxes: [{
2863                type: 'linear',
2864                id: 'y-axis-0'
2865            }]
2866        }
2867    });
2868
2869    module.exports = function(Chart) {
2870
2871        function lineEnabled(dataset, options) {
2872            return helpers.valueOrDefault(dataset.showLine, options.showLines);
2873        }
2874
2875        Chart.controllers.line = Chart.DatasetController.extend({
2876
2877            datasetElementType: elements.Line,
2878
2879            dataElementType: elements.Point,
2880
2881            update: function(reset) {
2882                var me = this;
2883                var meta = me.getMeta();
2884                var line = meta.dataset;
2885                var points = meta.data || [];
2886                var options = me.chart.options;
2887                var lineElementOptions = options.elements.line;
2888                var scale = me.getScaleForId(meta.yAxisID);
2889                var i, ilen, custom;
2890                var dataset = me.getDataset();
2891                var showLine = lineEnabled(dataset, options);
2892
2893                // Update Line
2894                if (showLine) {
2895                    custom = line.custom || {};
2896
2897                    // Compatibility: If the properties are defined with only the old name, use those values
2898                    if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
2899                        dataset.lineTension = dataset.tension;
2900                    }
2901
2902                    // Utility
2903                    line._scale = scale;
2904                    line._datasetIndex = me.index;
2905                    // Data
2906                    line._children = points;
2907                    // Model
2908                    line._model = {
2909                        // Appearance
2910                        // The default behavior of lines is to break at null values, according
2911                        // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
2912                        // This option gives lines the ability to span gaps
2913                        spanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,
2914                        tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
2915                        backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
2916                        borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
2917                        borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
2918                        borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
2919                        borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
2920                        borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
2921                        borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
2922                        fill: custom.fill ? custom.fill : (dataset.f
2922ill !== undefined ? dataset.fill : lineElementOptions.fill),
2923                        steppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped),
2924                        cubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),
2925                    };
2926
2927                    line.pivot();
2928                }
2929
2930                // Update Points
2931                for (i = 0, ilen = points.length; i < ilen; ++i) {
2932                    me.updateElement(points[i], i, reset);
2933                }
2934
2935                if (showLine && line._model.tension !== 0) {
2936                    me.updateBezierControlPoints();
2937                }
2938
2939                // Now pivot the point for animation
2940                for (i = 0, ilen = points.length; i < ilen; ++i) {
2941                    points[i].pivot();
2942                }
2943            },
2944
2945            getPointBackgroundColor: function(point, index) {
2946                var backgroundColor = this.chart.options.elements.point.backgroundColor;
2947                var dataset = this.getDataset();
2948                var custom = point.custom || {};
2949
2950                if (custom.backgroundColor) {
2951                    backgroundColor = custom.backgroundColor;
2952                }
2952 else if (dataset.pointBackgroundColor) {
2953                    backgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);
2954                } else if (dataset.backgroundColor) {
2955                    backgroundColor = dataset.backgroundColor;
2956                }
2957
2958                return backgroundColor;
2959            },
2960
2961            getPointBorderColor: function(point, index) {
2962                var borderColor = this.chart.options.elements.point.borderColor;
2963                var dataset = this.getDataset();
2964                var custom = point.custom || {};
2965
2966                if (custom.borderColor) {
2967                    borderColor = custom.borderColor;
2968                } else if (dataset.pointBorderColor) {
2969                    borderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);
2970                } else if (dataset.borderColor) {
2971                    borderColor = dataset.borderColor;
2972                }
2973
2974                return borderColor;
2975            },
2976
2977            getPointBorderWidth: function(point, index) {
2978                var borderWidth = this.chart.options.elements.point.borderWidth;
2979                var dataset = this.getDataset();
2980                var custom = point.custom || {};
2981
2982                if (!isNaN(custom.borderWidth)) {
2983                    borderWidth = custom.borderWidth;
2984                } else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) {
2985                    borderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);
2986                } else if (!isNaN(dataset.borderWidth)) {
2987                    borderWidth = dataset.borderWidth;
2988                }
2989
2990                return borderWidth;
2991            },
2992
2993            getPointRotation: function(point, index) {
2994                var pointRotation = this.chart.options.elements.point.rotation;
2995                var dataset = this.getDataset();
2996                var custom = point.custom || {};
2997
2998                if (!isNaN(custom.rotation)) {
2999                    pointRotation = custom.rotation;
3000                } else if (!isNaN(dataset.pointRotation) || helpers.isArray(dataset.pointRotation)) {
3001                    pointRotation = helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointRotation);
3002                }
3003                return pointRotation;
3004            },
3005
3006            updateElement: function(point, index, reset) {
3007                var me = this;
3008                var meta = me.getMeta();
3009                var custom = point.custom || {};
3010                var dataset = me.getDataset();
3011                var datasetIndex = me.index;
3012                var value = dataset.data[index];
3013                var yScale = me.getScaleForId(meta.yAxisID);
3014                var xScale = me.getScaleForId(meta.xAxisID);
3015                var pointOptions = me.chart.options.elements.point;
3016                var x, y;
3017
3018                // Compatibility: If the properties are defined with only the old name, use those values
3019                if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
3020                    dataset.pointRadius = dataset.radius;
3021                }
3022                if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
3023                    dataset.pointHitRadius = dataset.hitRadius;
3024                }
3025
3026                x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
3027                y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
3028
3029                // Utility
3030                point._xScale = xScale;
3031                point._yScale = yScale;
3032                point._datasetIndex = datasetIndex;
3033                point._index = index;
3034
3035                // Desired view properties
3036                point._model = {
3037                    x: x,
3038                    y: y,
3039                    skip: custom.skip || isNaN(x) || isNaN(y),
3040                    // Appearance
3041                    radius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),
3042                    pointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),
3043                    rotation: me.getPointRotation(point, index),
3044                    backgroundColor: me.getPointBackgroundColor(point, index),
3045                    borderColor: me.getPointBorderColor(point, index),
3046                    borderWidth: me.getPointBorderWidth(point, index),
3047                    tension: meta.dataset._model ? meta.dataset._model.tension : 0,
3048                    steppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,
3049                    // Tooltip
3050                    hitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)
3051                };
3052            },
3053
3054            calculatePointY: function(value, index, datasetIndex) {
3055                var me = this;
3056                var chart = me.chart;
3057                var meta = me.getMeta();
3058                var yScale = me.getScaleForId(meta.yAxisID);
3059                var sumPos = 0;
3060                var sumNeg = 0;
3061                var i, ds, dsMeta;
3062
3063                if (yScale.options.stacked) {
3064                    for (i = 0; i < datasetIndex; i++) {
3065                        ds = chart.data.datasets[i];
3066                        dsMeta = chart.getDatasetMeta(i);
3067                        if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
3068                            var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
3069                            if (stackedRightValue < 0) {
3070                                sumNeg += stackedRightValue || 0;
3071                            } else {
3072                                sumPos += stackedRightValue || 0;
3073                            }
3074                        }
3075                    }
3076
3077                    var rightValue = Number(yScale.getRightValue(value));
3078                    if (rightValue < 0) {
3079                        return yScale.getPixelForValue(sumNeg + rightValue);
3080                    }
3081                    return yScale.getPixelForValue(sumPos + rightValue);
3082                }
3083
3084                return yScale.getPixelForValue(value);
3085            },
3086
3087            updateBezierControlPoints: function() {
3088                var me = this;
3089                var meta = me.getMeta();
3090                var area = me.chart.chartArea;
3091                var points = (meta.data || []);
3092                var i, ilen, point, model, controlPoints;
3093
3094                // Only consider points that are drawn in case the spanGaps option is used
3095                if (meta.dataset._model.spanGaps) {
3096                    points = points.filter(function(pt) {
3097                        return !pt._model.skip;
3098                    });
3099                }
3100
3101                function capControlPoint(pt, min, max) {
3102                    return Math.max(Math.min(pt, max), min);
3103                }
3104
3105                if (meta.dataset._model.cubicInterpolationMode === 'monotone') {
3106                    helpers.splineCurveMonotone(points);
3107                } else {
3108                    for (i = 0, ilen = points.length; i < ilen; ++i) {
3109                        point = points[i];
3110                        model = point._model;
3111                        controlPoints = helpers.splineCurve(
3112                            helpers.previousItem(points, i)._model,
3113                            model,
3114                            helpers.nextItem(points, i)._model,
3115                            meta.dataset._model.tension
3116                        );
3117                        model.controlPointPreviousX = controlPoints.previous.x;
3118                        model.controlPointPreviousY = controlPoints.previous.y;
3119                        model.controlPointNextX = controlPoints.next.x;
3120                        model.controlPointNextY = controlPoints.next.y;
3121                    }
3122                }
3123
3124                if (me.chart.options.elements.line.capBezierPoints) {
3125                    for (i = 0, ilen = points.length; i < ilen; ++i) {
3126                        model = points[i]._model;
3127                        model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
3128                        model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
3129                        model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
3130                        model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
3131                    }
3132                }
3133            },
3134
3135            draw: function() {
3136                var me = this;
3137                var chart = me.chart;
3138                var meta = me.getMeta();
3139                var points = meta.data || [];
3140                var area = chart.chartArea;
3141                var ilen = points.length;
3142                var halfBorderWidth;
3143                var i = 0;
3144
3145                if (lineEnabled(me.getDataset(), chart.options)) {
3146                    halfBorderWidth = (meta.dataset._model.borderWidth || 0) / 2;
3147
3148                    helpers.canvas.clipArea(chart.ctx, {
3149                        left: area.left,
3150                        right: area.right,
3151                        top: area.top - halfBorderWidth,
3152                        bottom: area.bottom + halfBorderWidth
3153                    });
3154
3155                    meta.dataset.draw();
3156
3157                    helpers.canvas.unclipArea(chart.ctx);
3158                }
3159
3160                // Draw the points
3161                for (; i < ilen; ++i) {
3162                    points[i].draw(area);
3163                }
3164            },
3165
3166            setHoverStyle: function(element) {
3167                // Point
3168                var dataset = this.chart.data.datasets[element._datasetIndex];
3169                var index = element._index;
3170                var custom = element.custom || {};
3171                var model = element._model;
3172
3173                element.$previousStyle = {
3174                    backgroundColor: model.backgroundColor,
3175                    borderColor: model.borderColor,
3176                    borderWidth: model.borderWidth,
3177                    radius: model.radius
3178                };
3179
3180                model.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
3181                model.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
3182                model.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
3183                model.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
3184            },
3185        });
3186    };
3187
3188},{"26":26,"41":41,"46":46}],19:[function(require,module,exports){
3189    'use strict';
3190
3191    var defaults = require(26);
3192    var elements = require(41);
3193    var helpers = require(46);
3194
3195    defaults._set('polarArea', {
3196        scale: {
3197            type: 'radialLinear',
3198            angleLines: {
3199                display: false
3200            },
3201            gridLines: {
3202                circular: true
3203            },
3204            pointLabels: {
3205                display: false
3206            },
3207            ticks: {
3208                beginAtZero: true
3209            }
3210        },
3211
3212        // Boolean - Whether to animate the rotation of the chart
3213        animation: {
3214            animateRotate: true,
3215            animateScale: true
3216        },
3217
3218        startAngle: -0.5 * Math.PI,
3219        legendCallback: function(chart) {
3220            var text = [];
3221            text.push('<ul class="' + chart.id + '-legend">');
3222
3223            var data = chart.data;
3224            var datasets = data.datasets;
3225            var labels = data.labels;
3226
3227            if (datasets.length) {
3228                for (var i = 0; i < datasets[0].data.length; ++i) {
3229                    text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
3230                    if (labels[i]) {
3231                        text.push(labels[i]);
3232                    }
3233                    text.push('</li>');
3234                }
3235            }
3236
3237            text.push('</ul>');
3238            return text.join('');
3239        },
3240        legend: {
3241            labels: {
3242                generateLabels: function(chart) {
3243                    var data = chart.data;
3244                    if (data.labels.length && data.datasets.length) {
3245                        return data.labels.map(function(label, i) {
3246                            var meta = chart.getDatasetMeta(0);
3247                            var ds = data.datasets[0];
3248                            var arc = meta.data[i];
3249                            var custom = arc.custom || {};
3250                            var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
3251                            var arcOpts = chart.options.elements.arc;
3252                            var fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
3253                            var stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
3254                            var bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
3255
3256                            return {
3257                                text: label,
3258                                fillStyle: fill,
3259                                strokeStyle: stroke,
3260                                lineWidth: bw,
3261                                hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
3262
3263                                // Extra data used for toggling the correct item
3264                                index: i
3265                            };
3266                        });
3267                    }
3268                    return [];
3269                }
3270            },
3271
3272            onClick: function(e, legendItem) {
3273                var index = legendItem.index;
3274                var chart = this.chart;
3275                var i, ilen, meta;
3276
3277                for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
3278                    meta = chart.getDatasetMeta(i);
3279                    meta.data[index].hidden = !meta.data[index].hidden;
3280                }
3281
3282                chart.update();
3283            }
3284        },
3285
3286        // Need to override these to give a nice default
3287        tooltips: {
3288            callbacks: {
3289                title: function() {
3290                    return '';
3291                },
3292                label: function(item, data) {
3293                    return data.labels[item.index] + ': ' + item.yLabel;
3294                }
3295            }
3296        }
3297    });
3298
3299    module.exports = function(Chart) {
3300
3301        Chart.controllers.polarArea = Chart.DatasetController.extend({
3302
3303            dataElementType: elements.Arc,
3304
3305            linkScales: helpers.noop,
3306
3307            update: function(reset) {
3308                var me = this;
3309                var dataset = me.getDataset();
3310                var meta = me.getMeta();
3311                var start = me.chart.options.startAngle || 0;
3312                var starts = me._starts = [];
3313                var angles = me._angles = [];
3314                var i, ilen, angle;
3315
3316                me._updateRadius();
3317
3318                meta.count = me.countVisibleElements();
3319
3320                for (i = 0, ilen = dataset.data.length; i < ilen; i++) {
3321                    starts[i] = start;
3322                    angle = me._computeAngle(i);
3323                    angles[i] = angle;
3324                    start += angle;
3325                }
3326
3327                helpers.each(meta.data, function(arc, index) {
3328                    me.updateElement(arc, index, reset);
3329                });
3330            },
3331
3332            /**
3333             * @private
3334             */
3335            _updateRadius: function() {
3336                var me = this;
3337                var chart = me.chart;
3338                var chartArea = chart.chartArea;
3339                var opts = chart.options;
3340                var arcOpts = opts.elements.arc;
3341                var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
3342
3343                chart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);
3344                chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
3345                chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
3346
3347                me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
3348                me.innerRadius = me.outerRadius - chart.radiusLength;
3349            },
3350
3351            updateElement: function(arc, index, reset) {
3352                var me = this;
3353                var chart = me.chart;
3354                var dataset = me.getDataset();
3355                var opts = chart.options;
3356                var animationOpts = opts.animation;
3357                var scale = chart.scale;
3358                var labels = chart.data.labels;
3359
3360                var centerX = scale.xCenter;
3361                var centerY = scale.yCenter;
3362
3363                // var negHalfPI = -0.5 * Math.PI;
3364                var datasetStartAngle = opts.startAngle;
3365                var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
3366                var startAngle = me._starts[index];
3367                var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]);
3368
3369                var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
3370
3371                helpers.extend(arc, {
3372                    // Utility
3373                    _datasetIndex: me.index,
3374                    _index: index,
3375                    _scale: scale,
3376
3377                    // Desired view properties
3378                    _model: {
3379                        x: centerX,
3380                        y: centerY,
3381                        innerRadius: 0,
3382                        outerRadius: reset ? resetRadius : distance,
3383                        startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
3384                        endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
3385                        label: helpers.valueAtIndexOrDefault(labels, index, labels[index])
3386                    }
3387                });
3388
3389                // Apply border and fill style
3390                var elementOpts = this.chart.options.elements.arc;
3391                var custom = arc.custom || {};
3392                var valueOrDefault = helpers.valueAtIndexOrDefault;
3393                var model = arc._model;
3394
3395                model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
3396                model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
3397                model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
3398
3399                arc.pivot();
3400            },
3401
3402            countVisibleElements: function() {
3403                var dataset = this.getDataset();
3404                var meta = this.getMeta();
3405                var count = 0;
3406
3407                helpers.each(meta.data, function(element, index) {
3408                    if (!isNaN(dataset.data[index]) && !element.hidden) {
3409                        count++;
3410                    }
3411                });
3412
3413                return count;
3414            },
3415
3416            /**
3417             * @private
3418             */
3419            _computeAngle: function(index) {
3420                var me = this;
3421                var count = this.getMeta().count;
3422                var dataset = me.getDataset();
3423                var meta = me.getMeta();
3424
3425                if (isNaN(dataset.data[index]) || meta.data[index].hidden) {
3426                    return 0;
3427                }
3428
3429                // Scriptable options
3430                var context = {
3431                    chart: me.chart,
3432                    dataIndex: index,
3433                    dataset: dataset,
3434                    datasetIndex: me.index
3435                };
3436
3437                return helpers.options.resolve([
3438                    me.chart.options.elements.arc.angle,
3439                    (2 * Math.PI) / count
3440                ], context, index);
3441            }
3442        });
3443    };
3444
3445},{"26":26,"41":41,"46":46}],20:[function(require,module,exports){
3446    'use strict';
3447
3448    var defaults = require(26);
3449    var elements = require(41);
3450    var helpers = require(46);
3451
3452    defaults._set('radar', {
3453        scale: {
3454            type: 'radialLinear'
3455        },
3456        elements: {
3457            line: {
3458                tension: 0 // no bezier in radar
3459            }
3460        }
3461    });
3462
3463    module.exports = function(Chart) {
3464
3465        Chart.controllers.radar = Chart.DatasetController.extend({
3466
3467            datasetElementType: elements.Line,
3468
3469            dataElementType: elements.Point,
3470
3471            linkScales: helpers.noop,
3472
3473            update: function(reset) {
3474                var me = this;
3475                var meta = me.getMeta();
3476                var line = meta.dataset;
3477                var points = meta.data;
3478                var custom = line.custom || {};
3479                var dataset = me.getDataset();
3480                var lineElementOptions = me.chart.options.elements.line;
3481                var scale = me.chart.scale;
3482
3483                // Compatibility: If the properties are defined with only the old name, use those values
3484                if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
3485                    dataset.lineTension = dataset.tension;
3486                }
3487
3488                helpers.extend(meta.dataset, {
3489                    // Utility
3490                    _datasetIndex: me.index,
3491                    _scale: scale,
3492                    // Data
3493                    _children: points,
3494                    _loop: true,
3495                    // Model
3496                    _model: {
3497                        // Appearance
3498                        tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),
3499                        backgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),
3500                        borderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),
3501                        borderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),
3502                        fill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),
3503                        borderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),
3504                        borderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),
3505                        borderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),
3506                        borderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),
3507                    }
3508                });
3509
3510                meta.dataset.pivot();
3511
3512                // Update Points
3513                helpers.each(points, function(point, index) {
3514                    me.updateElement(point, index, reset);
3515                }, me);
3516
3517                // Update bezier control points
3518                me.updateBezierControlPoints();
3519            },
3520            updateElement: function(point, index, reset) {
3521                var me = this;
3522                var custom = point.custom || {};
3523                var dataset = me.getDataset();
3524                var scale = me.chart.scale;
3525                var pointElementOptions = me.chart.options.elements.point;
3526                var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
3527
3528                // Compatibility: If the properties are defined with only the old name, use those values
3529                if ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {
3530                    dataset.pointRadius = dataset.radius;
3531                }
3532                if ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {
3533                    dataset.pointHitRadius = dataset.hitRadius;
3534                }
3535
3536                helpers.extend(point, {
3537                    // Utility
3538                    _datasetIndex: me.index,
3539                    _index: index,
3540                    _scale: scale,
3541
3542                    // Desired view properties
3543                    _model: {
3544                        x: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales
3545                        y: reset ? scale.yCenter : pointPosition.y,
3546
3547                        // Appearance
3548                        tension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension),
3549                        radius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),
3550                        backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroun
3550dColor, index, pointElementOptions.backgroundColor),
3551                        borderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),
3552                        borderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),
3553                        pointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),
3554                        rotation: custom.rotation ? custom.rotation : helpers.valueAtIndexOrDefault(dataset.pointRotation, index, pointElementOptions.rotation),
3555
3556                        // Tooltip
3557                        hitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius)
3558                    }
3559                });
3560
3561                point._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));
3562            },
3563            updateBezierControlPoints: function() {
3564                var chartArea = this.chart.chartArea;
3565                var meta = this.getMeta();
3566
3567                helpers.each(meta.data, function(point, index) {
3568                    var model = point._model;
3569                    var controlPoints = helpers.splineCurve(
3570                        helpers.previousItem(meta.data, index, true)._model,
3571                        model,
3572                        helpers.nextItem(meta.data, index, true)._model,
3573                        model.tension
3574                    );
3575
3576                    // Prevent the bezier going outside of the bounds of the graph
3577                    model.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left);
3578                    model.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top);
3579
3580                    model.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left);
3581                    model.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top);
3582
3583                    // Now pivot the point for animation
3584                    point.pivot();
3585                });
3586            },
3587
3588            setHoverStyle: function(point) {
3589                // Point
3590                var dataset = this.chart.data.datasets[point._datasetIndex];
3591                var custom = point.custom || {};
3592                var index = point._index;
3593                var model = point._model;
3594
3595                point.$previousStyle = {
3596                    backgroundColor: model.backgroundColor,
3597                    borderColor: model.borderColor,
3598                    borderWidth: model.borderWidth,
3599                    radius: model.radius
3600                };
3601
3602                model.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);
3603                model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
3604                model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));
3605                model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);
3606            },
3607        });
3608    };
3609
3610},{"26":26,"41":41,"46":46}],21:[function(require,module,exports){
3611    'use strict';
3612
3613    var defaults = require(26);
3614
3615    defaults._set('scatter', {
3616        hover: {
3617            mode: 'single'
3618        },
3619
3620        scales: {
3621            xAxes: [{
3622                id: 'x-axis-1',    // need an ID so datasets can reference the scale
3623                type: 'linear',    // scatter should not use a category axis
3624                position: 'bottom'
3625            }],
3626            yAxes: [{
3627                id: 'y-axis-1',
3628                type: 'linear',
3629                position: 'left'
3630            }]
3631        },
3632
3633        showLines: false,
3634
3635        tooltips: {
3636            callbacks: {
3637                title: function() {
3638                    return '';     // doesn't make sense for scatter since data are formatted as a point
3639                },
3640                label: function(item) {
3641                    return '(' + item.xLabel + ', ' + item.yLabel + ')';
3642                }
3643            }
3644        }
3645    });
3646
3647    module.exports = function(Chart) {
3648
3649        // Scatter charts use line controllers
3650        Chart.controllers.scatter = Chart.controllers.line;
3651
3652    };
3653
3654},{"26":26}],22:[function(require,module,exports){
3655    'use strict';
3656
3657    var Element = require(27);
3658
3659    var exports = module.exports = Element.extend({
3660        chart: null, // the animation associated chart instance
3661        currentStep: 0, // the current animation step
3662        numSteps: 60, // default number of steps
3663        easing: '', // the easing to use for this animation
3664        render: null, // render function used by the animation service
3665
3666        onAnimationProgress: null, // user specified callback to fire on each step of the animation
3667        onAnimationComplete: null, // user specified callback to fire when the animation finishes
3668    });
3669
3670// DEPRECATIONS
3671
3672    /**
3673     * Provided for backward compatibility, use Chart.Animation instead
3674     * @prop Chart.Animation#animationObject
3675     * @deprecated since version 2.6.0
3676     * @todo remove at version 3
3677     */
3678    Object.defineProperty(exports.prototype, 'animationObject', {
3679        get: function() {
3680            return this;
3681        }
3682    });
3683
3684    /**
3685     * Provided for backward compatibility, use Chart.Animation#chart instead
3686     * @prop Chart.Animation#chartInstance
3687     * @deprecated since version 2.6.0
3688     * @todo remove at version 3
3689     */
3690    Object.defineProperty(exports.prototype, 'chartInstance', {
3691        get: function() {
3692            return this.chart;
3693        },
3694        set: function(value) {
3695            this.chart = value;
3696        }
3697    });
3698
3699},{"27":27}],23:[function(require,module,exports){
3700    /* global window: false */
3701    'use strict';
3702
3703    var defaults = require(26);
3704    var helpers = require(46);
3705
3706    defaults._set('global', {
3707        animation: {
3708            duration: 1000,
3709            easing: 'easeOutQuart',
3710            onProgress: helpers.noop,
3711            onComplete: helpers.noop
3712        }
3713    });
3714
3715    module.exports = {
3716        frameDuration: 17,
3717        animations: [],
3718        dropFrames: 0,
3719        request: null,
3720
3721        /**
3722         * @param {Chart} chart - The chart to animate.
3723         * @param {Chart.Animation} animation - The animation that we will animate.
3724         * @param {Number} duration - The animation duration in ms.
3725         * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
3726         */
3727        addAnimation: function(chart, animation, duration, lazy) {
3728            var animations = this.animations;
3729            var i, ilen;
3730
3731            animation.chart = chart;
3732
3733            if (!lazy) {
3734                chart.animating = true;
3735            }
3736
3737            for (i = 0, ilen = animations.length; i < ilen; ++i) {
3738                if (animations[i].chart === chart) {
3739                    animations[i] = animation;
3740                    return;
3741                }
3742            }
3743
3744            animations.push(animation);
3745
3746            // If there are no animations queued, manually kickstart a digest, for lack of a better word
3747            if (animations.length === 1) {
3748                this.requestAnimationFrame();
3749            }
3750        },
3751
3752        cancelAnimation: function(chart) {
3753            var index = helpers.findIndex(this.animations, function(animation) {
3754                return animation.chart === chart;
3755            });
3756
3757            if (index !== -1) {
3758                this.animations.splice(index, 1);
3759                chart.animating = false;
3760            }
3761        },
3762
3763        requestAnimationFrame: function() {
3764            var me = this;
3765            if (me.request === null) {
3766                // Skip animation frame requests until the active one is executed.
3767                // This can happen when processing mouse events, e.g. 'mousemove'
3768                // and 'mouseout' events will trigger multiple renders.
3769                me.request = helpers.requestAnimFrame.call(window, function() {
3770                    me.request = null;
3771                    me.startDigest();
3772                });
3773            }
3774        },
3775
3776        /**
3777         * @private
3778         */
3779        startDigest: function() {
3780            var me = this;
3781            var startTime = Date.now();
3782            var framesToDrop = 0;
3783
3784            if (me.dropFrames > 1) {
3785                framesToDrop = Math.floor(me.dropFrames);
3786                me.dropFrames = me.dropFrames % 1;
3787            }
3788
3789            me.advance(1 + framesToDrop);
3790
3791            var endTime = Date.now();
3792
3793            me.dropFrames += (endTime - startTime) / me.frameDuration;
3794
3795            // Do we have more stuff to animate?
3796            if (me.animations.length > 0) {
3797                me.requestAnimationFrame();
3798            }
3799        },
3800
3801        /**
3802         * @private
3803         */
3804        advance: function(count) {
3805            var animations = this.animations;
3806            var animation, chart;
3807            var i = 0;
3808
3809            while (i < animations.length) {
3810                animation = animations[i];
3811                chart = animation.chart;
3812
3813                animation.currentStep = (animation.currentStep || 0) + count;
3814                animation.currentStep = Math.min(animation.currentStep, animation.numSteps);
3815
3816                helpers.callback(animation.render, [chart, animation], chart);
3817                helpers.callback(animation.onAnimationProgress, [animation], chart);
3818
3819                if (animation.currentStep >= animation.numSteps) {
3820                    helpers.callback(animation.onAnimationComplete, [animation], chart);
3821                    chart.animating = false;
3822                    animations.splice(i, 1);
3823                } else {
3824                    ++i;
3825                }
3826            }
3827        }
3828    };
3829
3830},{"26":26,"46":46}],24:[function(require,module,exports){
3831    'use strict';
3832
3833    var Animation = require(22);
3834    var animations = require(23);
3835    var defaults = require(26);
3836    var helpers = require(46);
3837    var Interaction = require(29);
3838    var layouts = require(31);
3839    var platform = require(49);
3840    var plugins = require(32);
3841    var scaleService = require(34);
3842    var Tooltip = require(36);
3843
3844    module.exports = function(Chart) {
3845
3846        // Create a dictionary of chart types, to allow for extension of existing types
3847        Chart.types = {};
3848
3849        // Store a reference to each instance - allowing us to globally resize chart instances on window resize.
3850        // Destroy method on the chart will remove the instance of the chart from this reference.
3851        Chart.instances = {};
3852
3853        // Controllers available for dataset visualization eg. bar, line, slice, etc.
3854        Chart.controllers = {};
3855
3856        /**
3857         * Initializes the given config with global and chart default values.
3858         */
3859        function initConfig(config) {
3860            config = config || {};
3861
3862            // Do NOT use configMerge() for the data object because this method merges arrays
vendor: 17,635 bytes, lines 3863-4327
3863            // and so would change references to labels and datasets, preventing data updates.
3864            var data = config.data = config.data || {};
3865            data.datasets = data.datasets || [];
3866            data.labels = data.labels || [];
3867
3868            config.options = helpers.configMerge(
3869                defaults.global,
3870                defaults[config.type],
3871                config.options || {});
3872
3873            return config;
3874        }
3875
3876        /**
3877         * Updates the config of the chart
3878         * @param chart {Chart} chart to update the options for
3879         */
3880        function updateConfig(chart) {
3881            var newOptions = chart.options;
3882
3883            helpers.each(chart.scales, function(scale) {
3884                layouts.removeBox(chart, scale);
3885            });
3886
3887            newOptions = helpers.configMerge(
3888                Chart.defaults.global,
3889                Chart.defaults[chart.config.type],
3890                newOptions);
3891
3892            chart.options = chart.config.options = newOptions;
3893            chart.ensureScalesHaveIDs();
3894            chart.buildOrUpdateScales();
3895            // Tooltip
3896            chart.tooltip._options = newOptions.tooltips;
3897            chart.tooltip.initialize();
3898        }
3899
3900        function positionIsHorizontal(position) {
3901            return position === 'top' || position === 'bottom';
3902        }
3903
3904        helpers.extend(Chart.prototype, /** @lends Chart */ {
3905            /**
3906             * @private
3907             */
3908            construct: function(item, config) {
3909                var me = this;
3910
3911                config = initConfig(config);
3912
3913                var context = platform.acquireContext(item, config);
3914                var canvas = context && context.canvas;
3915                var height = canvas && canvas.height;
3916                var width = canvas && canvas.width;
3917
3918                me.id = helpers.uid();
3919                me.ctx = context;
3920                me.canvas = canvas;
3921                me.config = config;
3922                me.width = width;
3923                me.height = height;
3924                me.aspectRatio = height ? width / height : null;
3925                me.options = config.options;
3926                me._bufferedRender = false;
3927
3928                /**
3929                 * Provided for backward compatibility, Chart and Chart.Controller have been merged,
3930                 * the "instance" still need to be defined since it might be called from plugins.
3931                 * @prop Chart#chart
3932                 * @deprecated since version 2.6.0
3933                 * @todo remove at version 3
3934                 * @private
3935                 */
3936                me.chart = me;
3937                me.controller = me; // chart.chart.controller #inception
3938
3939                // Add the chart instance to the global namespace
3940                Chart.instances[me.id] = me;
3941
3942                // Define alias to the config data: `chart.data === chart.config.data`
3943                Object.defineProperty(me, 'data', {
3944                    get: function() {
3945                        return me.config.data;
3946                    },
3947                    set: function(value) {
3948                        me.config.data = value;
3949                    }
3950                });
3951
3952                if (!context || !canvas) {
3953                    // The given item is not a compatible context2d element, let's return before finalizing
3954                    // the chart initialization but after setting basic chart / controller properties that
3955                    // can help to figure out that the chart is not valid (e.g chart.canvas !== null);
3956                    // https://github.com/chartjs/Chart.js/issues/2807
3957                    console.error("Failed to create chart: can't acquire context from the given item");
3958                    return;
3959                }
3960
3961                me.initialize();
3962                me.update();
3963            },
3964
3965            /**
3966             * @private
3967             */
3968            initialize: function() {
3969                var me = this;
3970
3971                // Before init plugin notification
3972                plugins.notify(me, 'beforeInit');
3973
3974                helpers.retinaScale(me, me.options.devicePixelRatio);
3975
3976                me.bindEvents();
3977
3978                if (me.options.responsive) {
3979                    // Initial resize before chart draws (must be silent to preserve initial animations).
3980                    me.resize(true);
3981                }
3982
3983                // Make sure scales have IDs and are built before we build any controllers.
3984                me.ensureScalesHaveIDs();
3985                me.buildOrUpdateScales();
3986                me.initToolTip();
3987
3988                // After init plugin notification
3989                plugins.notify(me, 'afterInit');
3990
3991                return me;
3992            },
3993
3994            clear: function() {
3995                helpers.canvas.clear(this);
3996                return this;
3997            },
3998
3999            stop: function() {
4000                // Stops any current animation loop occurring
4001                animations.cancelAnimation(this);
4002                return this;
4003            },
4004
4005            resize: function(silent) {
4006                var me = this;
4007                var options = me.options;
4008                var canvas = me.canvas;
4009                var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;
4010
4011                // the canvas render width and height will be casted to integers so make sure that
4012                // the canvas display style uses the same integer values to avoid blurring effect.
4013
4014                // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed
4015                var newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas)));
4016                var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)));
4017
4018                if (me.width === newWidth && me.height === newHeight) {
4019                    return;
4020                }
4021
4022                canvas.width = me.width = newWidth;
4023                canvas.height = me.height = newHeight;
4024                canvas.style.width = newWidth + 'px';
4025                canvas.style.height = newHeight + 'px';
4026
4027                helpers.retinaScale(me, options.devicePixelRatio);
4028
4029                if (!silent) {
4030                    // Notify any plugins about the resize
4031                    var newSize = {width: newWidth, height: newHeight};
4032                    plugins.notify(me, 'resize', [newSize]);
4033
4034                    // Notify of resize
4035                    if (me.options.onResize) {
4036                        me.options.onResize(me, newSize);
4037                    }
4038
4039                    me.stop();
4040                    me.update({
4041                        duration: me.options.responsiveAnimationDuration
4042                    });
4043                }
4044            },
4045
4046            ensureScalesHaveIDs: function() {
4047                var options = this.options;
4048                var scalesOptions = options.scales || {};
4049                var scaleOptions = options.scale;
4050
4051                helpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {
4052                    xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
4053                });
4054
4055                helpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {
4056                    yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
4057                });
4058
4059                if (scaleOptions) {
4060                    scaleOptions.id = scaleOptions.id || 'scale';
4061                }
4062            },
4063
4064            /**
4065             * Builds a map of scale ID to scale object for future lookup.
4066             */
4067            buildOrUpdateScales: function() {
4068                var me = this;
4069                var options = me.options;
4070                var scales = me.scales || {};
4071                var items = [];
4072                var updated = Object.keys(scales).reduce(function(obj, id) {
4073                    obj[id] = false;
4074                    return obj;
4075                }, {});
4076
4077                if (options.scales) {
4078                    items = items.concat(
4079                        (options.scales.xAxes || []).map(function(xAxisOptions) {
4080                            return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};
4081                        }),
4082                        (options.scales.yAxes || []).map(function(yAxisOptions) {
4083                            return {options: yAxisOptions, dtype: 'linear', dposition: 'left'};
4084                        })
4085                    );
4086                }
4087
4088                if (options.scale) {
4089                    items.push({
4090                        options: options.scale,
4091                        dtype: 'radialLinear',
4092                        isDefault: true,
4093                        dposition: 'chartArea'
4094                    });
4095                }
4096
4097                helpers.each(items, function(item) {
4098                    var scaleOptions = item.options;
4099                    var id = scaleOptions.id;
4100                    var scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype);
4101
4102                    if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
4103                        scaleOptions.position = item.dposition;
4104                    }
4105
4106                    updated[id] = true;
4107                    var scale = null;
4108                    if (id in scales && scales[id].type === scaleType) {
4109                        scale = scales[id];
4110                        scale.options = scaleOptions;
4111                        scale.ctx = me.ctx;
4112                        scale.chart = me;
4113                    } else {
4114                        var scaleClass = scaleService.getScaleConstructor(scaleType);
4115                        if (!scaleClass) {
4116                            return;
4117                        }
4118                        scale = new scaleClass({
4119                            id: id,
4120                            type: scaleType,
4121                            options: scaleOptions,
4122                            ctx: me.ctx,
4123                            chart: me
4124                        });
4125                        scales[scale.id] = scale;
4126                    }
4127
4128                    scale.mergeTicksOptions();
4129
4130                    // TODO(SB): I think we should be able to remove this custom case (options.scale)
4131                    // and consider it as a regular scale part of the "scales"" map only! This would
4132                    // make the logic easier and remove some useless? custom code.
4133                    if (item.isDefault) {
4134                        me.scale = scale;
4135                    }
4136                });
4137                // clear up discarded scales
4138                helpers.each(updated, function(hasUpdated, id) {
4139                    if (!hasUpdated) {
4140                        delete scales[id];
4141                    }
4142                });
4143
4144                me.scales = scales;
4145
4146                scaleService.addScalesToLayout(this);
4147            },
4148
4149            buildOrUpdateControllers: function() {
4150                var me = this;
4151                var types = [];
4152                var newControllers = [];
4153
4154                helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4155                    var meta = me.getDatasetMeta(datasetIndex);
4156                    var type = dataset.type || me.config.type;
4157
4158                    if (meta.type && meta.type !== type) {
4159                        me.destroyDatasetMeta(datasetIndex);
4160                        meta = me.getDatasetMeta(datasetIndex);
4161                    }
4162                    meta.type = type;
4163
4164                    types.push(meta.type);
4165
4166                    if (meta.controller) {
4167                        meta.controller.updateIndex(datasetIndex);
4168                        meta.controller.linkScales();
4169                    } else {
4170                        var ControllerClass = Chart.controllers[meta.type];
4171                        if (ControllerClass === undefined) {
4172                            throw new Error('"' + meta.type + '" is not a chart type.');
4173                        }
4174
4175                        meta.controller = new ControllerClass(me, datasetIndex);
4176                        newControllers.push(meta.controller);
4177                    }
4178                }, me);
4179
4180                return newControllers;
4181            },
4182
4183            /**
4184             * Reset the elements of all datasets
4185             * @private
4186             */
4187            resetElements: function() {
4188                var me = this;
4189                helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4190                    me.getDatasetMeta(datasetIndex).controller.reset();
4191                }, me);
4192            },
4193
4194            /**
4195             * Resets the chart back to it's state before the initial animation
4196             */
4197            reset: function() {
4198                this.resetElements();
4199                this.tooltip.initialize();
4200            },
4201
4202            update: function(config) {
4203                var me = this;
4204
4205                if (!config || typeof config !== 'object') {
4206                    // backwards compatibility
4207                    config = {
4208                        duration: config,
4209                        lazy: arguments[1]
4210                    };
4211                }
4212
4213                updateConfig(me);
4214
4215                // plugins options references might have change, let's invalidate the cache
4216                // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
4217                plugins._invalidate(me);
4218
4219                if (plugins.notify(me, 'beforeUpdate') === false) {
4220                    return;
4221                }
4222
4223                // In case the entire data object changed
4224                me.tooltip._data = me.data;
4225
4226                // Make sure dataset controllers are updated and new controllers are reset
4227                var newControllers = me.buildOrUpdateControllers();
4228
4229                // Make sure all dataset controllers have correct meta data counts
4230                helpers.each(me.data.datasets, function(dataset, datasetIndex) {
4231                    me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
4232                }, me);
4233
4234                me.updateLayout();
4235
4236                // Can only reset the new controllers after the scales have been updated
4237                if (me.options.animation && me.options.animation.duration) {
4238                    helpers.each(newControllers, function(controller) {
4239                        controller.reset();
4240                    });
4241                }
4242
4243                me.updateDatasets();
4244
4245                // Need to reset tooltip in case it is displayed with elements that are removed
4246                // after update.
4247                me.tooltip.initialize();
4248
4249                // Last active contains items that were previously in the tooltip.
4250                // When we reset the tooltip, we need to clear it
4251                me.lastActive = [];
4252
4253                // Do this before render so that any plugins that need final scale updates can use it
4254                plugins.notify(me, 'afterUpdate');
4255
4256                if (me._bufferedRender) {
4257                    me._bufferedRequest = {
4258                        duration: config.duration,
4259                        easing: config.easing,
4260                        lazy: config.lazy
4261                    };
4262                } else {
4263                    me.render(config);
4264                }
4265            },
4266
4267            /**
4268             * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
4269             * hook, in which case, plugins will not be called on `afterLayout`.
4270             * @private
4271             */
4272            updateLayout: function() {
4273                var me = this;
4274
4275                if (plugins.notify(me, 'beforeLayout') === false) {
4276                    return;
4277                }
4278
4279                layouts.update(this, this.width, this.height);
4280
4281                /**
4282                 * Provided for backward compatibility, use `afterLayout` instead.
4283                 * @method IPlugin#afterScaleUpdate
4284                 * @deprecated since version 2.5.0
4285                 * @todo remove at version 3
4286                 * @private
4287                 */
4288                plugins.notify(me, 'afterScaleUpdate');
4289                plugins.notify(me, 'afterLayout');
4290            },
4291
4292            /**
4293             * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
4294             * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
4295             * @private
4296             */
4297            updateDatasets: function() {
4298                var me = this;
4299
4300                if (plugins.notify(me, 'beforeDatasetsUpdate') === false) {
4301                    return;
4302                }
4303
4304                for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
4305                    me.updateDataset(i);
4306                }
4307
4308                plugins.notify(me, 'afterDatasetsUpdate');
4309            },
4310
4311            /**
4312             * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
4313             * hook, in which case, plugins will not be called on `afterDatasetUpdate`.
4314             * @private
4315             */
4316            updateDataset: function(index) {
4317                var me = this;
4318                var meta = me.getDatasetMeta(index);
4319                var args = {
4320                    meta: meta,
4321                    index: index
4322                };
4323
4324                if (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
4325                    return;
4326                }
4327
4328                meta.controller.update();
4329
4330                plugins.notify(me, 'afterDatasetUpdate', [args]);
4331            },
4332
4333            render: function(config) {
4334                var me = this;
4335
4336                if (!config || typeof config !== 'object') {
4337                    // backwards compatibility
4338                    config = {
4339                        duration: config,
4340                        lazy: arguments[1]
4341                    };
4342                }
4343
4344                var duration = config.duration;
4345                var lazy = config.lazy;
4346
4347                if (plugins.notify(me, 'beforeRender') === false) {
4348                    return;
4349                }
4350
4351                var animationOptions = me.options.animation;
4352                var onComplete = function(animation) {
4353                    plugins.notify(me, 'afterRender');
4354                    helpers.callback(animationOptions && animationOptions.onComplete, [animation], me);
4355                };
4356
4357                if (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {
4358                    var animation = new Animation({
4359                        numSteps: (duration || animationOptions.duration) / 16.66, // 60 fps
4360                        easing: config.easing || animationOptions.easing,
4361
4362                        render: function(chart, animationObject) {
4363                            var easingFunction = helpers.easing.effects[animationObject.easing];
4364                            var currentStep = animationObject.currentStep;
4365                            var stepDecimal = currentStep / animationObject.numSteps;
4366
4367                            chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
4368                        },
4369
4370                        onAnimationProgress: animationOptions.onProgress,
4371                        onAnimationComplete: onComplete
4372                    });
4373
4374                    animations.addAnimation(me, animation, duration, lazy);
4375                } else {
4376                    me.draw();
4377
4378                    // See https://github.com/chartjs/Chart.js/issues/3781
4379                    onComplete(new Animation({numSteps: 0, chart: me}));
4380                }
4381
4382                return me;
4383            },
4384
4385            draw: function(easingValue) {
4386                var me = this;
4387
4388                me.clear();
4389
4390                if (helpers.isNullOrUndef(easingValue)) {
4391                    easingValue = 1;
4392                }
4393
4394                me.transition(easingValue);
4395
4396                if (me.width <= 0 || me.height <= 0) {
4397                    return;
4398                }
4399
4400                if (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
4401                    return;
4402                }
4403
4404                // Draw all the scales
4405                helpers.each(me.boxes, function(box) {
4406                    box.draw(me.chartArea);
4407                }, me);
4408
4409                if (me.scale) {
4410                    me.scale.draw();
4411                }
4412
4413                me.drawDatasets(easingValue);
4414                me._drawTooltip(easingValue);
4415
4416                plugins.notify(me, 'afterDraw', [easingValue]);
4417            },
4418
4419            /**
4420             * @private
4421             */
4422            transition: function(easingValue) {
4423                var me = this;
4424
4425                for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
4426                    if (me.isDatasetVisible(i)) {
4427                        me.getDatasetMeta(i).controller.transition(easingValue);
4428                    }
4429                }
4430
4431                me.tooltip.transition(easingValue);
4432            },
4433
4434            /**
4435             * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
4436             * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
4437             * @private
4438             */
4439            drawDatasets: function(easingValue) {
4440                var me = this;
4441
4442                if (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
4443                    return;
4444                }
4445
4446                // Draw datasets reversed to support proper line stacking
4447                for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) {
4448                    if (me.isDatasetVisible(i)) {
4449                        me.drawDataset(i, easingValue);
4450                    }
4451                }
4452
4453                plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
4454            },
4455
4456            /**
4457             * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
4458             * hook, in which case, plugins will not be called on `afterDatasetDraw`.
4459             * @private
4460             */
4461            drawDataset: function(index, easingValue) {
4462                var me = this;
4463                var meta = me.getDatasetMeta(index);
4464                var args = {
4465                    meta: meta,
4466                    index: index,
4467                    easingValue: easingValue
4468                };
4469
4470                if (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
4471                    return;
4472                }
4473
4474                meta.controller.draw(easingValue);
4475
4476                plugins.notify(me, 'afterDatasetDraw', [args]);
4477            },
4478
4479            /**
4480             * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw`
4481             * hook, in which case, plugins will not be called on `afterTooltipDraw`.
4482             * @private
4483             */
4484            _drawTooltip: function(easingValue) {
4485                var me = this;
4486                var tooltip = me.tooltip;
4487                var args = {
4488                    tooltip: tooltip,
4489                    easingValue: easingValue
4490                };
4491
4492                if (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) {
4493                    return;
4494                }
4495
4496                tooltip.draw();
4497
4498                plugins.notify(me, 'afterTooltipDraw', [args]);
4499            },
4500
4501            // Get the single element that was clicked on
vendor: 17,436 bytes, lines 4502-4996
4502            // @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
4503            getElementAtEvent: function(e) {
4504                return Interaction.modes.single(this, e);
4505            },
4506
4507            getElementsAtEvent: function(e) {
4508                return Interaction.modes.label(this, e, {intersect: true});
4509            },
4510
4511            getElementsAtXAxis: function(e) {
4512                return Interaction.modes['x-axis'](this, e, {intersect: true});
4513            },
4514
4515            getElementsAtEventForMode: function(e, mode, options) {
4516                var method = Interaction.modes[mode];
4517                if (typeof method === 'function') {
4518                    return method(this, e, options);
4519                }
4520
4521                return [];
4522            },
4523
4524            getDatasetAtEvent: function(e) {
4525                return Interaction.modes.dataset(this, e, {intersect: true});
4526            },
4527
4528            getDatasetMeta: function(datasetIndex) {
4529                var me = this;
4530                var dataset = me.data.datasets[datasetIndex];
4531                if (!dataset._meta) {
4532                    dataset._meta = {};
4533                }
4534
4535                var meta = dataset._meta[me.id];
4536                if (!meta) {
4537                    meta = dataset._meta[me.id] = {
4538                        type: null,
4539                        data: [],
4540                        dataset: null,
4541                        controller: null,
4542                        hidden: null,			// See isDatasetVisible() comment
4543                        xAxisID: null,
4544                        yAxisID: null
4545                    };
4546                }
4547
4548                return meta;
4549            },
4550
4551            getVisibleDatasetCount: function() {
4552                var count = 0;
4553                for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
4554                    if (this.isDatasetVisible(i)) {
4555                        count++;
4556                    }
4557                }
4558                return count;
4559            },
4560
4561            isDatasetVisible: function(datasetIndex) {
4562                var meta = this.getDatasetMeta(datasetIndex);
4563
4564                // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
4565                // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
4566                return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
4567            },
4568
4569            generateLegend: function() {
4570                return this.options.legendCallback(this);
4571            },
4572
4573            /**
4574             * @private
4575             */
4576            destroyDatasetMeta: function(datasetIndex) {
4577                var id = this.id;
4578                var dataset = this.data.datasets[datasetIndex];
4579                var meta = dataset._meta && dataset._meta[id];
4580
4581                if (meta) {
4582                    meta.controller.destroy();
4583                    delete dataset._meta[id];
4584                }
4585            },
4586
4587            destroy: function() {
4588                var me = this;
4589                var canvas = me.canvas;
4590                var i, ilen;
4591
4592                me.stop();
4593
4594                // dataset controllers need to cleanup associated data
4595                for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
4596                    me.destroyDatasetMeta(i);
4597                }
4598
4599                if (canvas) {
4600                    me.unbindEvents();
4601                    helpers.canvas.clear(me);
4602                    platform.releaseContext(me.ctx);
4603                    me.canvas = null;
4604                    me.ctx = null;
4605                }
4606
4607                plugins.notify(me, 'destroy');
4608
4609                delete Chart.instances[me.id];
4610            },
4611
4612            toBase64Image: function() {
4613                return this.canvas.toDataURL.apply(this.canvas, arguments);
4614            },
4615
4616            initToolTip: function() {
4617                var me = this;
4618                me.tooltip = new Tooltip({
4619                    _chart: me,
4620                    _chartInstance: me, // deprecated, backward compatibility
4621                    _data: me.data,
4622                    _options: me.options.tooltips
4623                }, me);
4624            },
4625
4626            /**
4627             * @private
4628             */
4629            bindEvents: function() {
4630                var me = this;
4631                var listeners = me._listeners = {};
4632                var listener = function() {
4633                    me.eventHandler.apply(me, arguments);
4634                };
4635
4636                helpers.each(me.options.events, function(type) {
4637                    platform.addEventListener(me, type, listener);
4638                    listeners[type] = listener;
4639                });
4640
4641                // Elements used to detect size change should not be injected for non responsive charts.
4642                // See https://github.com/chartjs/Chart.js/issues/2210
4643                if (me.options.responsive) {
4644                    listener = function() {
4645                        me.resize();
4646                    };
4647
4648                    platform.addEventListener(me, 'resize', listener);
4649                    listeners.resize = listener;
4650                }
4651            },
4652
4653            /**
4654             * @private
4655             */
4656            unbindEvents: function() {
4657                var me = this;
4658                var listeners = me._listeners;
4659                if (!listeners) {
4660                    return;
4661                }
4662
4663                delete me._listeners;
4664                helpers.each(listeners, function(listener, type) {
4665                    platform.removeEventListener(me, type, listener);
4666                });
4667            },
4668
4669            updateHoverStyle: function(elements, mode, enabled) {
4670                var method = enabled ? 'setHoverStyle' : 'removeHoverStyle';
4671                var element, i, ilen;
4672
4673                for (i = 0, ilen = elements.length; i < ilen; ++i) {
4674                    element = elements[i];
4675                    if (element) {
4676                        this.getDatasetMeta(element._datasetIndex).controller[method](element);
4677                    }
4678                }
4679            },
4680
4681            /**
4682             * @private
4683             */
4684            eventHandler: function(e) {
4685                var me = this;
4686                var tooltip = me.tooltip;
4687
4688                if (plugins.notify(me, 'beforeEvent', [e]) === false) {
4689                    return;
4690                }
4691
4692                // Buffer any update calls so that renders do not occur
4693                me._bufferedRender = true;
4694                me._bufferedRequest = null;
4695
4696                var changed = me.handleEvent(e);
4697                // for smooth tooltip animations issue #4989
4698                // the tooltip should be the source of change
4699                // Animation check workaround:
4700                // tooltip._start will be null when tooltip isn't animating
4701                if (tooltip) {
4702                    changed = tooltip._start
4703                        ? tooltip.handleEvent(e)
4704                        : changed | tooltip.handleEvent(e);
4705                }
4706
4707                plugins.notify(me, 'afterEvent', [e]);
4708
4709                var bufferedRequest = me._bufferedRequest;
4710                if (bufferedRequest) {
4711                    // If we have an update that was triggered, we need to do a normal render
4712                    me.render(bufferedRequest);
4713                } else if (changed && !me.animating) {
4714                    // If entering, leaving, or changing elements, animate the change via pivot
4715                    me.stop();
4716
4717                    // We only need to render at this point. Updating will cause scales to be
4718                    // recomputed generating flicker & using more memory than necessary.
4719                    me.render({
4720                        duration: me.options.hover.animationDuration,
4721                        lazy: true
4722                    });
4723                }
4724
4725                me._bufferedRender = false;
4726                me._bufferedRequest = null;
4727
4728                return me;
4729            },
4730
4731            /**
4732             * Handle an event
4733             * @private
4734             * @param {IEvent} event the event to handle
4735             * @return {Boolean} true if the chart needs to re-render
4736             */
4737            handleEvent: function(e) {
4738                var me = this;
4739                var options = me.options || {};
4740                var hoverOptions = options.hover;
4741                var changed = false;
4742
4743                me.lastActive = me.lastActive || [];
4744
4745                // Find Active Elements for hover and tooltips
4746                if (e.type === 'mouseout') {
4747                    me.active = [];
4748                } else {
4749                    me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
4750                }
4751
4752                // Invoke onHover hook
4753                // Need to call with native event here to not break backwards compatibility
4754                helpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);
4755
4756                if (e.type === 'mouseup' || e.type === 'click') {
4757                    if (options.onClick) {
4758                        // Use e.native here for backwards compatibility
4759                        options.onClick.call(me, e.native, me.active);
4760                    }
4761                }
4762
4763                // Remove styling for last active (even if it may still be active)
4764                if (me.lastActive.length) {
4765                    me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
4766                }
4767
4768                // Built in hover styling
4769                if (me.active.length && hoverOptions.mode) {
4770                    me.updateHoverStyle(me.active, hoverOptions.mode, true);
4771                }
4772
4773                changed = !helpers.arrayEquals(me.active, me.lastActive);
4774
4775                // Remember Last Actives
4776                me.lastActive = me.active;
4777
4778                return changed;
4779            }
4780        });
4781
4782        /**
4783         * Provided for backward compatibility, use Chart instead.
4784         * @class Chart.Controller
4785         * @deprecated since version 2.6.0
4786         * @todo remove at version 3
4787         * @private
4788         */
4789        Chart.Controller = Chart;
4790    };
4791
4792},{"22":22,"23":23,"26":26,"29":29,"31":31,"32":32,"34":34,"36":36,"46":46,"49":49}],25:[function(require,module,exports){
4793    'use strict';
4794
4795    var helpers = require(46);
4796
4797    module.exports = function(Chart) {
4798
4799        var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
4800
4801        /**
4802         * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
4803         * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
4804         * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
4805         */
4806        function listenArrayEvents(array, listener) {
4807            if (array._chartjs) {
4808                array._chartjs.listeners.push(listener);
4809                return;
4810            }
4811
4812            Object.defineProperty(array, '_chartjs', {
4813                configurable: true,
4814                enumerable: false,
4815                value: {
4816                    listeners: [listener]
4817                }
4818            });
4819
4820            arrayEvents.forEach(function(key) {
4821                var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
4822                var base = array[key];
4823
4824                Object.defineProperty(array, key, {
4825                    configurable: true,
4826                    enumerable: false,
4827                    value: function() {
4828                        var args = Array.prototype.slice.call(arguments);
4829                        var res = base.apply(this, args);
4830
4831                        helpers.each(array._chartjs.listeners, function(object) {
4832                            if (typeof object[method] === 'function') {
4833                                object[method].apply(object, args);
4834                            }
4835                        });
4836
4837                        return res;
4838                    }
4839                });
4840            });
4841        }
4842
4843        /**
4844         * Removes the given array event listener and cleanup extra attached properties (such as
4845         * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
4846         */
4847        function unlistenArrayEvents(array, listener) {
4848            var stub = array._chartjs;
4849            if (!stub) {
4850                return;
4851            }
4852
4853            var listeners = stub.listeners;
4854            var index = listeners.indexOf(listener);
4855            if (index !== -1) {
4856                listeners.splice(index, 1);
4857            }
4858
4859            if (listeners.length > 0) {
4860                return;
4861            }
4862
4863            arrayEvents.forEach(function(key) {
4864                delete array[key];
4865            });
4866
4867            delete array._chartjs;
4868        }
4869
4870        // Base class for all dataset controllers (line, bar, etc)
4871        Chart.DatasetController = function(chart, datasetIndex) {
4872            this.initialize(chart, datasetIndex);
4873        };
4874
4875        helpers.extend(Chart.DatasetController.prototype, {
4876
4877            /**
4878             * Element type used to generate a meta dataset (e.g. Chart.element.Line).
4879             * @type {Chart.core.element}
4880             */
4881            datasetElementType: null,
4882
4883            /**
4884             * Element type used to generate a meta data (e.g. Chart.element.Point).
4885             * @type {Chart.core.element}
4886             */
4887            dataElementType: null,
4888
4889            initialize: function(chart, datasetIndex) {
4890                var me = this;
4891                me.chart = chart;
4892                me.index = datasetIndex;
4893                me.linkScales();
4894                me.addElements();
4895            },
4896
4897            updateIndex: function(datasetIndex) {
4898                this.index = datasetIndex;
4899            },
4900
4901            linkScales: function() {
4902                var me = this;
4903                var meta = me.getMeta();
4904                var dataset = me.getDataset();
4905
4906                if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) {
4907                    meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
4908                }
4909                if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) {
4910                    meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
4911                }
4912            },
4913
4914            getDataset: function() {
4915                return this.chart.data.datasets[this.index];
4916            },
4917
4918            getMeta: function() {
4919                return this.chart.getDatasetMeta(this.index);
4920            },
4921
4922            getScaleForId: function(scaleID) {
4923                return this.chart.scales[scaleID];
4924            },
4925
4926            reset: function() {
4927                this.update(true);
4928            },
4929
4930            /**
4931             * @private
4932             */
4933            destroy: function() {
4934                if (this._data) {
4935                    unlistenArrayEvents(this._data, this);
4936                }
4937            },
4938
4939            createMetaDataset: function() {
4940                var me = this;
4941                var type = me.datasetElementType;
4942                return type && new type({
4943                    _chart: me.chart,
4944                    _datasetIndex: me.index
4945                });
4946            },
4947
4948            createMetaData: function(index) {
4949                var me = this;
4950                var type = me.dataElementType;
4951                return type && new type({
4952                    _chart: me.chart,
4953                    _datasetIndex: me.index,
4954                    _index: index
4955                });
4956            },
4957
4958            addElements: function() {
4959                var me = this;
4960                var meta = me.getMeta();
4961                var data = me.getDataset().data || [];
4962                var metaData = meta.data;
4963                var i, ilen;
4964
4965                for (i = 0, ilen = data.length; i < ilen; ++i) {
4966                    metaData[i] = metaData[i] || me.createMetaData(i);
4967                }
4968
4969                meta.dataset = meta.dataset || me.createMetaDataset();
4970            },
4971
4972            addElementAndReset: function(index) {
4973                var element = this.createMetaData(index);
4974                this.getMeta().data.splice(index, 0, element);
4975                this.updateElement(element, index, true);
4976            },
4977
4978            buildOrUpdateElements: function() {
4979                var me = this;
4980                var dataset = me.getDataset();
4981                var data = dataset.data || (dataset.data = []);
4982
4983                // In order to correctly handle data addition/deletion animation (an thus simulate
4984                // real-time charts), we need to monitor these data modifications and synchronize
4985                // the internal meta data accordingly.
4986                if (me._data !== data) {
4987                    if (me._data) {
4988                        // This case happens when the user replaced the data array instance.
4989                        unlistenArrayEvents(me._data, me);
4990                    }
4991
4992                    listenArrayEvents(data, me);
4993                    me._data = data;
4994                }
4995
4996                // Re-sync meta data in case the user replaced the data array or if we missed
4997                // any updates and so make sure that we handle number of datapoints changing.
4998                me.resyncElements();
4999            },
5000
5001            update: helpers.noop,
5002
5003            transition: function(easingValue) {
5004                var meta = this.getMeta();
5005                var elements = meta.data || [];
5006                var ilen = elements.length;
5007                var i = 0;
5008
5009                for (; i < ilen; ++i) {
5010                    elements[i].transition(easingValue);
5011                }
5012
5013                if (meta.dataset) {
5014                    meta.dataset.transition(easingValue);
5015                }
5016            },
5017
5018            draw: function() {
5019                var meta = this.getMeta();
5020                var elements = meta.data || [];
5021                var ilen = elements.length;
5022                var i = 0;
5023
5024                if (meta.dataset) {
5025                    meta.dataset.draw();
5026                }
5027
5028                for (; i < ilen; ++i) {
5029                    elements[i].draw();
5030                }
5031            },
5032
5033            removeHoverStyle: function(element) {
5034                helpers.merge(element._model, element.$previousStyle || {});
5035                delete element.$previousStyle;
5036            },
5037
5038            setHoverStyle: function(element) {
5039                var dataset = this.chart.data.datasets[element._datasetIndex];
5040                var index = element._index;
5041                var custom = element.custom || {};
5042                var valueOrDefault = helpers.valueAtIndexOrDefault;
5043                var getHoverColor = helpers.getHoverColor;
5044                var model = element._model;
5045
5046                element.$previousStyle = {
5047                    backgroundColor: model.backgroundColor,
5048                    borderColor: model.borderColor,
5049                    borderWidth: model.borderWidth
5050                };
5051
5052                model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));
5053                model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));
5054                model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
5055            },
5056
5057            /**
5058             * @private
5059             */
5060            resyncElements: function() {
5061                var me = this;
5062                var meta = me.getMeta();
5063                var data = me.getDataset().data;
5064                var numMeta = meta.data.length;
5065                var numData = data.length;
5066
5067                if (numData < numMeta) {
5068                    meta.data.splice(numData, numMeta - numData);
5069                } else if (numData > numMeta) {
5070                    me.insertElements(numMeta, numData - numMeta);
5071                }
5072            },
5073
5074            /**
5075             * @private
5076             */
5077            insertElements: function(start, count) {
5078                for (var i = 0; i < count; ++i) {
5079                    this.addElementAndReset(start + i);
5080                }
5081            },
5082
5083            /**
5084             * @private
5085             */
5086            onDataPush: function() {
5087                this.insertElements(this.getDataset().data.length - 1, arguments.length);
5088            },
5089
5090            /**
5091             * @private
5092             */
5093            onDataPop: function() {
5094                this.getMeta().data.pop();
5095            },
5096
5097            /**
5098             * @private
5099             */
5100            onDataShift: function() {
5101                this.getMeta().data.shift();
5102            },
5103
5104            /**
5105             * @private
5106             */
5107            onDataSplice: function(start, count) {
5108                this.getMeta().data.splice(start, count);
5109                this.insertElements(start, arguments.length - 2);
5110            },
5111
5112            /**
5113             * @private
5114             */
5115            onDataUnshift: function() {
5116                this.insertElements(0, arguments.length);
5117            }
5118        });
5119
5120        Chart.DatasetController.extend = helpers.inherits;
5121    };
5122
5123},{"46":46}],26:[function(require,module,exports){
5124    'use strict';
5125
5126    var helpers = require(46);
5127
5128    module.exports = {
5129        /**
5130         * @private
5131         */
5132        _set: function(scope, values) {
5133            return helpers.merge(this[scope] || (this[scope] = {}), values);
5134        }
5135    };
5136
5137},{"46":46}],27:[function(require,module,exports){
5138    'use strict';
5139
5140    var color = require(3);
5141    var helpers = require(46);
5142
5143    function interpolate(start, view, model, ease) {
5144        var keys = Object.keys(model);
5145        var i, ilen, key, actual, origin, target, type, c0, c1;
5146
5147        for (i = 0, ilen = keys.length; i < ilen; ++i) {
5148            key = keys[i];
5149
5150            target = model[key];
5151
5152            // if a value is added to the model after pivot() has been called, the view
5153            // doesn't contain it, so let's initialize the view to the target value.
5154            if (!view.hasOwnProperty(key)) {
5155                view[key] = target;
5156            }
5157
5158            actual = view[key];
5159
vendor: 39,071 bytes, lines 5160-6131
5160            if (actual === target || key[0] === '_') {
5161                continue;
5162            }
5163
5164            if (!start.hasOwnProperty(key)) {
5165                start[key] = actual;
5166            }
5167
5168            origin = start[key];
5169
5170            type = typeof target;
5171
5172            if (type === typeof origin) {
5173                if (type === 'string') {
5174                    c0 = color(origin);
5175                    if (c0.valid) {
5176                        c1 = color(target);
5177                        if (c1.valid) {
5178                            view[key] = c1.mix(c0, ease).rgbString();
5179                            continue;
5180                        }
5181                    }
5182                } else if (type === 'number' && isFinite(origin) && isFinite(target)) {
5183                    view[key] = origin + (target - origin) * ease;
5184                    continue;
5185                }
5186            }
5187
5188            view[key] = target;
5189        }
5190    }
5191
5192    var Element = function(configuration) {
5193        helpers.extend(this, configuration);
5194        this.initialize.apply(this, arguments);
5195    };
5196
5197    helpers.extend(Element.prototype, {
5198
5199        initialize: function() {
5200            this.hidden = false;
5201        },
5202
5203        pivot: function() {
5204            var me = this;
5205            if (!me._view) {
5206                me._view = helpers.clone(me._model);
5207            }
5208            me._start = {};
5209            return me;
5210        },
5211
5212        transition: function(ease) {
5213            var me = this;
5214            var model = me._model;
5215            var start = me._start;
5216            var view = me._view;
5217
5218            // No animation -> No Transition
5219            if (!model || ease === 1) {
5220                me._view = model;
5221                me._start = null;
5222                return me;
5223            }
5224
5225            if (!view) {
5226                view = me._view = {};
5227            }
5228
5229            if (!start) {
5230                start = me._start = {};
5231            }
5232
5233            interpolate(start, view, model, ease);
5234
5235            return me;
5236        },
5237
5238        tooltipPosition: function() {
5239            return {
5240                x: this._model.x,
5241                y: this._model.y
5242            };
5243        },
5244
5245        hasValue: function() {
5246            return helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);
5247        }
5248    });
5249
5250    Element.extend = helpers.inherits;
5251
5252    module.exports = Element;
5253
5254},{"3":3,"46":46}],28:[function(require,module,exports){
5255    /* global window: false */
5256    /* global document: false */
5257    'use strict';
5258
5259    var color = require(3);
5260    var defaults = require(26);
5261    var helpers = require(46);
5262    var scaleService = require(34);
5263
5264    module.exports = function() {
5265
5266        // -- Basic js utility methods
5267
5268        helpers.configMerge = function(/* objects ... */) {
5269            return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
5270                merger: function(key, target, source, options) {
5271                    var tval = target[key] || {};
5272                    var sval = source[key];
5273
5274                    if (key === 'scales') {
5275                        // scale config merging is complex. Add our own function here for that
5276                        target[key] = helpers.scaleMerge(tval, sval);
5277                    } else if (key === 'scale') {
5278                        // used in polar area & radar charts since there is only one scale
5279                        target[key] = helpers.merge(tval, [scaleService.getScaleDefaults(sval.type), sval]);
5280                    } else {
5281                        helpers._merger(key, target, source, options);
5282                    }
5283                }
5284            });
5285        };
5286
5287        helpers.scaleMerge = function(/* objects ... */) {
5288            return helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {
5289                merger: function(key, target, source, options) {
5290                    if (key === 'xAxes' || key === 'yAxes') {
5291                        var slen = source[key].length;
5292                        var i, type, scale;
5293
5294                        if (!target[key]) {
5295                            target[key] = [];
5296                        }
5297
5298                        for (i = 0; i < slen; ++i) {
5299                            scale = source[key][i];
5300                            type = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear');
5301
5302                            if (i >= target[key].length) {
5303                                target[key].push({});
5304                            }
5305
5306                            if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) {
5307                                // new/untyped scale or type changed: let's apply the new defaults
5308                                // then merge source scale to correctly overwrite the defaults.
5309                                helpers.merge(target[key][i], [scaleService.getScaleDefaults(type), scale]);
5310                            } else {
5311                                // scales type are the same
5312                                helpers.merge(target[key][i], scale);
5313                            }
5314                        }
5315                    } else {
5316                        helpers._merger(key, target, source, options);
5317                    }
5318                }
5319            });
5320        };
5321
5322        helpers.where = function(collection, filterCallback) {
5323            if (helpers.isArray(collection) && Array.prototype.filter) {
5324                return collection.filter(filterCallback);
5325            }
5326            var filtered = [];
5327
5328            helpers.each(collection, function(item) {
5329                if (filterCallback(item)) {
5330                    filtered.push(item);
5331                }
5332            });
5333
5334            return filtered;
5335        };
5336        helpers.findIndex = Array.prototype.findIndex ?
5337            function(array, callback, scope) {
5338                return array.findIndex(callback, scope);
5339            } :
5340            function(array, callback, scope) {
5341                scope = scope === undefined ? array : scope;
5342                for (var i = 0, ilen = array.length; i < ilen; ++i) {
5343                    if (callback.call(scope, array[i], i, array)) {
5344                        return i;
5345                    }
5346                }
5347                return -1;
5348            };
5349        helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
5350            // Default to start of the array
5351            if (helpers.isNullOrUndef(startIndex)) {
5352                startIndex = -1;
5353            }
5354            for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
5355                var currentItem = arrayToSearch[i];
5356                if (filterCallback(currentItem)) {
5357                    return currentItem;
5358                }
5359            }
5360        };
5361        helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
5362            // Default to end of the array
5363            if (helpers.isNullOrUndef(startIndex)) {
5364                startIndex = arrayToSearch.length;
5365            }
5366            for (var i = startIndex - 1; i >= 0; i--) {
5367                var currentItem = arrayToSearch[i];
5368                if (filterCallback(currentItem)) {
5369                    return currentItem;
5370                }
5371            }
5372        };
5373
5374        // -- Math methods
5375        helpers.isNumber = function(n) {
5376            return !isNaN(parseFloat(n)) && isFinite(n);
5377        };
5378        helpers.almostEquals = function(x, y, epsilon) {
5379            return Math.abs(x - y) < epsilon;
5380        };
5381        helpers.almostWhole = function(x, epsilon) {
5382            var rounded = Math.round(x);
5383            return (((rounded - epsilon) < x) && ((rounded + epsilon) > x));
5384        };
5385        helpers.max = function(array) {
5386            return array.reduce(function(max, value) {
5387                if (!isNaN(value)) {
5388                    return Math.max(max, value);
5389                }
5390                return max;
5391            }, Number.NEGATIVE_INFINITY);
5392        };
5393        helpers.min = function(array) {
5394            return array.reduce(function(min, value) {
5395                if (!isNaN(value)) {
5396                    return Math.min(min, value);
5397                }
5398                return min;
5399            }, Number.POSITIVE_INFINITY);
5400        };
5401        helpers.sign = Math.sign ?
5402            function(x) {
5403                return Math.sign(x);
5404            } :
5405            function(x) {
5406                x = +x; // convert to a number
5407                if (x === 0 || isNaN(x)) {
5408                    return x;
5409                }
5410                return x > 0 ? 1 : -1;
5411            };
5412        helpers.log10 = Math.log10 ?
5413            function(x) {
5414                return Math.log10(x);
5415            } :
5416            function(x) {
5417                var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
5418                // Check for whole powers of 10,
5419                // which due to floating point rounding error should be corrected.
5420                var powerOf10 = Math.round(exponent);
5421                var isPowerOf10 = x === Math.pow(10, powerOf10);
5422
5423                return isPowerOf10 ? powerOf10 : exponent;
5424            };
5425        helpers.toRadians = function(degrees) {
5426            return degrees * (Math.PI / 180);
5427        };
5428        helpers.toDegrees = function(radians) {
5429            return radians * (180 / Math.PI);
5430        };
5431        // Gets the angle from vertical upright to the point about a centre.
5432        helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
5433            var distanceFromXCenter = anglePoint.x - centrePoint.x;
5434            var distanceFromYCenter = anglePoint.y - centrePoint.y;
5435            var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
5436
5437            var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
5438
5439            if (angle < (-0.5 * Math.PI)) {
5440                angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
5441            }
5442
5443            return {
5444                angle: angle,
5445                distance: radialDistanceFromCenter
5446            };
5447        };
5448        helpers.distanceBetweenPoints = function(pt1, pt2) {
5449            return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
5450        };
5451        helpers.aliasPixel = function(pixelWidth) {
5452            return (pixelWidth % 2 === 0) ? 0 : 0.5;
5453        };
5454        helpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
5455            // Props to Rob Spencer at scaled innovation for his post on splining between points
5456            // http://scaledinnovation.com/analytics/splines/aboutSplines.html
5457
5458            // This function must also respect "skipped" points
5459
5460            var previous = firstPoint.skip ? middlePoint : firstPoint;
5461            var current = middlePoint;
5462            var next = afterPoint.skip ? middlePoint : afterPoint;
5463
5464            var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
5465            var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
5466
5467            var s01 = d01 / (d01 + d12);
5468            var s12 = d12 / (d01 + d12);
5469
5470            // If all points are the same, s01 & s02 will be inf
5471            s01 = isNaN(s01) ? 0 : s01;
5472            s12 = isNaN(s12) ? 0 : s12;
5473
5474            var fa = t * s01; // scaling factor for triangle Ta
5475            var fb = t * s12;
5476
5477            return {
5478                previous: {
5479                    x: current.x - fa * (next.x - previous.x),
5480                    y: current.y - fa * (next.y - previous.y)
5481                },
5482                next: {
5483                    x: current.x + fb * (next.x - previous.x),
5484                    y: current.y + fb * (next.y - previous.y)
5485                }
5486            };
5487        };
5488        helpers.EPSILON = Number.EPSILON || 1e-14;
5489        helpers.splineCurveMonotone = function(points) {
5490            // This function calculates Bézier control points in a similar way than |splineCurve|,
5491            // but preserves monotonicity of the provided data and ensures no local extremums are added
5492            // between the dataset discrete points due to the interpolation.
5493            // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
5494
5495            var pointsWithTangents = (points || []).map(function(point) {
5496                return {
5497                    model: point._model,
5498                    deltaK: 0,
5499                    mK: 0
5500                };
5501            });
5502
5503            // Calculate slopes (deltaK) and initialize tangents (mK)
5504            var pointsLen = pointsWithTangents.length;
5505            var i, pointBefore, pointCurrent, pointAfter;
5506            for (i = 0; i < pointsLen; ++i) {
5507                pointCurrent = pointsWithTangents[i];
5508                if (pointCurrent.model.skip) {
5509                    continue;
5510                }
5511
5512                pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
5513                pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
5514                if (pointAfter && !pointAfter.model.skip) {
5515                    var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);
5516
5517                    // In the case of two points that appear at the same x pixel, slopeDeltaX is 0
5518                    pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;
5519                }
5520
5521                if (!pointBefore || pointBefore.model.skip) {
5522                    pointCurrent.mK = pointCurrent.deltaK;
5523                } else if (!pointAfter || pointAfter.model.skip) {
5524                    pointCurrent.mK = pointBefore.deltaK;
5525                } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
5526                    pointCurrent.mK = 0;
5527                } else {
5528                    pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
5529                }
5530            }
5531
5532            // Adjust tangents to ensure monotonic properties
5533            var alphaK, betaK, tauK, squaredMagnitude;
5534            for (i = 0; i < pointsLen - 1; ++i) {
5535                pointCurrent = pointsWithTangents[i];
5536                pointAfter = pointsWithTangents[i + 1];
5537                if (pointCurrent.model.skip || pointAfter.model.skip) {
5538                    continue;
5539                }
5540
5541                if (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
5542                    pointCurrent.mK = pointAfter.mK = 0;
5543                    continue;
5544                }
5545
5546                alphaK = pointCurrent.mK / pointCurrent.deltaK;
5547                betaK = pointAfter.mK / pointCurrent.deltaK;
5548                squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
5549                if (squaredMagnitude <= 9) {
5550                    continue;
5551                }
5552
5553                tauK = 3 / Math.sqrt(squaredMagnitude);
5554                pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
5555                pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
5556            }
5557
5558            // Compute control points
5559            var deltaX;
5560            for (i = 0; i < pointsLen; ++i) {
5561                pointCurrent = pointsWithTangents[i];
5562                if (pointCurrent.model.skip) {
5563                    continue;
5564                }
5565
5566                pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
5567                pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
5568                if (pointBefore && !pointBefore.model.skip) {
5569                    deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
5570                    pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
5571                    pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
5572                }
5573                if (pointAfter && !pointAfter.model.skip) {
5574                    deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
5575                    pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
5576                    pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
5577                }
5578            }
5579        };
5580        helpers.nextItem = function(collection, index, loop) {
5581            if (loop) {
5582                return index >= collection.length - 1 ? collection[0] : collection[index + 1];
5583            }
5584            return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
5585        };
5586        helpers.previousItem = function(collection, index, loop) {
5587            if (loop) {
5588                return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
5589            }
5590            return index <= 0 ? collection[0] : collection[index - 1];
5591        };
5592        // Implementation of the nice number algorithm used in determining where axis labels will go
5593        helpers.niceNum = function(range, round) {
5594            var exponent = Math.floor(helpers.log10(range));
5595            var fraction = range / Math.pow(10, exponent);
5596            var niceFraction;
5597
5598            if (round) {
5599                if (fraction < 1.5) {
5600                    niceFraction = 1;
5601                } else if (fraction < 3) {
5602                    niceFraction = 2;
5603                } else if (fraction < 7) {
5604                    niceFraction = 5;
5605                } else {
5606                    niceFraction = 10;
5607                }
5608            } else if (fraction <= 1.0) {
5609                niceFraction = 1;
5610            } else if (fraction <= 2) {
5611                niceFraction = 2;
5612            } else if (fraction <= 5) {
5613                niceFraction = 5;
5614            } else {
5615                niceFraction = 10;
5616            }
5617
5618            return niceFraction * Math.pow(10, exponent);
5619        };
5620        // Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
5621        helpers.requestAnimFrame = (function() {
5622            if (typeof window === 'undefined') {
5623                return function(callback) {
5624                    callback();
5625                };
5626            }
5627            return window.requestAnimationFrame ||
5628                window.webkitRequestAnimationFrame ||
5629                window.mozRequestAnimationFrame ||
5630                window.oRequestAnimationFrame ||
5631                window.msRequestAnimationFrame ||
5632                function(callback) {
5633                    return window.setTimeout(callback, 1000 / 60);
5634                };
5635        }());
5636        // -- DOM methods
5637        helpers.getRelativePosition = function(evt, chart) {
5638            var mouseX, mouseY;
5639            var e = evt.originalEvent || evt;
5640            var canvas = evt.target || evt.srcElement;
5641            var boundingRect = canvas.getBoundingClientRect();
5642
5643            var touches = e.touches;
5644            if (touches && touches.length > 0) {
5645                mouseX = touches[0].clientX;
5646                mouseY = touches[0].clientY;
5647
5648            } else {
5649                mouseX = e.clientX;
5650                mouseY = e.clientY;
5651            }
5652
5653            // Scale mouse coordinates into canvas coordinates
5654            // by following the pattern laid out by 'jerryj' in the comments of
5655            // http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
5656            var paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left'));
5657            var paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top'));
5658            var paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right'));
5659            var paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom'));
5660            var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
5661            var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
5662
5663            // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
5664            // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
5665            mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
5666            mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);
5667
5668            return {
5669                x: mouseX,
5670                y: mouseY
5671            };
5672
5673        };
5674
5675        // Private helper function to convert max-width/max-height values that may be percentages into a number
5676        function parseMaxStyle(styleValue, node, parentProperty) {
5677            var valueInPixels;
5678            if (typeof styleValue === 'string') {
5679                valueInPixels = parseInt(styleValue, 10);
5680
5681                if (styleValue.indexOf('%') !== -1) {
5682                    // percentage * size in dimension
5683                    valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
5684                }
5685            } else {
5686                valueInPixels = styleValue;
5687            }
5688
5689            return valueInPixels;
5690        }
5691
5692        /**
5693         * Returns if the given value contains an effective constraint.
5694         * @private
5695         */
5696        function isConstrainedValue(value) {
5697            return value !== undefined && value !== null && value !== 'none';
5698        }
5699
5700        // Private helper to get a constraint dimension
5701        // @param domNode : the node to check the constraint on
5702        // @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight)
5703        // @param percentageProperty : property of parent to use when calculating width as a percentage
5704        // @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser
5705        function getConstraintDimension(domNode, maxStyle, percentageProperty) {
5706            var view = document.defaultView;
5707            var parentNode = helpers._getParentNode(domNode);
5708            var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
5709            var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
5710            var hasCNode = isConstrainedValue(constrainedNode);
5711            var hasCContainer = isConstrainedValue(constrainedContainer);
5712            var infinity = Number.POSITIVE_INFINITY;
5713
5714            if (hasCNode || hasCContainer) {
5715                return Math.min(
5716                    hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
5717                    hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
5718            }
5719
5720            return 'none';
5721        }
5722        // returns Number or undefined if no constraint
5723        helpers.getConstraintWidth = function(domNode) {
5724            return getConstraintDimension(domNode, 'max-width', 'clientWidth');
5725        };
5726        // returns Number or undefined if no constraint
5727        helpers.getConstraintHeight = function(domNode) {
5728            return getConstraintDimension(domNode, 'max-height', 'clientHeight');
5729        };
5730        /**
5731         * @private
5732         */
5733        helpers._calculatePadding = function(container, padding, parentDimension) {
5734            padding = helpers.getStyle(container, padding);
5735
5736            return padding.indexOf('%') > -1 ? parentDimension / parseInt(padding, 10) : parseInt(padding, 10);
5737        };
5738        /**
5739         * @private
5740         */
5741        helpers._getParentNode = function(domNode) {
5742            var parent = domNode.parentNode;
5743            if (parent && parent.host) {
5744                parent = parent.host;
5745            }
5746            return parent;
5747        };
5748        helpers.getMaximumWidth = function(domNode) {
5749            var container = helpers._getParentNode(domNode);
5750            if (!container) {
5751                return domNode.clientWidth;
5752            }
5753
5754            var clientWidth = container.clientWidth;
5755            var paddingLeft = helpers._calculatePadding(container, 'padding-left', clientWidth);
5756            var paddingRight = helpers._calculatePadding(container, 'padding-right', clientWidth);
5757
5758            var w = clientWidth - paddingLeft - paddingRight;
5759            var cw = helpers.getConstraintWidth(domNode);
5760            return isNaN(cw) ? w : Math.min(w, cw);
5761        };
5762        helpers.getMaximumHeight = function(domNode) {
5763            var container = helpers._getParentNode(domNode);
5764            if (!container) {
5765                return domNode.clientHeight;
5766            }
5767
5768            var clientHeight = container.clientHeight;
5769            var paddingTop = helpers._calculatePadding(container, 'padding-top', clientHeight);
5770            var paddingBottom = helpers._calculatePadding(container, 'padding-bottom', clientHeight);
5771
5772            var h = clientHeight - paddingTop - paddingBottom;
5773            var ch = helpers.getConstraintHeight(domNode);
5774            return isNaN(ch) ? h : Math.min(h, ch);
5775        };
5776        helpers.getStyle = function(el, property) {
5777            return el.currentStyle ?
5778                el.currentStyle[property] :
5779                document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
5780        };
5781        helpers.retinaScale = function(chart, forceRatio) {
5782            var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1;
5783            if (pixelRatio === 1) {
5784                return;
5785            }
5786
5787            var canvas = chart.canvas;
5788            var height = chart.height;
5789            var width = chart.width;
5790
5791            canvas.height = height * pixelRatio;
5792            canvas.width = width * pixelRatio;
5793            chart.ctx.scale(pixelRatio, pixelRatio);
5794
5795            // If no style has been set on the canvas, the render size is used as display size,
5796            // making the chart visually bigger, so let's enforce it to the "correct" values.
5797            // See https://github.com/chartjs/Chart.js/issues/3575
5798            if (!canvas.style.height && !canvas.style.width) {
5799                canvas.style.height = height + 'px';
5800                canvas.style.width = width + 'px';
5801            }
5802        };
5803        // -- Canvas methods
5804        helpers.fontString = function(pixelSize, fontStyle, fontFamily) {
5805            return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
5806        };
5807        helpers.longestText = function(ctx, font, arrayOfThings, cache) {
5808            cache = cache || {};
5809            var data = cache.data = cache.data || {};
5810            var gc = cache.garbageCollect = cache.garbageCollect || [];
5811
5812            if (cache.font !== font) {
5813                data = cache.data = {};
5814                gc = cache.garbageCollect = [];
5815                cache.font = font;
5816            }
5817
5818            ctx.font = font;
5819            var longest = 0;
5820            helpers.each(arrayOfThings, function(thing) {
5821                // Undefined strings and arrays should not be measured
5822                if (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) {
5823                    longest = helpers.measureText(ctx, data, gc, longest, thing);
5824                } else if (helpers.isArray(thing)) {
5825                    // if it is an array lets measure each element
5826                    // to do maybe simplify this function a bit so we can do this more recursively?
5827                    helpers.each(thing, function(nestedThing) {
5828                        // Undefined strings and arrays should not be measured
5829                        if (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) {
5830                            longest = helpers.measureText(ctx, data, gc, longest, nestedThing);
5831                        }
5832                    });
5833                }
5834            });
5835
5836            var gcLen = gc.length / 2;
5837            if (gcLen > arrayOfThings.length) {
5838                for (var i = 0; i < gcLen; i++) {
5839                    delete data[gc[i]];
5840                }
5841                gc.splice(0, gcLen);
5842            }
5843            return longest;
5844        };
5845        helpers.measureText = function(ctx, data, gc, longest, string) {
5846            var textWidth = data[string];
5847            if (!textWidth) {
5848                textWidth = data[string] = ctx.measureText(string).width;
5849                gc.push(string);
5850            }
5851            if (textWidth > longest) {
5852                longest = textWidth;
5853            }
5854            return longest;
5855        };
5856        helpers.numberOfLabelLines = function(arrayOfThings) {
5857            var numberOfLines = 1;
5858            helpers.each(arrayOfThings, function(thing) {
5859                if (helpers.isArray(thing)) {
5860                    if (thing.length > numberOfLines) {
5861                        numberOfLines = thing.length;
5862                    }
5863                }
5864            });
5865            return numberOfLines;
5866        };
5867
5868        helpers.color = !color ?
5869            function(value) {
5870                console.error('Color.js not found!');
5871                return value;
5872            } :
5873            function(value) {
5874                /* global CanvasGradient */
5875                if (value instanceof CanvasGradient) {
5876                    value = defaults.global.defaultColor;
5877                }
5878
5879                return color(value);
5880            };
5881
5882        helpers.getHoverColor = function(colorValue) {
5883            /* global CanvasPattern */
5884            return (colorValue instanceof CanvasPattern) ?
5885                colorValue :
5886                helpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();
5887        };
5888    };
5889
5890},{"26":26,"3":3,"34":34,"46":46}],29:[function(require,module,exports){
5891    'use strict';
5892
5893    var helpers = require(46);
5894
5895    /**
5896     * Helper function to get relative position for an event
5897     * @param {Event|IEvent} event - The event to get the position for
5898     * @param {Chart} chart - The chart
5899     * @returns {Point} the event position
5900     */
5901    function getRelativePosition(e, chart) {
5902        if (e.native) {
5903            return {
5904                x: e.x,
5905                y: e.y
5906            };
5907        }
5908
5909        return helpers.getRelativePosition(e, chart);
5910    }
5911
5912    /**
5913     * Helper function to traverse all of the visible elements in the chart
5914     * @param chart {chart} the chart
5915     * @param handler {Function} the callback to execute for each visible item
5916     */
5917    function parseVisibleItems(chart, handler) {
5918        var datasets = chart.data.datasets;
5919        var meta, i, j, ilen, jlen;
5920
5921        for (i = 0, ilen = datasets.length; i < ilen; ++i) {
5922            if (!chart.isDatasetVisible(i)) {
5923                continue;
5924            }
5925
5926            meta = chart.getDatasetMeta(i);
5927            for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
5928                var element = meta.data[j];
5929                if (!element._view.skip) {
5930                    handler(element);
5931                }
5932            }
5933        }
5934    }
5935
5936    /**
5937     * Helper function to get the items that intersect the event position
5938     * @param items {ChartElement[]} elements to filter
5939     * @param position {Point} the point to be nearest to
5940     * @return {ChartElement[]} the nearest items
5941     */
5942    function getIntersectItems(chart, position) {
5943        var elements = [];
5944
5945        parseVisibleItems(chart, function(element) {
5946            if (element.inRange(position.x, position.y)) {
5947                elements.push(element);
5948            }
5949        });
5950
5951        return elements;
5952    }
5953
5954    /**
5955     * Helper function to get the items nearest to the event position considering all visible items in teh chart
5956     * @param chart {Chart} the chart to look at elements from
5957     * @param position {Point} the point to be nearest to
5958     * @param intersect {Boolean} if true, only consider items that intersect the position
5959     * @param distanceMetric {Function} function to provide the distance between points
5960     * @return {ChartElement[]} the nearest items
5961     */
5962    function getNearestItems(chart, position, intersect, distanceMetric) {
5963        var minDistance = Number.POSITIVE_INFINITY;
5964        var nearestItems = [];
5965
5966        parseVisibleItems(chart, function(element) {
5967            if (intersect && !element.inRange(position.x, position.y)) {
5968                return;
5969            }
5970
5971            var center = element.getCenterPoint();
5972            var distance = distanceMetric(position, center);
5973
5974            if (distance < minDistance) {
5975                nearestItems = [element];
5976                minDistance = distance;
5977            } else if (distance === minDistance) {
5978                // Can have multiple items at the same distance in which case we sort by size
5979                nearestItems.push(element);
5980            }
5981        });
5982
5983        return nearestItems;
5984    }
5985
5986    /**
5987     * Get a distance metric function for two points based on the
5988     * axis mode setting
5989     * @param {String} axis the axis mode. x|y|xy
5990     */
5991    function getDistanceMetricForAxis(axis) {
5992        var useX = axis.indexOf('x') !== -1;
5993        var useY = axis.indexOf('y') !== -1;
5994
5995        return function(pt1, pt2) {
5996            var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
5997            var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
5998            return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
5999        };
6000    }
6001
6002    function indexMode(chart, e, options) {
6003        var position = getRelativePosition(e, chart);
6004        // Default axis for index mode is 'x' to match old behaviour
6005        options.axis = options.axis || 'x';
6006        var distanceMetric = getDistanceMetricForAxis(options.axis);
6007        var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
6008        var elements = [];
6009
6010        if (!items.length) {
6011            return [];
6012        }
6013
6014        chart.data.datasets.forEach(function(dataset, datasetIndex) {
6015            if (chart.isDatasetVisible(datasetIndex)) {
6016                var meta = chart.getDatasetMeta(datasetIndex);
6017                var element = meta.data[items[0]._index];
6018
6019                // don't count items that are skipped (null data)
6020                if (element && !element._view.skip) {
6021                    elements.push(element);
6022                }
6023            }
6024        });
6025
6026        return elements;
6027    }
6028
6029    /**
6030     * @interface IInteractionOptions
6031     */
6032    /**
6033     * If true, only consider items that intersect the point
6034     * @name IInterfaceOptions#boolean
6035     * @type Boolean
6036     */
6037
6038    /**
6039     * Contains interaction related functions
6040     * @namespace Chart.Interaction
6041     */
6042    module.exports = {
6043        // Helper function for different modes
6044        modes: {
6045            single: function(chart, e) {
6046                var position = getRelativePosition(e, chart);
6047                var elements = [];
6048
6049                parseVisibleItems(chart, function(element) {
6050                    if (element.inRange(position.x, position.y)) {
6051                        elements.push(element);
6052                        return elements;
6053                    }
6054                });
6055
6056                return elements.slice(0, 1);
6057            },
6058
6059            /**
6060             * @function Chart.Interaction.modes.label
6061             * @deprecated since version 2.4.0
6062             * @todo remove at version 3
6063             * @private
6064             */
6065            label: indexMode,
6066
6067            /**
6068             * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
6069             * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
6070             * @function Chart.Interaction.modes.index
6071             * @since v2.4.0
6072             * @param chart {chart} the chart we are returning items from
6073             * @param e {Event} the event we are find things at
6074             * @param options {IInteractionOptions} options to use during interaction
6075             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6076             */
6077            index: indexMode,
6078
6079            /**
6080             * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
6081             * If the options.intersect is false, we find the nearest item and return the items in that dataset
6082             * @function Chart.Interaction.modes.dataset
6083             * @param chart {chart} the chart we are returning items from
6084             * @param e {Event} the event we are find things at
6085             * @param options {IInteractionOptions} options to use during interaction
6086             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6087             */
6088            dataset: function(chart, e, options) {
6089                var position = getRelativePosition(e, chart);
6090                options.axis = options.axis || 'xy';
6091                var distanceMetric = getDistanceMetricForAxis(options.axis);
6092                var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
6093
6094                if (items.length > 0) {
6095                    items = chart.getDatasetMeta(items[0]._datasetIndex).data;
6096                }
6097
6098                return items;
6099            },
6100
6101            /**
6102             * @function Chart.Interaction.modes.x-axis
6103             * @deprecated since version 2.4.0. Use index mode and intersect == true
6104             * @todo remove at version 3
6105             * @private
6106             */
6107            'x-axis': function(chart, e) {
6108                return indexMode(chart, e, {intersect: false});
6109            },
6110
6111            /**
6112             * Point mode returns all elements that hit test based on the event position
6113             * of the event
6114             * @function Chart.Interaction.modes.intersect
6115             * @param chart {chart} the chart we are returning items from
6116             * @param e {Event} the event we are find things at
6117             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6118             */
6119            point: function(chart, e) {
6120                var position = getRelativePosition(e, chart);
6121                return getIntersectItems(chart, position);
6122            },
6123
6124            /**
6125             * nearest mode returns the element closest to the point
6126             * @function Chart.Interaction.modes.intersect
6127             * @param chart {chart} the chart we are returning items from
6128             * @param e {Event} the event we are find things at
6129             * @param options {IInteractionOptions} options to use
6130             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6131             */
6132            nearest: function(chart, e, options) {
6133                var position = getRelativePosition(e, chart);
6134                options.axis = options.axis || 'xy';
6135                var distanceMetric = getDistanceMetricForAxis(options.axis);
6136                var nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric);
6137
6138                // We have multiple items at the same distance from the event. Now sort by smallest
6139                if (nearestItems.length > 1) {
6140                    nearestItems.sort(function(a, b) {
6141                        var sizeA = a.getArea();
6142                        var sizeB = b.getArea();
6143                        var ret = sizeA - sizeB;
6144
6145                        if (ret === 0) {
6146                            // if equal sort by dataset index
6147                            ret = a._datasetIndex - b._datasetIndex;
6148                        }
6149
6150                        return ret;
6151                    });
6152                }
6153
6154                // Return only 1 item
6155                return nearestItems.slice(0, 1);
6156            },
6157
6158            /**
6159             * x mode returns the elements that hit-test at the current x coordinate
6160             * @function Chart.Interaction.modes.x
6161             * @param chart {chart} the chart we are returning items from
6162             * @param e {Event} the event we are find things at
6163             * @param options {IInteractionOptions} options to use
6164             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6165             */
6166            x: function(chart, e, options) {
6167                var position = getRelativePosition(e, chart);
6168                var items = [];
6169                var intersectsItem = false;
6170
6171                parseVisibleItems(chart, function(element) {
6172                    if (element.inXRange(position.x)) {
6173                        items.push(element);
6174                    }
6175
6176                    if (element.inRange(position.x, position.y)) {
6177                        intersectsItem = true;
6178                    }
6179                });
6180
6181                // If we want to trigger on an intersect and we don't have any items
6182                // that intersect the position, return nothing
6183                if (options.intersect && !intersectsItem) {
6184                    items = [];
6185                }
6186                return items;
6187            },
6188
6189            /**
6190             * y mode returns the elements that hit-test at the current y coordinate
6191             * @function Chart.Interaction.modes.y
6192             * @param chart {chart} the chart we are returning items from
6193             * @param e {Event} the event we are find things at
6194             * @param options {IInteractionOptions} options to use
6195             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
6196             */
6197            y: function(chart, e, options) {
6198                var position = getRelativePosition(e, chart);
6199                var items = [];
6200                var intersectsItem = false;
6201
6202                parseVisibleItems(chart, function(element) {
6203                    if (element.inYRange(position.y)) {
6204                        items.push(element);
6205                    }
6206
6207                    if (element.inRange(position.x, position.y)) {
6208                        intersectsItem = true;
6209                    }
6210                });
6211
6212                // If we want to trigger on an intersect and we don't have any items
6213                // that intersect the position, return nothing
6214                if (options.intersect && !intersectsItem) {
6215                    items = [];
6216                }
6217                return items;
6218            }
6219        }
6220    };
6221
6222},{"46":46}],30:[function(require,module,exports){
6223    'use strict';
6224
6225    var defaults = require(26);
6226
6227    defaults._set('global', {
6228        responsive: true,
6229        responsiveAnimationDuration: 0,
6230        maintainAspectRatio: true,
6231        events: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],
6232        hover: {
6233            onHover: null,
6234            mode: 'nearest',
6235            intersect: true,
6236            animationDuration: 400
6237        },
6238        onClick: null,
6239        defaultColor: 'rgba(0,0,0,0.1)',
6240        defaultFontColor: '#666',
6241        defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
6242        defaultFontSize: 12,
6243        defaultFontStyle: 'normal',
6244        showLines: true,
6245
6246        // Element defaults defined in element extensions
6247        elements: {},
6248
6249        // Layout options such as padding
6250        layout: {
6251            padding: {
6252                top: 0,
6253                right: 0,
6254                bottom: 0,
6255                left: 0
6256            }
6257        }
6258    });
6259
6260    module.exports = function() {
6261
6262        // Occupy the global variable of Chart, and create a simple base class
6263        var Chart = function(item, config) {
6264            this.construct(item, config);
6265            return this;
6266        };
6267
6268        Chart.Chart = Chart;
6269
6270        return Chart;
6271    };
6272
6273},{"26":26}],31:[function(require,module,exports){
6274    'use strict';
6275
6276    var helpers = require(46);
6277
6278    function filterByPosition(array, position) {
6279        return helpers.where(array, function(v) {
6280            return v.position === position;
6281        });
6282    }
6283
6284    function sortByWeight(array, reverse) {
6285        array.forEach(function(v, i) {
6286            v._tmpIndex_ = i;
6287            return v;
6288        });
6289        array.sort(function(a, b) {
6290            var v0 = reverse ? b : a;
6291            var v1 = reverse ? a : b;
6292            return v0.weight === v1.weight ?
6293                v0._tmpIndex_ - v1._tmpIndex_ :
6294                v0.weight - v1.weight;
6295        });
6296        array.forEach(function(v) {
6297            delete v._tmpIndex_;
6298        });
6299    }
6300
6301    /**
6302     * @interface ILayoutItem
6303     * @prop {String} position - The position of the item in the chart layout. Possible values are
6304     * 'left', 'top', 'right', 'bottom', and 'chartArea'
6305     * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area
6306     * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down
6307     * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)
6308     * @prop {Function} update - Takes two parameters: width and height. Returns size of item
6309     * @prop {Function} getPadding -  Returns an object with padding on the edges
6310     * @prop {Number} width - Width of item. Must be valid after update()
6311     * @prop {Number} height - Height of item. Must be valid after update()
6312     * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update
6313     * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update
6314     * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update
6315     * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update
6316     */
6317
6318// The layout service is very self explanatory.  It's responsible for the layout within a chart.
6319// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
6320// It is this service's responsibility of carrying out that layout.
6321    module.exports = {
6322        defaults: {},
6323
6324        /**
6325         * Register a box to a chart.
6326         * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
6327         * @param {Chart} chart - the chart to use
6328         * @param {ILayoutItem} item - the item to add to be layed out
6329         */
6330        addBox: function(chart, item) {
6331            if (!chart.boxes) {
6332                chart.boxes = [];
6333            }
6334
6335            // initialize item with default values
6336            item.fullWidth = item.fullWidth || false;
6337            item.position = item.position || 'top';
6338            item.weight = item.weight || 0;
6339
6340            chart.boxes.push(item);
6341        },
6342
6343        /**
6344         * Remove a layoutItem from a chart
6345         * @param {Chart} chart - the chart to remove the box from
6346         * @param {Object} layoutItem - the item to remove from the layout
6347         */
6348        removeBox: function(chart, layoutItem) {
6349            var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
6350            if (index !== -1) {
6351                chart.boxes.splice(index, 1);
6352            }
6353        },
6354
6355        /**
6356         * Sets (or updates) options on the given `item`.
6357         * @param {Chart} chart - the chart in which the item lives (or will be added to)
6358         * @param {Object} item - the item to configure with the given options
6359         * @param {Object} options - the new item options.
6360         */
6361        configure: function(chart, item, options) {
6362            var props = ['fullWidth', 'position', 'weight'];
6363            var ilen = props.length;
6364            var i = 0;
6365            var prop;
6366
6367            for (; i < ilen; ++i) {
6368                prop = props[i];
6369                if (options.hasOwnProperty(prop)) {
6370                    item[prop] = options[prop];
6371                }
6372            }
6373        },
6374
6375        /**
6376         * Fits boxes of the given chart into the given size by having each box measure itself
6377         * then running a fitting algorithm
6378         * @param {Chart} chart - the chart
6379         * @param {Number} width - the width to fit into
6380         * @param {Number} height - the height to fit into
6381         */
6382        update: function(chart, width, height) {
6383            if (!chart) {
6384                return;
6385            }
6386
6387            var layoutOptions = chart.options.layout || {};
6388            var padding = helpers.options.toPadding(layoutOptions.padding);
6389            var leftPadding = padding.left;
6390            var rightPadding = padding.right;
6391            var topPadding = padding.top;
6392            var bottomPadding = padding.bottom;
6393
6394            var leftBoxes = filterByPosition(chart.boxes, 'left');
6395            var rightBoxes = filterByPosition(chart.boxes, 'right');
6396            var topBoxes = filterByPosition(chart.boxes, 'top');
6397            var bottomBoxes = filterByPosition(chart.boxes, 'bottom');
6398            var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea');
6399
6400            // Sort boxes by weight. A higher weight is further away from the chart area
6401            sortByWeight(leftBoxes, true);
6402            sortByWeight(rightBoxes, false);
6403            sortByWeight(topBoxes, true);
6404            sortByWeight(bottomBoxes, false);
6405
6406            // Essentially we now have any number of boxes on each of the 4 sides.
6407            // Our canvas looks like the following.
6408            // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
6409            // B1 is the bottom axis
6410            // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
6411            // These locations are single-box locations only, when trying to register a chartArea location that is already taken,
6412            // an error will be thrown.
6413            //
6414            // |----------------------------------------------------|
6415            // |                  T1 (Full Width)                   |
6416            // |----------------------------------------------------|
6417            // |    |    |                 T2                  |    |
6418            // |    |----|-------------------------------------|----|
6419            // |    |    | C1 |                           | C2 |    |
6420            // |    |    |----|                           |----|    |
6421            // |    |    |                                     |    |
6422            // | L1 | L2 |           ChartArea (C0)            | R1 |
6423            // |    |    |                                     |    |
6424            // |    |    |----|                           |----|    |
6425            // |    |    | C3 |                           | C4 |    |
6426            // |    |----|-------------------------------------|----|
6427            // |    |    |                 B1                  |    |
6428            // |----------------------------------------------------|
6429            // |                  B2 (Full Width)                   |
6430            // |----------------------------------------------------|
6431            //
6432            // What we do to find the best sizing, we do the following
6433            // 1. Determine the minimum size of the chart area.
6434            // 2. Split the remaining width equally between each vertical axis
6435            // 3. Split the remaining height equally between each horizontal axis
6436            // 4. Give each layout the maximum size it can be. The layout will return it's minimum size
6437            // 5. Adjust the sizes of each axis based on it's minimum reported size.
6438            // 6. Refit each axis
6439            // 7. Position each axis in the final location
6440            // 8. Tell the chart the final location of the chart area
6441            // 9. Tell any axes that overlay the chart area the positions of the chart area
6442
6443            // Step 1
6444            var chartWidth = width - leftPadding - rightPadding;
6445            var chartHeight = height - topPadding - bottomPadding;
6446            var chartAreaWidth = chartWidth / 2; // min 50%
6447            var chartAreaHeight = chartHeight / 2; // min 50%
6448
6449            // Step 2
6450            var verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);
6451
6452            // Step 3
6453            var horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);
6454
6455            // Step 4
6456            var maxChartAreaWidth = chartWidth;
6457            var maxChartAreaHeight = chartHeight;
6458            var minBoxSizes = [];
6459
6460            function getMinimumBoxSize(box) {
6461                var minSize;
6462                var isHorizontal = box.isHorizontal();
6463
6464                if (isHorizontal) {
6465                    minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);
6466                    maxChartAreaHeight -= minSize.height;
6467                } else {
6468                    minSize = box.update(verticalBoxWidth, maxChartAreaHeight);
6469                    maxChartAreaWidth -= minSize.width;
6470                }
6471
6472                minBoxSizes.push({
6473                    horizontal: isHorizontal,
6474                    minSize: minSize,
6475                    box: box,
6476                });
6477            }
6478
6479            helpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);
6480
6481            // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478)
6482            var maxHorizontalLeftPadding = 0;
6483            var maxHorizontalRightPadding = 0;
6484            var maxVerticalTopPadding = 0;
6485            var maxVerticalBottomPadding = 0;
6486
6487            helpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) {
6488                if (horizontalBox.getPadding) {
6489                    var boxPadding = horizontalBox.getPadding();
6490                    maxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left);
6491                    maxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right);
6492                }
6493            });
6494
6495            helpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) {
6496                if (verticalBox.getPadding) {
6497                    var boxPadding = verticalBox.getPadding();
6498                    maxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top);
6499                    maxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom);
6500                }
6501            });
6502
6503            // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
6504            // be if the axes are drawn at their minimum sizes.
6505            // Steps 5 & 6
6506            var totalLeftBoxesWidth = leftPadding;
6507            var totalRightBoxesWidth = rightPadding;
6508            var totalTopBoxesHeight = topPadding;
6509            var totalBottomBoxesHeight = bottomPadding;
6510
6511            // Function to fit a box
6512            function fitBox(box) {
6513                var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) {
6514                    return minBox.box === box;
6515                });
6516
6517                if (minBoxSize) {
6518                    if (box.isHorizontal()) {
6519                        var scaleMargin = {
6520                            left: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding),
6521                            right: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding),
6522                            top: 0,
6523                            bottom: 0
6524                        };
6525
6526                        // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
6527                        // on the margin. Sometimes they need to increase in size slightly
6528                        box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
6529                    } else {
6530                        box.update(minBoxSize.minSize.width, maxChartAreaHeight);
6531                    }
6532                }
6533            }
6534
6535            // Update, and calculate the left and right margins for the horizontal boxes
6536            helpers.each(leftBoxes.concat(rightBoxes), fitBox);
6537
6538            helpers.each(leftBoxes, function(box) {
6539                totalLeftBoxesWidth += box.width;
6540            });
6541
6542            helpers.each(rightBoxes, function(box) {
6543                totalRightBoxesWidth += box.width;
6544            });
6545
6546            // Set the Left and Right margins for the horizontal boxes
6547            helpers.each(topBoxes.concat(bottomBoxes), fitBox);
6548
6549            // Figure out how much margin is on the top and bottom of the vertical boxes
6550            helpers.each(topBoxes, function(box) {
6551                totalTopBoxesHeight += box.height;
6552            });
6553
6554            helpers.each(bottomBoxes, function(box) {
6555                totalBottomBoxesHeight += box.height;
6556            });
6557
6558            function finalFitVerticalBox(box) {
6559                var minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) {
6560                    return minSize.box === box;
6561                });
6562
6563                var scaleMargin = {
6564                    left: 0,
6565                    right: 0,
6566                    top: totalTopBoxesHeight,
6567                    bottom: totalBottomBoxesHeight
6568                };
6569
6570                if (minBoxSize) {
6571                    box.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);
6572                }
6573            }
6574
6575            // Let the left layout know the final margin
6576            helpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);
6577
6578            // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
6579            totalLeftBoxesWidth = leftPadding;
6580            totalRightBoxesWidth = rightPadding;
6581            totalTopBoxesHeight = topPadding;
6582            totalBottomBoxesHeight = bottomPadding;
6583
6584            helpers.each(leftBoxes, function(box) {
6585                totalLeftBoxesWidth += box.width;
6586            });
6587
6588            helpers.each(rightBoxes, function(box) {
6589                totalRightBoxesWidth += box.width;
6590            });
6591
6592            helpers.each(topBoxes, function(box) {
6593                totalTopBoxesHeight += box.height;
6594            });
6595            helpers.each(bottomBoxes, function(box) {
6596                totalBottomBoxesHeight += box.height;
6597            });
6598
6599            // We may be adding some padding to account for rotated x axis labels
6600            var leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0);
6601            totalLeftBoxesWidth += leftPaddingAddition;
6602            totalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0);
6603
6604            var topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0);
6605            totalTopBoxesHeight += topPaddingAddition;
6606            totalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0);
6607
6608            // Figure out if our chart area changed. This would occur if the dataset layout label rotation
6609            // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
6610            // without calling `fit` again
6611            var newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;
6612            var newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;
6613
6614            if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
6615                helpers.each(leftBoxes, function(box) {
6616                    box.height = newMaxChartAreaHeight;
6617                });
6618
6619                helpers.each(rightBoxes, function(box) {
6620                    box.height = newMaxChartAreaHeight;
6621                });
6622
6623                helpers.each(topBoxes, function(box) {
6624                    if (!box.fullWidth) {
6625                        box.width = newMaxChartAreaWidth;
6626                    }
6627                });
6628
6629                helpers.each(bottomBoxes, function(box) {
6630                    if (!box.fullWidth) {
6631                        box.width = newMaxChartAreaWidth;
6632                    }
6633                });
6634
6635                maxChartAreaHeight = newMaxChartAreaHeight;
6636                maxChartAreaWidth = newMaxChartAreaWidth;
6637            }
6638
6639            // Step 7 - Position the boxes
6640            var left = leftPadding + leftPaddingAddition;
6641            var top = topPadding + topPaddingAddition;
6642
6643            function placeBox(box) {
6644                if (box.isHorizontal()) {
6645                    box.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth;
6646                    box.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth;
6647                    box.top = top;
6648                    box.bottom = top + box.height;
6649
6650                    // Move to next point
6651                    top = box.bottom;
6652
6653                } else {
6654
6655                    box.left = left;
6656                    box.right = left + box.width;
6657                    box.top = totalTopBoxesHeight;
6658                    box.bottom = totalTopBoxesHeight + maxChartAreaHeight;
6659
6660                    // Move to next point
6661                    left = box.right;
6662                }
6663            }
6664
6665            helpers.each(leftBoxes.concat(topBoxes), placeBox);
6666
6667            // Account for chart width and height
6668            left += maxChartAreaWidth;
6669            top += maxChartAreaHeight;
6670
6671            helpers.each(rightBoxes, placeBox);
6672            helpers.each(bottomBoxes, placeBox);
6673
6674            // Step 8
6675            chart.chartArea = {
6676                left: totalLeftBoxesWidth,
6677                top: totalTopBoxesHeight,
6678                right: totalLeftBoxesWidth + maxChartAreaWidth,
6679                bottom: totalTopBoxesHeight + maxChartAreaHeight
6680            };
6681
6682            // Step 9
6683            helpers.each(chartAreaBoxes, function(box) {
6684                box.left = chart.chartArea.left;
6685                box.top = chart.chartArea.top;
6686                box.right = chart.chartArea.right;
6687                box.bottom = chart.chartArea.bottom;
6688
6689                box.update(maxChartAreaWidth, maxChartAreaHeight);
6690            });
6691        }
6692    };
6693
6694},{"46":46}],32:[function(require,module,exports){
6695    'use strict';
6696
6697    var defaults = require(26);
6698    var helpers = require(46);
6699
6700    defaults._set('global', {
6701        plugins: {}
6702    });
6703
6704    /**
6705     * The plugin service singleton
6706     * @namespace Chart.plugins
6707     * @since 2.1.0
6708     */
6709    module.exports = {
6710        /**
6711         * Globally registered plugins.
6712         * @private
6713         */
6714        _plugins: [],
6715
6716        /**
6717         * This identifier is used to invalidate the descriptors cache attached to each chart
6718         * when a global plugin is registered or unregistered. In this case, the cache ID is
6719         * incremented and descriptors are regenerated during following API calls.
6720         * @private
6721         */
6722        _cacheId: 0,
6723
6724        /**
6725         * Registers the given plugin(s) if not already registered.
6726         * @param {Array|Object} plugins plugin instance(s).
6727         */
6728        register: function(plugins) {
6729            var p = this._plugins;
6730            ([]).concat(plugins).forEach(function(plugin) {
6731                if (p.indexOf(plugin) === -1) {
6732                    p.push(plugin);
6733                }
6734            });
6735
6736            this._cacheId++;
6737        },
6738
6739        /**
6740         * Unregisters the given plugin(s) only if registered.
6741         * @param {Array|Object} plugins plugin instance(s).
6742         */
6743        unregister: function(plugins) {
6744            var p = this._plugins;
6745            ([]).concat(plugins).forEach(function(plugin) {
6746                var idx = p.indexOf(plugin);
6747                if (idx !== -1) {
6748                    p.splice(idx, 1);
6749                }
6750            });
6751
6752            this._cacheId++;
6753        },
6754
6755        /**
6756         * Remove all registered plugins.
6757         * @since 2.1.5
6758         */
6759        clear: function() {
6760            this._plugins = [];
6761            this._cacheId++;
6762        },
6763
6764        /**
6765         * Returns the number of registered plugins?
6766         * @returns {Number}
6767         * @since 2.1.5
6768         */
6769        count: function() {
6770            return this._plugins.length;
6771        },
6772
6773        /**
6774         * Returns all registered plugin instances.
6775         * @returns {Array} array of plugin objects.
6776         * @since 2.1.5
6777         */
6778        getAll: function() {
6779            return this._plugins;
6780        },
6781
6782        /**
6783         * Calls enabled plugins for `chart` on the specified hook and with the given args.
6784         * This method immediately returns as soon as a plugin explicitly returns false. The
6785         * returned value can be used, for instance, to interrupt the current action.
6786         * @param {Object} chart - The chart instance for which plugins should be called.
6787         * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
6788         * @param {Array} [args] - Extra arguments to apply to the hook call.
6789         * @returns {Boolean} false if any of the plugins return false, else returns true.
6790         */
vendor: 4,295 bytes, lines 6791-6909
6791        notify: function(chart, hook, args) {
6792            var descriptors = this.descriptors(chart);
6793            var ilen = descriptors.length;
6794            var i, descriptor, plugin, params, method;
6795
6796            for (i = 0; i < ilen; ++i) {
6797                descriptor = descriptors[i];
6798                plugin = descriptor.plugin;
6799                method = plugin[hook];
6800                if (typeof method === 'function') {
6801                    params = [chart].concat(args || []);
6802                    params.push(descriptor.options);
6803                    if (method.apply(plugin, params) === false) {
6804                        return false;
6805                    }
6806                }
6807            }
6808
6809            return true;
6810        },
6811
6812        /**
6813         * Returns descriptors of enabled plugins for the given chart.
6814         * @returns {Array} [{ plugin, options }]
6815         * @private
6816         */
6817        descriptors: function(chart) {
6818            var cache = chart.$plugins || (chart.$plugins = {});
6819            if (cache.id === this._cacheId) {
6820                return cache.descriptors;
6821            }
6822
6823            var plugins = [];
6824            var descriptors = [];
6825            var config = (chart && chart.config) || {};
6826            var options = (config.options && config.options.plugins) || {};
6827
6828            this._plugins.concat(config.plugins || []).forEach(function(plugin) {
6829                var idx = plugins.indexOf(plugin);
6830                if (idx !== -1) {
6831                    return;
6832                }
6833
6834                var id = plugin.id;
6835                var opts = options[id];
6836                if (opts === false) {
6837                    return;
6838                }
6839
6840                if (opts === true) {
6841                    opts = helpers.clone(defaults.global.plugins[id]);
6842                }
6843
6844                plugins.push(plugin);
6845                descriptors.push({
6846                    plugin: plugin,
6847                    options: opts || {}
6848                });
6849            });
6850
6851            cache.descriptors = descriptors;
6852            cache.id = this._cacheId;
6853            return descriptors;
6854        },
6855
6856        /**
6857         * Invalidates cache for the given chart: descriptors hold a reference on plugin option,
6858         * but in some cases, this reference can be changed by the user when updating options.
6859         * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
6860         * @private
6861         */
6862        _invalidate: function(chart) {
6863            delete chart.$plugins;
6864        }
6865    };
6866
6867    /**
6868     * Plugin extension hooks.
6869     * @interface IPlugin
6870     * @since 2.1.0
6871     */
6872    /**
6873     * @method IPlugin#beforeInit
6874     * @desc Called before initializing `chart`.
6875     * @param {Chart.Controller} chart - The chart instance.
6876     * @param {Object} options - The plugin options.
6877     */
6878    /**
6879     * @method IPlugin#afterInit
6880     * @desc Called after `chart` has been initialized and before the first update.
6881     * @param {Chart.Controller} chart - The chart instance.
6882     * @param {Object} options - The plugin options.
6883     */
6884    /**
6885     * @method IPlugin#beforeUpdate
6886     * @desc Called before updating `chart`. If any plugin returns `false`, the update
6887     * is cancelled (and thus subsequent render(s)) until another `update` is triggered.
6888     * @param {Chart.Controller} chart - The chart instance.
6889     * @param {Object} options - The plugin options.
6890     * @returns {Boolean} `false` to cancel the chart update.
6891     */
6892    /**
6893     * @method IPlugin#afterUpdate
6894     * @desc Called after `chart` has been updated and before rendering. Note that this
6895     * hook will not be called if the chart update has been previously cancelled.
6896     * @param {Chart.Controller} chart - The chart instance.
6897     * @param {Object} options - The plugin options.
6898     */
6899    /**
6900     * @method IPlugin#beforeDatasetsUpdate
6901     * @desc Called before updating the `chart` datasets. If any plugin returns `false`,
6902     * the datasets update is cancelled until another `update` is triggered.
6903     * @param {Chart.Controller} chart - The chart instance.
6904     * @param {Object} options - The plugin options.
6905     * @returns {Boolean} false to cancel the datasets update.
6906     * @since version 2.1.5
6907     */
6908    /**
6909     * @method IPlugin#afterDatasetsUpdate
6910     * @desc Called after the `chart` datasets have been updated. Note that this hook
6911     * will not be called if the datasets update has been previously cancelled.
6912     * @param {Chart.Controller} chart - The chart instance.
6913     * @param {Object} options - The plugin options.
6914     * @since version 2.1.5
6915     */
6916    /**
6917     * @method IPlugin#beforeDatasetUpdate
6918     * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin
6919     * returns `false`, the datasets update is cancelled until another `update` is triggered.
6920     * @param {Chart} chart - The chart instance.
6921     * @param {Object} args - The call arguments.
6922     * @param {Number} args.index - The dataset index.
6923     * @param {Object} args.meta - The dataset metadata.
6924     * @param {Object} options - The plugin options.
6925     * @returns {Boolean} `false` to cancel the chart datasets drawing.
6926     */
6927    /**
6928     * @method IPlugin#afterDatasetUpdate
6929     * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note
6930     * that this hook will not be called if the datasets update has been previously cancelled.
6931     * @param {Chart} chart - The chart instance.
6932     * @param {Object} args - The call arguments.
6933     * @param {Number} args.index - The dataset index.
6934     * @param {Object} args.meta - The dataset metadata.
6935     * @param {Object} options - The plugin options.
6936     */
6937    /**
6938     * @method IPlugin#beforeLayout
6939     * @desc Called before laying out `chart`. If any plugin returns `false`,
6940     * the layout update is cancelled until another `update` is triggered.
6941     * @param {Chart.Controller} chart - The chart instance.
6942     * @param {Object} options - The plugin options.
6943     * @returns {Boolean} `false` to cancel the chart layout.
6944     */
6945    /**
6946     * @method IPlugin#afterLayout
6947     * @desc Called after the `chart` has been layed out. Note that this hook will not
6948     * be called if the layout update has been previously cancelled.
6949     * @param {Chart.Controller} chart - The chart instance.
6950     * @param {Object} options - The plugin options.
6951     */
6952    /**
6953     * @method IPlugin#beforeRender
6954     * @desc Called before rendering `chart`. If any plugin returns `false`,
6955     * the rendering is cancelled until another `render` is triggered.
6956     * @param {Chart.Controller} chart - The chart instance.
6957     * @param {Object} options - The plugin options.
6958     * @returns {Boolean} `false` to cancel the chart rendering.
6959     */
6960    /**
6961     * @method IPlugin#afterRender
6962     * @desc Called after the `chart` has been fully rendered (and animation completed). Note
6963     * that this hook will not be called if the rendering has been previously cancelled.
6964     * @param {Chart.Controller} chart - The chart instance.
6965     * @param {Object} options - The plugin options.
6966     */
6967    /**
6968     * @method IPlugin#beforeDraw
6969     * @desc Called before drawing `chart` at every animation frame specified by the given
6970     * easing value. If any plugin returns `false`, the frame drawing is cancelled until
6971     * another `render` is triggered.
6972     * @param {Chart.Controller} chart - The chart instance.
6973     * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
6974     * @param {Object} options - The plugin options.
6975     * @returns {Boolean} `false` to cancel the chart drawing.
6976     */
6977    /**
6978     * @method IPlugin#afterDraw
6979     * @desc Called after the `chart` has been drawn for the specific easing value. Note
6980     * that this hook will not be called if the drawing has been previously cancelled.
6981     * @param {Chart.Controller} chart - The chart instance.
6982     * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
6983     * @param {Object} options - The plugin options.
6984     */
6985    /**
6986     * @method IPlugin#beforeDatasetsDraw
6987     * @desc Called before drawing the `chart` datasets. If any plugin returns `false`,
6988     * the datasets drawing is cancelled until another `render` is triggered.
6989     * @param {Chart.Controller} chart - The chart instance.
6990     * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
6991     * @param {Object} options - The plugin options.
6992     * @returns {Boolean} `false` to cancel the chart datasets drawing.
6993     */
6994    /**
6995     * @method IPlugin#afterDatasetsDraw
6996     * @desc Called after the `chart` datasets have been drawn. Note that this hook
6997     * will not be called if the datasets drawing has been previously cancelled.
6998     * @param {Chart.Controller} chart - The chart instance.
6999     * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.
7000     * @param {Object} options - The plugin options.
7001     */
7002    /**
7003     * @method IPlugin#beforeDatasetDraw
7004     * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets
7005     * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing
7006     * is cancelled until another `render` is triggered.
7007     * @param {Chart} chart - The chart instance.
7008     * @param {Object} args - The call arguments.
7009     * @param {Number} args.index - The dataset index.
7010     * @param {Object} args.meta - The dataset metadata.
7011     * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7012     * @param {Object} options - The plugin options.
7013     * @returns {Boolean} `false` to cancel the chart datasets drawing.
7014     */
7015    /**
7016     * @method IPlugin#afterDatasetDraw
7017     * @desc Called after the `chart` datasets at the given `args.index` have been drawn
7018     * (datasets are drawn in the reverse order). Note that this hook will not be called
7019     * if the datasets drawing has been previously cancelled.
7020     * @param {Chart} chart - The chart instance.
7021     * @param {Object} args - The call arguments.
7022     * @param {Number} args.index - The dataset index.
7023     * @param {Object} args.meta - The dataset metadata.
7024     * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7025     * @param {Object} options - The plugin options.
7026     */
7027    /**
7028     * @method IPlugin#beforeTooltipDraw
7029     * @desc Called before drawing the `tooltip`. If any plugin returns `false`,
7030     * the tooltip drawing is cancelled until another `render` is triggered.
7031     * @param {Chart} chart - The chart instance.
7032     * @param {Object} args - The call arguments.
7033     * @param {Object} args.tooltip - The tooltip.
7034     * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7035     * @param {Object} options - The plugin options.
7036     * @returns {Boolean} `false` to cancel the chart tooltip drawing.
7037     */
7038    /**
7039     * @method IPlugin#afterTooltipDraw
7040     * @desc Called after drawing the `tooltip`. Note that this hook will not
7041     * be called if the tooltip drawing has been previously cancelled.
7042     * @param {Chart} chart - The chart instance.
7043     * @param {Object} args - The call arguments.
7044     * @param {Object} args.tooltip - The tooltip.
7045     * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.
7046     * @param {Object} options - The plugin options.
7047     */
7048    /**
7049     * @method IPlugin#beforeEvent
7050     * @desc Called before processing the specified `event`. If any plugin returns `false`,
7051     * the event will be discarded.
7052     * @param {Chart.Controller} chart - The chart instance.
7053     * @param {IEvent} event - The event object.
7054     * @param {Object} options - The plugin options.
7055     */
7056    /**
7057     * @method IPlugin#afterEvent
7058     * @desc Called after the `event` has been consumed. Note that this hook
7059     * will not be called if the `event` has been previously discarded.
7060     * @param {Chart.Controller} chart - The chart instance.
7061     * @param {IEvent} event - The event object.
7062     * @param {Object} options - The plugin options.
7063     */
7064    /**
7065     * @method IPlugin#resize
7066     * @desc Called after the chart as been resized.
7067     * @param {Chart.Controller} chart - The chart instance.
7068     * @param {Number} size - The new canvas display size (eq. canvas.style width & height).
7069     * @param {Object} options - The plugin options.
7070     */
7071    /**
7072     * @method IPlugin#destroy
7073     * @desc Called after the chart as been destroyed.
7074     * @param {Chart.Controller} chart - The chart instance.
7075     * @param {Object} options - The plugin options.
7076     */
7077
7078},{"26":26,"46":46}],33:[function(require,module,exports){
7079    'use strict';
7080
7081    var defaults = require(26);
7082    var Element = require(27);
7083    var helpers = require(46);
7084    var Ticks = require(35);
7085
7086    defaults._set('scale', {
7087        display: true,
7088        position: 'left',
7089        offset: false,
7090
7091        // grid line settings
7092        gridLines: {
7093            display: true,
7094            color: 'rgba(0, 0, 0, 0.1)',
7095            lineWidth: 1,
7096            drawBorder: true,
7097            drawOnChartArea: true,
7098            drawTicks: true,
7099            tickMarkLength: 10,
7100            zeroLineWidth: 1,
7101            zeroLineColor: 'rgba(0,0,0,0.25)',
7102            zeroLineBorderDash: [],
7103            zeroLineBorderDashOffset: 0.0,
7104            offsetGridLines: false,
7105            borderDash: [],
7106            borderDashOffset: 0.0
7107        },
7108
7109        // scale label
7110        scaleLabel: {
7111            // display property
7112            display: false,
7113
7114            // actual label
7115            labelString: '',
7116
7117            // line height
7118            lineHeight: 1.2,
7119
7120            // top/bottom padding
7121            padding: {
7122                top: 4,
7123                bottom: 4
7124            }
7125        },
7126
7127        // label settings
7128        ticks: {
7129            beginAtZero: false,
7130            minRotation: 0,
7131            maxRotation: 50,
7132            mirror: false,
7133            padding: 0,
7134            reverse: false,
7135            display: true,
7136            autoSkip: true,
7137            autoSkipPadding: 0,
7138            labelOffset: 0,
7139            // We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
7140            callback: Ticks.formatters.values,
7141            minor: {},
7142            major: {}
7143        }
7144    });
7145
7146    function labelsFromTicks(ticks) {
7147        var labels = [];
7148        var i, ilen;
7149
7150        for (i = 0, ilen = ticks.length; i < ilen; ++i) {
7151            labels.push(ticks[i].label);
7152        }
7153
7154        return labels;
7155    }
7156
7157    function getLineValue(scale, index, offsetGridLines) {
7158        var lineValue = scale.getPixelForTick(index);
7159
7160        if (offsetGridLines) {
7161            if (index === 0) {
7162                lineValue -= (scale.getPixelForTick(1) - lineValue) / 2;
7163            } else {
7164                lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2;
7165            }
7166        }
7167        return lineValue;
7168    }
7169
7170    function computeTextSize(context, tick, font) {
7171        return helpers.isArray(tick) ?
7172            helpers.longestText(context, font, tick) :
7173            context.measureText(tick).width;
7174    }
7175
7176    function parseFontOptions(options) {
7177        var valueOrDefault = helpers.valueOrDefault;
7178        var globalDefaults = defaults.global;
7179        var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
7180        var style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle);
7181        var family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily);
7182
7183        return {
7184            size: size,
7185            style: style,
7186            family: family,
7187            font: helpers.fontString(size, style, family)
7188        };
7189    }
7190
7191    function parseLineHeight(options) {
7192        return helpers.options.toLineHeight(
7193            helpers.valueOrDefault(options.lineHeight, 1.2),
7194            helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize));
7195    }
7196
7197    module.exports = Element.extend({
7198        /**
7199         * Get the padding needed for the scale
7200         * @method getPadding
7201         * @private
7202         * @returns {Padding} the necessary padding
7203         */
7204        getPadding: function() {
7205            var me = this;
7206            return {
7207                left: me.paddingLeft || 0,
7208                top: me.paddingTop || 0,
7209                right: me.paddingRight || 0,
7210                bottom: me.paddingBottom || 0
7211            };
7212        },
7213
7214        /**
7215         * Returns the scale tick objects ({label, major})
7216         * @since 2.7
7217         */
7218        getTicks: function() {
7219            return this._ticks;
7220        },
7221
7222        // These methods are ordered by lifecyle. Utilities then follow.
7223        // Any function defined here is inherited by all scale types.
7224        // Any function can be extended by the scale type
7225
7226        mergeTicksOptions: function() {
7227            var ticks = this.options.ticks;
7228            if (ticks.minor === false) {
7229                ticks.minor = {
7230                    display: false
7231                };
7232            }
7233            if (ticks.major === false) {
7234                ticks.major = {
7235                    display: false
7236                };
7237            }
7238            for (var key in ticks) {
7239                if (key !== 'major' && key !== 'minor') {
7240                    if (typeof ticks.minor[key] === 'undefined') {
7241                        ticks.minor[key] = ticks[key];
7242                    }
7243                    if (typeof ticks.major[key] === 'undefined') {
7244                        ticks.major[key] = ticks[key];
7245                    }
7246                }
7247            }
7248        },
7249        beforeUpdate: function() {
7250            helpers.callback(this.options.beforeUpdate, [this]);
7251        },
7252
7253        update: function(maxWidth, maxHeight, margins) {
7254            var me = this;
7255            var i, ilen, labels, label, ticks, tick;
7256
7257            // Update Lifecycle - Probably don't want to ever extend or over
vendor: 5,573 bytes, lines 7257-7417
7257write this function ;)
7258            me.beforeUpdate();
7259
7260            // Absorb the master measurements
7261            me.maxWidth = maxWidth;
7262            me.maxHeight = maxHeight;
7263            me.margins = helpers.extend({
7264                left: 0,
7265                right: 0,
7266                top: 0,
7267                bottom: 0
7268            }, margins);
7269            me.longestTextCache = me.longestTextCache || {};
7270
7271            // Dimensions
7272            me.beforeSetDimensions();
7273            me.setDimensions();
7274            me.afterSetDimensions();
7275
7276            // Data min/max
7277            me.beforeDataLimits();
7278            me.determineDataLimits();
7279            me.afterDataLimits();
7280
7281            // Ticks - `this.ticks` is now DEPRECATED!
7282            // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member
7283            // and must not be accessed directly from outside this class. `this.ticks` being
7284            // around for long time and not marked as private, we can't change its structure
7285            // without unexpected breaking changes. If you need to access the scale ticks,
7286            // use scale.getTicks() instead.
7287
7288            me.beforeBuildTicks();
7289
7290            // New implementations should return an array of objects but for BACKWARD COMPAT,
7291            // we still support no return (`this.ticks` internally set by calling this method).
7292            ticks = me.buildTicks() || [];
7293
7294            me.afterBuildTicks();
7295
7296            me.beforeTickToLabelConversion();
7297
7298            // New implementations should return the formatted tick labels but for BACKWARD
7299            // COMPAT, we still support no return (`this.ticks` internally changed by calling
7300            // this method and supposed to contain only string values).
7301            labels = me.convertTicksToLabels(ticks) || me.ticks;
7302
7303            me.afterTickToLabelConversion();
7304
7305            me.ticks = labels;   // BACKWARD COMPATIBILITY
7306
7307            // IMPORTANT: from this point, we consider that `this.ticks` will NEVER change!
7308
7309            // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
7310            for (i = 0, ilen = labels.length; i < ilen; ++i) {
7311                label = labels[i];
7312                tick = ticks[i];
7313                if (!tick) {
7314                    ticks.push(tick = {
7315                        label: label,
7316                        major: false
7317                    });
7318                } else {
7319                    tick.label = label;
7320                }
7321            }
7322
7323            me._ticks = ticks;
7324
7325            // Tick Rotation
7326            me.beforeCalculateTickRotation();
7327            me.calculateTickRotation();
7328            me.afterCalculateTickRotation();
7329            // Fit
7330            me.beforeFit();
7331            me.fit();
7332            me.afterFit();
7333            //
7334            me.afterUpdate();
7335
7336            return me.minSize;
7337
7338        },
7339        afterUpdate: function() {
7340            helpers.callback(this.options.afterUpdate, [this]);
7341        },
7342
7343        //
7344
7345        beforeSetDimensions: function() {
7346            helpers.callback(this.options.beforeSetDimensions, [this]);
7347        },
7348        setDimensions: function() {
7349            var me = this;
7350            // Set the unconstrained dimension before label rotation
7351            if (me.isHorizontal()) {
7352                // Reset position before calculating rotation
7353                me.width = me.maxWidth;
7354                me.left = 0;
7355                me.right = me.width;
7356            } else {
7357                me.height = me.maxHeight;
7358
7359                // Reset position before calculating rotation
7360                me.top = 0;
7361                me.bottom = me.height;
7362            }
7363
7364            // Reset padding
7365            me.paddingLeft = 0;
7366            me.paddingTop = 0;
7367            me.paddingRight = 0;
7368            me.paddingBottom = 0;
7369        },
7370        afterSetDimensions: function() {
7371            helpers.callback(this.options.afterSetDimensions, [this]);
7372        },
7373
7374        // Data limits
7375        beforeDataLimits: function() {
7376            helpers.callback(this.options.beforeDataLimits, [this]);
7377        },
7378        determineDataLimits: helpers.noop,
7379        afterDataLimits: function() {
7380            helpers.callback(this.options.afterDataLimits, [this]);
7381        },
7382
7383        //
7384        beforeBuildTicks: function() {
7385            helpers.callback(this.options.beforeBuildTicks, [this]);
7386        },
7387        buildTicks: helpers.noop,
7388        afterBuildTicks: function() {
7389            helpers.callback(this.options.afterBuildTicks, [this]);
7390        },
7391
7392        beforeTickToLabelConversion: function() {
7393            helpers.callback(this.options.beforeTickToLabelConversion, [this]);
7394        },
7395        convertTicksToLabels: function() {
7396            var me = this;
7397            // Convert ticks to strings
7398            var tickOpts = me.options.ticks;
7399            me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this);
7400        },
7401        afterTickToLabelConversion: function() {
7402            helpers.callback(this.options.afterTickToLabelConversion, [this]);
7403        },
7404
7405        //
7406
7407        beforeCalculateTickRotation: function() {
7408            helpers.callback(this.options.beforeCalculateTickRotation, [this]);
7409        },
7410        calculateTickRotation: function() {
7411            var me = this;
7412            var context = me.ctx;
7413            var tickOpts = me.options.ticks;
7414            var labels = labelsFromTicks(me._ticks);
7415
7416            // Get the width of each grid by calculating the difference
7417            // between x offsets between 0 and 1.
7418            var tickFont = parseFontOptions(tickOpts);
7419            context.font = tickFont.font;
7420
7421            var labelRotation = tickOpts.minRotation || 0;
7422
7423            if (labels.length && me.options.display && me.isHorizontal()) {
7424                var originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache);
7425                var labelWidth = originalLabelWidth;
7426                var cosRotation, sinRotation;
7427
7428                // Allow 3 pixels x2 padding either side for label readability
7429                var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;
7430
7431                // Max label rotation can be set or default to 90 - also act as a loop counter
7432                while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) {
7433                    var angleRadians = helpers.toRadians(labelRotation);
7434                    cosRotation = Math.cos(angleRadians);
7435                    sinRotation = Math.sin(angleRadians);
7436
7437                    if (sinRotation * originalLabelWidth > me.maxHeight) {
7438                        // go back one step
7439                        labelRotation--;
7440                        break;
7441                    }
7442
7443                    labelRotation++;
7444                    labelWidth = cosRotation * originalLabelWidth;
7445                }
7446            }
7447
7448            me.labelRotation = labelRotation;
7449        },
7450        afterCalculateTickRotation: function() {
7451            helpers.callback(this.options.afterCalculateTickRotation, [this]);
7452        },
7453
7454        //
7455
7456        beforeFit: function() {
7457            helpers.callback(this.options.beforeFit, [this]);
7458        },
7459        fit: function() {
7460            var me = this;
7461            // Reset
7462            var minSize = me.minSize = {
7463                width: 0,
7464                height: 0
7465            };
7466
7467            var labels = labelsFromTicks(me._ticks);
7468
7469            var opts = me.options;
7470            var tickOpts = opts.ticks;
7471            var scaleLabelOpts = opts.scaleLabel;
7472            var gridLineOpts = opts.gridLines;
7473            var display = opts.display;
7474            var isHorizontal = me.isHorizontal();
7475
7476            var tickFont = parseFontOptions(tickOpts);
7477            var tickMarkLength = opts.gridLines.tickMarkLength;
7478
7479            // Width
7480            if (isHorizontal) {
7481                // subtract the margins to line up with the chartArea if we are a full width scale
7482                minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
7483            } else {
7484                minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
7485            }
7486
7487            // height
7488            if (isHorizontal) {
7489                minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
7490            } else {
7491                minSize.height = me.maxHeight; // fill all the height
7492            }
7493
7494            // Are we showing a title for the scale?
7495            if (scaleLabelOpts.display && display) {
7496                var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts);
7497                var scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding);
7498                var deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height;
7499
7500                if (isHorizontal) {
7501                    minSize.height += deltaHeight;
7502                } else {
7503                    minSize.width += deltaHeight;
7504                }
7505            }
7506
7507            // Don't bother fitting the ticks if we are not showing them
7508            if (tickOpts.display && display) {
7509                var largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache);
7510                var tallestLabelHeightInLines = helpers.numberOfLabelLines(labels);
7511                var lineSpace = tickFont.size * 0.5;
7512                var tickPadding = me.options.ticks.padding;
7513
7514                if (isHorizontal) {
7515                    // A horizontal axis is more constrained by the height.
7516                    me.longestLabelWidth = largestTextWidth;
7517
7518                    var angleRadians = helpers.toRadians(me.labelRotation);
7519                    var cosRotation = Math.cos(angleRadians);
7520                    var sinRotation = Math.sin(angleRadians);
7521
7522                    // TODO - improve this calculation
7523                    var labelHeight = (sinRotation * largestTextWidth)
7524                        + (tickFont.size * tallestLabelHeightInLines)
7525                        + (lineSpace * (tallestLabelHeightInLines - 1))
7526                        + lineSpace; // padding
7527
7528                    minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);
7529
7530                    me.ctx.font = tickFont.font;
7531                    var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font);
7532                    var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font);
7533
7534                    // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
7535                    // which means that the right padding is dominated by the font height
7536                    if (me.labelRotation !== 0) {
7537                        me.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges
7538                        me.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3;
7539                    } else {
7540                        me.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges
7541                        me.paddingRight = lastLabelWidth / 2 + 3;
7542                    }
7543                } else {
7544                    // A vertical axis is more constrained by the width. Labels are the
7545                    // dominant factor here, so get that length first and account for padding
7546                    if (tickOpts.mirror) {
7547                        largestTextWidth = 0;
7548                    } else {
7549                        // use lineSpace for consistency with horizontal axis
7550                        // tickPadding is not implemented for horizontal
7551                        largestTextWidth += tickPadding + lineSpace;
7552                    }
7553
7554                    minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth);
7555
7556                    me.paddingTop = tickFont.size / 2;
7557                    me.paddingBottom = tickFont.size / 2;
7558                }
7559            }
7560
7561            me.handleMargins();
7562
7563            me.width = minSize.width;
7564            me.height = minSize.height;
7565        },
7566
7567        /**
7568         * Handle margins and padding interactions
7569         * @private
7570         */
7571        handleMargins: function() {
7572            var me = this;
7573            if (me.margins) {
7574                me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
7575                me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
7576                me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
7577                me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
7578            }
7579        },
7580
7581        afterFit: function() {
7582            helpers.callback(this.options.afterFit, [this]);
7583        },
7584
7585        // Shared Methods
7586        isHorizontal: function() {
7587            return this.options.position === 'top' || this.options.position === 'bottom';
7588        },
7589        isFullWidth: function() {
7590            return (this.options.fullWidth);
7591        },
7592
7593        // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
7594        getRightValue: function(rawValue) {
7595            // Null and undefined values first
7596            if (helpers.isNullOrUndef(rawValue)) {
7597                return NaN;
7598            }
7599            // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
7600            if (typeof rawValue === 'number' && !isFinite(rawValue)) {
7601                return NaN;
7602            }
7603            // If it is in fact an object, dive in one more level
7604            if (rawValue) {
7605                if (this.isHorizontal()) {
7606                    if (rawValue.x !== undefined) {
7607                        return this.getRightValue(rawValue.x);
7608                    }
7609                } else if (rawValue.y !== undefined) {
7610                    return this.getRightValue(rawValue.y);
7611                }
7612            }
7613
7614            // Value is good, return it
7615            return rawValue;
7616        },
7617
7618        /**
7619         * Used to get the value to display in the tooltip for the data at the given index
7620         * @param index
7621         * @param datasetIndex
7622         */
7623        getLabelForIndex: helpers.noop,
7624
7625        /**
7626         * Returns the location of the given data point. Value can either be an index or a numerical value
7627         * The coordinate (0, 0) is at the upper-left corner of the canvas
7628         * @param value
7629         * @param index
7630         * @param datasetIndex
7631         */
7632        getPixelForValue: helpers.noop,
7633
7634        /**
7635         * Used to get the data value from a given pixel. This is the inverse of getPixelForValue
7636         * The coordinate (0, 0) is at the upper-left corner of the canvas
7637         * @param pixel
7638         */
7639        getValueForPixel: helpers.noop,
7640
7641        /**
7642         * Returns the location of the tick at the given index
7643         * The coordinate (0, 0) is at the upper-left corner of the canvas
7644         */
7645        getPixelForTick: function(index) {
7646            var me = this;
7647            var offset = me.options.offset;
7648            if (me.isHorizontal()) {
7649                var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
7650                var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
7651                var pixel = (tickWidth * index) + me.paddingLeft;
7652
7653                if (offset) {
7654                    pixel += tickWidth / 2;
7655                }
7656
7657                var finalVal = me.left + Math.round(pixel);
7658                finalVal += me.isFullWidth() ? me.margins.left : 0;
7659                return finalVal;
7660            }
7661            var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
7662            return me.top + (index * (innerHeight / (me._ticks.length - 1)));
7663        },
7664
7665        /**
7666         * Utility for getting the pixel location of a percentage of scale
7667         * The coordinate (0, 0) is at the upper-left corner of the canvas
7668         */
7669        getPixelForDecimal: function(decimal) {
7670            var me = this;
7671            if (me.isHorizontal()) {
7672                var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
7673                var valueOffset = (innerWidth * decimal) + me.paddingLeft;
7674
7675                var finalVal = me.left + Math.round(valueOffset);
7676                finalVal += me.isFullWidth() ? me.margins.left : 0;
7677                return finalVal;
7678            }
7679            return me.top + (decimal * me.height);
7680        },
7681
7682        /**
7683         * Returns the pixel for the minimum chart value
7684         * The coordinate (0, 0) is at the upper-left corner of the canvas
7685         */
7686        getBasePixel: function() {
7687            return this.getPixelForValue(this.getBaseValue());
7688        },
7689
7690        getBaseValue: function() {
7691            var me = this;
7692            var min = me.min;
7693            var max = me.max;
7694
7695            return me.beginAtZero ? 0 :
7696                min < 0 && max < 0 ? max :
7697                    min > 0 && max > 0 ? min :
7698                        0;
7699        },
7700
7701        /**
7702         * Returns a subset of ticks to be plotted to avoid overlapping labels.
7703         * @private
7704         */
7705        _autoSkip: function(ticks) {
7706            var skipRatio;
7707            var me = this;
7708            var isHorizontal = me.isHorizontal();
7709            var optionTicks = me.options.ticks.minor;
7710            var tickCount = ticks.length;
7711            var labelRotationRadians = helpers.toRadians(me.labelRotation);
7712            var cosRotation = Math.cos(labelRotationRadians);
7713            var longestRotatedLabel = me.longestLabelWidth * cosRotation;
7714            var result = [];
7715            var i, tick, shouldSkip;
7716
7717            // figure out the maximum number of gridlines to show
7718            var maxTicks;
7719            if (optionTicks.maxTicksLimit) {
7720                maxTicks = optionTicks.maxTicksLimit;
7721            }
7722
7723            if (isHorizontal) {
7724                skipRatio = false;
7725
7726                if ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) {
7727                    skipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight)));
7728                }
7729
7730                // if they defined a max number of optionTicks,
7731                // increase skipRatio until that number is met
7732                if (maxTicks && tickCount > maxTicks) {
7733                    skipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks));
7734                }
7735            }
7736
7737            for (i = 0; i < tickCount; i++) {
7738                tick = ticks[i];
7739
7740                // Since we always show the last tick,we need may need to hide the last shown one before
7741                shouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount);
7742                if (shouldSkip && i !== tickCount - 1) {
7743                    // leave tick in place but make sure it's not displayed (#4635)
7744                    delete tick.label;
7745                }
7746                result.push(tick);
7747            }
7748            return result;
7749        },
7750
7751        // Actually draw the scale on the canvas
7752        // @param {rectangle} chartArea : the area of the chart to draw full grid lines on
7753        draw: function(chartArea) {
7754            var me = this;
7755            var options = me.options;
7756            if (!options.display) {
7757                return;
7758            }
7759
7760            var context = me.ctx;
7761            var globalDefaults = defaults.global;
7762            var optionTicks = options.ticks.minor;
7763            var optionMajorTicks = options.ticks.major || optionTicks;
7764            var gridLines = options.gridLines;
7765            var scaleLabel = options.scaleLabel;
7766
7767            var isRotated = me.labelRotation !== 0;
7768            var isHorizontal = me.isHorizontal();
7769
7770            var ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks();
7771            var tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);
7772            var tickFont = parseFontOptions(optionTicks);
7773            var majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor);
7774            var majorTickFont = parseFontOptions(optionMajorTicks);
7775
7776            var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;
7777
7778            var scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);
7779            var scaleLabelFont = parseFontOptions(scaleLabel);
7780            var scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding);
7781            var labelRotationRadians = helpers.toRadians(me.labelRotation);
7782
7783            var itemsToDraw = [];
7784
7785            var axisWidth = me.options.gridLines.lineWidth;
7786            var xTickStart = options.position === 'right' ? me.left : me.right - axisWidth - tl;
7787            var xTickEnd = options.position === 'right' ? me.left + tl : me.right;
7788            var yTickStart = options.position === 'bottom' ? me.top + axisWidth : me.bottom - tl - axisWidth;
7789            var yTickEnd = options.position === 'bottom' ? me.top + axisWidth + tl : me.bottom + axisWidth;
7790
7791            helpers.each(ticks, function(tick, index) {
7792                // autoskipper skipped this tick (#4635)
7793                if (helpers.isNullOrUndef(tick.label)) {
7794                    return;
7795                }
7796
7797                var label = tick.label;
7798                var lineWidth, lineColor, borderDash, borderDashOffset;
7799                if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) {
7800                    // Draw the first index specially
7801                    lineWidth = gridLines.zeroLineWidth;
7802                    lineColor = gridLines.zeroLineColor;
7803                    borderDash = gridLines.zeroLineBorderDash;
7804                    borderDashOffset = gridLines.zeroLineBorderDashOffset;
7805                } else {
7806                    lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index);
7807                    lineColor = helpers.valueAtIndexOrDefault(gridLines.color, index);
7808                    borderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash);
7809                    borderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);
7810                }
7811
7812                // Common properties
7813                var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;
7814                var textAlign = 'middle';
7815                var textBaseline = 'middle';
7816                var tickPadding = optionTicks.padding;
7817
7818                if (isHorizontal) {
7819                    var labelYOffset = tl + tickPadding;
7820
7821                    if (options.position === 'bottom') {
7822                        // bottom
7823                        textBaseline = !isRotated ? 'top' : 'middle';
7824                        textAlign = !isRotated ? 'center' : 'right';
7825                        labelY = me.top + labelYOffset;
7826                    } else {
7827                        // top
7828                        textBaseline = !isRotated ? 'bottom' : 'middle';
7829                        textAlign = !isRotated ? 'center' : 'left';
7830                        labelY = me.bottom - labelYOffset;
7831                    }
7832
7833                    var xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
7834                    if (xLineValue < me.left) {
7835                        lineColor = 'rgba(0,0,0,0)';
7836                    }
7837                    xLineValue += helpers.aliasPixel(lineWidth);
7838
7839                    labelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)
7840
7841                    tx1 = tx2 = x1 = x2 = xLineValue;
7842                    ty1 = yTickStart;
7843                    ty2 = yTickEnd;
7844                    y1 = chartArea.top;
7845                    y2 = chartArea.bottom + axisWidth;
7846                } else {
7847                    var isLeft = options.position === 'left';
7848                    var labelXOffset;
7849
7850                    if (optionTicks.mirror) {
7851                        textAlign = isLeft ? 'left' : 'right';
7852                        labelXOffset = tickPadding;
7853                    } else {
7854                        textAlign = isLeft ? 'right' : 'left';
7855                        labelXOffset = tl + tickPadding;
7856                    }
7857
7858                    labelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset;
7859
7860                    var yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);
7861                    if (yLineValue < me.top) {
7862                        lineColor = 'rgba(0,0,0,0)';
7863                    }
7864                    yLineValue += helpers.aliasPixel(lineWidth);
7865
7866                    labelY = me.getPixelForTick(index) + optionTicks.labelOffset;
7867
7868                    tx1 = xTickStart;
7869                    tx2 = xTickEnd;
7870                    x1 = chartArea.left;
7871                    x2 = chartArea.right + axisWidth;
7872                    ty1 = ty2 = y1 = y2 = yLineValue;
7873                }
7874
7875                itemsToDraw.push({
7876                    tx1: tx1,
7877                    ty1: ty1,
7878                    tx2: tx2,
7879                    ty2: ty2,
7880                    x1: x1,
7881                    y1: y1,
7882                    x2: x2,
7883                    y2: y2,
7884                    labelX: labelX,
7885                    labelY: labelY,
7886                    glWidth: lineWidth,
7887                    glColor: lineColor,
7888                    glBorderDash: borderDash,
7889                    glBorderDashOffset: borderDashOffset,
7890                    rotation: -1 * labelRotationRadians,
7891                    label: label,
7892                    major: tick.major,
7893                    textBaseline: textBaseline,
7894                    textAlign: textAlign
7895                });
7896            });
7897
7898            // Draw all of the tick labels, tick marks, and grid lines at the correct places
7899            helpers.each(itemsToDraw, function(itemToDraw) {
7900                if (gridLines.display) {
7901                    context.save();
7902                    context.lineWidth = itemToDraw.glWidth;
7903                    context.strokeStyle = itemToDraw.glColor;
7904                    if (context.setLineDash) {
7905                        context.setLineDash(itemToDraw.glBorderDash);
7906                        context.lineDashOffset = itemToDraw.glBorderDashOffset;
7907                    }
7908
7909                    context.beginPath();
7910
7911                    if (gridLines.drawTicks) {
7912                        context.moveTo(itemToDraw.tx1, itemToDraw.ty1);
7913                        context.lineTo(itemToDraw.tx2, itemToDraw.ty2);
7914                    }
7915
7916                    if (gridLines.drawOnChartArea) {
7917                        context.moveTo(itemToDraw.x1, itemToDraw.y1);
7918                        context.lineTo(itemToDraw.x2, itemToDraw.y2);
7919                    }
7920
7921                    context.stroke();
7922                    context.restore();
7923                }
7924
7925                if (optionTicks.display) {
7926                    // Make sure we draw text in the correct color and font
7927                    context.save();
7928                    context.translate(itemToDraw.labelX, itemToDraw.labelY);
7929                    context.rotate(itemToDraw.rotation);
7930                    context.font = itemToDraw.major ? majorTickFont.font : tickFont.font;
7931                    context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor;
7932                    context.textBaseline = itemToDraw.textBaseline;
7933                    context.textAlign = itemToDraw.textAlign;
7934
7935                    var label = itemToDraw.label;
7936                    if (helpers.isArray(label)) {
7937                        var lineCount = label.length;
7938                        var lineHeight = tickFont.size * 1.5;
7939                        var y = me.isHorizontal() ? 0 : -lineHeight * (lineCount - 1) / 2;
7940
7941                        for (var i = 0; i < lineCount; ++i) {
7942                            // We just make sure the multiline element is a string here..
7943                            context.fillText('' + label[i], 0, y);
7944                            // apply same lineSpacing as calculated @ L#320
7945                            y += lineHeight;
7946                        }
7947                    } else {
7948                        context.fillText(label, 0, 0);
7949                    }
7950                    context.restore();
7951                }
7952            });
7953
7954            if (scaleLabel.display) {
7955                // Draw the scale label
7956                var scaleLabelX;
7957                var scaleLabelY;
7958                var rotation = 0;
7959                var halfLineHeight = parseLineHeight(scaleLabel) / 2;
7960
7961                if (isHorizontal) {
7962                    scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
7963                    scaleLabelY = options.position === 'bottom'
7964                        ? me.bottom - halfLineHeight - scaleLabelPadding.bottom
7965                        : me.top + halfLineHeight + scaleLabelPadding.top;
7966                } else {
7967                    var isLeft = options.position === 'left';
7968                    scaleLabelX = isLeft
7969                        ? me.left + halfLineHeight + scaleLabelPadding.top
7970                        : me.right - halfLineHeight - scaleLabelPadding.top;
7971                    scaleLabelY = me.top + ((me.bottom - me.top) / 2);
7972                    rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
7973                }
7974
7975                context.save();
7976                context.translate(scaleLabelX, scaleLabelY);
7977                context.rotate(rotation);
7978                context.textAlign = 'center';
7979                context.textBaseline = 'middle';
7980                context.fillStyle = scaleLabelFontColor; // render in correct colour
7981                context.font = scaleLabelFont.font;
7982                context.fillText(scaleLabel.labelString, 0, 0);
7983                context.restore();
7984            }
7985
7986            if (gridLines.drawBorder) {
7987                // Draw the line at the edge of the axis
7988                context.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0);
7989                context.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0);
7990                var x1 = me.left;
7991                var x2 = me.right + axisWidth;
7992                var y1 = me.top;
7993                var y2 = me.bottom + axisWidth;
7994
7995                var aliasPixel = helpers.aliasPixel(context.lineWidth);
7996                if (isHorizontal) {
7997                    y1 = y2 = options.position === 'top' ? me.bottom : me.top;
7998                    y1 += aliasPixel;
7999                    y2 += aliasPixel;
8000                } else {
8001                    x1 = x2 = options.position === 'left' ? me.right : me.left;
8002                    x1 += aliasPixel;
8003                    x2 += aliasPixel;
8004                }
8005
8006                context.beginPath();
8007                context.moveTo(x1, y1);
8008                context.lineTo(x2, y2);
8009                context.stroke();
8010            }
8011        }
8012    });
8013
8014},{"26":26,"27":27,"35":35,"46":46}],34:[function(require,module,exports){
8015    'use strict';
8016
8017    var defaults = require(26);
8018    var helpers = require(46);
8019    var layouts = require(31);
8020
8021    module.exports = {
8022        // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
8023        // use the new chart options to grab the correct scale
8024        constructors: {},
8025        // Use a registration function so that we can move to an ES6 map when we no longer need to support
8026        // old browsers
8027
8028        // Scale config defaults
8029        defaults: {},
8030        registerScaleType: function(type, scaleConstructor, scaleDefaults) {
8031            this.constructors[type] = scaleConstructor;
8032            this.defaults[type] = helpers.clone(scaleDefaults);
8033        },
8034        getScaleConstructor: function(type) {
8035            return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
8036        },
8037        getScaleDefaults: function(type) {
8038            // Return the scale defaults merged with the global settings so that we always use the latest ones
8039            return this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {};
8040        },
8041        updateScaleDefaults: function(type, additions) {
8042            var me = this;
8043            if (me.defaults.hasOwnProperty(type)) {
8044                me.defaults[type] = helpers.extend(me.defaults[type], additions);
8045            }
8046        },
8047        addScalesToLayout: function(chart) {
8048            // Adds each scale to the chart.boxes array to be sized accordingly
8049            helpers.each(chart.scales, function(scale) {
8050                // Set ILayoutItem parameters for backwards compatibility
8051                scale.fullWidth = scale.options.fullWidth;
8052                scale.position = scale.options.position;
8053                scale.weight = scale.options.weight;
8054                layouts.addBox(chart, scale);
8055            });
8056        }
8057    };
8058
8059},{"26":26,"31":31,"46":46}],35:[function(require,module,exports){
8060    'use strict';
8061
8062    var helpers = require(46);
8063
8064    /**
8065     * Namespace to hold static tick generation functions
8066     * @namespace Chart.Ticks
8067     */
8068    module.exports = {
8069        /**
8070         * Namespace to hold formatters for different types of ticks
8071         * @namespace Chart.Ticks.formatters
8072         */
8073        formatters: {
8074            /**
8075             * Formatter for value labels
8076             * @method Chart.Ticks.formatters.values
8077             * @param value the value to display
8078             * @return {String|Array} the label to display
8079             */
8080            values: function(value) {
8081                return helpers.isArray(value) ? value : '' + value;
8082            },
8083
8084            /**
8085             * Formatter for linear numeric ticks
8086             * @method Chart.Ticks.formatters.linear
8087             * @param tickValue {Number} the value to be formatted
8088             * @param index {Number} the position of the tickValue parameter in the ticks array
8089             * @param ticks {Array<Number>} the list of ticks being converted
8090             * @return {String} string representation of the tickValue parameter
8091             */
8092            linear: function(tickValue, index, ticks) {
8093                // If we have lots of ticks, don't use the ones
8094                var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
8095
8096                // If we have a number like 2.5 as the delta, figure out how many decimal places we need
8097                if (Math.abs(delta) > 1) {
8098                    if (tickValue !== Math.floor(tickValue)) {
8099                        // not an integer
8100                        delta = tickValue - Math.floor(tickValue);
8101                    }
8102                }
8103
8104                var logDelta = helpers.log10(Math.abs(delta));
8105                var tickString = '';
8106
8107                if (tickValue !== 0) {
8108                    var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1]));
8109                    if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation
8110                        var logTick = helpers.log10(Math.abs(tickValue));
8111                        tickString = tickValue.toExponential(Math.floor(logTick) - Math.floor(logDelta));
8112                    } else {
8113                        var numDecimal = -1 * Math.floor(logDelta);
8114                        numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
8115                        tickString = tickValue.toFixed(numDecimal);
8116                    }
8117                } else {
8118                    tickString = '0'; // never show decimal places for 0
8119                }
8120
8121                return tickString;
8122            },
8123
8124            logarithmic: function(tickValue, index, ticks) {
8125                var remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));
8126
8127                if (tickValue === 0) {
8128                    return '0';
8129                } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
8130                    return tickValue.toExponential();
8131                }
8132                return '';
8133            }
8134        }
8135    };
8136
8137},{"46":46}],36:[function(require,module,exports){
8138    'use strict';
8139
8140    var defaults = require(26);
8141    var Element = require(27);
8142    var helpers = require(46);
8143
8144    defaults._set('global', {
8145        tooltips: {
8146            enabled: true,
8147            custom: null,
8148            mode: 'nearest',
8149            position: 'average',
8150            intersect: true,
8151            backgroundColor: 'rgba(0,0,0,0.8)',
8152            titleFontStyle: 'bold',
8153            titleSpacing: 2,
8154            titleMarginBottom: 6,
8155            titleFontColor: '#fff',
8156            titleAlign: 'left',
8157            bodySpacing: 2,
8158            bodyFontColor: '#fff',
8159            bodyAlign: 'left',
8160            footerFontStyle: 'bold',
8161            footerSpacing: 2,
8162            footerMarginTop: 6,
8163            footerFontColor: '#fff',
8164            footerAlign: 'left',
8165            yPadding: 6,
8166            xPadding: 6,
8167            caretPadding: 2,
8168            caretSize: 5,
8169            cornerRadius: 6,
vendor: 28,061 bytes, lines 8170-8932
8170            multiKeyBackground: '#fff',
8171            displayColors: true,
8172            borderColor: 'rgba(0,0,0,0)',
8173            borderWidth: 0,
8174            callbacks: {
8175                // Args are: (tooltipItems, data)
8176                beforeTitle: helpers.noop,
8177                title: function(tooltipItems, data) {
8178                    // Pick first xLabel for now
8179                    var title = '';
8180                    var labels = data.labels;
8181                    var labelCount = labels ? labels.length : 0;
8182
8183                    if (tooltipItems.length > 0) {
8184                        var item = tooltipItems[0];
8185
8186                        if (item.xLabel) {
8187                            title = item.xLabel;
8188                        } else if (labelCount > 0 && item.index < labelCount) {
8189                            title = labels[item.index];
8190                        }
8191                    }
8192
8193                    return title;
8194                },
8195                afterTitle: helpers.noop,
8196
8197                // Args are: (tooltipItems, data)
8198                beforeBody: helpers.noop,
8199
8200                // Args are: (tooltipItem, data)
8201                beforeLabel: helpers.noop,
8202                label: function(tooltipItem, data) {
8203                    var label = data.datasets[tooltipItem.datasetIndex].label || '';
8204
8205                    if (label) {
8206                        label += ': ';
8207                    }
8208                    label += tooltipItem.yLabel;
8209                    return label;
8210                },
8211                labelColor: function(tooltipItem, chart) {
8212                    var meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
8213                    var activeElement = meta.data[tooltipItem.index];
8214                    var view = activeElement._view;
8215                    return {
8216                        borderColor: view.borderColor,
8217                        backgroundColor: view.backgroundColor
8218                    };
8219                },
8220                labelTextColor: function() {
8221                    return this._options.bodyFontColor;
8222                },
8223                afterLabel: helpers.noop,
8224
8225                // Args are: (tooltipItems, data)
8226                afterBody: helpers.noop,
8227
8228                // Args are: (tooltipItems, data)
8229                beforeFooter: helpers.noop,
8230                footer: helpers.noop,
8231                afterFooter: helpers.noop
8232            }
8233        }
8234    });
8235
8236    var positioners = {
8237        /**
8238         * Average mode places the tooltip at the average position of the elements shown
8239         * @function Chart.Tooltip.positioners.average
8240         * @param elements {ChartElement[]} the elements being displayed in the tooltip
8241         * @returns {Point} tooltip position
8242         */
8243        average: function(elements) {
8244            if (!elements.length) {
8245                return false;
8246            }
8247
8248            var i, len;
8249            var x = 0;
8250            var y = 0;
8251            var count = 0;
8252
8253            for (i = 0, len = elements.length; i < len; ++i) {
8254                var el = elements[i];
8255                if (el && el.hasValue()) {
8256                    var pos = el.tooltipPosition();
8257                    x += pos.x;
8258                    y += pos.y;
8259                    ++count;
8260                }
8261            }
8262
8263            return {
8264                x: Math.round(x / count),
8265                y: Math.round(y / count)
8266            };
8267        },
8268
8269        /**
8270         * Gets the tooltip position nearest of the item nearest to the event position
8271         * @function Chart.Tooltip.positioners.nearest
8272         * @param elements {Chart.Element[]} the tooltip elements
8273         * @param eventPosition {Point} the position of the event in canvas coordinates
8274         * @returns {Point} the tooltip position
8275         */
8276        nearest: function(elements, eventPosition) {
8277            var x = eventPosition.x;
8278            var y = eventPosition.y;
8279            var minDistance = Number.POSITIVE_INFINITY;
8280            var i, len, nearestElement;
8281
8282            for (i = 0, len = elements.length; i < len; ++i) {
8283                var el = elements[i];
8284                if (el && el.hasValue()) {
8285                    var center = el.getCenterPoint();
8286                    var d = helpers.distanceBetweenPoints(eventPosition, center);
8287
8288                    if (d < minDistance) {
8289                        minDistance = d;
8290                        nearestElement = el;
8291                    }
8292                }
8293            }
8294
8295            if (nearestElement) {
8296                var tp = nearestElement.tooltipPosition();
8297                x = tp.x;
8298                y = tp.y;
8299            }
8300
8301            return {
8302                x: x,
8303                y: y
8304            };
8305        }
8306    };
8307
8308    /**
8309     * Helper method to merge the opacity into a color
8310     */
8311    function mergeOpacity(colorString, opacity) {
8312        var color = helpers.color(colorString);
8313        return color.alpha(opacity * color.alpha()).rgbaString();
8314    }
8315
8316// Helper to push or concat based on if the 2nd parameter is an array or not
8317    function pushOrConcat(base, toPush) {
8318        if (toPush) {
8319            if (helpers.isArray(toPush)) {
8320                // base = base.concat(toPush);
8321                Array.prototype.push.apply(base, toPush);
8322            } else {
8323                base.push(toPush);
8324            }
8325        }
8326
8327        return base;
8328    }
8329
8330    /**
8331     * Returns array of strings split by newline
8332     * @param {String} value - The value to split by newline.
8333     * @returns {Array} value if newline present - Returned from String split() method
8334     * @function
8335     */
8336    function splitNewlines(str) {
8337        if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) {
8338            return str.split('\n');
8339        }
8340        return str;
8341    }
8342
8343
8344// Private helper to create a tooltip item model
8345// @param element : the chart element (point, arc, bar) to create the tooltip item for
8346// @return : new tooltip item
8347    function createTooltipItem(element) {
8348        var xScale = element._xScale;
8349        var yScale = element._yScale || element._scale; // handle radar || polarArea charts
8350        var index = element._index;
8351        var datasetIndex = element._datasetIndex;
8352
8353        return {
8354            xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
8355            yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
8356            index: index,
8357            datasetIndex: datasetIndex,
8358            x: element._model.x,
8359            y: element._model.y
8360        };
8361    }
8362
8363    /**
8364     * Helper to get the reset model for the tooltip
8365     * @param tooltipOpts {Object} the tooltip options
8366     */
8367    function getBaseModel(tooltipOpts) {
8368        var globalDefaults = defaults.global;
8369        var valueOrDefault = helpers.valueOrDefault;
8370
8371        return {
8372            // Positioning
8373            xPadding: tooltipOpts.xPadding,
8374            yPadding: tooltipOpts.yPadding,
8375            xAlign: tooltipOpts.xAlign,
8376            yAlign: tooltipOpts.yAlign,
8377
8378            // Body
8379            bodyFontColor: tooltipOpts.bodyFontColor,
8380            _bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
8381            _bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
8382            _bodyAlign: tooltipOpts.bodyAlign,
8383            bodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
8384            bodySpacing: tooltipOpts.bodySpacing,
8385
8386            // Title
8387            titleFontColor: tooltipOpts.titleFontColor,
8388            _titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
8389            _titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
8390            titleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
8391            _titleAlign: tooltipOpts.titleAlign,
8392            titleSpacing: tooltipOpts.titleSpacing,
8393            titleMarginBottom: tooltipOpts.titleMarginBottom,
8394
8395            // Footer
8396            footerFontColor: tooltipOpts.footerFontColor,
8397            _footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
8398            _footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
8399            footerFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
8400            _footerAlign: tooltipOpts.footerAlign,
8401            footerSpacing: tooltipOpts.footerSpacing,
8402            footerMarginTop: tooltipOpts.footerMarginTop,
8403
8404            // Appearance
8405            caretSize: tooltipOpts.caretSize,
8406            cornerRadius: tooltipOpts.cornerRadius,
8407            backgroundColor: tooltipOpts.backgroundColor,
8408            opacity: 0,
8409            legendColorBackground: tooltipOpts.multiKeyBackground,
8410            displayColors: tooltipOpts.displayColors,
8411            borderColor: tooltipOpts.borderColor,
8412            borderWidth: tooltipOpts.borderWidth
8413        };
8414    }
8415
8416    /**
8417     * Get the size of the tooltip
8418     */
8419    function getTooltipSize(tooltip, model) {
8420        var ctx = tooltip._chart.ctx;
8421
8422        var height = model.yPadding * 2; // Tooltip Padding
8423        var width = 0;
8424
8425        // Count of all lines in the body
8426        var body = model.body;
8427        var combinedBodyLength = body.reduce(function(count, bodyItem) {
8428            return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
8429        }, 0);
8430        combinedBodyLength += model.beforeBody.length + model.afterBody.length;
8431
8432        var titleLineCount = model.title.length;
8433        var footerLineCount = model.footer.length;
8434        var titleFontSize = model.titleFontSize;
8435        var bodyFontSize = model.bodyFontSize;
8436        var footerFontSize = model.footerFontSize;
8437
8438        height += titleLineCount * titleFontSize; // Title Lines
8439        height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
8440        height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
8441        height += combinedBodyLength * bodyFontSize; // Body Lines
8442        height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
8443        height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
8444        height += footerLineCount * (footerFontSize); // Footer Lines
8445        height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing
8446
8447        // Title width
8448        var widthPadding = 0;
8449        var maxLineWidth = function(line) {
8450            width = Math.max(width, ctx.measureText(line).width + widthPadding);
8451        };
8452
8453        ctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
8454        helpers.each(model.title, maxLineWidth);
8455
8456        // Body width
8457        ctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
8458        helpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth);
8459
8460        // Body lines may include some extra width due to the color box
8461        widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
8462        helpers.each(body, function(bodyItem) {
8463            helpers.each(bodyItem.before, maxLineWidth);
8464            helpers.each(bodyItem.lines, maxLineWidth);
8465            helpers.each(bodyItem.after, maxLineWidth);
8466        });
8467
8468        // Reset back to 0
8469        widthPadding = 0;
8470
8471        // Footer width
8472        ctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
8473        helpers.each(model.footer, maxLineWidth);
8474
8475        // Add padding
8476        width += 2 * model.xPadding;
8477
8478        return {
8479            width: width,
8480            height: height
8481        };
8482    }
8483
8484    /**
8485     * Helper to get the alignment of a tooltip given the size
8486     */
8487    function determineAlignment(tooltip, size) {
8488        var model = tooltip._model;
8489        var chart = tooltip._chart;
8490        var chartArea = tooltip._chart.chartArea;
8491        var xAlign = 'center';
8492        var yAlign = 'center';
8493
8494        if (model.y < size.height) {
8495            yAlign = 'top';
8496        } else if (model.y > (chart.height - size.height)) {
8497            yAlign = 'bottom';
8498        }
8499
8500        var lf, rf; // functions to determine left, right alignment
8501        var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
8502        var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
8503        var midX = (chartArea.left + chartArea.right) / 2;
8504        var midY = (chartArea.top + chartArea.bottom) / 2;
8505
8506        if (yAlign === 'center') {
8507            lf = function(x) {
8508                return x <= midX;
8509            };
8510            rf = function(x) {
8511                return x > midX;
8512            };
8513        } else {
8514            lf = function(x) {
8515                return x <= (size.width / 2);
8516            };
8517            rf = function(x) {
8518                return x >= (chart.width - (size.width / 2));
8519            };
8520        }
8521
8522        olf = function(x) {
8523            return x + size.width + model.caretSize + model.caretPadding > chart.width;
8524        };
8525        orf = function(x) {
8526            return x - size.width - model.caretSize - model.caretPadding < 0;
8527        };
8528        yf = function(y) {
8529            return y <= midY ? 'top' : 'bottom';
8530        };
8531
8532        if (lf(model.x)) {
8533            xAlign = 'left';
8534
8535            // Is tooltip too wide and goes over the right side of the chart.?
8536            if (olf(model.x)) {
8537                xAlign = 'center';
8538                yAlign = yf(model.y);
8539            }
8540        } else if (rf(model.x)) {
8541            xAlign = 'right';
8542
8543            // Is tooltip too wide and goes outside left edge of canvas?
8544            if (orf(model.x)) {
8545                xAlign = 'center';
8546                yAlign = yf(model.y);
8547            }
8548        }
8549
8550        var opts = tooltip._options;
8551        return {
8552            xAlign: opts.xAlign ? opts.xAlign : xAlign,
8553            yAlign: opts.yAlign ? opts.yAlign : yAlign
8554        };
8555    }
8556
8557    /**
8558     * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
8559     */
8560    function getBackgroundPoint(vm, size, alignment, chart) {
8561        // Background Position
8562        var x = vm.x;
8563        var y = vm.y;
8564
8565        var caretSize = vm.caretSize;
8566        var caretPadding = vm.caretPadding;
8567        var cornerRadius = vm.cornerRadius;
8568        var xAlign = alignment.xAlign;
8569        var yAlign = alignment.yAlign;
8570        var paddingAndSize = caretSize + caretPadding;
8571        var radiusAndPadding = cornerRadius + caretPadding;
8572
8573        if (xAlign === 'right') {
8574            x -= size.width;
8575        } else if (xAlign === 'center') {
8576            x -= (size.width / 2);
8577            if (x + size.width > chart.width) {
8578                x = chart.width - size.width;
8579            }
8580            if (x < 0) {
8581                x = 0;
8582            }
8583        }
8584
8585        if (yAlign === 'top') {
8586            y += paddingAndSize;
8587        } else if (yAlign === 'bottom') {
8588            y -= size.height + paddingAndSize;
8589        } else {
8590            y -= (size.height / 2);
8591        }
8592
8593        if (yAlign === 'center') {
8594            if (xAlign === 'left') {
8595                x += paddingAndSize;
8596            } else if (xAlign === 'right') {
8597                x -= paddingAndSize;
8598            }
8599        } else if (xAlign === 'left') {
8600            x -= radiusAndPadding;
8601        } else if (xAlign === 'right') {
8602            x += radiusAndPadding;
8603        }
8604
8605        return {
8606            x: x,
8607            y: y
8608        };
8609    }
8610
8611    /**
8612     * Helper to build before and after body lines
8613     */
8614    function getBeforeAfterBodyLines(callback) {
8615        return pushOrConcat([], splitNewlines(callback));
8616    }
8617
8618    var exports = module.exports = Element.extend({
8619        initialize: function() {
8620            this._model = getBaseModel(this._options);
8621            this._lastActive = [];
8622        },
8623
8624        // Get the title
8625        // Args are: (tooltipItem, data)
8626        getTitle: function() {
8627            var me = this;
8628            var opts = me._options;
8629            var callbacks = opts.callbacks;
8630
8631            var beforeTitle = callbacks.beforeTitle.apply(me, arguments);
8632            var title = callbacks.title.apply(me, arguments);
8633            var afterTitle = callbacks.afterTitle.apply(me, arguments);
8634
8635            var lines = [];
8636            lines = pushOrConcat(lines, splitNewlines(beforeTitle));
8637            lines = pushOrConcat(lines, splitNewlines(title));
8638            lines = pushOrConcat(lines, splitNewlines(afterTitle));
8639
8640            return lines;
8641        },
8642
8643        // Args are: (tooltipItem, data)
8644        getBeforeBody: function() {
8645            return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments));
8646        },
8647
8648        // Args are: (tooltipItem, data)
8649        getBody: function(tooltipItems, data) {
8650            var me = this;
8651            var callbacks = me._options.callbacks;
8652            var bodyItems = [];
8653
8654            helpers.each(tooltipItems, function(tooltipItem) {
8655                var bodyItem = {
8656                    before: [],
8657                    lines: [],
8658                    after: []
8659                };
8660                pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data)));
8661                pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
8662                pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data)));
8663
8664                bodyItems.push(bodyItem);
8665            });
8666
8667            return bodyItems;
8668        },
8669
8670        // Args are: (tooltipItem, data)
8671        getAfterBody: function() {
8672            return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments));
8673        },
8674
8675        // Get the footer and beforeFooter and afterFooter lines
8676        // Args are: (tooltipItem, data)
8677        getFooter: function() {
8678            var me = this;
8679            var callbacks = me._options.callbacks;
8680
8681            var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
8682            var footer = callbacks.footer.apply(me, arguments);
8683            var afterFooter = callbacks.afterFooter.apply(me, arguments);
8684
8685            var lines = [];
8686            lines = pushOrConcat(lines, splitNewlines(beforeFooter));
8687            lines = pushOrConcat(lines, splitNewlines(footer));
8688            lines = pushOrConcat(lines, splitNewlines(afterFooter));
8689
8690            return lines;
8691        },
8692
8693        update: function(changed) {
8694            var me = this;
8695            var opts = me._options;
8696
8697            // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
8698            // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time
8699            // which breaks any animations.
8700            var existingModel = me._model;
8701            var model = me._model = getBaseModel(opts);
8702            var active = me._active;
8703
8704            var data = me._data;
8705
8706            // In the case where active.length === 0 we need to keep these at existing values for good animations
8707            var alignment = {
8708                xAlign: existingModel.xAlign,
8709                yAlign: existingModel.yAlign
8710            };
8711            var backgroundPoint = {
8712                x: existingModel.x,
8713                y: existingModel.y
8714            };
8715            var tooltipSize = {
8716                width: existingModel.width,
8717                height: existingModel.height
8718            };
8719            var tooltipPosition = {
8720                x: existingModel.caretX,
8721                y: existingModel.caretY
8722            };
8723
8724            var i, len;
8725
8726            if (active.length) {
8727                model.opacity = 1;
8728
8729                var labelColors = [];
8730                var labelTextColors = [];
8731                tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition);
8732
8733                var tooltipItems = [];
8734                for (i = 0, len = active.length; i < len; ++i) {
8735                    tooltipItems.push(createTooltipItem(active[i]));
8736                }
8737
8738                // If the user provided a filter function, use it to modify the tooltip items
8739                if (opts.filter) {
8740                    tooltipItems = tooltipItems.filter(function(a) {
8741                        return opts.filter(a, data);
8742                    });
8743                }
8744
8745                // If the user provided a sorting function, use it to modify the tooltip items
8746                if (opts.itemSort) {
8747                    tooltipItems = tooltipItems.sort(function(a, b) {
8748                        return opts.itemSort(a, b, data);
8749                    });
8750                }
8751
8752                // Determine colors for boxes
8753                helpers.each(tooltipItems, function(tooltipItem) {
8754                    labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
8755                    labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
8756                });
8757
8758
8759                // Build the Text Lines
8760                model.title = me.getTitle(tooltipItems, data);
8761                model.beforeBody = me.getBeforeBody(tooltipItems, data);
8762                model.body = me.getBody(tooltipItems, data);
8763                model.afterBody = me.getAfterBody(tooltipItems, data);
8764                model.footer = me.getFooter(tooltipItems, data);
8765
8766                // Initial positioning and colors
8767                model.x = Math.round(tooltipPosition.x);
8768                model.y = Math.round(tooltipPosition.y);
8769                model.caretPadding = opts.caretPadding;
8770                model.labelColors = labelColors;
8771                model.labelTextColors = labelTextColors;
8772
8773                // data points
8774                model.dataPoints = tooltipItems;
8775
8776                // We need to determine alignment of the tooltip
8777                tooltipSize = getTooltipSize(this, model);
8778                alignment = determineAlignment(this, tooltipSize);
8779                // Final Size and Position
8780                backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart);
8781            } else {
8782                model.opacity = 0;
8783            }
8784
8785            model.xAlign = alignment.xAlign;
8786            model.yAlign = alignment.yAlign;
8787            model.x = backgroundPoint.x;
8788            model.y = backgroundPoint.y;
8789            model.width = tooltipSize.width;
8790            model.height = tooltipSize.height;
8791
8792            // Point where the caret on the tooltip points to
8793            model.caretX = tooltipPosition.x;
8794            model.caretY = tooltipPosition.y;
8795
8796            me._model = model;
8797
8798            if (changed && opts.custom) {
8799                opts.custom.call(me, model);
8800            }
8801
8802            return me;
8803        },
8804
8805        drawCaret: function(tooltipPoint, size) {
8806            var ctx = this._chart.ctx;
8807            var vm = this._view;
8808            var caretPosition = this.getCaretPosition(tooltipPoint, size, vm);
8809
8810            ctx.lineTo(caretPosition.x1, caretPosition.y1);
8811            ctx.lineTo(caretPosition.x2, caretPosition.y2);
8812            ctx.lineTo(caretPosition.x3, caretPosition.y3);
8813        },
8814        getCaretPosition: function(tooltipPoint, size, vm) {
8815            var x1, x2, x3, y1, y2, y3;
8816            var caretSize = vm.caretSize;
8817            var cornerRadius = vm.cornerRadius;
8818            var xAlign = vm.xAlign;
8819            var yAlign = vm.yAlign;
8820            var ptX = tooltipPoint.x;
8821            var ptY = tooltipPoint.y;
8822            var width = size.width;
8823            var height = size.height;
8824
8825            if (yAlign === 'center') {
8826                y2 = ptY + (height / 2);
8827
8828                if (xAlign === 'left') {
8829                    x1 = ptX;
8830                    x2 = x1 - caretSize;
8831                    x3 = x1;
8832
8833                    y1 = y2 + caretSize;
8834                    y3 = y2 - caretSize;
8835                } else {
8836                    x1 = ptX + width;
8837                    x2 = x1 + caretSize;
8838                    x3 = x1;
8839
8840                    y1 = y2 - caretSize;
8841                    y3 = y2 + caretSize;
8842                }
8843            } else {
8844                if (xAlign === 'left') {
8845                    x2 = ptX + cornerRadius + (caretSize);
8846                    x1 = x2 - caretSize;
8847                    x3 = x2 + caretSize;
8848                } else if (xAlign === 'right') {
8849                    x2 = ptX + width - cornerRadius - caretSize;
8850                    x1 = x2 - caretSize;
8851                    x3 = x2 + caretSize;
8852                } else {
8853                    x2 = vm.caretX;
8854                    x1 = x2 - caretSize;
8855                    x3 = x2 + caretSize;
8856                }
8857                if (yAlign === 'top') {
8858                    y1 = ptY;
8859                    y2 = y1 - caretSize;
8860                    y3 = y1;
8861                } else {
8862                    y1 = ptY + height;
8863                    y2 = y1 + caretSize;
8864                    y3 = y1;
8865                    // invert drawing order
8866                    var tmp = x3;
8867                    x3 = x1;
8868                    x1 = tmp;
8869                }
8870            }
8871            return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3};
8872        },
8873
8874        drawTitle: function(pt, vm, ctx, opacity) {
8875            var title = vm.title;
8876
8877            if (title.length) {
8878                ctx.textAlign = vm._titleAlign;
8879                ctx.textBaseline = 'top';
8880
8881                var titleFontSize = vm.titleFontSize;
8882                var titleSpacing = vm.titleSpacing;
8883
8884                ctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity);
8885                ctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
8886
8887                var i, len;
8888                for (i = 0, len = title.length; i < len; ++i) {
8889                    ctx.fillText(title[i], pt.x, pt.y);
8890                    pt.y += titleFontSize + titleSpacing; // Line Height and spacing
8891
8892                    if (i + 1 === title.length) {
8893                        pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
8894                    }
8895                }
8896            }
8897        },
8898
8899        drawBody: function(pt, vm, ctx, opacity) {
8900            var bodyFontSize = vm.bodyFontSize;
8901            var bodySpacing = vm.bodySpacing;
8902            var body = vm.body;
8903
8904            ctx.textAlign = vm._bodyAlign;
8905            ctx.textBaseline = 'top';
8906            ctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
8907
8908            // Before Body
8909            var xLinePadding = 0;
8910            var fillLineOfText = function(line) {
8911                ctx.fillText(line, pt.x + xLinePadding, pt.y);
8912                pt.y += bodyFontSize + bodySpacing;
8913            };
8914
8915            // Before body lines
8916            ctx.fillStyle = mergeOpacity(vm.bodyFontColor, opacity);
8917            helpers.each(vm.beforeBody, fillLineOfText);
8918
8919            var drawColorBoxes = vm.displayColors;
8920            xLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;
8921
8922            // Draw body lines now
8923            helpers.each(body, function(bodyItem, i) {
8924                var textColor = mergeOpacity(vm.labelTextColors[i], opacity);
8925                ctx.fillStyle = textColor;
8926                helpers.each(bodyItem.before, fillLineOfText);
8927
8928                helpers.each(bodyItem.lines, function(line) {
8929                    // Draw Legend-like boxes if needed
8930                    if (drawColorBoxes) {
8931                        // Fill a white rect so that colours merge nicely if the opacity is < 1
8932                        ctx.fillStyle = mergeOpacity(vm.legendColorBackgroun
8932d, opacity);
8933                        ctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
8934
8935                        // Border
8936                        ctx.lineWidth = 1;
8937                        ctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity);
8938                        ctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);
8939
8940                        // Inner square
8941                        ctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity);
8942                        ctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
8943                        ctx.fillStyle = textColor;
8944                    }
8945
8946                    fillLineOfText(line);
8947                });
8948
8949                helpers.each(bodyItem.after, fillLineOfText);
8950            });
8951
8952            // Reset back to 0 for after body
8953            xLinePadding = 0;
8954
8955            // After body lines
8956            helpers.each(vm.afterBody, fillLineOfText);
8957            pt.y -= bodySpacing; // Remove last body spacing
8958        },
8959
8960        drawFooter: function(pt, vm, ctx, opacity) {
8961            var footer = vm.footer;
8962
8963            if (footer.length) {
8964                pt.y += vm.footerMarginTop;
8965
8966                ctx.textAlign = vm._footerAlign;
8967                ctx.textBaseline = 'top';
8968
8969                ctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity);
8970                ctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
8971
8972                helpers.each(footer, function(line) {
8973                    ctx.fillText(line, pt.x, pt.y);
8974                    pt.y += vm.footerFontSize + vm.footerSpacing;
8975                });
8976            }
8977        },
8978
8979        drawBackground: function(pt, vm, ctx, tooltipSize, opacity) {
8980            ctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);
8981            ctx.strokeStyle = mergeOpacity(vm.borderColor, opacity);
8982            ctx.lineWidth = vm.borderWidth;
8983            var xAlign = vm.xAlign;
8984            var yAlign = vm.yAlign;
8985            var x = pt.x;
8986            var y = pt.y;
8987            var width = tooltipSize.width;
8988            var height = tooltipSize.height;
8989            var radius = vm.cornerRadius;
8990
8991            ctx.beginPath();
8992            ctx.moveTo(x + radius, y);
8993            if (yAlign === 'top') {
8994                this.drawCaret(pt, tooltipSize);
8995            }
8996            ctx.lineTo(x + width - radius, y);
8997            ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
8998            if (yAlign === 'center' && xAlign === 'right') {
8999                this.drawCaret(pt, tooltipSize);
9000            }
9001            ctx.lineTo(x + width, y + height - radius);
9002            ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
9003            if (yAlign === 'bottom') {
9004                this.drawCaret(pt, tooltipSize);
9005            }
9006            ctx.lineTo(x + radius, y + height);
9007            ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
9008            if (yAlign === 'center' && xAlign === 'left') {
9009                this.drawCaret(pt, tooltipSize);
9010            }
9011            ctx.lineTo(x, y + radius);
9012            ctx.quadraticCurveTo(x, y, x + radius, y);
9013            ctx.closePath();
9014
9015            ctx.fill();
9016
9017            if (vm.borderWidth > 0) {
9018                ctx.stroke();
9019            }
9020        },
9021
9022        draw: function() {
9023            var ctx = this._chart.ctx;
9024            var vm = this._view;
9025
9026            if (vm.opacity === 0) {
9027                return;
9028            }
9029
9030            var tooltipSize = {
9031                width: vm.width,
9032                height: vm.height
9033            };
9034            var pt = {
9035                x: vm.x,
9036                y: vm.y
9037            };
9038
9039            // IE11/Edge does not like very small opacities, so snap to 0
9040            var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
9041
9042            // Truthy/falsey value for empty tooltip
9043            var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;
9044
9045            if (this._options.enabled && hasTooltipContent) {
9046                // Draw Background
9047                this.drawBackground(pt, vm, ctx, tooltipSize, opacity);
9048
9049                // Draw Title, Body, and Footer
9050                pt.x += vm.xPadding;
9051                pt.y += vm.yPadding;
9052
9053                // Titles
9054                this.drawTitle(pt, vm, ctx, opacity);
9055
9056                // Body
9057                this.drawBody(pt, vm, ctx, opacity);
9058
9059                // Footer
9060                this.drawFooter(pt, vm, ctx, opacity);
9061            }
9062        },
9063
9064        /**
9065         * Handle an event
9066         * @private
9067         * @param {IEvent} event - The event to handle
9068         * @returns {Boolean} true if the tooltip changed
9069         */
9070        handleEvent: function(e) {
9071            var me = this;
9072            var options = me._options;
9073            var changed = false;
9074
9075            me._lastActive = me._lastActive || [];
9076
9077            // Find Active Elements for tooltips
9078            if (e.type === 'mouseout') {
9079                me._active = [];
9080            } else {
9081                me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
9082            }
9083
9084            // Remember Last Actives
9085            changed = !helpers.arrayEquals(me._active, me._lastActive);
9086
9087            // Only handle target event on tooltip change
9088            if (changed) {
9089                me._lastActive = me._active;
9090
9091                if (options.enabled || options.custom) {
9092                    me._eventPosition = {
9093                        x: e.x,
9094                        y: e.y
9095                    };
9096
9097                    me.update(true);
9098                    me.pivot();
9099                }
9100            }
9101
9102            return changed;
9103        }
9104    });
9105
9106    /**
9107     * @namespace Chart.Tooltip.positioners
9108     */
9109    exports.positioners = positioners;
9110
9111
9112},{"26":26,"27":27,"46":46}],37:[function(require,module,exports){
9113    'use strict';
9114
9115    var defaults = require(26);
9116    var Element = require(27);
9117    var helpers = require(46);
9118
9119    defaults._set('global', {
9120        elements: {
9121            arc: {
9122                backgroundColor: defaults.global.defaultColor,
9123                borderColor: '#fff',
9124                borderWidth: 2
9125            }
9126        }
9127    });
9128
9129    module.exports = Element.extend({
9130        inLabelRange: function(mouseX) {
9131            var vm = this._view;
9132
9133            if (vm) {
9134                return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
9135            }
9136            return false;
9137        },
9138
9139        inRange: function(chartX, chartY) {
9140            var vm = this._view;
9141
9142            if (vm) {
9143                var pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY});
9144                var	angle = pointRelativePosition.angle;
9145                var distance = pointRelativePosition.distance;
9146
9147                // Sanitise angle range
9148                var startAngle = vm.startAngle;
9149                var endAngle = vm.endAngle;
9150                while (endAngle < startAngle) {
9151                    endAngle += 2.0 * Math.PI;
9152                }
9153                while (angle > endAngle) {
9154                    angle -= 2.0 * Math.PI;
9155                }
9156                while (angle < startAngle) {
9157                    angle += 2.0 * Math.PI;
9158                }
9159
9160                // Check if within the range of the open/close angle
9161                var betweenAngles = (angle >= startAngle && angle <= endAngle);
9162                var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
9163
9164                return (betweenAngles && withinRadius);
9165            }
9166            return false;
9167        },
9168
9169        getCenterPoint: function() {
9170            var vm = this._view;
9171            var halfAngle = (vm.startAngle + vm.endAngle) / 2;
9172            var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
9173            return {
9174                x: vm.x + Math.cos(halfAngle) * halfRadius,
9175                y: vm.y + Math.sin(halfAngle) * halfRadius
9176            };
9177        },
9178
9179        getArea: function() {
9180            var vm = this._view;
9181            return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
9182        },
9183
9184        tooltipPosition: function() {
9185            var vm = this._view;
9186            var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
9187            var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
9188
9189            return {
9190                x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
9191                y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
9192            };
9193        },
9194
9195        draw: function() {
9196            var ctx = this._chart.ctx;
9197            var vm = this._view;
9198            var sA = vm.startAngle;
9199            var eA = vm.endAngle;
9200
9201            ctx.beginPath();
9202
9203            ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
9204            ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
9205
9206            ctx.closePath();
9207            ctx.strokeStyle = vm.borderColor;
9208            ctx.lineWidth = vm.borderWidth;
9209
9210            ctx.fillStyle = vm.backgroundColor;
9211
9212            ctx.fill();
9213            ctx.lineJoin = 'bevel';
9214
9215            if (vm.borderWidth) {
9216                ctx.stroke();
9217            }
9218        }
9219    });
9220
9221},{"26":26,"27":27,"46":46}],38:[function(require,module,exports){
9222    'use strict';
9223
9224    var defaults = require(26);
9225    var Element = require(27);
9226    var helpers = require(46);
9227
9228    var globalDefaults = defaults.global;
9229
9230    defaults._set('global', {
9231        elements: {
9232            line: {
9233                tension: 0.4,
9234                backgroundColor: globalDefaults.defaultColor,
9235                borderWidth: 3,
9236                borderColor: globalDefaults.defaultColor,
9237                borderCapStyle: 'butt',
9238                borderDash: [],
9239                borderDashOffset: 0.0,
9240                borderJoinStyle: 'miter',
9241                capBezierPoints: true,
9242                fill: true, // do we fill in the area between the line and its base axis
9243            }
9244        }
9245    });
9246
9247    module.exports = Element.extend({
9248        draw: function() {
9249            var me = this;
9250            var vm = me._view;
9251            var ctx = me._chart.ctx;
9252            var spanGaps = vm.spanGaps;
9253            var points = me._children.slice(); // clone array
9254            var globalOptionLineElements = globalDefaults.elements.line;
9255            var lastDrawnIndex = -1;
9256            var index, current, previous, currentVM;
9257
9258            // If we are looping, adding the first point again
9259            if (me._loop && points.length) {
9260                points.push(points[0]);
9261            }
9262
9263            ctx.save();
9264
9265            // Stroke Line Options
9266            ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
9267
9268            // IE 9 and 10 do not support line dash
9269            if (ctx.setLineDash) {
9270                ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
9271            }
9272
9273            ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
9274            ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
9275            ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
9276            ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
9277
9278            // Stroke Line
9279            ctx.beginPath();
9280            lastDrawnIndex = -1;
9281
9282            for (index = 0; index < points.length; ++index) {
9283                current = points[index];
9284                previous = helpers.previousItem(points, index);
9285                currentVM = current._view;
9286
9287                // First point moves to it's starting position no matter what
9288                if (index === 0) {
9289                    if (!currentVM.skip) {
9290                        ctx.moveTo(currentVM.x, currentVM.y);
9291                        lastDrawnIndex = index;
9292                    }
9293                } else {
9294                    previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];
9295
9296                    if (!currentVM.skip) {
9297                        if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
9298                            // There was a gap and this is the first point after the gap
9299                            ctx.moveTo(currentVM.x, currentVM.y);
9300                        } else {
9301                            // Line to next point
9302                            helpers.canvas.lineTo(ctx, previous._view, current._view);
9303                        }
9304                        lastDrawnIndex = index;
9305                    }
9306                }
9307            }
9308
9309            ctx.stroke();
9310            ctx.restore();
9311        }
9312    });
9313
9314},{"26":26,"27":27,"46":46}],39:[function(require,module,exports){
9315    'use strict';
9316
9317    var defaults = require(26);
9318    var Element = require(27);
9319    var helpers = require(46);
9320
9321    var defaultColor = defaults.global.defaultColor;
9322
9323    defaults._set('global', {
9324        elements: {
9325            point: {
9326                radius: 3,
9327                pointStyle: 'circle',
9328                backgroundColor: defaultColor,
9329                borderColor: defaultColor,
9330                borderWidth: 1,
9331                // Hover
9332                hitRadius: 1,
9333                hoverRadius: 4,
9334                hoverBorderWidth: 1
9335            }
9336        }
9337    });
9338
9339    function xRange(mouseX) {
9340        var vm = this._view;
9341        return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false;
9342    }
9343
9344    function yRange(mouseY) {
9345        var vm = this._view;
9346        return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false;
9347    }
9348
9349    module.exports = Element.extend({
9350        inRange: function(mouseX, mouseY) {
9351            var vm = this._view;
9352            return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
9353        },
9354
9355        inLabelRange: xRange,
9356        inXRange: xRange,
9357        inYRange: yRange,
9358
9359        getCenterPoint: function() {
9360            var vm = this._view;
9361            return {
9362                x: vm.x,
9363                y: vm.y
9364            };
9365        },
9366
9367        getArea: function() {
9368            return Math.PI * Math.pow(this._view.radius, 2);
9369        },
9370
9371        tooltipPosition: function() {
9372            var vm = this._view;
9373            return {
9374                x: vm.x,
9375                y: vm.y,
9376                padding: vm.radius + vm.borderWidth
9377            };
9378        },
9379
9380        draw: function(chartArea) {
9381            var vm = this._view;
9382            var model = this._model;
9383            var ctx = this._chart.ctx;
9384            var pointStyle = vm.pointStyle;
9385            var rotation = vm.rotation;
9386            var radius = vm.radius;
9387            var x = vm.x;
9388            var y = vm.y;
9389            var errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)
9390
9391            if (vm.skip) {
9392                return;
9393            }
9394
9395            // Clipping for Points.
9396            if (chartArea === undefined || (model.x >= chartArea.left && chartArea.right * errMargin >= model.x && model.y >= chartArea.top && chartArea.bottom * errMargin >= model.y)) {
9397                ctx.strokeStyle = vm.borderColor || defaultColor;
9398                ctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth);
9399                ctx.fillStyle = vm.backgroundColor || defaultColor;
9400                helpers.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation);
9401            }
9402        }
9403    });
9404
9405},{"26":26,"27":27,"46":46}],40:[function(require,module,exports){
9406    'use strict';
9407
9408    var defaults = require(26);
9409    var Element = require(27);
9410
9411    defaults._set('global', {
9412        elements: {
9413            rectangle: {
9414                backgroundColor: defaults.global.defaultColor,
9415                borderColor: defaults.global.defaultColor,
9416                borderSkipped: 'bottom',
9417                borderWidth: 0
9418            }
9419        }
9420    });
9421
9422    function isVertical(bar) {
9423        return bar._view.width !== undefined;
9424    }
9425
9426    /**
9427     * Helper function to get the bounds of the bar regardless of the orientation
9428     * @param bar {Chart.Element.Rectangle} the bar
9429     * @return {Bounds} bounds of the bar
9430     * @private
9431     */
9432    function getBarBounds(bar) {
9433        var vm = bar._view;
9434        var x1, x2, y1, y2;
9435
9436        if (isVertical(bar)) {
9437            // vertical
9438            var halfWidth = vm.width / 2;
9439            x1 = vm.x - halfWidth;
9440            x2 = vm.x + halfWidth;
9441            y1 = Math.min(vm.y, vm.base);
9442            y2 = Math.max(vm.y, vm.base);
9443        } else {
9444            // horizontal bar
9445            var halfHeight = vm.height / 2;
9446            x1 = Math.min(vm.x, vm.base);
9447            x2 = Math.max(vm.x, vm.base);
9448            y1 = vm.y - halfHeight;
9449            y2 = vm.y + halfHeight;
9450        }
9451
9452        return {
9453            left: x1,
9454            top: y1,
9455            right: x2,
9456            bottom: y2
9457        };
9458    }
9459
9460    module.exports = Element.extend({
9461        draw: function() {
9462            var ctx = this._chart.ctx;
9463            var vm = this._view;
9464            var left, right, top, bottom, signX, signY, borderSkipped;
9465            var borderWidth = vm.borderWidth;
9466
9467            if (!vm.horizontal) {
9468                // bar
9469                left = vm.x - vm.width / 2;
9470                right = vm.x + vm.width / 2;
9471                top = vm.y;
9472                bottom = vm.base;
9473                signX = 1;
9474                signY = bottom > top ? 1 : -1;
9475                borderSkipped = vm.borderSkipped || 'bottom';
9476            } else {
9477                // horizontal bar
9478                left = vm.base;
9479                right = vm.x;
9480                top = vm.y - vm.height / 2;
9481                bottom = vm.y + vm.height / 2;
9482                signX = right > left ? 1 : -1;
9483                signY = 1;
9484                borderSkipped = vm.borderSkipped || 'left';
9485            }
9486
9487            // Canvas doesn't allow us to stroke inside the width so we can
9488            // adjust the sizes to fit if we're setting a stroke on the line
9489            if (borderWidth) {
9490                // borderWidth shold be less than bar width and bar height.
9491                var barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
9492                borderWidth = borderWidth > barSize ? barSize : borderWidth;
9493                var halfStroke = borderWidth / 2;
9494                // Adjust borderWidth when bar top position is near vm.base(zero).
9495                var borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
9496                var borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
9497                var borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
9498                var borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);
9499                // not become a vertical line?
9500                if (borderLeft !== borderRight) {
9501                    top = borderTop;
9502                    bottom = borderBottom;
9503                }
9504                // not become a horizontal line?
9505                if (borderTop !== borderBottom) {
9506                    left = borderLeft;
9507                    right = borderRight;
9508                }
9509            }
9510
9511            ctx.beginPath();
9512            ctx.fillStyle = vm.backgroundColor;
9513            ctx.strokeStyle = vm.borderColor;
9514            ctx.lineWidth = borderWidth;
9515
9516            // Corner points, from bottom-left to bottom-right clockwise
9517            // | 1 2 |
9518            // | 0 3 |
9519            var corners = [
9520                [left, bottom],
9521                [left, top],
9522                [right, top],
9523                [right, bottom]
9524            ];
9525
9526            // Find first (starting) corner with fallback to 'bottom'
9527            var borders = ['bottom', 'left', 'top', 'right'];
9528            var startCorner = borders.indexOf(borderSkipped, 0);
9529            if (startCorner === -1) {
9530                startCorner = 0;
9531            }
9532
9533            function cornerAt(index) {
9534                return corners[(startCorner + index) % 4];
9535            }
9536
9537            // Draw rectangle from 'startCorner'
9538            var corner = cornerAt(0);
9539            ctx.moveTo(corner[0], corner[1]);
9540
9541            for (var i = 1; i < 4; i++) {
9542                corner = cornerAt(i);
9543                ctx.lineTo(corner[0], corner[1]);
9544            }
9545
9546            ctx.fill();
9547            if (borderWidth) {
9548                ctx.stroke();
9549            }
9550        },
9551
9552        height: function() {
9553            var vm = this._view;
9554            return vm.base - vm.y;
9555        },
9556
9557        inRange: function(mouseX, mouseY) {
9558            var inRange = false;
9559
9560            if (this._view) {
9561                var bounds = getBarBounds(this);
9562                inRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;
9563            }
9564
9565            return inRange;
9566        },
9567
9568        inLabelRange: function(mouseX, mouseY) {
9569            var me = this;
9570            if (!me._view) {
9571                return false;
9572            }
9573
9574            var inRange = false;
9575            var bounds = getBarBounds(me);
9576
9577            if (isVertical(me)) {
9578                inRange = mouseX >= bounds.left && mouseX <= bounds.right;
9579            } else {
9580                inRange = mouseY >= bounds.top && mouseY <= bounds.bottom;
9581            }
9582
9583            return inRange;
9584        },
9585
9586        inXRange: function(mouseX) {
9587            var bounds = getBarBounds(this);
9588            return mouseX >= bounds.left && mouseX <= bounds.right;
9589        },
9590
9591        inYRange: function(mouseY) {
9592            var bounds = getBarBounds(this);
9593            return mouseY >= bounds.top && mouseY <= bounds.bottom;
9594        },
9595
9596        getCenterPoint: function() {
9597            var vm = this._view;
9598            var x, y;
9599            if (isVertical(this)) {
9600                x = vm.x;
9601                y = (vm.y + vm.base) / 2;
9602            } else {
9603                x = (vm.x + vm.base) / 2;
9604                y = vm.y;
9605            }
9606
9607            return {x: x, y: y};
9608        },
9609
9610        getArea: function() {
9611            var vm = this._view;
9612            return vm.width * Math.abs(vm.y - vm.base);
9613        },
9614
9615        tooltipPosition: function() {
9616            var vm = this._view;
9617            return {
9618                x: vm.x,
9619                y: vm.y
9620            };
9621        }
9622    });
9623
9624},{"26":26,"27":27}],41:[function(require,module,exports){
9625    'use strict';
9626
9627    module.exports = {};
9628    module.exports.Arc = require(37);
9629    module.exports.Line = require(38);
9630    module.exports.Point = require(39);
9631    module.exports.Rectangle = require(40);
9632
9633},{"37":37,"38":38,"39":39,"40":40}],42:[function(require,module,exports){
9634    'use strict';
9635
9636    var helpers = require(43);
9637
9638    /**
9639     * @namespace Chart.helpers.canvas
9640     */
9641    var exports = module.exports = {
9642        /**
9643         * Clears the entire canvas associated to the given `chart`.
9644         * @param {Chart} chart - The chart for which to clear the canvas.
9645         */
9646        clear: function(chart) {
9647            chart.ctx.clearRect(0, 0, chart.width, chart.height);
9648        },
9649
9650        /**
9651         * Creates a "path" for a rectangle with rounded corners at position (x, y) with a
9652         * given size (width, height) and the same `radius` for all corners.
9653         * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
9654         * @param {Number} x - The x axis of the coordinate for the rectangle starting point.
9655         * @param {Number} y - The y axis of the coordinate for the rectangle starting point.
9656         * @param {Number} width - The rectangle's width.
9657         * @param {Number} height - The rectangle's height.
9658         * @param {Number} radius - The rounded amount (in pixels) for the four corners.
9659         * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
9660         */
9661        roundedRect: function(ctx, x, y, width, height, radius) {
9662            if (radius) {
9663                // NOTE(SB) `epsilon` helps to prevent minor artifacts appearing
9664                // on Chrome when `r` is exactly half the height or the width.
9665                var epsilon = 0.0000001;
9666                var r = Math.min(radius, (height / 2) - epsilon, (width / 2) - epsilon);
9667
9668                ctx.moveTo(x + r, y);
9669                ctx.lineTo(x + width - r, y);
9670                ctx.arcTo(x + width, y, x + width, y + r, r);
9671                ctx.lineTo(x + width, y + height - r);
9672                ctx.arcTo(x + width, y + height, x + width - r, y + height, r);
9673                ctx.lineTo(x + r, y + height);
9674                ctx.arcTo(x, y + height, x, y + height - r, r);
9675                ctx.lineTo(x, y + r);
9676                ctx.arcTo(x, y, x + r, y, r);
9677                ctx.closePath();
9678                ctx.moveTo(x, y);
9679            } else {
9680                ctx.rect(x, y, width, height);
9681            }
9682        },
9683
9684        drawPoint: function(ctx, style, radius, x, y, rotation) {
9685            var type, edgeLength, xOffset, yOffset, height, size;
9686            rotation = rotation || 0;
9687
9688            if (style && typeof style === 'object') {
9689                type = style.toString();
9690                if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
9691                    ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height);
9692                    return;
9693                }
9694            }
9695
9696            if (isNaN(radius) || radius <= 0) {
9697                return;
9698            }
9699
9700            ctx.save();
9701            ctx.translate(x, y);
9702            ctx.rotate(rotation * Math.PI / 180);
9703            ctx.beginPath();
9704
9705            switch (style) {
9706                // Default includes circle
9707                default:
9708                    ctx.arc(0, 0, radius, 0, Math.PI * 2);
9709                    ctx.closePath();
9710                    break;
9711                case 'triangle':
9712                    edgeLength = 3 * radius / Math.sqrt(3);
9713                    height = edgeLength * Math.sqrt(3) / 2;
9714                    ctx.moveTo(-edgeLength / 2, height / 3);
9715                    ctx.lineTo(edgeLength / 2, height / 3);
9716                    ctx.lineTo(0, -2 * height / 3);
9717                    ctx.closePath();
9718                    break;
9719                case 'rect':
9720                    size = 1 / Math.SQRT2 * radius;
9721                    ctx.rect(-size, -size, 2 * size, 2 * size);
9722                    break;
9723                case 'rectRounded':
9724                    var offset = radius / Math.SQRT2;
9725                    var leftX = -offset;
9726                    var topY = -offset;
9727                    var sideSize = Math.SQRT2 * radius;
9728
9729                    // NOTE(SB) the rounded rect implementation changed to use `arcTo`
9730                    // instead of `quadraticCurveTo` since it generates better results
9731                    // when rect is almost a circle. 0.425 (instead of 0.5) produces
9732                    // results visually closer to the previous impl.
9733                    this.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius * 0.425);
9734                    break;
9735                case 'rectRot':
9736                    size = 1 / Math.SQRT2 * radius;
9737                    ctx.moveTo(-size, 0);
9738                    ctx.lineTo(0, size);
9739                    ctx.lineTo(size, 0);
9740                    ctx.lineTo(0, -size);
9741                    ctx.closePath();
9742                    break;
9743                case 'cross':
9744                    ctx.moveTo(0, radius);
9745                    ctx.lineTo(0, -radius);
9746                    ctx.moveTo(-radius, 0);
9747                    ctx.lineTo(radius, 0);
9748                    break;
9749                case 'crossRot':
9750                    xOffset = Math.cos(Math.PI / 4) * radius;
9751                    yOffset = Math.sin(Math.PI / 4) * radius;
9752                    ctx.moveTo(-xOffset, -yOffset);
9753                    ctx.lineTo(xOffset, yOffset);
9754                    ctx.moveTo(-xOffset, yOffset);
9755                    ctx.lineTo(xOffset, -yOffset);
9756                    break;
9757                case 'star':
9758                    ctx.moveTo(0, radius);
9759                    ctx.lineTo(0, -radius);
9760                    ctx.moveTo(-radius, 0);
9761                    ctx.lineTo(radius, 0);
9762                    xOffset = Math.cos(Math.PI / 4) * radius;
9763                    yOffset = Math.sin(Math.PI / 4) * radius;
9764                    ctx.moveTo(-xOffset, -yOffset);
9765                    ctx.lineTo(xOffset, yOffset);
9766                    ctx.moveTo(-xOffset, yOffset);
9767                    ctx.lineTo(xOffset, -yOffset);
9768                    break;
9769                case 'line':
9770                    ctx.moveTo(-radius, 0);
9771                    ctx.lineTo(radius, 0);
9772                    break;
9773                case 'dash':
9774                    ctx.moveTo(0, 0);
9775                    ctx.lineTo(radius, 0);
9776                    break;
9777            }
9778
9779            ctx.fill();
9780            ctx.stroke();
9781            ctx.restore();
9782        },
9783
9784        clipArea: function(ctx, area) {
9785            ctx.save();
9786            ctx.beginPath();
9787            ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
9788            ctx.clip();
9789        },
9790
9791        unclipArea: function(ctx) {
9792            ctx.restore();
9793        },
9794
9795        lineTo: function(ctx, previous, target, flip) {
9796            if (target.steppedLine) {
9797                if ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) {
9798                    ctx.lineTo(previous.x, target.y);
9799                } else {
9800                    ctx.lineTo(target.x, previous.y);
9801                }
9802                ctx.lineTo(target.x, target.y);
9803                return;
9804            }
9805
9806            if (!target.tension) {
9807                ctx.lineTo(target.x, target.y);
9808                return;
9809            }
9810
9811            ctx.bezierCurveTo(
9812                flip ? previous.controlPointPreviousX : previous.controlPointNextX,
9813                flip ? previous.controlPointPreviousY : previous.controlPointNextY,
9814                flip ? target.controlPointNextX : target.controlPointPreviousX,
9815                flip ? target.controlPointNextY : target.controlPointPreviousY,
9816                target.x,
9817                target.y);
9818        }
9819    };
9820
9821// DEPRECATIONS
9822
9823    /**
9824     * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
9825     * @namespace Chart.helpers.clear
9826     * @deprecated since version 2.7.0
9827     * @todo remove at version 3
9828     * @private
9829     */
9830    helpers.clear = exports.clear;
9831
9832    /**
9833     * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
9834     * @namespace Chart.helpers.drawRoundedRectangle
9835     * @deprecated since version 2.7.0
9836     * @todo remove at version 3
9837     * @private
9838     */
9839    helpers.drawRoundedRectangle = function(ctx) {
9840        ctx.beginPath();
9841        exports.roundedRect.apply(exports, arguments);
9842    };
9843
9844},{"43":43}
vendor: 21,953 bytes, lines 9844-10516
9844],43:[function(require,module,exports){
9845    'use strict';
9846
9847    /**
9848     * @namespace Chart.helpers
9849     */
9850    var helpers = {
9851        /**
9852         * An empty function that can be used, for example, for optional callback.
9853         */
9854        noop: function() {},
9855
9856        /**
9857         * Returns a unique id, sequentially generated from a global variable.
9858         * @returns {Number}
9859         * @function
9860         */
9861        uid: (function() {
9862            var id = 0;
9863            return function() {
9864                return id++;
9865            };
9866        }()),
9867
9868        /**
9869         * Returns true if `value` is neither null nor undefined, else returns false.
9870         * @param {*} value - The value to test.
9871         * @returns {Boolean}
9872         * @since 2.7.0
9873         */
9874        isNullOrUndef: function(value) {
9875            return value === null || typeof value === 'undefined';
9876        },
9877
9878        /**
9879         * Returns true if `value` is an array, else returns false.
9880         * @param {*} value - The value to test.
9881         * @returns {Boolean}
9882         * @function
9883         */
9884        isArray: Array.isArray ? Array.isArray : function(value) {
9885            return Object.prototype.toString.call(value) === '[object Array]';
9886        },
9887
9888        /**
9889         * Returns true if `value` is an object (excluding null), else returns false.
9890         * @param {*} value - The value to test.
9891         * @returns {Boolean}
9892         * @since 2.7.0
9893         */
9894        isObject: function(value) {
9895            return value !== null && Object.prototype.toString.call(value) === '[object Object]';
9896        },
9897
9898        /**
9899         * Returns `value` if defined, else returns `defaultValue`.
9900         * @param {*} value - The value to return if defined.
9901         * @param {*} defaultValue - The value to return if `value` is undefined.
9902         * @returns {*}
9903         */
9904        valueOrDefault: function(value, defaultValue) {
9905            return typeof value === 'undefined' ? defaultValue : value;
9906        },
9907
9908        /**
9909         * Returns value at the given `index` in array if defined, else returns `defaultValue`.
9910         * @param {Array} value - The array to lookup for value at `index`.
9911         * @param {Number} index - The index in `value` to lookup for value.
9912         * @param {*} defaultValue - The value to return if `value[index]` is undefined.
9913         * @returns {*}
9914         */
9915        valueAtIndexOrDefault: function(value, index, defaultValue) {
9916            return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
9917        },
9918
9919        /**
9920         * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
9921         * value returned by `fn`. If `fn` is not a function, this method returns undefined.
9922         * @param {Function} fn - The function to call.
9923         * @param {Array|undefined|null} args - The arguments with which `fn` should be called.
9924         * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
9925         * @returns {*}
9926         */
9927        callback: function(fn, args, thisArg) {
9928            if (fn && typeof fn.call === 'function') {
9929                return fn.apply(thisArg, args);
9930            }
9931        },
9932
9933        /**
9934         * Note(SB) for performance sake, this method should only be used when loopable type
9935         * is unknown or in none intensive code (not called often and small loopable). Else
9936         * it's preferable to use a regular for() loop and save extra function calls.
9937         * @param {Object|Array} loopable - The object or array to be iterated.
9938         * @param {Function} fn - The function to call for each item.
9939         * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.
9940         * @param {Boolean} [reverse] - If true, iterates backward on the loopable.
9941         */
9942        each: function(loopable, fn, thisArg, reverse) {
9943            var i, len, keys;
9944            if (helpers.isArray(loopable)) {
9945                len = loopable.length;
9946                if (reverse) {
9947                    for (i = len - 1; i >= 0; i--) {
9948                        fn.call(thisArg, loopable[i], i);
9949                    }
9950                } else {
9951                    for (i = 0; i < len; i++) {
9952                        fn.call(thisArg, loopable[i], i);
9953                    }
9954                }
9955            } else if (helpers.isObject(loopable)) {
9956                keys = Object.keys(loopable);
9957                len = keys.length;
9958                for (i = 0; i < len; i++) {
9959                    fn.call(thisArg, loopable[keys[i]], keys[i]);
9960                }
9961            }
9962        },
9963
9964        /**
9965         * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
9966         * @see http://stackoverflow.com/a/14853974
9967         * @param {Array} a0 - The array to compare
9968         * @param {Array} a1 - The array to compare
9969         * @returns {Boolean}
9970         */
9971        arrayEquals: function(a0, a1) {
9972            var i, ilen, v0, v1;
9973
9974            if (!a0 || !a1 || a0.length !== a1.length) {
9975                return false;
9976            }
9977
9978            for (i = 0, ilen = a0.length; i < ilen; ++i) {
9979                v0 = a0[i];
9980                v1 = a1[i];
9981
9982                if (v0 instanceof Array && v1 instanceof Array) {
9983                    if (!helpers.arrayEquals(v0, v1)) {
9984                        return false;
9985                    }
9986                } else if (v0 !== v1) {
9987                    // NOTE: two different object instances will never be equal: {x:20} != {x:20}
9988                    return false;
9989                }
9990            }
9991
9992            return true;
9993        },
9994
9995        /**
9996         * Returns a deep copy of `source` without keeping references on objects and arrays.
9997         * @param {*} source - The value to clone.
9998         * @returns {*}
9999         */
10000        clone: function(source) {
10001            if (helpers.isArray(source)) {
10002                return source.map(helpers.clone);
10003            }
10004
10005            if (helpers.isObject(source)) {
10006                var target = {};
10007                var keys = Object.keys(source);
10008                var klen = keys.length;
10009                var k = 0;
10010
10011                for (; k < klen; ++k) {
10012                    target[keys[k]] = helpers.clone(source[keys[k]]);
10013                }
10014
10015                return target;
10016            }
10017
10018            return source;
10019        },
10020
10021        /**
10022         * The default merger when Chart.helpers.merge is called without merger option.
10023         * Note(SB): this method is also used by configMerge and scaleMerge as fallback.
10024         * @private
10025         */
10026        _merger: function(key, target, source, options) {
10027            var tval = target[key];
10028            var sval = source[key];
10029
10030            if (helpers.isObject(tval) && helpers.isObject(sval)) {
10031                helpers.merge(tval, sval, options);
10032            } else {
10033                target[key] = helpers.clone(sval);
10034            }
10035        },
10036
10037        /**
10038         * Merges source[key] in target[key] only if target[key] is undefined.
10039         * @private
10040         */
10041        _mergerIf: function(key, target, source) {
10042            var tval = target[key];
10043            var sval = source[key];
10044
10045            if (helpers.isObject(tval) && helpers.isObject(sval)) {
10046                helpers.mergeIf(tval, sval);
10047            } else if (!target.hasOwnProperty(key)) {
10048                target[key] = helpers.clone(sval);
10049            }
10050        },
10051
10052        /**
10053         * Recursively deep copies `source` properties into `target` with the given `options`.
10054         * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
10055         * @param {Object} target - The target object in which all sources are merged into.
10056         * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
10057         * @param {Object} [options] - Merging options:
10058         * @param {Function} [options.merger] - The merge method (key, target, source, options)
10059         * @returns {Object} The `target` object.
10060         */
10061        merge: function(target, source, options) {
10062            var sources = helpers.isArray(source) ? source : [source];
10063            var ilen = sources.length;
10064            var merge, i, keys, klen, k;
10065
10066            if (!helpers.isObject(target)) {
10067                return target;
10068            }
10069
10070            options = options || {};
10071            merge = options.merger || helpers._merger;
10072
10073            for (i = 0; i < ilen; ++i) {
10074                source = sources[i];
10075                if (!helpers.isObject(source)) {
10076                    continue;
10077                }
10078
10079                keys = Object.keys(source);
10080                for (k = 0, klen = keys.length; k < klen; ++k) {
10081                    merge(keys[k], target, source, options);
10082                }
10083            }
10084
10085            return target;
10086        },
10087
10088        /**
10089         * Recursively deep copies `source` properties into `target` *only* if not defined in target.
10090         * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
10091         * @param {Object} target - The target object in which all sources are merged into.
10092         * @param {Object|Array(Object)} source - Object(s) to merge into `target`.
10093         * @returns {Object} The `target` object.
10094         */
10095        mergeIf: function(target, source) {
10096            return helpers.merge(target, source, {merger: helpers._mergerIf});
10097        },
10098
10099        /**
10100         * Applies the contents of two or more objects together into the first object.
10101         * @param {Object} target - The target object in which all objects are merged into.
10102         * @param {Object} arg1 - Object containing additional properties to merge in target.
10103         * @param {Object} argN - Additional objects containing properties to merge in target.
10104         * @returns {Object} The `target` object.
10105         */
10106        extend: function(target) {
10107            var setFn = function(value, key) {
10108                target[key] = value;
10109            };
10110            for (var i = 1, ilen = arguments.length; i < ilen; ++i) {
10111                helpers.each(arguments[i], setFn);
10112            }
10113            return target;
10114        },
10115
10116        /**
10117         * Basic javascript inheritance based on the model created in Backbone.js
10118         */
10119        inherits: function(extensions) {
10120            var me = this;
10121            var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
10122                return me.apply(this, arguments);
10123            };
10124
10125            var Surrogate = function() {
10126                this.constructor = ChartElement;
10127            };
10128
10129            Surrogate.prototype = me.prototype;
10130            ChartElement.prototype = new Surrogate();
10131            ChartElement.extend = helpers.inherits;
10132
10133            if (extensions) {
10134                helpers.extend(ChartElement.prototype, extensions);
10135            }
10136
10137            ChartElement.__super__ = me.prototype;
10138            return ChartElement;
10139        }
10140    };
10141
10142    module.exports = helpers;
10143
10144// DEPRECATIONS
10145
10146    /**
10147     * Provided for backward compatibility, use Chart.helpers.callback instead.
10148     * @function Chart.helpers.callCallback
10149     * @deprecated since version 2.6.0
10150     * @todo remove at version 3
10151     * @private
10152     */
10153    helpers.callCallback = helpers.callback;
10154
10155    /**
10156     * Provided for backward compatibility, use Array.prototype.indexOf instead.
10157     * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
10158     * @function Chart.helpers.indexOf
10159     * @deprecated since version 2.7.0
10160     * @todo remove at version 3
10161     * @private
10162     */
10163    helpers.indexOf = function(array, item, fromIndex) {
10164        return Array.prototype.indexOf.call(array, item, fromIndex);
10165    };
10166
10167    /**
10168     * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
10169     * @function Chart.helpers.getValueOrDefault
10170     * @deprecated since version 2.7.0
10171     * @todo remove at version 3
10172     * @private
10173     */
10174    helpers.getValueOrDefault = helpers.valueOrDefault;
10175
10176    /**
10177     * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
10178     * @function Chart.helpers.getValueAtIndexOrDefault
10179     * @deprecated since version 2.7.0
10180     * @todo remove at version 3
10181     * @private
10182     */
10183    helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
10184
10185},{}],44:[function(require,module,exports){
10186    'use strict';
10187
10188    var helpers = require(43);
10189
10190    /**
10191     * Easing functions adapted from Robert Penner's easing equations.
10192     * @namespace Chart.helpers.easingEffects
10193     * @see http://www.robertpenner.com/easing/
10194     */
10195    var effects = {
10196        linear: function(t) {
10197            return t;
10198        },
10199
10200        easeInQuad: function(t) {
10201            return t * t;
10202        },
10203
10204        easeOutQuad: function(t) {
10205            return -t * (t - 2);
10206        },
10207
10208        easeInOutQuad: function(t) {
10209            if ((t /= 0.5) < 1) {
10210                return 0.5 * t * t;
10211            }
10212            return -0.5 * ((--t) * (t - 2) - 1);
10213        },
10214
10215        easeInCubic: function(t) {
10216            return t * t * t;
10217        },
10218
10219        easeOutCubic: function(t) {
10220            return (t = t - 1) * t * t + 1;
10221        },
10222
10223        easeInOutCubic: function(t) {
10224            if ((t /= 0.5) < 1) {
10225                return 0.5 * t * t * t;
10226            }
10227            return 0.5 * ((t -= 2) * t * t + 2);
10228        },
10229
10230        easeInQuart: function(t) {
10231            return t * t * t * t;
10232        },
10233
10234        easeOutQuart: function(t) {
10235            return -((t = t - 1) * t * t * t - 1);
10236        },
10237
10238        easeInOutQuart: function(t) {
10239            if ((t /= 0.5) < 1) {
10240                return 0.5 * t * t * t * t;
10241            }
10242            return -0.5 * ((t -= 2) * t * t * t - 2);
10243        },
10244
10245        easeInQuint: function(t) {
10246            return t * t * t * t * t;
10247        },
10248
10249        easeOutQuint: function(t) {
10250            return (t = t - 1) * t * t * t * t + 1;
10251        },
10252
10253        easeInOutQuint: function(t) {
10254            if ((t /= 0.5) < 1) {
10255                return 0.5 * t * t * t * t * t;
10256            }
10257            return 0.5 * ((t -= 2) * t * t * t * t + 2);
10258        },
10259
10260        easeInSine: function(t) {
10261            return -Math.cos(t * (Math.PI / 2)) + 1;
10262        },
10263
10264        easeOutSine: function(t) {
10265            return Math.sin(t * (Math.PI / 2));
10266        },
10267
10268        easeInOutSine: function(t) {
10269            return -0.5 * (Math.cos(Math.PI * t) - 1);
10270        },
10271
10272        easeInExpo: function(t) {
10273            return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
10274        },
10275
10276        easeOutExpo: function(t) {
10277            return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
10278        },
10279
10280        easeInOutExpo: function(t) {
10281            if (t === 0) {
10282                return 0;
10283            }
10284            if (t === 1) {
10285                return 1;
10286            }
10287            if ((t /= 0.5) < 1) {
10288                return 0.5 * Math.pow(2, 10 * (t - 1));
10289            }
10290            return 0.5 * (-Math.pow(2, -10 * --t) + 2);
10291        },
10292
10293        easeInCirc: function(t) {
10294            if (t >= 1) {
10295                return t;
10296            }
10297            return -(Math.sqrt(1 - t * t) - 1);
10298        },
10299
10300        easeOutCirc: function(t) {
10301            return Math.sqrt(1 - (t = t - 1) * t);
10302        },
10303
10304        easeInOutCirc: function(t) {
10305            if ((t /= 0.5) < 1) {
10306                return -0.5 * (Math.sqrt(1 - t * t) - 1);
10307            }
10308            return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
10309        },
10310
10311        easeInElastic: function(t) {
10312            var s = 1.70158;
10313            var p = 0;
10314            var a = 1;
10315            if (t === 0) {
10316                return 0;
10317            }
10318            if (t === 1) {
10319                return 1;
10320            }
10321            if (!p) {
10322                p = 0.3;
10323            }
10324            if (a < 1) {
10325                a = 1;
10326                s = p / 4;
10327            } else {
10328                s = p / (2 * Math.PI) * Math.asin(1 / a);
10329            }
10330            return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
10331        },
10332
10333        easeOutElastic: function(t) {
10334            var s = 1.70158;
10335            var p = 0;
10336            var a = 1;
10337            if (t === 0) {
10338                return 0;
10339            }
10340            if (t === 1) {
10341                return 1;
10342            }
10343            if (!p) {
10344                p = 0.3;
10345            }
10346            if (a < 1) {
10347                a = 1;
10348                s = p / 4;
10349            } else {
10350                s = p / (2 * Math.PI) * Math.asin(1 / a);
10351            }
10352            return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
10353        },
10354
10355        easeInOutElastic: function(t) {
10356            var s = 1.70158;
10357            var p = 0;
10358            var a = 1;
10359            if (t === 0) {
10360                return 0;
10361            }
10362            if ((t /= 0.5) === 2) {
10363                return 1;
10364            }
10365            if (!p) {
10366                p = 0.45;
10367            }
10368            if (a < 1) {
10369                a = 1;
10370                s = p / 4;
10371            } else {
10372                s = p / (2 * Math.PI) * Math.asin(1 / a);
10373            }
10374            if (t < 1) {
10375                return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
10376            }
10377            return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
10378        },
10379        easeInBack: function(t) {
10380            var s = 1.70158;
10381            return t * t * ((s + 1) * t - s);
10382        },
10383
10384        easeOutBack: function(t) {
10385            var s = 1.70158;
10386            return (t = t - 1) * t * ((s + 1) * t + s) + 1;
10387        },
10388
10389        easeInOutBack: function(t) {
10390            var s = 1.70158;
10391            if ((t /= 0.5) < 1) {
10392                return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
10393            }
10394            return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
10395        },
10396
10397        easeInBounce: function(t) {
10398            return 1 - effects.easeOutBounce(1 - t);
10399        },
10400
10401        easeOutBounce: function(t) {
10402            if (t < (1 / 2.75)) {
10403                return 7.5625 * t * t;
10404            }
10405            if (t < (2 / 2.75)) {
10406                return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
10407            }
10408            if (t < (2.5 / 2.75)) {
10409                return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
10410            }
10411            return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
10412        },
10413
10414        easeInOutBounce: function(t) {
10415            if (t < 0.5) {
10416                return effects.easeInBounce(t * 2) * 0.5;
10417            }
10418            return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
10419        }
10420    };
10421
10422    module.exports = {
10423        effects: effects
10424    };
10425
10426// DEPRECATIONS
10427
10428    /**
10429     * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
10430     * @function Chart.helpers.easingEffects
10431     * @deprecated since version 2.7.0
10432     * @todo remove at version 3
10433     * @private
10434     */
10435    helpers.easingEffects = effects;
10436
10437},{"43":43}],45:[function(require,module,exports){
10438    'use strict';
10439
10440    var helpers = require(43);
10441
10442    /**
10443     * @alias Chart.helpers.options
10444     * @namespace
10445     */
10446    module.exports = {
10447        /**
10448         * Converts the given line height `value` in pixels for a specific font `size`.
10449         * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
10450         * @param {Number} size - The font size (in pixels) used to resolve relative `value`.
10451         * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid).
10452         * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
10453         * @since 2.7.0
10454         */
10455        toLineHeight: function(value, size) {
10456            var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
10457            if (!matches || matches[1] === 'normal') {
10458                return size * 1.2;
10459            }
10460
10461            value = +matches[2];
10462
10463            switch (matches[3]) {
10464                case 'px':
10465                    return value;
10466                case '%':
10467                    value /= 100;
10468                    break;
10469                default:
10470                    break;
10471            }
10472
10473            return size * value;
10474        },
10475
10476        /**
10477         * Converts the given value into a padding object with pre-computed width/height.
10478         * @param {Number|Object} value - If a number, set the value to all TRBL component,
10479         *  else, if and object, use defined properties and sets undefined ones to 0.
10480         * @returns {Object} The padding values (top, right, bottom, left, width, height)
10481         * @since 2.7.0
10482         */
10483        toPadding: function(value) {
10484            var t, r, b, l;
10485
10486            if (helpers.isObject(value)) {
10487                t = +value.top || 0;
10488                r = +value.right || 0;
10489                b = +value.bottom || 0;
10490                l = +value.left || 0;
10491            } else {
10492                t = r = b = l = +value || 0;
10493            }
10494
10495            return {
10496                top: t,
10497                right: r,
10498                bottom: b,
10499                left: l,
10500                height: t + b,
10501                width: l + r
10502            };
10503        },
10504
10505        /**
10506         * Evaluates the given `inputs` sequentially and returns the first defined value.
10507         * @param {Array[]} inputs - An array of values, falling back to the last value.
10508         * @param {Object} [context] - If defined and the current value is a function, the value
10509         * is called with `context` as first argument and the result becomes the new input.
10510         * @param {Number} [index] - If defined and the current value is an array, the value
10511         * at `index` become the new input.
10512         * @since 2.7.0
10513         */
10514        resolve: function(inputs, context, index) {
10515            var i, ilen, value;
10516
10517            for (i = 0, ilen = inputs.length; i < ilen; ++i) {
10518                value = inputs[i];
10519                if (value === undefined) {
10520                    continue;
10521                }
10522                if (context !== undefined && typeof value === 'function') {
10523                    value = value(context);
10524                }
10525                if (index !== undefined && helpers.isArray(value)) {
10526                    value = value[index];
10527                }
10528                if (value !== undefined) {
10529                    return value;
10530                }
10531            }
10532        }
10533    };
10534
10535},{"43":43}],46:[function(require,module,exports){
10536    'use strict';
10537
10538    module.exports = require(43);
10539    module.exports.easing = require(44);
10540    module.exports.canvas = require(42);
10541    module.exports.options = require(45);
10542
10543},{"42":42,"43":43,"44":44,"45":45}],47:[function(require,module,exports){
10544    /**
10545     * Platform fallback implementation (minimal).
10546     * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
10547     */
10548
10549    module.exports = {
10550        acquireContext: function(item) {
10551            if (item && item.canvas) {
10552                // Support for any object associated to a canvas (including a context2d)
10553                item = item.canvas;
10554            }
10555
10556            return item && item.getContext('2d') || null;
10557        }
10558    };
10559
10560},{}],48:[function(require,module,exports){
10561    /**
10562     * Chart.Platform implementation for targeting a web browser
10563     */
10564
10565    'use strict';
10566
10567    var helpers = require(46);
10568
10569    var EXPANDO_KEY = '$chartjs';
10570    var CSS_PREFIX = 'chartjs-';
10571    var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
10572    var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
10573    var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];
10574
10575    /**
10576     * DOM event types -> Chart.js event types.
10577     * Note: only events with different types are mapped.
10578     * @see https://developer.mozilla.org/en-US/docs/Web/Events
10579     */
10580    var EVENT_TYPES = {
10581        touchstart: 'mousedown',
10582        touchmove: 'mousemove',
10583        touchend: 'mouseup',
10584        pointerenter: 'mouseenter',
10585        pointerdown: 'mousedown',
10586        pointermove: 'mousemove',
10587        pointerup: 'mouseup',
10588        pointerleave: 'mouseout',
10589        pointerout: 'mouseout'
10590    };
10591
10592    /**
10593     * The "used" size is the final value of a dimension property after all calculations have
10594     * been performed. This method uses the computed style of `element` but returns undefined
10595     * if the computed style is not expressed in pixels. That can happen in some cases where
10596     * `element` has a size relative to its parent and this last one is not yet displayed,
10597     * for example because of `display: none` on a parent node.
10598     * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
10599     * @returns {Number} Size in pixels or undefined if unknown.
10600     */
10601    function readUsedSize(element, property) {
10602        var value = helpers.getStyle(element, property);
10603        var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
10604        return matches ? Number(matches[1]) : undefined;
10605    }
10606
10607    /**
10608     * Initializes the canvas style and render size without modifying the canvas display size,
10609     * since responsiveness is handled by the controller.resize() method. The config is used
10610     * to determine the aspect ratio to apply in case no explicit height has been specified.
10611     */
10612    function initCanvas(canvas, config) {
10613        var style = canvas.style;
10614
10615        // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
10616        // returns null or '' if no explicit value has been set to the canvas attribute.
10617        var renderHeight = canvas.getAttribute('height');
10618        var renderWidth = canvas.getAttribute('width');
10619
10620        // Chart.js modifies some canvas values that we want to restore on de
vendor: 5,851 bytes, lines 10620-10794
10620stroy
10621        canvas[EXPANDO_KEY] = {
10622            initial: {
10623                height: renderHeight,
10624                width: renderWidth,
10625                style: {
10626                    display: style.display,
10627                    height: style.height,
10628                    width: style.width
10629                }
10630            }
10631        };
10632
10633        // Force canvas to display as block to avoid extra space caused by inline
10634        // elements, which would interfere with the responsive resize process.
10635        // https://github.com/chartjs/Chart.js/issues/2538
10636        style.display = style.display || 'block';
10637
10638        if (renderWidth === null || renderWidth === '') {
10639            var displayWidth = readUsedSize(canvas, 'width');
10640            if (displayWidth !== undefined) {
10641                canvas.width = displayWidth;
10642            }
10643        }
10644
10645        if (renderHeight === null || renderHeight === '') {
10646            if (canvas.style.height === '') {
10647                // If no explicit render height and style height, let's apply the aspect ratio,
10648                // which one can be specified by the user but also by charts as default option
10649                // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
10650                canvas.height = canvas.width / (config.options.aspectRatio || 2);
10651            } else {
10652                var displayHeight = readUsedSize(canvas, 'height');
10653                if (displayWidth !== undefined) {
10654                    canvas.height = displayHeight;
10655                }
10656            }
10657        }
10658
10659        return canvas;
10660    }
10661
10662    /**
10663     * Detects support for options object argument in addEventListener.
10664     * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
10665     * @private
10666     */
10667    var supportsEventListenerOptions = (function() {
10668        var supports = false;
10669        try {
10670            var options = Object.defineProperty({}, 'passive', {
10671                get: function() {
10672                    supports = true;
10673                }
10674            });
10675            window.addEventListener('e', null, options);
10676        } catch (e) {
10677            // continue regardless of error
10678        }
10679        return supports;
10680    }());
10681
10682// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
10683// https://github.com/chartjs/Chart.js/issues/4287
10684    var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;
10685
10686    function addEventListener(node, type, listener) {
10687        node.addEventListener(type, listener, eventListenerOptions);
10688    }
10689
10690    function removeEventListener(node, type, listener) {
10691        node.removeEventListener(type, listener, eventListenerOptions);
10692    }
10693
10694    function createEvent(type, chart, x, y, nativeEvent) {
10695        return {
10696            type: type,
10697            chart: chart,
10698            native: nativeEvent || null,
10699            x: x !== undefined ? x : null,
10700            y: y !== undefined ? y : null,
10701        };
10702    }
10703
10704    function fromNativeEvent(event, chart) {
10705        var type = EVENT_TYPES[event.type] || event.type;
10706        var pos = helpers.getRelativePosition(event, chart);
10707        return createEvent(type, chart, pos.x, pos.y, event);
10708    }
10709
10710    function throttled(fn, thisArg) {
10711        var ticking = false;
10712        var args = [];
10713
10714        return function() {
10715            args = Array.prototype.slice.call(arguments);
10716            thisArg = thisArg || this;
10717
10718            if (!ticking) {
10719                ticking = true;
10720                helpers.requestAnimFrame.call(window, function() {
10721                    ticking = false;
10722                    fn.apply(thisArg, args);
10723                });
10724            }
10725        };
10726    }
10727
10728// Implementation based on https://github.com/marcj/css-element-queries
10729    function createResizer(handler) {
10730        var resizer = document.createElement('div');
10731        var cls = CSS_PREFIX + 'size-monitor';
10732        var maxSize = 1000000;
10733        var style =
10734            'position:absolute;' +
10735            'left:0;' +
10736            'top:0;' +
10737            'right:0;' +
10738            'bottom:0;' +
10739            'overflow:hidden;' +
10740            'pointer-events:none;' +
10741            'visibility:hidden;' +
10742            'z-index:-1;';
10743
10744        resizer.style.cssText = style;
10745        resizer.className = cls;
10746        resizer.innerHTML =
10747            '<div class="' + cls + '-expand" style="' + style + '">' +
10748            '<div style="' +
10749            'position:absolute;' +
10750            'width:' + maxSize + 'px;' +
10751            'height:' + maxSize + 'px;' +
10752            'left:0;' +
10753            'top:0">' +
10754            '</div>' +
10755            '</div>' +
10756            '<div class="' + cls + '-shrink" style="' + style + '">' +
10757            '<div style="' +
10758            'position:absolute;' +
10759            'width:200%;' +
10760            'height:200%;' +
10761            'left:0; ' +
10762            'top:0">' +
10763            '</div>' +
10764            '</div>';
10765
10766        var expand = resizer.childNodes[0];
10767        var shrink = resizer.childNodes[1];
10768
10769        resizer._reset = function() {
10770            expand.scrollLeft = maxSize;
10771            expand.scrollTop = maxSize;
10772            shrink.scrollLeft = maxSize;
10773            shrink.scrollTop = maxSize;
10774        };
10775        var onScroll = function() {
10776            resizer._reset();
10777            handler();
10778        };
10779
10780        addEventListener(expand, 'scroll', onScroll.bind(expand, 'expand'));
10781        addEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));
10782
10783        return resizer;
10784    }
10785
10786// https://davidwalsh.name/detect-node-insertion
10787    function watchForRender(node, handler) {
10788        var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
10789        var proxy = expando.renderProxy = function(e) {
10790            if (e.animationName === CSS_RENDER_ANIMATION) {
10791                handler();
10792            }
10793        };
10794
10795        helpers.each(ANIMATION_START_EVENTS, function(type) {
10796            addEventListener(node, type, proxy);
10797        });
10798
10799        // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class
10800        // is removed then added back immediately (same animation frame?). Accessing the
10801        // `offsetParent` property will force a reflow and re-evaluate the CSS animation.
10802        // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics
10803        // https://github.com/chartjs/Chart.js/issues/4737
10804        expando.reflow = !!node.offsetParent;
10805
10806        node.classList.add(CSS_RENDER_MONITOR);
10807    }
10808
10809    function unwatchForRender(node) {
10810        var expando = node[EXPANDO_KEY] || {};
10811        var proxy = expando.renderProxy;
10812
10813        if (proxy) {
10814            helpers.each(ANIMATION_START_EVENTS, function(type) {
10815                removeEventListener(node, type, proxy);
10816            });
10817
10818            delete expando.renderProxy;
10819        }
10820
10821        node.classList.remove(CSS_RENDER_MONITOR);
10822    }
10823
10824    function addResizeListener(node, listener, chart) {
10825        var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
10826
10827        // Let's keep track of this added resizer and thus avoid DOM query when removing it.
10828        var resizer = expando.resizer = createResizer(throttled(function() {
10829            if (expando.resizer) {
10830                return listener(createEvent('resize', chart));
10831            }
10832        }));
10833
10834        // The resizer needs to be attached to the node parent, so we first need to be
10835        // sure that `node` is attached to the DOM before injecting the resizer element.
10836        watchForRender(node, function() {
10837            if (expando.resizer) {
10838                var container = node.parentNode;
10839                if (container && container !== resizer.parentNode) {
10840                    container.insertBefore(resizer, container.firstChild);
10841                }
10842
10843                // The container size might have changed, let's reset the resizer state.
10844                resizer._reset();
10845            }
10846        });
10847    }
10848
10849    function removeResizeListener(node) {
10850        var expando = node[EXPANDO_KEY] || {};
10851        var resizer = expando.resizer;
10852
10853        delete expando.resizer;
10854        unwatchForRender(node);
10855
10856        if (resizer && resizer.parentNode) {
10857            resizer.parentNode.removeChild(resizer);
10858        }
10859    }
10860
10861    function injectCSS(platform, css) {
10862        // http://stackoverflow.com/q/3922139
10863        var style = platform._style || document.createElement('style');
10864        if (!platform._style) {
10865            platform._style = style;
10866            css = '/* Chart.js */\n' + css;
10867            style.setAttribute('type', 'text/css');
10868            document.getElementsByTagName('head')[0].appendChild(style);
10869        }
10870
10871        style.appendChild(document.createTextNode(css));
10872    }
10873
10874    module.exports = {
10875        /**
10876         * This property holds whether this platform is enabled for the current environment.
10877         * Currently used by platform.js to select the proper implementation.
10878         * @private
10879         */
10880        _enabled: typeof window !== 'undefined' && typeof document !== 'undefined',
10881
10882        initialize: function() {
10883            var keyframes = 'from{opacity:0.99}to{opacity:1}';
10884
10885            injectCSS(this,
10886                // DOM rendering detection
10887                // https://davidwalsh.name/detect-node-insertion
10888                '@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
10889                '@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +
10890                '.' + CSS_RENDER_MONITOR + '{' +
10891                '-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
10892                'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +
10893                '}'
10894            );
10895        },
10896
10897        acquireContext: function(item, config) {
10898            if (typeof item === 'string') {
10899                item = document.getElementById(item);
10900            } else if (item.length) {
10901                // Support for array based queries (such as jQuery)
10902                item = item[0];
10903            }
10904
10905            if (item && item.canvas) {
10906                // Support for any object associated to a canvas (including a context2d)
10907                item = item.canvas;
10908            }
10909
10910            // To prevent canvas fingerprinting, some add-ons undefine the getContext
10911            // method, for example: https://github.com/kkapsner/CanvasBlocker
10912            // https://github.com/chartjs/Chart.js/issues/2807
10913            var context = item && item.getContext && item.getContext('2d');
10914
10915            // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
10916            // inside an iframe or when running in a protected environment. We could guess the
10917            // types from their toString() value but let's keep things flexible and assume it's
10918            // a sufficient condition if the item has a context2D which has item as `canvas`.
10919            // https://github.com/chartjs/Chart.js/issues/3887
10920            // https://github.com/chartjs/Chart.js/issues/4102
10921            // https://github.com/chartjs/Chart.js/issues/4152
10922            if (context && context.canvas === item) {
10923                initCanvas(item, config);
10924                return context;
10925            }
10926
10927            return null;
10928        },
10929
10930        releaseContext: function(context) {
10931            var canvas = context.canvas;
10932            if (!canvas[EXPANDO_KEY]) {
10933                return;
10934            }
10935
10936            var initial = canvas[EXPANDO_KEY].initial;
10937            ['height', 'width'].forEach(function(prop) {
10938                var value = initial[prop];
10939                if (helpers.isNullOrUndef(value)) {
10940                    canvas.removeAttribute(prop);
10941                } else {
10942                    canvas.setAttribute(prop, value);
10943                }
10944            });
10945
10946            helpers.each(initial.style || {}, function(value, key) {
10947                canvas.style[key] = value;
10948            });
10949
10950            // The canvas render size might have been changed (and thus the state stack discarded),
10951            // we can't use save() and restore() to restore the initial state. So make sure that at
10952            // least the canvas context is reset to the default state by setting the canvas width.
10953            // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
10954            canvas.width = canvas.width;
10955
10956            delete canvas[EXPANDO_KEY];
10957        },
10958
10959        addEventListener: function(chart, type, listener) {
10960            var canvas = chart.canvas;
10961            if (type === 'resize') {
10962                // Note: the resize event is not supported on all browsers.
10963                addResizeListener(canvas, listener, chart);
10964                return;
10965            }
10966
10967            var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {});
10968            var proxies = expando.proxies || (expando.proxies = {});
10969            var proxy = proxies[chart.id + '_' + type] = function(event) {
10970                listener(fromNativeEvent(event, chart));
10971            };
10972
10973            addEventListener(canvas, type, proxy);
10974        },
10975
10976        removeEventListener: function(chart, type, listener) {
10977            var canvas = chart.canvas;
10978            if (type === 'resize') {
10979                // Note: the resize event is not supported on all browsers.
10980                removeResizeListener(canvas, listener);
10981                return;
10982            }
10983
10984            var expando = listener[EXPANDO_KEY] || {};
10985            var proxies = expando.proxies || {};
10986            var proxy = proxies[chart.id + '_' + type];
10987            if (!proxy) {
10988                return;
10989            }
10990
10991            removeEventListener(canvas, type, proxy);
10992        }
10993    };
10994
10995// DEPRECATIONS
10996
10997    /**
10998     * Provided for backward compatibility, use EventTarget.addEventListener instead.
10999     * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
11000     * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
11001     * @function Chart.helpers.addEvent
11002     * @deprecated since version 2.7.0
11003     * @todo remove at version 3
11004     * @private
11005     */
11006    helpers.addEvent = addEventListener;
11007
11008    /**
11009     * Provided for backward compatibility, use EventTarget.removeEventListener instead.
11010     * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
11011     * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
11012     * @function Chart.helpers.removeEvent
11013     * @deprecated since version 2.7.0
11014     * @todo remove at version 3
11015     * @private
11016     */
11017    helpers.removeEvent = removeEventListener;
11018
11019},{"46":46}],49:[function(require,module,exports){
11020    'use strict';
11021
11022    var helpers = require(46);
11023    var basic = require(47);
11024    var dom = require(48);
11025
11026// @TODO Make possible to select another platform at build time.
11027    var implementation = dom._enabled ? dom : basic;
11028
11029    /**
11030     * @namespace Chart.platform
11031     * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
11032     * @since 2.4.0
11033     */
11034    module.exports = helpers.extend({
11035        /**
11036         * @since 2.7.0
11037         */
11038        initialize: function() {},
11039
11040        /**
11041         * Called at chart construction time, returns a context2d instance implementing
11042         * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
11043         * @param {*} item - The native item from which to acquire context (platform specific)
11044         * @param {Object} options - The chart options
11045         * @returns {CanvasRenderingContext2D} context2d instance
11046         */
11047        acquireContext: function() {},
11048
11049        /**
11050         * Called at chart destruction time, releases any resources associated to the context
11051         * previously returned by the acquireContext() method.
11052         * @param {CanvasRenderingContext2D} context - The context2d instance
11053         * @returns {Boolean} true if the method succeeded, else false
11054         */
11055        releaseContext: function() {},
11056
11057        /**
11058         * Registers the specified listener on the given chart.
11059         * @param {Chart} chart - Chart from which to listen for event
11060         * @param {String} type - The ({@link IEvent}) type to listen for
11061         * @param {Function} listener - Receives a notification (an object that implements
11062         * the {@link IEvent} interface) when an event of the specified type occurs.
11063         */
11064        addEventListener: function() {},
11065
11066        /**
11067         * Removes the specified listener previously registered with addEventListener.
11068         * @param {Chart} chart -Chart from which to remove the listener
11069         * @param {String} type - The ({@link IEvent}) type to remove
11070         * @param {Function} listener - The listener function to remove from the event target.
11071         */
11072        removeEventListener: function() {}
11073
11074    }, implementation);
11075
11076    /**
11077     * @interface IPlatform
11078     * Allows abstracting platform dependencies away from the chart
11079     * @borrows Chart.platform.acquireContext as acquireContext
11080     * @borrows Chart.platform.releaseContext as releaseContext
11081     * @borrows Chart.platform.addEventListener as addEventListener
11082     * @borrows Chart.platform.removeEventListener as removeEventListener
11083     */
11084
11085    /**
11086     * @interface IEvent
11087     * @prop {String} type - The event type name, possible values are:
11088     * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',
11089     * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize'
11090     * @prop {*} native - The original native event (null for emulated events, e.g. 'resize')
11091     * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events)
11092     * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)
11093     */
11094
11095},{"46":46,"47":47,"48":48}],50:[function(require,module,exports){
11096    'use strict';
11097
11098    module.exports = {};
11099    module.exports.filler = require(51);
11100    module.exports.legend = require(52);
11101    module.exports.title = require(53);
11102
11103},{"51":51,"52":52,"53":53}],51:[function(require,module,exports){
11104    /**
11105     * Plugin based on discussion from the following Chart.js issues:
11106     * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569
11107     * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897
11108     */
11109
11110    'use strict';
11111
11112    var defaults = require(26);
11113    var elements = require(41);
11114    var helpers = require(46);
11115
11116    defaults._set('global', {
11117        plugins: {
11118            filler: {
11119                propagate: true
11120            }
11121        }
11122    });
11123
11124    var mappers = {
11125        dataset: function(source) {
11126            var index = source.fill;
11127            var chart = source.chart;
11128            var meta = chart.getDatasetMeta(index);
11129            var visible = meta && chart.isDatasetVisible(index);
11130            var points = (visible && meta.dataset._children) || [];
11131            var length = points.length || 0;
11132
11133            return !length ? null : function(point, i) {
11134                return (i < length && points[i]._view) || null;
11135            };
11136        },
11137
11138        boundary: function(source) {
11139            var boundary = source.boundary;
11140            var x = boundary ? boundary.x : null;
11141            var y = boundary ? boundary.y : null;
11142
11143            return function(point) {
11144                return {
11145                    x: x === null ? point.x : x,
11146                    y: y === null ? point.y : y,
11147                };
11148            };
11149        }
11150    };
11151
11152// @todo if (fill[0] === '#')
11153    function decodeFill(el, index, count) {
11154        var model = el._model || {};
11155        var fill = model.fill;
11156        var target;
11157
11158        if (fill === undefined) {
11159            fill = !!model.backgroundColor;
11160        }
11161
11162        if (fill === false || fill === null) {
11163            return false;
11164        }
11165
11166        if (fill === true) {
11167            return 'origin';
11168        }
11169
11170        target = parseFloat(fill, 10);
11171        if (isFinite(target) && Math.floor(target) === target) {
11172            if (fill[0] === '-' || fill[0] === '+') {
11173                target = index + target;
11174            }
11175
11176            if (target === index || target < 0 || target >= count) {
11177                return false;
11178            }
11179
11180            return target;
11181        }
11182
11183        switch (fill) {
11184            // compatibility
11185            case 'bottom':
11186                return 'start';
11187            case 'top':
11188                return 'end';
11189            case 'zero':
11190                return 'origin';
11191            // supported boundaries
11192            case 'origin':
11193            case 'start':
11194            case 'end':
11195                return fill;
11196            // invalid fill values
11197            default:
11198                return false;
11199        }
11200    }
11201
11202    function computeBoundary(source) {
11203        var model = source.el._model || {};
11204        var scale = source.el._scale || {};
11205        var fill = source.fill;
11206        var target = null;
11207        var horizontal;
11208
11209        if (isFinite(fill)) {
11210            return null;
11211        }
11212
11213        // Backward compatibility: until v3, we still need to support boundary values set on
11214        // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
11215        // controllers might still use it (e.g. the Smith chart).
11216
11217        if (fill === 'start') {
11218            target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
11219        } else if (fill === 'end') {
11220            target = model.scaleTop === undefined ? scale.top : model.scaleTop;
11221        } else if (model.scaleZero !== undefined) {
11222            target = model.scaleZero;
11223        } else if (scale.getBasePosition) {
11224            target = scale.getBasePosition();
11225        } else if (scale.getBasePixel) {
11226            target = scale.getBasePixel();
11227        }
11228
11229        if (target !== undefined && target !== null) {
11230            if (target.x !== undefined && target.y !== undefined) {
11231                return target;
11232            }
11233
11234            if (typeof target === 'number' && isFinite(target)) {
11235                horizontal = scale.isHorizontal();
11236                return {
11237                    x: horizontal ? target : null,
11238                    y: horizontal ? null : target
11239                };
11240            }
11241        }
11242
11243        return null;
11244    }
11245
11246    function resolveTarget(sources, index, propagate) {
11247        var source = sources[index];
11248        var fill = source.fill;
11249        var visited = [index];
11250        var target;
11251
11252        if (!propagate) {
11253            return fill;
11254        }
11255
11256        while (fill !== false && visited.indexOf(fill) === -1) {
11257            if (!isFinite(fill)) {
11258                return fill;
11259            }
11260
11261            target = sources[fill];
11262            if (!target) {
11263                return false;
vendor: 5,869 bytes, lines 11264-11469
11264            }
11265
11266            if (target.visible) {
11267                return fill;
11268            }
11269
11270            visited.push(fill);
11271            fill = target.fill;
11272        }
11273
11274        return false;
11275    }
11276
11277    function createMapper(source) {
11278        var fill = source.fill;
11279        var type = 'dataset';
11280
11281        if (fill === false) {
11282            return null;
11283        }
11284
11285        if (!isFinite(fill)) {
11286            type = 'boundary';
11287        }
11288
11289        return mappers[type](source);
11290    }
11291
11292    function isDrawable(point) {
11293        return point && !point.skip;
11294    }
11295
11296    function drawArea(ctx, curve0, curve1, len0, len1) {
11297        var i;
11298
11299        if (!len0 || !len1) {
11300            return;
11301        }
11302
11303        // building first area curve (normal)
11304        ctx.moveTo(curve0[0].x, curve0[0].y);
11305        for (i = 1; i < len0; ++i) {
11306            helpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
11307        }
11308
11309        // joining the two area curves
11310        ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);
11311
11312        // building opposite area curve (reverse)
11313        for (i = len1 - 1; i > 0; --i) {
11314            helpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
11315        }
11316    }
11317
11318    function doFill(ctx, points, mapper, view, color, loop) {
11319        var count = points.length;
11320        var span = view.spanGaps;
11321        var curve0 = [];
11322        var curve1 = [];
11323        var len0 = 0;
11324        var len1 = 0;
11325        var i, ilen, index, p0, p1, d0, d1;
11326
11327        ctx.beginPath();
11328
11329        for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
11330            index = i % count;
11331            p0 = points[index]._view;
11332            p1 = mapper(p0, index, view);
11333            d0 = isDrawable(p0);
11334            d1 = isDrawable(p1);
11335
11336            if (d0 && d1) {
11337                len0 = curve0.push(p0);
11338                len1 = curve1.push(p1);
11339            } else if (len0 && len1) {
11340                if (!span) {
11341                    drawArea(ctx, curve0, curve1, len0, len1);
11342                    len0 = len1 = 0;
11343                    curve0 = [];
11344                    curve1 = [];
11345                } else {
11346                    if (d0) {
11347                        curve0.push(p0);
11348                    }
11349                    if (d1) {
11350                        curve1.push(p1);
11351                    }
11352                }
11353            }
11354        }
11355
11356        drawArea(ctx, curve0, curve1, len0, len1);
11357
11358        ctx.closePath();
11359        ctx.fillStyle = color;
11360        ctx.fill();
11361    }
11362
11363    module.exports = {
11364        id: 'filler',
11365
11366        afterDatasetsUpdate: function(chart, options) {
11367            var count = (chart.data.datasets || []).length;
11368            var propagate = options.propagate;
11369            var sources = [];
11370            var meta, i, el, source;
11371
11372            for (i = 0; i < count; ++i) {
11373                meta = chart.getDatasetMeta(i);
11374                el = meta.dataset;
11375                source = null;
11376
11377                if (el && el._model && el instanceof elements.Line) {
11378                    source = {
11379                        visible: chart.isDatasetVisible(i),
11380                        fill: decodeFill(el, i, count),
11381                        chart: chart,
11382                        el: el
11383                    };
11384                }
11385
11386                meta.$filler = source;
11387                sources.push(source);
11388            }
11389
11390            for (i = 0; i < count; ++i) {
11391                source = sources[i];
11392                if (!source) {
11393                    continue;
11394                }
11395
11396                source.fill = resolveTarget(sources, i, propagate);
11397                source.boundary = computeBoundary(source);
11398                source.mapper = createMapper(source);
11399            }
11400        },
11401
11402        beforeDatasetDraw: function(chart, args) {
11403            var meta = args.meta.$filler;
11404            if (!meta) {
11405                return;
11406            }
11407
11408            var ctx = chart.ctx;
11409            var el = meta.el;
11410            var view = el._view;
11411            var points = el._children || [];
11412            var mapper = meta.mapper;
11413            var color = view.backgroundColor || defaults.global.defaultColor;
11414
11415            if (mapper && color && points.length) {
11416                helpers.canvas.clipArea(ctx, chart.chartArea);
11417                doFill(ctx, points, mapper, view, color, el._loop);
11418                helpers.canvas.unclipArea(ctx);
11419            }
11420        }
11421    };
11422
11423},{"26":26,"41":41,"46":46}],52:[function(require,module,exports){
11424    'use strict';
11425
11426    var defaults = require(26);
11427    var Element = require(27);
11428    var helpers = require(46);
11429    var layouts = require(31);
11430
11431    var noop = helpers.noop;
11432
11433    defaults._set('global', {
11434        legend: {
11435            display: true,
11436            position: 'top',
11437            fullWidth: true,
11438            reverse: false,
11439            weight: 1000,
11440
11441            // a callback that will handle
11442            onClick: function(e, legendItem) {
11443                var index = legendItem.datasetIndex;
11444                var ci = this.chart;
11445                var meta = ci.getDatasetMeta(index);
11446
11447                // See controller.isDatasetVisible comment
11448                meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
11449
11450                // We hid a dataset ... rerender the chart
11451                ci.update();
11452            },
11453
11454            onHover: null,
11455
11456            labels: {
11457                boxWidth: 40,
11458                padding: 10,
11459                // Generates labels shown in the legend
11460                // Valid properties to return:
11461                // text : text to display
11462                // fillStyle : fill of coloured box
11463                // strokeStyle: stroke of coloured box
11464                // hidden : if this legend item refers to a hidden item
11465                // lineCap : cap style for line
11466                // lineDash
11467                // lineDashOffset :
11468                // lineJoin :
11469                // lineWidth :
11470                generateLabels: function(chart) {
11471                    var data = chart.data;
11472                    return helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {
11473                        return {
11474                            text: dataset.label,
11475                            fillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
11476                            hidden: !chart.isDatasetVisible(i),
11477                            lineCap: dataset.borderCapStyle,
11478                            lineDash: dataset.borderDash,
11479                            lineDashOffset: dataset.borderDashOffset,
11480                            lineJoin: dataset.borderJoinStyle,
11481                            lineWidth: dataset.borderWidth,
11482                            strokeStyle: dataset.borderColor,
11483                            pointStyle: dataset.pointStyle,
11484
11485                            // Below is extra data used for toggling the datasets
11486                            datasetIndex: i
11487                        };
11488                    }, this) : [];
11489                }
11490            }
11491        },
11492
11493        legendCallback: function(chart) {
11494            var text = [];
11495            text.push('<ul class="' + chart.id + '-legend">');
11496            for (var i = 0; i < chart.data.datasets.length; i++) {
11497                text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
11498                if (chart.data.datasets[i].label) {
11499                    text.push(chart.data.datasets[i].label);
11500                }
11501                text.push('</li>');
11502            }
11503            text.push('</ul>');
11504            return text.join('');
11505        }
11506    });
11507
11508    /**
11509     * Helper function to get the box width based on the usePointStyle option
11510     * @param labelopts {Object} the label options on the legend
11511     * @param fontSize {Number} the label font size
11512     * @return {Number} width of the color box area
11513     */
11514    function getBoxWidth(labelOpts, fontSize) {
11515        return labelOpts.usePointStyle ?
11516            fontSize * Math.SQRT2 :
11517            labelOpts.boxWidth;
11518    }
11519
11520    /**
11521     * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
11522     */
11523    var Legend = Element.extend({
11524
11525        initialize: function(config) {
11526            helpers.extend(this, config);
11527
11528            // Contains hit boxes for each dataset (in dataset order)
11529            this.legendHitBoxes = [];
11530
11531            // Are we in doughnut mode which has a different data type
11532            this.doughnutMode = false;
11533        },
11534
11535        // These methods are ordered by lifecycle. Utilities then follow.
11536        // Any function defined here is inherited by all legend types.
11537        // Any function can be extended by the legend type
11538
11539        beforeUpdate: noop,
11540        update: function(maxWidth, maxHeight, margins) {
11541            var me = this;
11542
11543            // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
11544            me.beforeUpdate();
11545
11546            // Absorb the master measurements
11547            me.maxWidth = maxWidth;
11548            me.maxHeight = maxHeight;
11549            me.margins = margins;
11550
11551            // Dimensions
11552            me.beforeSetDimensions();
11553            me.setDimensions();
11554            me.afterSetDimensions();
11555            // Labels
11556            me.beforeBuildLabels();
11557            me.buildLabels();
11558            me.afterBuildLabels();
11559
11560            // Fit
11561            me.beforeFit();
11562            me.fit();
11563            me.afterFit();
11564            //
11565            me.afterUpdate();
11566
11567            return me.minSize;
11568        },
11569        afterUpdate: noop,
11570
11571        //
11572
11573        beforeSetDimensions: noop,
11574        setDimensions: function() {
11575            var me = this;
11576            // Set the unconstrained dimension before label rotation
11577            if (me.isHorizontal()) {
11578                // Reset position before calculating rotation
11579                me.width = me.maxWidth;
11580                me.left = 0;
11581                me.right = me.width;
11582            } else {
11583                me.height = me.maxHeight;
11584
11585                // Reset position before calculating rotation
11586                me.top = 0;
11587                me.bottom = me.height;
11588            }
11589
11590            // Reset padding
11591            me.paddingLeft = 0;
11592            me.paddingTop = 0;
11593            me.paddingRight = 0;
11594            me.paddingBottom = 0;
11595
11596            // Reset minSize
11597            me.minSize = {
11598                width: 0,
11599                height: 0
11600            };
11601        },
11602        afterSetDimensions: noop,
11603
11604        //
11605
11606        beforeBuildLabels: noop,
11607        buildLabels: function() {
11608            var me = this;
11609            var labelOpts = me.options.labels || {};
11610            var legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || [];
11611
11612            if (labelOpts.filter) {
11613                legendItems = legendItems.filter(function(item) {
11614                    return labelOpts.filter(item, me.chart.data);
11615                });
11616            }
11617
11618            if (me.options.reverse) {
11619                legendItems.reverse();
11620            }
11621
11622            me.legendItems = legendItems;
11623        },
11624        afterBuildLabels: noop,
11625
11626        //
11627
11628        beforeFit: noop,
11629        fit: function() {
11630            var me = this;
11631            var opts = me.options;
11632            var labelOpts = opts.labels;
11633            var display = opts.display;
11634
11635            var ctx = me.ctx;
11636
11637            var globalDefault = defaults.global;
11638            var valueOrDefault = helpers.valueOrDefault;
11639            var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
11640            var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
11641            var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
11642            var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
11643
11644            // Reset hit boxes
11645            var hitboxes = me.legendHitBoxes = [];
11646
11647            var minSize = me.minSize;
11648            var isHorizontal = me.isHorizontal();
11649
11650            if (isHorizontal) {
11651                minSize.width = me.maxWidth; // fill all the width
11652                minSize.height = display ? 10 : 0;
11653            } else {
11654                minSize.width = display ? 10 : 0;
11655                minSize.height = me.maxHeight; // fill all the height
11656            }
11657
11658            // Increase sizes here
11659            if (display) {
11660                ctx.font = labelFont;
11661
11662                if (isHorizontal) {
11663                    // Labels
11664
11665                    // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
11666                    var lineWidths = me.lineWidths = [0];
11667                    var totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;
11668
11669                    ctx.textAlign = 'left';
11670                    ctx.textBaseline = 'top';
11671
11672                    helpers.each(me.legendItems, function(legendItem, i) {
11673                        var boxWidth = getBoxWidth(labelOpts, fontSize);
11674                        var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
11675
11676                        if (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {
11677                            totalHeight += fontSize + (labelOpts.padding);
11678                            lineWidths[lineWidths.length] = me.left;
11679                        }
11680
11681                        // Store the hitbox width and height here. Final position will be updated in `draw`
11682                        hitboxes[i] = {
11683                            left: 0,
11684                            top: 0,
11685                            width: width,
11686                            height: fontSize
11687                        };
11688
11689                        lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
11690                    });
11691
11692                    minSize.height += totalHeight;
11693
11694                } else {
11695                    var vPadding = labelOpts.padding;
11696                    var columnWidths = me.columnWidths = [];
11697                    var totalWidth = labelOpts.padding;
11698                    var currentColWidth = 0;
11699                    var currentColHeight = 0;
11700                    var itemHeight = fontSize + vPadding;
11701
11702                    helpers.each(me.legendItems, function(legendItem, i) {
11703                        var boxWidth = getBoxWidth(labelOpts, fontSize);
11704                        var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
11705
11706                        // If too tall, go to new column
11707                        if (currentColHeight + itemHeight > minSize.height) {
11708                            totalWidth += currentColWidth + labelOpts.padding;
11709                            columnWidths.push(currentColWidth); // previous column width
11710
11711                            currentColWidth = 0;
11712                            currentColHeight = 0;
11713                        }
11714
11715                        // Get max width
11716                        currentColWidth = Math.max(currentColWidth, itemWidth);
11717                        currentColHeight += itemHeight;
11718
11719                        // Store the hitbox width and height here. Final position will be updated in `draw`
11720                        hitboxes[i] = {
11721                            left: 0,
11722                            top: 0,
11723                            width: itemWidth,
11724                            height: fontSize
11725                        };
11726                    });
11727
11728                    totalWidth += currentColWidth;
11729                    columnWidths.push(currentColWidth);
11730                    minSize.width += totalWidth;
11731                }
11732            }
11733
11734            me.width = minSize.width;
11735            me.height = minSize.height;
11736        },
11737        afterFit: noop,
11738
11739        // Shared Methods
11740        isHorizontal: function() {
11741            return this.options.position === 'top' || this.options.position === 'bottom';
11742        },
11743
11744        // Actually draw the legend on the canvas
11745        draw: function() {
11746            var me = this;
11747            var opts = me.options;
11748            var labelOpts = opts.labels;
11749            var globalDefault = defaults.global;
11750            var lineDefault = globalDefault.elements.line;
11751            var legendWidth = me.width;
11752            var lineWidths = me.lineWidths;
11753
11754            if (opts.display) {
11755                var ctx = me.ctx;
11756                var valueOrDefault = helpers.valueOrDefault;
11757                var fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor);
11758                var fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);
11759                var fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);
11760                var fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);
11761                var labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);
11762                var cursor;
11763
11764                // Canvas setup
11765                ctx.textAlign = 'left';
11766                ctx.textBaseline = 'middle';
11767                ctx.lineWidth = 0.5;
11768                ctx.strokeStyle = fontColor; // for strikethrough effect
11769                ctx.fillStyle = fontColor; // render in correct colour
11770                ctx.font = labelFont;
11771
11772                var boxWidth = getBoxWidth(labelOpts, fontSize);
11773                var hitboxes = me.legendHitBoxes;
11774
11775                // current position
11776                var drawLegendBox = function(x, y, legendItem) {
11777                    if (isNaN(boxWidth) || boxWidth <= 0) {
11778                        return;
11779                    }
11780
11781                    // Set the ctx for the box
11782                    ctx.save();
11783
11784                    ctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor);
11785                    ctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);
11786                    ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);
11787                    ctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);
11788                    ctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);
11789                    ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);
11790                    var isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);
11791
11792                    if (ctx.setLineDash) {
11793                        // IE 9 and 10 do not support line dash
11794                        ctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash));
11795                    }
11796
11797                    if (opts.labels && opts.labels.usePointStyle) {
11798                        // Recalculate x and y for drawPoint() because its expecting
11799                        // x and y to be center of figure (instead of top left)
11800                        var radius = fontSize * Math.SQRT2 / 2;
11801                        var offSet = radius / Math.SQRT2;
11802                        var centerX = x + offSet;
11803                        var centerY = y + offSet;
11804
11805                        // Draw pointStyle as legend symbol
11806                        helpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
11807                    } else {
11808                        // Draw box as legend symbol
11809                        if (!isLineWidthZero) {
11810                            ctx.strokeRect(x, y, boxWidth, fontSize);
11811                        }
11812                        ctx.fillRect(x, y, boxWidth, fontSize);
11813                    }
11814
11815                    ctx.restore();
11816                };
11817                var fillText = function(x, y, legendItem, textWidth) {
11818                    var halfFontSize = fontSize / 2;
11819                    var xLeft = boxWidth + halfFontSize + x;
11820                    var yMiddle = y + halfFontSize;
11821
11822                    ctx.fillText(legendItem.text, xLeft, yMiddle);
11823
11824                    if (legendItem.hidden) {
11825                        // Strikethrough the text if hidden
11826                        ctx.beginPath();
11827                        ctx.lineWidth = 2;
11828                        ctx.moveTo(xLeft, yMiddle);
11829                        ctx.lineTo(xLeft + textWidth, yMiddle);
11830                        ctx.stroke();
11831                    }
11832                };
11833
11834                // Horizontal
11835                var isHorizontal = me.isHorizontal();
11836                if (isHorizontal) {
11837                    cursor = {
11838                        x: me.left + ((legendWidth - lineWidths[0]) / 2),
11839                        y: me.top + labelOpts.padding,
11840                        line: 0
11841                    };
11842                } else {
11843                    cursor = {
11844                        x: me.left + labelOpts.padding,
11845                        y: me.top + labelOpts.padding,
11846                        line: 0
11847                    };
11848                }
11849
11850                var itemHeight = fontSize + labelOpts.padding;
11851                helpers.each(me.legendItems, function(legendItem, i) {
11852                    var textWidth = ctx.measureText(legendItem.text).width;
11853                    var width = boxWidth + (fontSize / 2) + textWidth;
11854                    var x = cursor.x;
11855                    var y = cursor.y;
11856
11857                    if (isHorizontal) {
11858                        if (x + width >= legendWidth) {
11859                            y = cursor.y += itemHeight;
11860                            cursor.line++;
11861                            x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);
11862                        }
11863                    } else if (y + itemHeight > me.bottom) {
11864                        x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
11865                        y = cursor.y = me.top + labelOpts.padding;
11866                        cursor.line++;
11867                    }
11868
11869                    drawLegendBox(x, y, legendItem);
11870
11871                    hitboxes[i].left = x;
11872                    hitboxes[i].top = y;
11873
11874                    // Fill the actual label
11875                    fillText(x, y, legendItem, textWidth);
11876
11877                    if (isHorizontal) {
11878                        cursor.x += width + (labelOpts.padding);
11879                    } else {
11880                        cursor.y += itemHeight;
11881                    }
11882
11883                });
11884            }
11885        },
11886
11887        /**
11888         * Handle an event
11889         * @private
11890         * @param {IEvent} event - The event to handle
11891         * @return {Boolean} true if a change occured
11892         */
11893        handleEvent: function(e) {
11894            var me = this;
11895            var opts = me.options;
11896            var type = e.type === 'mouseup' ? 'click' : e.type;
11897            var changed = false;
11898
11899            if (type === 'mousemove') {
11900                if (!opts.onHover) {
11901                    return;
11902                }
11903            } else if (type === 'click') {
11904                if (!opts.onClick) {
11905                    return;
11906                }
11907            } else {
11908                return;
11909            }
11910
11911            // Chart event already has relative position in it
11912            var x = e.x;
11913            var y = e.y;
11914
11915            if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
11916                // See if we are touching one of the dataset boxes
11917                var lh = me.legendHitBoxes;
11918                for (var i = 0; i < lh.length; ++i) {
11919                    var hitBox = lh[i];
11920
11921                    if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
11922                        // Touching an element
11923                        if (type === 'click') {
11924                            // use e.native for backwards compatibility
11925                            opts.onClick.call(me, e.native, me.legendItems[i]);
11926                            changed = true;
11927                            break;
11928                        } else if (type === 'mousemove') {
11929                            // use e.native for backwards compatibility
11930                            opts.onHover.call(me, e.native, me.legendItems[i]);
11931                            changed = true;
11932                            break;
11933                        }
11934                    }
11935                }
11936            }
11937
11938            return changed;
11939        }
11940    });
11941
11942    function createNewLegendAndAttach(chart, legendOpts) {
11943        var legend = new Legend({
11944            ctx: chart.ctx,
11945            options: legendOpts,
11946            chart: chart
11947        });
11948
11949        layouts.configure(chart, legend, legendOpts);
11950        layouts.addBox(chart, legend);
11951        chart.legend = legend;
11952    }
11953
11954    module.exports = {
11955        id: 'legend',
11956
11957        /**
11958         * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making
11959         * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of
11960         * the plugin, which one will be re-exposed in the chart.js file.
11961         * https://github.com/chartjs/Chart.js/pull/2640
11962         * @private
11963         */
11964        _element: Legend,
11965
vendor: 7,531 bytes, lines 11966-12197
11966        beforeInit: function(chart) {
11967            var legendOpts = chart.options.legend;
11968
11969            if (legendOpts) {
11970                createNewLegendAndAttach(chart, legendOpts);
11971            }
11972        },
11973
11974        beforeUpdate: function(chart) {
11975            var legendOpts = chart.options.legend;
11976            var legend = chart.legend;
11977
11978            if (legendOpts) {
11979                helpers.mergeIf(legendOpts, defaults.global.legend);
11980
11981                if (legend) {
11982                    layouts.configure(chart, legend, legendOpts);
11983                    legend.options = legendOpts;
11984                } else {
11985                    createNewLegendAndAttach(chart, legendOpts);
11986                }
11987            } else if (legend) {
11988                layouts.removeBox(chart, legend);
11989                delete chart.legend;
11990            }
11991        },
11992
11993        afterEvent: function(chart, e) {
11994            var legend = chart.legend;
11995            if (legend) {
11996                legend.handleEvent(e);
11997            }
11998        }
11999    };
12000
12001},{"26":26,"27":27,"31":31,"46":46}],53:[function(require,module,exports){
12002    'use strict';
12003
12004    var defaults = require(26);
12005    var Element = require(27);
12006    var helpers = require(46);
12007    var layouts = require(31);
12008
12009    var noop = helpers.noop;
12010
12011    defaults._set('global', {
12012        title: {
12013            display: false,
12014            fontStyle: 'bold',
12015            fullWidth: true,
12016            lineHeight: 1.2,
12017            padding: 10,
12018            position: 'top',
12019            text: '',
12020            weight: 2000         // by default greater than legend (1000) to be above
12021        }
12022    });
12023
12024    /**
12025     * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
12026     */
12027    var Title = Element.extend({
12028        initialize: function(config) {
12029            var me = this;
12030            helpers.extend(me, config);
12031
12032            // Contains hit boxes for each dataset (in dataset order)
12033            me.legendHitBoxes = [];
12034        },
12035
12036        // These methods are ordered by lifecycle. Utilities then follow.
12037
12038        beforeUpdate: noop,
12039        update: function(maxWidth, maxHeight, margins) {
12040            var me = this;
12041
12042            // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
12043            me.beforeUpdate();
12044
12045            // Absorb the master measurements
12046            me.maxWidth = maxWidth;
12047            me.maxHeight = maxHeight;
12048            me.margins = margins;
12049
12050            // Dimensions
12051            me.beforeSetDimensions();
12052            me.setDimensions();
12053            me.afterSetDimensions();
12054            // Labels
12055            me.beforeBuildLabels();
12056            me.buildLabels();
12057            me.afterBuildLabels();
12058
12059            // Fit
12060            me.beforeFit();
12061            me.fit();
12062            me.afterFit();
12063            //
12064            me.afterUpdate();
12065
12066            return me.minSize;
12067
12068        },
12069        afterUpdate: noop,
12070
12071        //
12072
12073        beforeSetDimensions: noop,
12074        setDimensions: function() {
12075            var me = this;
12076            // Set the unconstrained dimension before label rotation
12077            if (me.isHorizontal()) {
12078                // Reset position before calculating rotation
12079                me.width = me.maxWidth;
12080                me.left = 0;
12081                me.right = me.width;
12082            } else {
12083                me.height = me.maxHeight;
12084
12085                // Reset position before calculating rotation
12086                me.top = 0;
12087                me.bottom = me.height;
12088            }
12089
12090            // Reset padding
12091            me.paddingLeft = 0;
12092            me.paddingTop = 0;
12093            me.paddingRight = 0;
12094            me.paddingBottom = 0;
12095
12096            // Reset minSize
12097            me.minSize = {
12098                width: 0,
12099                height: 0
12100            };
12101        },
12102        afterSetDimensions: noop,
12103
12104        //
12105
12106        beforeBuildLabels: noop,
12107        buildLabels: noop,
12108        afterBuildLabels: noop,
12109
12110        //
12111
12112        beforeFit: noop,
12113        fit: function() {
12114            var me = this;
12115            var valueOrDefault = helpers.valueOrDefault;
12116            var opts = me.options;
12117            var display = opts.display;
12118            var fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize);
12119            var minSize = me.minSize;
12120            var lineCount = helpers.isArray(opts.text) ? opts.text.length : 1;
12121            var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
12122            var textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0;
12123
12124            if (me.isHorizontal()) {
12125                minSize.width = me.maxWidth; // fill all the width
12126                minSize.height = textSize;
12127            } else {
12128                minSize.width = textSize;
12129                minSize.height = me.maxHeight; // fill all the height
12130            }
12131
12132            me.width = minSize.width;
12133            me.height = minSize.height;
12134
12135        },
12136        afterFit: noop,
12137
12138        // Shared Methods
12139        isHorizontal: function() {
12140            var pos = this.options.position;
12141            return pos === 'top' || pos === 'bottom';
12142        },
12143
12144        // Actually draw the title block on the canvas
12145        draw: function() {
12146            var me = this;
12147            var ctx = me.ctx;
12148            var valueOrDefault = helpers.valueOrDefault;
12149            var opts = me.options;
12150            var globalDefaults = defaults.global;
12151
12152            if (opts.display) {
12153                var fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize);
12154                var fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle);
12155                var fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily);
12156                var titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);
12157                var lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);
12158                var offset = lineHeight / 2 + opts.padding;
12159                var rotation = 0;
12160                var top = me.top;
12161                var left = me.left;
12162                var bottom = me.bottom;
12163                var right = me.right;
12164                var maxWidth, titleX, titleY;
12165
12166                ctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour
12167                ctx.font = titleFont;
12168
12169                // Horizontal
12170                if (me.isHorizontal()) {
12171                    titleX = left + ((right - left) / 2); // midpoint of the width
12172                    titleY = top + offset;
12173                    maxWidth = right - left;
12174                } else {
12175                    titleX = opts.position === 'left' ? left + offset : right - offset;
12176                    titleY = top + ((bottom - top) / 2);
12177                    maxWidth = bottom - top;
12178                    rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
12179                }
12180
12181                ctx.save();
12182                ctx.translate(titleX, titleY);
12183                ctx.rotate(rotation);
12184                ctx.textAlign = 'center';
12185                ctx.textBaseline = 'middle';
12186
12187                var text = opts.text;
12188                if (helpers.isArray(text)) {
12189                    var y = 0;
12190                    for (var i = 0; i < text.length; ++i) {
12191                        ctx.fillText(text[i], 0, y, maxWidth);
12192                        y += lineHeight;
12193                    }
12194                } else {
12195                    ctx.fillText(text, 0, 0, maxWidth);
12196                }
12197
12198                ctx.restore();
12199            }
12200        }
12201    });
12202
12203    function createNewTitleBlockAndAttach(chart, titleOpts) {
12204        var title = new Title({
12205            ctx: chart.ctx,
12206            options: titleOpts,
12207            chart: chart
12208        });
12209
12210        layouts.configure(chart, title, titleOpts);
12211        layouts.addBox(chart, title);
12212        chart.titleBlock = title;
12213    }
12214
12215    module.exports = {
12216        id: 'title',
12217
12218        /**
12219         * Backward compatibility: since 2.1.5, the title is registered as a plugin, making
12220         * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of
12221         * the plugin, which one will be re-exposed in the chart.js file.
12222         * https://github.com/chartjs/Chart.js/pull/2640
12223         * @private
12224         */
12225        _element: Title,
12226
12227        beforeInit: function(chart) {
12228            var titleOpts = chart.options.title;
12229
12230            if (titleOpts) {
12231                createNewTitleBlockAndAttach(chart, titleOpts);
12232            }
12233        },
12234
12235        beforeUpdate: function(chart) {
12236            var titleOpts = chart.options.title;
12237            var titleBlock = chart.titleBlock;
12238
12239            if (titleOpts) {
12240                helpers.mergeIf(titleOpts, defaults.global.title);
12241
12242                if (titleBlock) {
12243                    layouts.configure(chart, titleBlock, titleOpts);
12244                    titleBlock.options = titleOpts;
12245                } else {
12246                    createNewTitleBlockAndAttach(chart, titleOpts);
12247                }
12248            } else if (titleBlock) {
12249                layouts.removeBox(chart, titleBlock);
12250                delete chart.titleBlock;
12251            }
12252        }
12253    };
12254
12255},{"26":26,"27":27,"31":31,"46":46}],54:[function(require,module,exports){
12256    'use strict';
12257
12258    var Scale = require(33);
12259    var scaleService = require(34);
12260
12261    module.exports = function() {
12262
12263        // Default config for a category scale
12264        var defaultConfig = {
12265            position: 'bottom'
12266        };
12267
12268        var DatasetScale = Scale.extend({
12269            /**
12270             * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
12271             * else fall back to data.labels
12272             * @private
12273             */
12274            getLabels: function() {
12275                var data = this.chart.data;
12276                return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
12277            },
12278
12279            determineDataLimits: function() {
12280                var me = this;
12281                var labels = me.getLabels();
12282                me.minIndex = 0;
12283                me.maxIndex = labels.length - 1;
12284                var findIndex;
12285
12286                if (me.options.ticks.min !== undefined) {
12287                    // user specified min value
12288                    findIndex = labels.indexOf(me.options.ticks.min);
12289                    me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
12290                }
12291
12292                if (me.options.ticks.max !== undefined) {
12293                    // user specified max value
12294                    findIndex = labels.indexOf(me.options.ticks.max);
12295                    me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
12296                }
12297
12298                me.min = labels[me.minIndex];
12299                me.max = labels[me.maxIndex];
12300            },
12301
12302            buildTicks: function() {
12303                var me = this;
12304                var labels = me.getLabels();
12305                // If we are viewing some subset of labels, slice the original array
12306                me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
12307            },
12308
12309            getLabelForIndex: function(index, datasetIndex) {
12310                var me = this;
12311                var data = me.chart.data;
12312                var isHorizontal = me.isHorizontal();
12313
12314                if (data.yLabels && !isHorizontal) {
12315                    return me.getRightValue(data.datasets[datasetIndex].data[index]);
12316                }
12317                return me.ticks[index - me.minIndex];
12318            },
12319
12320            // Used to get data value locations.  Value can either be an index or a numerical value
12321            getPixelForValue: function(value, index) {
12322                var me = this;
12323                var offset = me.options.offset;
12324                // 1 is added because we need the length but we have the indexes
12325                var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);
12326
12327                // If value is a data object, then index is the index in the data array,
12328                // not the index of the scale. We need to change that.
12329                var valueCategory;
12330                if (value !== undefined && value !== null) {
12331                    valueCategory = me.isHorizontal() ? value.x : value.y;
12332                }
12333                if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
12334                    var labels = me.getLabels();
12335                    value = valueCategory || value;
12336                    var idx = labels.indexOf(value);
12337                    index = idx !== -1 ? idx : index;
12338                }
12339
12340                if (me.isHorizontal()) {
12341                    var valueWidth = me.width / offsetAmt;
12342                    var widthOffset = (valueWidth * (index - me.minIndex));
12343
12344                    if (offset) {
12345                        widthOffset += (valueWidth / 2);
12346                    }
12347
12348                    return me.left + Math.round(widthOffset);
12349                }
12350                var valueHeight = me.height / offsetAmt;
12351                var heightOffset = (valueHeight * (index - me.minIndex));
12352
12353                if (offset) {
12354                    heightOffset += (valueHeight / 2);
12355                }
12356
12357                return me.top + Math.round(heightOffset);
12358            },
12359            getPixelForTick: function(index) {
12360                return this.getPixelForValue(this.ticks[index], index + this.minIndex, null);
12361            },
12362            getValueForPixel: function(pixel) {
12363                var me = this;
12364                var offset = me.options.offset;
12365                var value;
12366                var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
12367                var horz = me.isHorizontal();
12368                var valueDimension = (horz ? me.width : me.height) / offsetAmt;
12369
12370                pixel -= horz ? me.left : me.top;
12371
12372                if (offset) {
12373                    pixel -= (valueDimension / 2);
12374                }
12375
12376                if (pixel <= 0) {
12377                    value = 0;
12378                } else {
12379                    value = Math.round(pixel / valueDimension);
12380                }
12381
12382                return value + me.minIndex;
12383            },
12384            getBasePixel: function() {
12385                return this.bottom;
12386            }
12387        });
12388
12389        scaleService.registerScaleType('category', DatasetScale, defaultConfig);
12390    };
12391
12392},{"33":33,"34":34}],55:[function(require,module,exports){
12393    'use strict';
12394
12395    var defaults = require(26);
12396    var helpers = require(46);
12397    var scaleService = require(34);
12398    var Ticks = require(35);
12399
12400    module.exports = function(Chart) {
12401
12402        var defaultConfig = {
12403            position: 'left',
12404            ticks: {
12405                callback: Ticks.formatters.linear
12406            }
12407        };
12408
12409        var LinearScale = Chart.LinearScaleBase.extend({
12410
12411            determineDataLimits: function() {
12412                var me = this;
12413                var opts = me.options;
12414                var chart = me.chart;
12415                var data = chart.data;
12416                var datasets = data.datasets;
12417                var isHorizontal = me.isHorizontal();
12418                var DEFAULT_MIN = 0;
12419                var DEFAULT_MAX = 1;
12420
12421                function IDMatches(meta) {
12422                    return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
12423                }
12424
12425                // First Calculate the range
12426                me.min = null;
12427                me.max = null;
12428
12429                var hasStacks = opts.stacked;
12430                if (hasStacks === undefined) {
12431                    helpers.each(datasets, function(dataset, datasetIndex) {
12432                        if (hasStacks) {
12433                            return;
12434                        }
12435
12436                        var meta = chart.getDatasetMeta(datasetIndex);
12437                        if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
12438                            meta.stack !== undefined) {
12439                            hasStacks = true;
12440                        }
12441                    });
12442                }
12443
12444                if (opts.stacked || hasStacks) {
12445                    var valuesPerStack = {};
12446
12447                    helpers.each(datasets, function(dataset, datasetIndex) {
12448                        var meta = chart.getDatasetMeta(datasetIndex);
12449                        var key = [
12450                            meta.type,
12451                            // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
12452                            ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
12453                            meta.stack
12454                        ].join('.');
12455
12456                        if (valuesPerStack[key] === undefined) {
12457                            valuesPerStack[key] = {
12458                                positiveValues: [],
12459                                negativeValues: []
12460                            };
12461                        }
12462
12463                        // Store these per type
12464                        var positiveValues = valuesPerStack[key].positiveValues;
12465                        var negativeValues = valuesPerStack[key].negativeValues;
12466
12467                        if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12468                            helpers.each(dataset.data, function(rawValue, index) {
12469                                var value = +me.getRightValue(rawValue);
12470                                if (isNaN(value) || meta.data[index].hidden) {
12471                                    return;
12472                                }
12473
12474                                positiveValues[index] = positiveValues[index] || 0;
12475                                negativeValues[index] = negativeValues[index] || 0;
12476
12477                                if (opts.relativePoints) {
12478                                    positiveValues[index] = 100;
12479                                } else if (value < 0) {
12480                                    negativeValues[index] += value;
12481                                } else {
12482                                    positiveValues[index] += value;
12483                                }
12484                            });
12485                        }
12486                    });
12487
12488                    helpers.each(valuesPerStack, function(valuesForType) {
12489                        var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
12490                        var minVal = helpers.min(values);
12491                        var maxVal = helpers.max(values);
12492                        me.min = me.min === null ? minVal : Math.min(me.min, minVal);
12493                        me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
12494                    });
12495
12496                } else {
12497                    helpers.each(datasets, function(dataset, datasetIndex) {
12498                        var meta = chart.getDatasetMeta(datasetIndex);
12499                        if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12500                            helpers.each(dataset.data, function(rawValue, index) {
12501                                var value = +me.getRightValue(rawValue);
12502                                if (isNaN(value) || meta.data[index].hidden) {
12503                                    return;
12504                                }
12505
12506                                if (me.min === null) {
12507                                    me.min = value;
12508                                } else if (value < me.min) {
12509                                    me.min = value;
12510                                }
12511
12512                                if (me.max === null) {
12513                                    me.max = value;
12514                                } else if (value > me.max) {
12515                                    me.max = value;
12516                                }
12517                            });
12518                        }
12519                    });
12520                }
12521
12522                me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
12523                me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;
12524
12525                // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
12526                this.handleTickRangeOptions();
12527            },
12528            getTickLimit: function() {
12529                var maxTicks;
12530                var me = this;
12531                var tickOpts = me.options.ticks;
12532
12533                if (me.isHorizontal()) {
12534                    maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));
12535                } else {
12536                    // The factor of 2 used to scale the font size has been experimentally determined.
12537                    var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize);
12538                    maxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));
12539                }
12540
12541                return maxTicks;
12542            },
12543            // Called after the ticks are built. We need
12544            handleDirectionalChanges: function() {
12545                if (!this.isHorizontal()) {
12546                    // We are in a vertical orientation. The top value is the highest. So reverse the array
12547                    this.ticks.reverse();
12548                }
12549            },
12550            getLabelForIndex: function(index, datasetIndex) {
12551                return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
12552            },
12553            // Utils
12554            getPixelForValue: function(value) {
12555                // This must be called after fit has been run so that
12556                // this.left, this.top, this.right, and this.bottom have been defined
12557                var me = this;
12558                var start = me.start;
12559
12560                var rightValue = +me.getRightValue(value);
12561                var pixel;
12562                var range = me.end - start;
12563
12564                if (me.isHorizontal()) {
12565                    pixel = me.left + (me.width / range * (rightValue - start));
12566                } else {
12567                    pixel = me.bottom - (me.height / range * (rightValue - start));
12568                }
12569                return pixel;
12570            },
12571            getValueForPixel: function(pixel) {
12572                var me = this;
12573                var isHorizontal = me.isHorizontal();
12574                var innerDimension = isHorizontal ? me.width : me.height;
12575                var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
12576                return me.start + ((me.end - me.start) * offset);
12577            },
12578            getPixelForTick: function(index) {
12579                return this.getPixelForValue(this.ticksAsNumbers[index]);
12580            }
12581        });
12582
12583        scaleService.registerScaleType('linear', LinearScale, defaultConfig);
12584    };
12585
12586},{"26":26,"34":34,"35":35,"46":46}],56:[function(require,module,exports){
12587    'use strict';
12588
12589    var helpers = require(46);
12590    var Scale = require(33);
12591
12592    /**
12593     * Generate a set of linear ticks
12594     * @param generationOptions the options used to generate the ticks
12595     * @param dataRange the range of the data
12596     * @returns {Array<Number>} array of tick values
12597     */
12598    function generateTicks(generationOptions, dataRange) {
12599        var ticks = [];
12600        // To get a "nice" value for the tick spacing, we will use the appropriately named
12601        // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
12602        // for details.
12603
12604        var factor;
12605        var precision;
12606        var spacing;
12607
12608        if (generationOptions.stepSize && generationOptions.stepSize > 0) {
12609            spacing = generationOptions.stepSize;
12610        } else {
12611            var niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);
12612            spacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);
12613
12614            precision = generationOptions.precision;
12615            if (precision !== undefined) {
12616                // If the user specified a precision, round to that number of decimal places
12617                factor = Math.pow(10, precision);
12618                spacing = Math.ceil(spacing * factor) / factor;
12619            }
12620        }
12621        var niceMin = Math.floor(dataRange.min / spacing) * spacing;
12622        var niceMax = Math.ceil(dataRange.max / spacing) * spacing;
12623
12624        // If min, max and stepSize is set and they make an evenly spaced scale use it.
12625        if (!helpers.isNullOrUndef(generationOptions.min) && !helpers.isNullOrUndef(generationOptions.max) && generationOptions.stepSize) {
12626            // If very close to our whole number, use it.
12627            if (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {
12628                niceMin = generationOptions.min;
12629                niceMax = generationOptions.max;
12630            }
12631        }
12632
12633        var numSpaces = (niceMax - niceMin) / spacing;
12634        // If very close to our rounded value, use it.
12635        if (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
12636            numSpaces = Math.round(numSpaces);
12637        } else {
12638            numSpaces = Math.ceil(numSpaces);
12639        }
12640
12641        precision = 1;
12642        if (spacing < 1) {
12643            precision = Math.pow(10, 1 - Math.floor(helpers.log10(spacing)));
12644            niceMin = Math.round(niceMin * precision) / precision;
12645            niceMax = Math.round(niceMax * precision) / precision;
12646        }
12647        ticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);
12648        for (var j = 1; j < numSpaces; ++j) {
12649            ticks.push(Math.round((niceMin + j * spacing) * precision) / precision);
12650        }
12651        ticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);
12652
12653        return ticks;
12654    }
12655
12656    module.exports = function(Chart) {
12657
12658        var noop = helpers.noop;
12659
12660        Chart.LinearScaleBase = Scale.extend({
12661            getRightValue: function(value) {
12662                if (typeof value === 'string') {
12663                    return +value;
12664                }
12665                return Scale.prototype.getRightValue.call(this, value);
12666            },
12667
12668            handleTickRangeOptions: function() {
12669                var me = this;
12670                var opts = me.options;
12671                var tickOpts = opts.ticks;
12672
12673                // If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
12674                // do nothing since that would make the chart weird. If the user really wants a weird chart
12675                // axis, they can manually override it
12676                if (tickOpts.beginAtZero) {
12677                    var minSign = helpers.sign(me.min);
12678                    var maxSign = helpers.sign(me.max);
12679
12680                    if (minSign < 0 && maxSign < 0) {
12681                        // move the top up to 0
12682                        me.max = 0;
12683                    } else if (minSign > 0 && maxSign > 0) {
12684                        // move the bottom down to 0
12685                        me.min = 0;
12686                    }
12687                }
12688
12689                var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
12690                var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;
12691
12692                if (tickOpts.min !== undefined) {
12693                    me.min = tickOpts.min;
12694                } else if (tickOpts.suggestedMin !== undefined) {
12695                    if (me.min === null) {
12696                        me.min = tickOpts.suggestedMin;
12697                    } else {
12698                        me.min = Math.min(me.min, tickOpts.suggestedMin);
12699                    }
12700                }
12701
12702                if (tickOpts.max !== undefined) {
12703                    me.max = tickOpts.max;
12704                } else if (tickOpts.suggestedMax !== undefined) {
12705                    if (me.max === null) {
12706                        me.max = tickOpts.suggestedMax;
12707                    } else {
12708                        me.max = Math.max(me.max, tickOpts.suggestedMax);
12709                    }
12710                }
12711
12712                if (setMin !== setMax) {
12713                    // We set the min or the max but not both.
12714                    // So ensure that our range is good
12715                    // Inverted or 0 length range can happen when
12716                    // ticks.min is set, and no datasets are visible
12717                    if (me.min >= me.max) {
12718                        if (setMin) {
12719                            me.max = me.min + 1;
12720                        } else {
12721                            me.min = me.max - 1;
12722                        }
12723                    }
12724                }
12725
12726                if (me.min === me.max) {
12727                    me.max++;
12728
12729                    if (!tickOpts.beginAtZero) {
12730                        me.min--;
12731                    }
12732                }
12733            },
12734            getTickLimit: noop,
12735            handleDirectionalChanges: noop,
12736
12737            buildTicks: function() {
12738                var me = this;
12739                var opts = me.options;
12740                var tickOpts = opts.ticks;
12741
12742                // Figure out what the max number of ticks we can support it is based on the size of
12743                // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
12744                // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
12745                // the graph. Make sure we always have at least 2 ticks
12746                var maxTicks = me.getTickLimit();
12747                maxTicks = Math.max(2, maxTicks);
12748
12749                var numericGeneratorOptions = {
12750                    maxTicks: maxTicks,
12751                    min: tickOpts.min,
12752                    max: tickOpts.max,
12753                    precision: tickOpts.precision,
12754                    stepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
12755                };
12756                var ticks = me.ticks = generateTicks(numericGeneratorOptions, me);
12757
12758                me.handleDirectionalChanges();
12759
12760                // At this point, we need to update our max and min given the tick values since we have expanded the
12761                // range of the scale
12762                me.max = helpers.max(ticks);
12763                me.min = helpers.min(ticks);
12764
12765                if (tickOpts.reverse) {
12766                    ticks.reverse();
12767
12768                    me.start = me.max;
12769                    me.end = me.min;
12770                } else {
12771                    me.start = me.min;
12772                    me.end = me.max;
12773                }
12774            },
12775            convertTicksToLabels: function() {
12776                var me = this;
12777                me.ticksAsNumbers = me.ticks.slice();
12778                me.zeroLineIndex = me.ticks.indexOf(0);
12779
12780                Scale.prototype.convertTicksToLabels.call(me);
12781            }
12782        });
12783    };
12784
12785},{"33":33,"46":46}],57:[function(require,module,exports){
12786    'use strict';
12787
12788    var helpers = require(46);
12789    var Scale = require(33);
12790    var scaleService = require(34);
12791    var Ticks = require(35);
12792
12793    /**
12794     * Generate a set of logarithmic ticks
12795     * @param generationOptions the options used to generate the ticks
12796     * @param dataRange the range of the data
12797     * @returns {Array<Number>} array of tick values
12798     */
12799    function generateTicks(generationOptions, dataRange) {
12800        var ticks = [];
12801        var valueOrDefault = helpers.valueOrDefault;
12802
12803        // Figure out what the max number of ticks we can support it is based on the size of
12804        // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
12805        // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
12806        // the graph
12807        var tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));
12808
12809        var endExp = Math.floor(helpers.log10(dataRange.max));
12810        var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
12811        var exp, significand;
12812
12813        if (tickVal === 0) {
12814            exp = Math.floor(helpers.log10(dataRange.minNotZero));
12815            significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));
12816
12817            ticks.push(tickVal);
12818            tickVal = significand * Math.pow(10, exp);
12819        } else {
12820            exp = Math.floor(helpers.log10(tickVal));
12821            significand = Math.floor(tickVal / Math.pow(10, exp));
12822        }
12823        var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;
12824
12825        do {
12826            ticks.push(tickVal);
12827
12828            ++significand;
12829            if (significand === 10) {
12830                significand = 1;
12831                ++exp;
12832                precision = exp >= 0 ? 1 : precision;
12833            }
12834
12835            tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision;
12836        } while (exp < endExp || (exp === endExp && significand < endSignificand));
12837
12838        var lastTick = valueOrDefault(generationOptions.max, tickVal);
12839        ticks.push(lastTick);
12840
12841        return ticks;
12842    }
12843
12844
12845    module.exports = function(Chart) {
12846
12847        var defaultConfig = {
12848            position: 'left',
12849
12850            // label settings
12851            ticks: {
12852                callback: Ticks.formatters.logarithmic
12853            }
12854        };
12855
12856        var LogarithmicScale = Scale.extend({
12857            determineDataLimits: function() {
12858                var me = this;
12859                var opts = me.options;
12860                var chart = me.chart;
12861                var data = chart.data;
12862                var datasets = data.datasets;
12863                var isHorizontal = me.isHorizontal();
12864                function IDMatches(meta) {
12865                    return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
12866                }
12867
12868                // Calculate Range
12869                me.min = null;
12870                me.max = null;
12871                me.minNotZero = null;
12872
12873                var hasStacks = opts.stacked;
12874                if (hasStacks === undefined) {
12875                    helpers.each(datasets, function(dataset, datasetIndex) {
12876                        if (hasStacks) {
12877                            return;
12878                        }
12879
12880                        var meta = chart.getDatasetMeta(datasetIndex);
12881                        if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
12882                            meta.stack !== undefined) {
12883                            hasStacks = true;
12884                        }
12885                    });
12886                }
12887
12888                if (opts.stacked || hasStacks) {
12889                    var valuesPerStack = {};
12890
12891                    helpers.each(datasets, function(dataset, datasetIndex) {
12892                        var meta = chart.getDatasetMeta(datasetIndex);
12893                        var key = [
12894                            meta.type,
12895                            // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
12896                            ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
12897                            meta.stack
12898                        ].join('.');
12899
12900                        if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12901                            if (valuesPerStack[key] === undefined) {
12902                                valuesPerStack[key] = [];
12903                            }
12904
12905                            helpers.each(dataset.data, function(rawValue, index) {
12906                                var values = valuesPerStack[key];
12907                                var value = +me.getRightValue(rawValue);
12908                                // invalid, hidden and negative values are ignored
12909                                if (isNaN(value) || meta.data[index].hidden || value < 0) {
12910                                    return;
12911                                }
12912                                values[index] = values[index] || 0;
12913                                values[index] += value;
12914                            });
12915                        }
12916                    });
12917
12918                    helpers.each(valuesPerStack, function(valuesForType) {
12919                        if (valuesForType.length > 0) {
12920                            var minVal = helpers.min(valuesForType);
12921                            var maxVal = helpers.max(valuesForType);
12922                            me.min = me.min === null ? minVal : Math.min(me.min, minVal);
12923                            me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
12924                        }
12925                    });
12926
12927                } else {
12928                    helpers.each(datasets, function(dataset, datasetIndex) {
12929                        var meta = chart.getDatasetMeta(datasetIndex);
12930                        if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
12931                            helpers.each(dataset.data, function(rawValue, index) {
12932                                var value = +me.getRightValue(rawValue);
12933                                // invalid, hidden and negative values are ignored
12934                                if (isNaN(value) || meta.data[index].hidden || value < 0) {
12935                                    return;
12936                                }
12937
12938                                if (me.min === null) {
12939                                    me.min = value;
12940                                } else if (value < me.min) {
12941                                    me.min = value;
12942                                }
12943
12944                                if (me.max === null) {
12945                                    me.max = value;
12946                                } else if (value > me.max) {
12947                                    me.max = value;
12948                                }
12949
12950                                if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
12951                                    me.minNotZero = value;
12952                                }
12953                            });
12954                        }
12955                    });
12956                }
12957
12958                // Common base implementation to handle ticks.min, ticks.max
12959                this.handleTickRangeOptions();
12960            },
12961            handleTickRangeOptions: function() {
12962                var me = this;
12963                var opts = me.options;
12964                var tickOpts = opts.ticks;
12965                var valueOrDefault = helpers.valueOrDefault;
12966                var DEFAULT_MIN = 1;
12967                var DEFAULT_MAX = 10;
12968
12969                me.min = valueOrDefault(tickOpts.min, me.min);
12970                me.max = valueOrDefault(tickOpts.max, me.max);
12971
12972                if (me.min === me.max) {
12973                    if (me.min !== 0 && me.min !== null) {
12974                        me.min = Math.pow(10, Math.floor(helpers.log10(me.min)) 
12974- 1);
12975                        me.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);
12976                    } else {
12977                        me.min = DEFAULT_MIN;
12978                        me.max = DEFAULT_MAX;
12979                    }
12980                }
12981                if (me.min === null) {
12982                    me.min = Math.pow(10, Math.floor(helpers.log10(me.max)) - 1);
12983                }
12984                if (me.max === null) {
12985                    me.max = me.min !== 0
12986                        ? Math.pow(10, Math.floor(helpers.log10(me.min)) + 1)
12987                        : DEFAULT_MAX;
12988                }
12989                if (me.minNotZero === null) {
12990                    if (me.min > 0) {
12991                        me.minNotZero = me.min;
12992                    } else if (me.max < 1) {
12993                        me.minNotZero = Math.pow(10, Math.floor(helpers.log10(me.max)));
12994                    } else {
12995                        me.minNotZero = DEFAULT_MIN;
12996                    }
12997                }
12998            },
12999            buildTicks: function() {
13000                var me = this;
13001                var opts = me.options;
13002                var tickOpts = opts.ticks;
13003                var reverse = !me.isHorizontal();
13004
13005                var generationOptions = {
13006                    min: tickOpts.min,
13007                    max: tickOpts.max
13008                };
13009                var ticks = me.ticks = generateTicks(generationOptions, me);
13010
13011                // At this point, we need to update our max and min given the tick values since we have expanded the
13012                // range of the scale
13013                me.max = helpers.max(ticks);
13014                me.min = helpers.min(ticks);
13015
13016                if (tickOpts.reverse) {
13017                    reverse = !reverse;
13018                    me.start = me.max;
13019                    me.end = me.min;
13020                } else {
13021                    me.start = me.min;
13022                    me.end = me.max;
13023                }
13024                if (reverse) {
13025                    ticks.reverse();
13026                }
13027            },
13028            convertTicksToLabels: function() {
13029                this.tickValues = this.ticks.slice();
13030
13031                Scale.prototype.convertTicksToLabels.call(this);
13032            },
13033            // Get the correct tooltip label
13034            getLabelForIndex: function(index, datasetIndex) {
13035                return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
13036            },
13037            getPixelForTick: function(index) {
13038                return this.getPixelForValue(this.tickValues[index]);
13039            },
13040            /**
13041             * Returns the value of the first tick.
13042             * @param {Number} value - The minimum not zero value.
13043             * @return {Number} The first tick value.
13044             * @private
13045             */
13046            _getFirstTickValue: function(value) {
13047                var exp = Math.floor(helpers.log10(value));
13048                var significand = Math.floor(value / Math.pow(10, exp));
13049
13050                return significand * Math.pow(10, exp);
13051            },
13052            getPixelForValue: function(value) {
13053                var me = this;
13054                var reverse = me.options.ticks.reverse;
13055                var log10 = helpers.log10;
13056                var firstTickValue = me._getFirstTickValue(me.minNotZero);
13057                var offset = 0;
13058                var innerDimension, pixel, start, end, sign;
13059
13060                value = +me.getRightValue(value);
13061                if (reverse) {
13062                    start = me.end;
13063                    end = me.start;
13064                    sign = -1;
13065                } else {
13066                    start = me.start;
13067                    end = me.end;
13068                    sign = 1;
13069                }
13070                if (me.isHorizontal()) {
13071                    innerDimension = me.width;
13072                    pixel = reverse ? me.right : me.left;
13073                } else {
13074                    innerDimension = me.height;
13075                    sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0)
13076                    pixel = reverse ? me.top : me.bottom;
13077                }
13078                if (value !== start) {
13079                    if (start === 0) { // include zero tick
13080                        offset = helpers.getValueOrDefault(
13081                            me.options.ticks.fontSize,
13082                            Chart.defaults.global.defaultFontSize
13083                        );
13084                        innerDimension -= offset;
13085                        start = firstTickValue;
13086                    }
13087                    if (value !== 0) {
13088                        offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start));
13089                    }
13090                    pixel += sign * offset;
13091                }
13092                return pixel;
13093            },
13094            getValueForPixel: function(pixel) {
13095                var me = this;
13096                var reverse = me.options.ticks.reverse;
13097                var log10 = helpers.log10;
13098                var firstTickValue = me._getFirstTickValue(me.minNotZero);
13099                var innerDimension, start, end, value;
13100
13101                if (reverse) {
13102                    start = me.end;
13103                    end = me.start;
13104                } else {
13105                    start = me.start;
13106                    end = me.end;
13107                }
13108                if (me.isHorizontal()) {
13109                    innerDimension = me.width;
13110                    value = reverse ? me.right - pixel : pixel - me.left;
13111                } else {
13112                    innerDimension = me.height;
13113                    value = reverse ? pixel - me.top : me.bottom - pixel;
13114                }
13115                if (value !== start) {
13116                    if (start === 0) { // include zero tick
13117                        var offset = helpers.getValueOrDefault(
13118                            me.options.ticks.fontSize,
13119                            Chart.defaults.global.defaultFontSize
13120                        );
13121                        value -= offset;
13122                        innerDimension -= offset;
13123                        start = firstTickValue;
13124                    }
13125                    value *= log10(end) - log10(start);
13126                    value /= innerDimension;
13127                    value = Math.pow(10, log10(start) + value);
13128                }
13129                return value;
13130            }
13131        });
13132
13133        scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);
13134    };
13135
13136},{"33":33,"34":34,"35":35,"46":46}],58:[function(require,module,exports){
13137    'use strict';
13138
13139    var defaults = require(26);
13140    var helpers = require(46);
13141    var scaleService = require(34);
13142    var Ticks = require(35);
13143
13144    module.exports = function(Chart) {
13145
13146        var globalDefaults = defaults.global;
13147
13148        var defaultConfig = {
13149            display: true,
13150
13151            // Boolean - Whether to animate scaling the chart from the centre
13152            animate: true,
13153            position: 'chartArea',
13154
13155            angleLines: {
13156                display: true,
13157                color: 'rgba(0, 0, 0, 0.1)',
13158                lineWidth: 1
13159            },
13160
13161            gridLines: {
13162                circular: false
13163            },
13164
13165            // label settings
13166            ticks: {
13167                // Boolean - Show a backdrop to the scale label
13168                showLabelBackdrop: true,
13169
13170                // String - The colour of the label backdrop
13171                backdropColor: 'rgba(255,255,255,0.75)',
13172
13173                // Number - The backdrop padding above & below the label in pixels
13174                backdropPaddingY: 2,
13175
13176                // Number - The backdrop padding to the side of the label in pixels
13177                backdropPaddingX: 2,
13178
13179                callback: Ticks.formatters.linear
13180            },
13181
13182            pointLabels: {
13183                // Boolean - if true, show point labels
13184                display: true,
13185
13186                // Number - Point label font size in pixels
13187                fontSize: 10,
13188
13189                // Function - Used to convert point labels
13190                callback: function(label) {
13191                    return label;
13192                }
13193            }
13194        };
13195
13196        function getValueCount(scale) {
13197            var opts = scale.options;
13198            return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0;
13199        }
13200
13201        function getPointLabelFontOptions(scale) {
13202            var pointLabelOptions = scale.options.pointLabels;
13203            var fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);
13204            var fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);
13205            var fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);
13206            var font = helpers.fontString(fontSize, fontStyle, fontFamily);
13207
13208            return {
13209                size: fontSize,
13210                style: fontStyle,
13211                family: fontFamily,
13212                font: font
13213            };
13214        }
13215
13216        function measureLabelSize(ctx, fontSize, label) {
13217            if (helpers.isArray(label)) {
13218                return {
13219                    w: helpers.longestText(ctx, ctx.font, label),
13220                    h: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize)
13221                };
13222            }
13223
13224            return {
13225                w: ctx.measureText(label).width,
13226                h: fontSize
13227            };
13228        }
13229
13230        function determineLimits(angle, pos, size, min, max) {
13231            if (angle === min || angle === max) {
13232                return {
13233                    start: pos - (size / 2),
13234                    end: pos + (size / 2)
13235                };
13236            } else if (angle < min || angle > max) {
13237                return {
13238                    start: pos - size - 5,
13239                    end: pos
13240                };
13241            }
13242
13243            return {
13244                start: pos,
13245                end: pos + size + 5
13246            };
13247        }
13248
13249        /**
13250         * Helper function to fit a radial linear scale with point labels
13251         */
13252        function fitWithPointLabels(scale) {
13253            /*
13254		 * Right, this is really confusing and there is a lot of maths going on here
13255		 * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
13256		 *
13257		 * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
13258		 *
13259		 * Solution:
13260		 *
13261		 * We assume the radius of the polygon is half the size of the canvas at first
13262		 * at each index we check if the text overlaps.
13263		 *
13264		 * Where it does, we store that angle and that index.
13265		 *
13266		 * After finding the largest index and angle we calculate how much we need to remove
13267		 * from the shape radius to move the point inwards by that x.
13268		 *
13269		 * We average the left and right distances to get the maximum shape radius that can fit in the box
13270		 * along with labels.
13271		 *
13272		 * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
13273		 * on each side, removing that from the size, halving it and adding the left x protrusion width.
13274		 *
13275		 * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
13276		 * and position it in the most space efficient manner
13277		 *
13278		 * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
13279		 */
13280
13281            var plFont = getPointLabelFontOptions(scale);
13282
13283            // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
13284            // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
13285            var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
13286            var furthestLimits = {
13287                r: scale.width,
13288                l: 0,
13289                t: scale.height,
13290                b: 0
13291            };
13292            var furthestAngles = {};
13293            var i, textSize, pointPosition;
13294
13295            scale.ctx.font = plFont.font;
13296            scale._pointLabelSizes = [];
13297
13298            var valueCount = getValueCount(scale);
13299            for (i = 0; i < valueCount; i++) {
13300                pointPosition = scale.getPointPosition(i, largestPossibleRadius);
13301                textSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || '');
13302                scale._pointLabelSizes[i] = textSize;
13303
13304                // Add quarter circle to make degree 0 mean top of circle
13305                var angleRadians = scale.getIndexAngle(i);
13306                var angle = helpers.toDegrees(angleRadians) % 360;
13307                var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
13308                var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
13309
13310                if (hLimits.start < furthestLimits.l) {
13311                    furthestLimits.l = hLimits.start;
13312                    furthestAngles.l = angleRadians;
13313                }
13314
13315                if (hLimits.end > furthestLimits.r) {
13316                    furthestLimits.r = hLimits.end;
13317                    furthestAngles.r = angleRadians;
13318                }
13319
13320                if (vLimits.start < furthestLimits.t) {
13321                    furthestLimits.t = vLimits.start;
13322                    furthestAngles.t = angleRadians;
13323                }
13324
13325                if (vLimits.end > furthestLimits.b) {
13326                    furthestLimits.b = vLimits.end;
13327                    furthestAngles.b = angleRadians;
13328                }
13329            }
13330
13331            scale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles);
13332        }
13333
13334        /**
13335         * Helper function to fit a radial linear scale with no point labels
13336         */
13337        function fit(scale) {
13338            var largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);
13339            scale.drawingArea = Math.round(largestPossibleRadius);
13340            scale.setCenterPoint(0, 0, 0, 0);
13341        }
13342
13343        function getTextAlignForAngle(angle) {
13344            if (angle === 0 || angle === 180) {
13345                return 'center';
13346            } else if (angle < 180) {
13347                return 'left';
13348            }
13349
13350            return 'right';
13351        }
13352
13353        function fillText(ctx, text, position, fontSize) {
13354            if (helpers.isArray(text)) {
13355                var y = position.y;
13356                var spacing = 1.5 * fontSize;
13357
13358                for (var i = 0; i < text.length; ++i) {
13359                    ctx.fillText(text[i], position.x, y);
13360                    y += spacing;
13361                }
13362            } else {
13363                ctx.fillText(text, position.x, position.y);
13364            }
13365        }
13366
13367        function adjustPointPositionForLabelHeight(angle, textSize, position) {
13368            if (angle === 90 || angle === 270) {
13369                position.y -= (textSize.h / 2);
13370            } else if (angle > 270 || angle < 90) {
13371                position.y -= textSize.h;
13372            }
13373        }
13374
13375        function drawPointLabels(scale) {
13376            var ctx = scale.ctx;
13377            var opts = scale.options;
13378            var angleLineOpts = opts.angleLines;
13379            var pointLabelOpts = opts.pointLabels;
13380
13381            ctx.lineWidth = angleLineOpts.lineWidth;
13382            ctx.strokeStyle = angleLineOpts.color;
13383
13384            var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
13385
13386            // Point Label Font
13387            var plFont = getPointLabelFontOptions(scale);
13388
13389            ctx.textBaseline = 'top';
13390
13391            for (var i = getValueCount(scale) - 1; i >= 0; i--) {
13392                if (angleLineOpts.display) {
13393                    var outerPosition = scale.getPointPosition(i, outerDistance);
13394                    ctx.beginPath();
13395                    ctx.moveTo(scale.xCenter, scale.yCenter);
13396                    ctx.lineTo(outerPosition.x, outerPosition.y);
13397                    ctx.stroke();
13398                    ctx.closePath();
13399                }
13400
13401                if (pointLabelOpts.display) {
13402                    // Extra 3px out for some label spacing
13403                    var pointLabelPosition = scale.getPointPosition(i, outerDistance + 5);
13404
13405                    // Keep this in loop since we may support array properties here
13406                    var pointLabelFo
13406ntColor = helpers.valueAtIndexOrDefault(pointLabelOpts.fontColor, i, globalDefaults.defaultFontColor);
13407                    ctx.font = plFont.font;
13408                    ctx.fillStyle = pointLabelFontColor;
13409
13410                    var angleRadians = scale.getIndexAngle(i);
13411                    var angle = helpers.toDegrees(angleRadians);
13412                    ctx.textAlign = getTextAlignForAngle(angle);
13413                    adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
13414                    fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size);
13415                }
13416            }
13417        }
13418
13419        function drawRadiusLine(scale, gridLineOpts, radius, index) {
13420            var ctx = scale.ctx;
13421            ctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1);
13422            ctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);
13423
13424            if (scale.options.gridLines.circular) {
13425                // Draw circular arcs between the points
13426                ctx.beginPath();
13427                ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
13428                ctx.closePath();
13429                ctx.stroke();
13430            } else {
13431                // Draw straight lines connecting each index
13432                var valueCount = getValueCount(scale);
13433
13434                if (valueCount === 0) {
13435                    return;
13436                }
13437
13438                ctx.beginPath();
13439                var pointPosition = scale.getPointPosition(0, radius);
13440                ctx.moveTo(pointPosition.x, pointPosition.y);
13441
13442                for (var i = 1; i < valueCount; i++) {
13443                    pointPosition = scale.getPointPosition(i, radius);
13444                    ctx.lineTo(pointPosition.x, pointPosition.y);
13445                }
13446
13447                ctx.closePath();
13448                ctx.stroke();
13449            }
13450        }
13451
13452        function numberOrZero(param) {
13453            return helpers.isNumber(param) ? param : 0;
13454        }
13455
13456        var LinearRadialScale = Chart.LinearScaleBase.extend({
13457            setDimensions: function() {
13458                var me = this;
13459                var opts = me.options;
13460                var tickOpts = opts.ticks;
13461                // Set the unconstrained dimension before label rotation
13462                me.width = me.maxWidth;
13463                me.height = me.maxHeight;
13464                me.xCenter = Math.round(me.width / 2);
13465                me.yCenter = Math.round(me.height / 2);
13466
13467                var minSize = helpers.min([me.height, me.width]);
13468                var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
13469                me.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);
13470            },
13471            determineDataLimits: function() {
13472                var me = this;
13473                var chart = me.chart;
13474                var min = Number.POSITIVE_INFINITY;
13475                var max = Number.NEGATIVE_INFINITY;
13476
13477                helpers.each(chart.data.datasets, function(dataset, datasetIndex) {
13478                    if (chart.isDatasetVisible(datasetIndex)) {
13479                        var meta = chart.getDatasetMeta(datasetIndex);
13480
13481                        helpers.each(dataset.data, function(rawValue, index) {
13482                            var value = +me.getRightValue(rawValue);
13483                            if (isNaN(value) || meta.data[index].hidden) {
13484                                return;
13485                            }
13486
13487                            min = Math.min(value, min);
13488                            max = Math.max(value, max);
13489                        });
13490                    }
13491                });
13492
13493                me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
13494                me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);
13495
13496                // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
13497                me.handleTickRangeOptions();
13498            },
13499            getTickLimit: function() {
13500                var tickOpts = this.options.ticks;
13501                var tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
13502                return Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));
13503            },
13504            convertTicksToLabels: function() {
13505                var me = this;
13506
13507                Chart.LinearScaleBase.prototype.convertTicksToLabels.call(me);
13508
13509                // Point labels
13510                me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
13511            },
13512            getLabelForIndex: function(index, datasetIndex) {
13513                return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
13514            },
13515            fit: function() {
13516                if (this.options.pointLabels.display) {
13517                    fitWithPointLabels(this);
13518                } else {
13519                    fit(this);
13520                }
13521            },
13522            /**
13523             * Set radius reductions and determine new radius and center point
13524             * @private
13525             */
13526            setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
13527                var me = this;
13528                var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
13529                var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
13530                var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
13531                var radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b);
13532
13533                radiusReductionLeft = numberOrZero(radiusReductionLeft);
13534                radiusReductionRight = numberOrZero(radiusReductionRight);
13535                radiusReductionTop = numberOrZero(radiusReductionTop);
13536                radiusReductionBottom = numberOrZero(radiusReductionBottom);
13537
13538                me.drawingArea = Math.min(
13539                    Math.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
13540                    Math.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
13541                me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
13542            },
13543            setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
13544                var me = this;
13545                var maxRight = me.width - rightMovement - me.drawingArea;
13546                var maxLeft = leftMovement + me.drawingArea;
13547                var maxTop = topMovement + me.drawingArea;
13548                var maxBottom = me.height - bottomMovement - me.drawingArea;
13549
13550                me.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);
13551                me.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top);
13552            },
13553
13554            getIndexAngle: function(index) {
13555                var angleMultiplier = (Math.PI * 2) / getValueCount(this);
13556                var startAngle = this.chart.options && this.chart.options.startAngle ?
13557                    this.chart.options.startAngle :
13558                    0;
13559
13560                var startAngleRadians = startAngle * Math.PI * 2 / 360;
13561
13562                // Start from the top instead of right, so remove a quarter of the circle
13563                return index * angleMultiplier + startAngleRadians;
13564            },
13565            getDistanceFromCenterForValue: function(value) {
13566                var me = this;
13567
13568                if (value === null) {
13569                    return 0; // null always in center
13570                }
13571
13572                // Take into account half font size + the yPadding of the top value
13573                var scalingFactor = me.drawingArea / (me.max - me.min);
13574                if (me.options.ticks.reverse) {
13575                    return (me.max - value) * scalingFactor;
13576                }
13577                return (value - me.min) * scalingFactor;
13578            },
13579            getPointPosition: function(index, distanceFromCenter) {
13580                var me = this;
13581                var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
13582                return {
13583                    x: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,
13584                    y: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter
13585                };
13586            },
13587            getPointPositionForValue: function(index, value) {
13588                return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
13589            },
13590
13591            getBasePosition: function() {
13592                var me = this;
13593                var min = me.min;
13594                var max = me.max;
13595
13596                return me.getPointPositionForValue(0,
13597                    me.beginAtZero ? 0 :
13598                        min < 0 && max < 0 ? max :
13599                            min > 0 && max > 0 ? min :
13600                                0);
13601            },
13602
13603            draw: function() {
13604                var me = this;
13605                var opts = me.options;
13606                var gridLineOpts = opts.gridLines;
13607                var tickOpts = opts.ticks;
13608                var valueOrDefault = helpers.valueOrDefault;
13609
13610                if (opts.display) {
13611                    var ctx = me.ctx;
13612                    var startAngle = this.getIndexAngle(0);
13613
13614                    // Tick Font
13615                    var tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);
13616                    var tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);
13617                    var tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);
13618                    var tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);
13619
13620                    helpers.each(me.ticks, function(label, index) {
13621                        // Don't draw a centre value (if it is minimum)
13622                        if (index > 0 || tickOpts.reverse) {
13623                            var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
13624
13625                            // Draw circular lines around the scale
13626                            if (gridLineOpts.display && index !== 0) {
13627                                drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
13628                            }
13629
13630                            if (tickOpts.display) {
13631                                var tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);
13632                                ctx.font = tickLabelFont;
13633
13634                                ctx.save();
13635                                ctx.translate(me.xCenter, me.yCenter);
13636                                ctx.rotate(startAngle);
13637
13638                                if (tickOpts.showLabelBackdrop) {
13639                                    var labelWidth = ctx.measureText(label).width;
13640                                    ctx.fillStyle = tickOpts.backdropColor;
13641                                    ctx.fillRect(
13642                                        -labelWidth / 2 - tickOpts.backdropPaddingX,
13643                                        -yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY,
13644                                        labelWidth + tickOpts.backdropPaddingX * 2,
13645                                        tickFontSize + tickOpts.backdropPaddingY * 2
13646                                    );
13647                                }
13648
13649                                ctx.textAlign = 'center';
13650                                ctx.textBaseline = 'middle';
13651                                ctx.fillStyle = tickFontColor;
13652                                ctx.fillText(label, 0, -yCenterOffset);
13653                                ctx.restore();
13654                            }
13655                        }
13656                    });
13657
13658                    if (opts.angleLines.display || opts.pointLabels.display) {
13659                        drawPointLabels(me);
13660                    }
13661                }
13662            }
13663        });
13664
13665        scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig);
13666    };
13667
13668},{"26":26,"34":34,"35":35,"46":46}],59:[function(require,module,exports){
13669    /* global window: false */
13670    'use strict';
13671
13672    var moment = require(1);
13673    moment = typeof moment === 'function' ? moment : window.moment;
13674
13675    var defaults = require(26);
13676    var helpers = require(46);
13677    var Scale = require(33);
13678    var scaleService = require(34);
13679
13680// Integer constants are from the ES6 spec.
13681    var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;
13682    var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
13683
13684    var INTERVALS = {
13685        millisecond: {
13686            common: true,
13687            size: 1,
13688            steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
13689        },
13690        second: {
13691            common: true,
13692            size: 1000,
13693            steps: [1, 2, 5, 10, 15, 30]
13694        },
13695        minute: {
13696            common: true,
13697            size: 60000,
13698            steps: [1, 2, 5, 10, 15, 30]
13699        },
13700        hour: {
13701            common: true,
13702            size: 3600000,
13703            steps: [1, 2, 3, 6, 12]
13704        },
13705        day: {
13706            common: true,
13707            size: 86400000,
13708            steps: [1, 2, 5]
13709        },
13710        week: {
13711            common: false,
13712            size: 604800000,
13713            steps: [1, 2, 3, 4]
13714        },
13715        month: {
13716            common: true,
13717            size: 2.628e9,
13718            steps: [1, 2, 3]
13719        },
13720        quarter: {
13721            common: false,
13722            size: 7.884e9,
13723            steps: [1, 2, 3, 4]
13724        },
13725        year: {
13726            common: true,
13727            size: 3.154e10
13728        }
13729    };
13730
13731    var UNITS = Object.keys(INTERVALS);
13732
13733    function sorter(a, b) {
13734        return a - b;
13735    }
13736
13737    function arrayUnique(items) {
13738        var hash = {};
13739        var out = [];
13740        var i, ilen, item;
13741
13742        for (i = 0, ilen = items.length; i < ilen; ++i) {
13743            item = items[i];
13744            if (!hash[item]) {
13745                hash[item] = true;
13746                out.push(item);
13747            }
13748        }
13749
13750        return out;
13751    }
13752
13753    /**
13754     * Returns an array of {time, pos} objects used to interpolate a specific `time` or position
13755     * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is
13756     * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other
13757     * extremity (left + width or top + height). Note that it would be more optimized to directly
13758     * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need
13759     * to create the lookup table. The table ALWAYS contains at least two items: min and max.
13760     *
13761     * @param {Number[]} timestamps - timestamps sorted from lowest to highest.
13762     * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min
13763     * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}.
13764     * If 'series', timestamps will be positioned at the same distance from each other. In this
13765     * case, only timestamps that break the time linearity are registered, meaning that in the
13766     * best case, all timestamps are linear, the table contains only min and max.
13767     */
13768    function buildLookupTable(timestamps, min, max, distribution) {
13769        if (distribution === 'linear' || !timestamps.length) {
13770            return [
13771                {time: min, pos: 0},
13772                {time: max, pos: 1}
13773            ];
13774        }
13775
13776        var table = [];
13777        var items = [min];
13778        var i, ilen, prev, curr, next;
13779
13780        for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
13781            curr = timestamps[i];
13782            if (curr > min && curr < max) {
13783                items.push(curr);
13784            }
13785        }
13786
13787        items.push(max);
13788
13789        for (i = 0, ilen = items.length; i < ilen; ++i) {
13790            next = items[i + 1];
13791            prev = items[i - 1];
13792            curr = items[i];
13793
13794            // only add points that breaks the scale linearity
13795            if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) {
13796                table.push({time: curr, pos: i / (ilen - 1)});
13797            }
13798        }
13799
13800        return table;
13801    }
13802
13803// @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/
13804    function lookup(table, key, value) {
13805        var lo = 0;
13806        var hi = table.length - 1;
13807        var mid, i0, i1;
13808
13809        while (lo >= 0 && lo <= hi) {
13810            mid = (lo + hi) >> 1;
13811            i0 = table[mid - 1] || null;
13812            i1 = table[mid];
13813
13814            if (!i0) {
13815                // given value is outside table (before first item)
13816                return {lo: null, hi: i1};
13817            } else if (i1[key] < value) {
13818                lo = mid + 1;
13819            } else if (i0[key] > value) {
13820                hi = mid - 1;
13821            } else {
13822                return {lo: i0, hi: i1};
13823            }
13824        }
13825
13826        // given value is outside table (after last item)
13827        return {lo: i1, hi: null};
13828    }
13829
13830    /**
13831     * Linearly interpolates the given source `value` using the table items `skey` values and
13832     * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos')
13833     * returns the position for a timestamp equal to 42. If value is out of bounds, values at
13834     * index [0, 1] or [n - 1, n] are used for the interpolation.
13835     */
13836    function interpolate(table, skey, sval, tkey) {
13837        var range = lookup(table, skey, sval);
13838
13839        // Note: the lookup table ALWAYS contains at least 2 items (min and max)
13840        var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo;
13841        var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi;
13842
13843        var span = next[skey] - prev[skey];
13844        var ratio = span ? (sval - prev[skey]) / span : 0;
13845        var offset = (next[tkey] - prev[tkey]) * ratio;
13846
13847        return prev[tkey] + offset;
13848    }
13849
13850    /**
13851     * Convert the given value to a moment object using the given time options.
13852     * @see http://momentjs.com/docs/#/parsing/
13853     */
13854    function momentify(value, options) {
13855        var parser = options.parser;
13856        var format = options.parser || options.format;
13857
13858        if (typeof parser === 'function') {
13859            return parser(value);
13860        }
13861
13862        if (typeof value === 'string' && typeof format === 'string') {
13863            return moment(value, format);
13864        }
13865
13866        if (!(value instanceof moment)) {
13867            value = moment(value);
13868        }
13869
13870        if (value.isValid()) {
13871            return value;
13872        }
13873
13874        // Labels are in an incompatible moment format and no `parser` has been provided.
13875        // The user might still use the deprecated `format` option to convert his inputs.
13876        if (typeof format === 'function') {
13877            return format(value);
13878        }
13879
13880        return value;
13881    }
13882
13883    function parse(input, scale) {
13884        if (helpers.isNullOrUndef(input)) {
13885            return null;
13886        }
13887
13888        var options = scale.options.time;
13889        var value = momentify(scale.getRightValue(input), options);
13890        if (!value.isValid()) {
13891            return null;
13892        }
13893
13894        if (options.round) {
13895            value.startOf(options.round);
13896        }
13897
13898        return value.valueOf();
13899    }
13900
13901    /**
13902     * Returns the number of unit to skip to be able to display up to `capacity` number of ticks
13903     * in `unit` for the given `min` / `max` range and respecting the interval steps constraints.
13904     */
13905    function determineStepSize(min, max, unit, capacity) {
13906        var range = max - min;
13907        var interval = INTERVALS[unit];
13908        var milliseconds = interval.size;
13909        var steps = interval.steps;
13910        var i, ilen, factor;
13911
13912        if (!steps) {
13913            return Math.ceil(range / (capacity * milliseconds));
13914        }
13915
13916        for (i = 0, ilen = steps.length; i < ilen; ++i) {
13917            factor = steps[i];
13918            if (Math.ceil(range / (milliseconds * factor)) <= capacity) {
13919                break;
13920            }
13921        }
13922
13923        return factor;
13924    }
13925
13926    /**
13927     * Figures out what unit results in an appropriate number of auto-generated ticks
13928     */
13929    function determineUnitForAutoTicks(minUnit, min, max, capacity) {
13930        var ilen = UNITS.length;
13931        var i, interval, factor;
13932
13933        for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
13934            interval = INTERVALS[UNITS[i]];
13935            factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER;
13936
13937            if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
13938                return UNITS[i];
13939            }
13940        }
13941
13942        return UNITS[ilen - 1];
13943    }
13944
13945    /**
13946     * Figures out what unit to format a set of ticks with
13947     */
13948    function determineUnitForFormatting(ticks, minUnit, min, max) {
13949        var duration = moment.duration(moment(max).diff(moment(min)));
13950        var ilen = UNITS.length;
13951        var i, unit;
13952
13953        for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) {
13954            unit = UNITS[i];
13955            if (INTERVALS[unit].common && duration.as(unit) >= ticks.length) {
13956                return unit;
13957            }
13958        }
13959
13960        return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];
13961    }
13962
13963    function determineMajorUnit(unit) {
13964        for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
13965            if (INTERVALS[UNITS[i]].common) {
13966                return UNITS[i];
13967            }
13968        }
13969    }
13970
13971    /**
13972     * Generates a maximum of `capacity` timestamps between min and max, rounded to the
13973     * `minor` unit, aligned on the `major` unit and using the given scale time `options`.
13974     * Important: this method can return ticks outside the min and max range, it's the
13975     * responsibility of the calling code to clamp values if needed.
13976     */
13977    function generate(min, max, capacity, options) {
13978        var timeOpts = options.time;
13979        var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity);
13980        var major = determineMajorUnit(minor);
13981        var stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize);
13982        var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
13983        var majorTicksEnabled = options.ticks.major.enabled;
13984        var interval = INTERVALS[minor];
13985        var first = moment(min);
13986        var last = moment(max);
13987        var ticks = [];
13988        var time;
13989
13990        if (!stepSize) {
13991            stepSize = determineStepSize(min, max, minor, capacity);
13992        }
13993
13994        // For 'week' unit, handle the first day of week option
13995        if (weekday) {
13996            first = first.isoWeekday(weekday);
13997            last = last.isoWeekday(weekday);
13998        }
13999
14000        // Align first/last ticks on unit
14001        first = first.startOf(weekday ? 'day' : minor);
14002        last = last.startOf(weekday ? 'day' : minor);
14003
14004        // Make sure that the last tick include max
14005        if (last < max) {
14006            last.add(1, minor);
14007        }
14008
14009        time = moment(first);
14010
14011        if (majorTicksEnabled && major && !weekday && !timeOpts.round) {
14012            // Align the first tick on the previous `minor` unit aligned on the `major` unit:
14013            // we first aligned time on the previous `major` unit then add the number of full
14014            // stepSize there is between first and the previous major time.
14015            time.startOf(major);
14016            time.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor);
14017        }
14018
14019        for (; time < last; time.add(stepSize, minor)) {
14020            ticks.push(+time);
14021        }
14022
14023        ticks.push(+time);
14024
14025        return ticks;
14026    }
14027
14028    /**
14029     * Returns the right and left offsets from edges in the form of {left, right}.
14030     * Offsets are added when the `offset` option is true.
14031     */
14032    function computeOffsets(table, ticks, min, max, options) {
14033        var left = 0;
14034        var right = 0;
14035        var upper, lower;
14036
14037        if (options.offset && ticks.length) {
14038            if (!options.time.min) {
14039                upper = ticks.length > 1 ? ticks[1] : max;
14040                lower = ticks[0];
14041                left = (
14042                    interpolate(table, 'time', upper, 'pos') -
14043                    interpolate(table, 'time', lower, 'pos')
14044                ) / 2;
14045            }
14046            if (!options.time.max) {
14047                upper = ticks[ticks.length - 1];
14048                lower = ticks.length > 1 ? ticks[ticks.length - 2] : min;
14049                right = (
14050                    interpolate(table, 'time', upper, 'pos') -
14051                    interpolate(table, 'time', lower, 'pos')
14052                ) / 2;
14053            }
14054        }
14055
14056        return {left: left, right: right};
14057    }
14058
14059    function ticksFromTimestamps(values, majorUnit) {
14060        var ticks = [];
14061        var i, ilen, value, major;
14062
14063        for (i = 0, ilen = values.length; i < ilen; ++i) {
14064            value = values[i];
14065            major = majorUnit ? value === +moment(value).startOf(majorUnit) : false;
14066
14067            ticks.push({
14068                value: value,
14069                major: major
14070            });
14071        }
14072
14073        return ticks;
14074    }
14075
14076    function determineLabelFormat(data, timeOpts) {
14077        var i, momentDate, hasTime;
14078        var ilen = data.length;
14079
14080        // find the label with the most parts (milliseconds, minutes, etc.)
14081        // format all labels with the same level of detail as the most specific label
14082        for (i = 0; i < ilen; i++) {
14083            momentDate = momentify(data[i], timeOpts);
14084            if (momentDate.millisecond() !== 0) {
14085                return 'MMM D, YYYY h:mm:ss.SSS a';
14086            }
14087            if (momentDate.second() !== 0 || momentDate.minute() !== 0 || momentDate.hour() !== 0) {
14088                hasTime = true;
14089            }
14090        }
14091        if (hasTime) {
14092            return 'MMM D, YYYY h:mm:ss a';
14093        }
14094        return 'MMM D, YYYY';
14095    }
14096
14097    module.exports = function() {
14098
14099        var defaultConfig = {
14100            position: 'bottom',
14101
14102            /**
14103             * Data distribution along the scale:
14104             * - 'linear': data are spread according to their time (distances can vary),
14105             * - 'series': data are spread at the same distance from each other.
14106             * @see https://github.com/chartjs/Chart.js/pull/4507
14107             * @since 2.7.0
14108             */
14109            distribution: 'linear',
14110
14111            /**
14112             * Scale boundary strategy (bypassed by min/max time options)
14113             * - `data`: make sure data are fully visible, ticks outside are removed
14114             * - `ticks`: make sure ticks are fully visible, data outside are truncated
14115             * @see https://github.com/chartjs/Chart.js/pull/4556
14116             * @since 2.7.0
14117             */
14118            bounds: 'data',
14119
14120            time: {
14121                parser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
14122                format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/
14123                unit: false, // false == automatic or override with week, month, year, etc.
14124                round: false, // none, or override with week, month, year, etc.
14125                displayFormat: false, // DEPRECATED
14126                isoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/
14127                minUnit: 'millisecond',
14128
14129                // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
14130                displayFormats: {
14131                    millisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,
14132                    second: 'h:mm:ss a', // 11:20:01 AM
14133                    minute: 'h:mm a', // 11:20 AM
14134                    hour: 'hA', // 5PM
14135                    day: 'MMM D', // Sep 4
14136                    week: 'll', // Week 46, or maybe "[W]WW - YYYY" ?
14137                    month: 'MMM YYYY', // Sept 2015
14138                    quarter: '[Q]Q - YYYY', // Q3
14139                    year: 'YYYY' // 2015
14140                },
14141            },
14142            ticks: {
14143                autoSkip: false,
14144
14145                /**
14146                 * Ticks generation input values:
14147                 * - 'auto': generates "optimal" ticks based on scale size and time options.
14148                 * - 'data': generates ticks from data (including labels from data {t|x|y} objects).
14149                 * - 'labels': generates ticks from user given `data.labels` values ONLY.
14150                 * @see https://github.com/chartjs/Chart.js/pull/4507
14151                 * @since 2.7.0
14152                 */
14153                source: 'auto',
14154
14155                major: {
14156                    enabled: false
14157                }
14158            }
14159        };
14160
14161        var TimeScale = Scale.extend({
14162            initialize: function() {
14163                if (!moment) {
14164                    throw new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com');
14165                }
14166
14167                this.mergeTicksOptions();
14168
14169                Scale.prototype.initialize.call(this);
14170            },
14171
14172            update: function() {
14173                var me = this;
14174                var options = me.options;
14175
14176                // DEPRECATIONS: output a message only one time per update
14177                if (options.time && options.time.format) {
14178                    console.warn('options.time.format is deprecated and replaced by options.time.parser.');
14179                }
14180
14181                return Scale.prototype.update.apply(me, arguments);
14182            },
14183
14184            /**
14185             * Allows data to be referenced via 't' attribute
14186             */
14187            getRightValue: function(rawValue) {
14188                if (rawValue && rawValue.t !== undefined) {
14189                    rawValue = rawValue.t;
14190                }
14191                return Scale.prototype.getRightValue.call(this, rawValue);
14192            },
14193
14194            determineDataLimits: function() {
14195                var me = this;
14196                var chart = me.chart;
14197                var timeOpts = me.options.time;
14198                var unit = timeOpts.unit || 'day';
14199                var min = MAX_INTEGER;
14200                var max = MIN_INTEGER;
14201                var timestamps = [];
14202                var datasets = [];
14203                var labels = [];
14204                var i, j, ilen, jlen, data, timestamp;
14205
14206                // Convert labels to timestamps
14207                for (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) {
14208                    labels.push(parse(chart.data.labels[i], me));
14209                }
14210
14211                // Convert data to timestamps
14212                for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
14213                    if (chart.isDatasetVisible(i)) {
14214                        data = chart.data.datasets[i].data;
14215
14216                        // Let's consider that all data have the same format.
14217                        if (helpers.isObject(data[0])) {
14218                            datasets[i] = [];
14219
14220                            for (j = 0, jlen = data.length; j < jlen; ++j) {
14221                                timestamp = parse(data[j], me);
14222                                timestamps.push(timestamp);
14223                                datasets[i][j] = timestamp;
14224                            }
14225                        } else {
14226                            timestamps.push.apply(timestamps, labels);
14227                            datasets[i] = labels.slice(0);
14228                        }
14229                    } else {
14230                        datasets[i] = [];
14231                    }
14232                }
14233
14234                if (labels.length) {
14235                    // Sort labels **after** data have been converted
14236                    labels = arrayUnique(labels).sort(sorter);
14237                    min = Math.min(min, labels[0]);
14238                    max = Math.max(max, labels[labels.length - 1]);
14239                }
14240
14241                if (timestamps.length) {
14242                    timestamps = arrayUnique(timestamps).sort(sorter);
14243                    min = Math.min(min, timestamps[0]);
14244                    max = Math.max(max, timestamps[timestamps.length - 1]);
14245                }
14246
14247                min = parse(timeOpts.min, me) || min;
14248                max = parse(timeOpts.max, me) || max;
14249
14250                // In case there is no valid min/max, set limits based on unit time option
14251                min = min === MAX_INTEGER ? +moment().startOf(unit) : min;
14252                max = max === MIN_INTEGER ? +moment().endOf(unit) + 1 : max;
14253
14254                // Make sure that max is strictly higher than min (required by the lookup table)
14255                me.min = Math.min(min, max);
14256                me.max = Math.max(min + 1, max);
14257
14258                // PRIVATE
14259                me._horizontal = me.isHorizontal();
14260                me._table = [];
14261                me._timestamps = {
14262                    data: timestamps,
14263                    datasets: datasets,
14264                    labels: labels
14265                };
14266            },
14267
14268            buildTicks: function() {
14269                var me = this;
14270                var min = me.min;
14271                var max = me.max;
14272                var options = me.options;
14273                var timeOpts = options.time;
14274                var timestamps = [];
14275                var ticks = [];
14276                var i, ilen, timestamp;
14277
14278                switch (options.ticks.source) {
14279                    case 'data':
14280                        timestamps = me._timestamps.data;
14281                        break;
14282                    case 'labels':
14283                        timestamps = me._timestamps.labels;
14284                        break;
14285                    case 'auto':
14286                    default:
14287                        timestamps = generate(min, max, me.getLabelCapacity(min), options);
14288                }
14289
14290                if (options.bounds === 'ticks' && timestamps.length) {
14291                    min = timestamps[0];
14292                    max = timestamps[timestamps.length - 1];
14293                }
14294
14295                // Enforce limits with user min/max options
14296                min = parse(timeOpts.min, me) || min;
14297                max = parse(timeOpts.max, me) || max;
14298
14299                // Remove ticks outside the min/max range
14300                for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
14301                    timestamp = timestamps[i];
14302                    if (timestamp >= min && timestamp <= max) {
14303                        ticks.push(timestamp);
14304                    }
14305                }
14306
14307                me.min = min;
14308                me.max = max;
14309
14310                // PRIVATE
14311                me._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max);
14312                me._majorUnit = determineMajorUnit(me._unit);
14313                me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution);
14314                me._offsets = computeOffsets(me._table, ticks, min, max, options);
14315                me._labelFormat = determineLabelFormat(me._timestamps.data, timeOpts);
14316
14317                return ticksFromTimestamps(ticks, me._majorUnit);
14318            },
14319
14320            getLabelForIndex: function(index, datasetIndex) {
14321                var me = this;
14322                var data = me.chart.data;
14323                var timeOpts = me.options.time;
14324                var label = data.labels && index < data.labels.length ? data.labels[index] : '';
14325                var value = data.datasets[datasetIndex].data[index];
14326
14327                if (helpers.isObject(value)) {
14328                    label = me.getRightValue(value);
14329                }
14330                if (timeOpts.tooltipFormat) {
14331                    return momentify(label, timeOpts).format(timeOpts.tooltipFormat);
14332                }
14333                if (typeof label === 'string') {
14334                    return label;
14335                }
14336
14337                return momentify(label, timeOpts).format(me._labelFormat);
14338            },
14339
14340            /**
14341             * Function to format an individual tick mark
14342             * @private
14343             */
14344            tickFormatFunction: function(tick, index, ticks, formatOverride) {
14345                var me = this;
14346                var options = me.options;
14347                var time = tick.valueOf();
14348                var formats = options.time.displayFormats;
14349                var minorFormat = formats[me._unit];
14350                var majorUnit = me._majorUnit;
14351                var majorFormat = formats[majorUnit];
14352                var majorTime = tick.clone().startOf(majorUnit).valueOf();
14353                var majorTickOpts = options.ticks.major;
14354                var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime;
14355                var label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat);
14356                var tickOpts = major ? majorTickOpts : options.ticks.minor;
14357                var formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback);
14358
14359                return formatter ? formatter(label, index, ticks) : label;
14360            },
14361
14362            convertTicksToLabels: function(ticks) {
14363                var labels = [];
14364                var i, ilen;
14365
14366                for (i = 0, ilen = ticks.length; i < ilen; ++i) {
14367                    labels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks));
14368                }
14369
14370                return labels;
14371            },
14372
14373            /**
14374             * @private
14375             */
14376            getPixelForOffset: function(time) {
14377                var me = this;
14378                var size = me._horizontal ? me.width : me.height;
14379                var start = me._horizontal ? me.left : me.top;
14380                var pos = interpolate(me._table, 'time', time, 'pos');
14381
14382                return start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right);
14383            },
14384
14385            getPixelForValue: function(value, index, datasetIndex) {
14386                var me = this;
14387                var time = null;
14388
14389                if (index !== undefined && datasetIndex !== undefined) {
14390                    time = me._timestamps.datasets[datasetIndex][index];
14391                }
14392
14393                if (time === null) {
14394                    time = parse(value, me);
14395                }
14396
14397                if (time !== null) {
14398                    return me.getPixelForOffset(time);
14399                }
14400            },
14401
14402            getPi
14402xelForTick: function(index) {
14403                var ticks = this.getTicks();
14404                return index >= 0 && index < ticks.length ?
14405                    this.getPixelForOffset(ticks[index].value) :
14406                    null;
14407            },
14408
14409            getValueForPixel: function(pixel) {
14410                var me = this;
14411                var size = me._horizontal ? me.width : me.height;
14412                var start = me._horizontal ? me.left : me.top;
14413                var pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right;
14414                var time = interpolate(me._table, 'pos', pos, 'time');
14415
14416                return moment(time);
14417            },
14418
14419            /**
14420             * Crude approximation of what the label width might be
14421             * @private
14422             */
14423            getLabelWidth: function(label) {
14424                var me = this;
14425                var ticksOpts = me.options.ticks;
14426                var tickLabelWidth = me.ctx.measureText(label).width;
14427                var angle = helpers.toRadians(ticksOpts.maxRotation);
14428                var cosRotation = Math.cos(angle);
14429                var sinRotation = Math.sin(angle);
14430                var tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize);
14431
14432                return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
14433            },
14434
14435            /**
14436             * @private
14437             */
14438            getLabelCapacity: function(exampleTime) {
14439                var me = this;
14440
14441                var formatOverride = me.options.time.displayFormats.millisecond;	// Pick the longest format for guestimation
14442
14443                var exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride);
14444                var tickLabelWidth = me.getLabelWidth(exampleLabel);
14445                var innerWidth = me.isHorizontal() ? me.width : me.height;
14446
14447                var capacity = Math.floor(innerWidth / tickLabelWidth);
14448                return capacity > 0 ? capacity : 1;
14449            }
14450        });
14451
14452        scaleService.registerScaleType('time', TimeScale, defaultConfig);
14453    };
14454
14455},{"1":1,"26":26,"33":33,"34":34,"46":46}]},{},[7])(7)
14456});

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