PageSourceSearch

https://philippecloutier.com/public/generated/js/vendor_dist/chart.js/dist/chart.js

js philippecloutier.com collected 2026-10-02 16:41:13 UTC 403,805 bytes, 11,492 lines download raw bytes

1/*!
2 * Chart.js v4.4.8
3 * https://www.chartjs.org
4 * (c) 2025
vendor: 12,620 bytes, lines 4-355
4 Chart.js Contributors
5 * Released under the MIT License
6 */
7import { r as requestAnimFrame, a as resolve, e as effects, c as color, i as isObject, d as defaults, b as isArray, v as valueOrDefault, u as unlistenArrayEvents, l as listenArrayEvents, f as resolveObjectKey, g as isNumberFinite, h as defined, s as sign, j as createContext, k as isNullOrUndef, _ as _arrayUnique, t as toRadians, m as toPercentage, n as toDimension, T as TAU, o as formatNumber, p as _angleBetween, H as HALF_PI, P as PI, q as _getStartAndCountOfVisiblePoints, w as _scaleRangesChanged, x as isNumber, y as _parseObjectDataRadialScale, z as getRelativePosition, A as _rlookupByKey, B as _lookupByKey, C as _isPointInArea, D as getAngleFromPoint, E as toPadding, F as each, G as getMaximumSize, I as _getParentNode, J as readUsedSize, K as supportsEventListenerOptions, L as throttled, M as _isDomSupported, N as _factorize, O as finiteOrDefault, Q as callback, R as _addGrace, S as _limitValue, U as toDegrees, V as _measureText, W as _int16Range, X as _alignPixel, Y as clipArea, Z as renderText, $ as unclipArea, a0 as toFont, a1 as _toLeftRightCenter, a2 as _alignStartEnd, a3 as overrides, a4 as merge, a5 as _capitalize, a6 as descriptors, a7 as isFunction, a8 as _attachContext, a9 as _createResolver, aa as _descriptors, ab as mergeIf, ac as uid, ad as debounce, ae as retinaScale, af as clearCanvas, ag as setsEqual, ah as _elementsEqual, ai as _isClickEvent, aj as _isBetween, ak as _readValueToProps, al as _updateBezierControlPoints, am as _computeSegments, an as _boundSegments, ao as _steppedInterpolation, ap as _bezierInterpolation, aq as _pointInLine, ar as _steppedLineTo, as as _bezierCurveTo, at as drawPoint, au as addRoundedRectPath, av as toTRBL, aw as toTRBLCorners, ax as _boundSegment, ay as _normalizeAngle, az as getRtlAdapter, aA as overrideTextDirection, aB as _textX, aC as restoreTextDirection, aD as drawPointLegend, aE as distanceBetweenPoints, aF as noop, aG as _setMinAndMaxByKey, aH as niceNum, aI as almostWhole, aJ as almostEquals, aK as _decimalPlaces, aL as Ticks, aM as log10, aN as _longestText, aO as _filterBetween, aP as _lookup } from './chunks/helpers.segment.js';
8import '@kurkle/color';
9
10class Animator {
11    constructor(){
12        this._request = null;
13        this._charts = new Map();
14        this._running = false;
15        this._lastDate = undefined;
16    }
17 _notify(chart, anims, date, type) {
18        const callbacks = anims.listeners[type];
19        const numSteps = anims.duration;
20        callbacks.forEach((fn)=>fn({
21                chart,
22                initial: anims.initial,
23                numSteps,
24                currentStep: Math.min(date - anims.start, numSteps)
25            }));
26    }
27 _refresh() {
28        if (this._request) {
29            return;
30        }
31        this._running = true;
32        this._request = requestAnimFrame.call(window, ()=>{
33            this._update();
34            this._request = null;
35            if (this._running) {
36                this._refresh();
37            }
38        });
39    }
40 _update(date = Date.now()) {
41        let remaining = 0;
42        this._charts.forEach((anims, chart)=>{
43            if (!anims.running || !anims.items.length) {
44                return;
45            }
46            const items = anims.items;
47            let i = items.length - 1;
48            let draw = false;
49            let item;
50            for(; i >= 0; --i){
51                item = items[i];
52                if (item._active) {
53                    if (item._total > anims.duration) {
54                        anims.duration = item._total;
55                    }
56                    item.tick(date);
57                    draw = true;
58                } else {
59                    items[i] = items[items.length - 1];
60                    items.pop();
61                }
62            }
63            if (draw) {
64                chart.draw();
65                this._notify(chart, anims, date, 'progress');
66            }
67            if (!items.length) {
68                anims.running = false;
69                this._notify(chart, anims, date, 'complete');
70                anims.initial = false;
71            }
72            remaining += items.length;
73        });
74        this._lastDate = date;
75        if (remaining === 0) {
76            this._running = false;
77        }
78    }
79 _getAnims(chart) {
80        const charts = this._charts;
81        let anims = charts.get(chart);
82        if (!anims) {
83            anims = {
84                running: false,
85                initial: true,
86                items: [],
87                listeners: {
88                    complete: [],
89                    progress: []
90                }
91            };
92            charts.set(chart, anims);
93        }
94        return anims;
95    }
96 listen(chart, event, cb) {
97        this._getAnims(chart).listeners[event].push(cb);
98    }
99 add(chart, items) {
100        if (!items || !items.length) {
101            return;
102        }
103        this._getAnims(chart).items.push(...items);
104    }
105 has(chart) {
106        return this._getAnims(chart).items.length > 0;
107    }
108 start(chart) {
109        const anims = this._charts.get(chart);
110        if (!anims) {
111            return;
112        }
113        anims.running = true;
114        anims.start = Date.now();
115        anims.duration = anims.items.reduce((acc, cur)=>Math.max(acc, cur._duration), 0);
116        this._refresh();
117    }
118    running(chart) {
119        if (!this._running) {
120            return false;
121        }
122        const anims = this._charts.get(chart);
123        if (!anims || !anims.running || !anims.items.length) {
124            return false;
125        }
126        return true;
127    }
128 stop(chart) {
129        const anims = this._charts.get(chart);
130        if (!anims || !anims.items.length) {
131            return;
132        }
133        const items = anims.items;
134        let i = items.length - 1;
135        for(; i >= 0; --i){
136            items[i].cancel();
137        }
138        anims.items = [];
139        this._notify(chart, anims, Date.now(), 'complete');
140    }
141 remove(chart) {
142        return this._charts.delete(chart);
143    }
144}
145var animator = /* #__PURE__ */ new Animator();
146
147const transparent = 'transparent';
148const interpolators = {
149    boolean (from, to, factor) {
150        return factor > 0.5 ? to : from;
151    },
152 color (from, to, factor) {
153        const c0 = color(from || transparent);
154        const c1 = c0.valid && color(to || transparent);
155        return c1 && c1.valid ? c1.mix(c0, factor).hexString() : to;
156    },
157    number (from, to, factor) {
158        return from + (to - from) * factor;
159    }
160};
161class Animation {
162    constructor(cfg, target, prop, to){
163        const currentValue = target[prop];
164        to = resolve([
165            cfg.to,
166            to,
167            currentValue,
168            cfg.from
169        ]);
170        const from = resolve([
171            cfg.from,
172            currentValue,
173            to
174        ]);
175        this._active = true;
176        this._fn = cfg.fn || interpolators[cfg.type || typeof from];
177        this._easing = effects[cfg.easing] || effects.linear;
178        this._start = Math.floor(Date.now() + (cfg.delay || 0));
179        this._duration = this._total = Math.floor(cfg.duration);
180        this._loop = !!cfg.loop;
181        this._target = target;
182        this._prop = prop;
183        this._from = from;
184        this._to = to;
185        this._promises = undefined;
186    }
187    active() {
188        return this._active;
189    }
190    update(cfg, to, date) {
191        if (this._active) {
192            this._notify(false);
193            const currentValue = this._target[this._prop];
194            const elapsed = date - this._start;
195            const remain = this._duration - elapsed;
196            this._start = date;
197            this._duration = Math.floor(Math.max(remain, cfg.duration));
198            this._total += elapsed;
199            this._loop = !!cfg.loop;
200            this._to = resolve([
201                cfg.to,
202                to,
203                currentValue,
204                cfg.from
205            ]);
206            this._from = resolve([
207                cfg.from,
208                currentValue,
209                to
210            ]);
211        }
212    }
213    cancel() {
214        if (this._active) {
215            this.tick(Date.now());
216            this._active = false;
217            this._notify(false);
218        }
219    }
220    tick(date) {
221        const elapsed = date - this._start;
222        const duration = this._duration;
223        const prop = this._prop;
224        const from = this._from;
225        const loop = this._loop;
226        const to = this._to;
227        let factor;
228        this._active = from !== to && (loop || elapsed < duration);
229        if (!this._active) {
230            this._target[prop] = to;
231            this._notify(true);
232            return;
233        }
234        if (elapsed < 0) {
235            this._target[prop] = from;
236            return;
237        }
238        factor = elapsed / duration % 2;
239        factor = loop && factor > 1 ? 2 - factor : factor;
240        factor = this._easing(Math.min(1, Math.max(0, factor)));
241        this._target[prop] = this._fn(from, to, factor);
242    }
243    wait() {
244        const promises = this._promises || (this._promises = []);
245        return new Promise((res, rej)=>{
246            promises.push({
247                res,
248                rej
249            });
250        });
251    }
252    _notify(resolved) {
253        const method = resolved ? 'res' : 'rej';
254        const promises = this._promises || [];
255        for(let i = 0; i < promises.length; i++){
256            promises[i][method]();
257        }
258    }
259}
260
261class Animations {
262    constructor(chart, config){
263        this._chart = chart;
264        this._properties = new Map();
265        this.configure(config);
266    }
267    configure(config) {
268        if (!isObject(config)) {
269            return;
270        }
271        const animationOptions = Object.keys(defaults.animation);
272        const animatedProps = this._properties;
273        Object.getOwnPropertyNames(config).forEach((key)=>{
274            const cfg = config[key];
275            if (!isObject(cfg)) {
276                return;
277            }
278            const resolved = {};
279            for (const option of animationOptions){
280                resolved[option] = cfg[option];
281            }
282            (isArray(cfg.properties) && cfg.properties || [
283                key
284            ]).forEach((prop)=>{
285                if (prop === key || !animatedProps.has(prop)) {
286                    animatedProps.set(prop, resolved);
287                }
288            });
289        });
290    }
291 _animateOptions(target, values) {
292        const newOptions = values.options;
293        const options = resolveTargetOptions(target, newOptions);
294        if (!options) {
295            return [];
296        }
297        const animations = this._createAnimations(options, newOptions);
298        if (newOptions.$shared) {
299            awaitAll(target.options.$animations, newOptions).then(()=>{
300                target.options = newOptions;
301            }, ()=>{
302            });
303        }
304        return animations;
305    }
306 _createAnimations(target, values) {
307        const animatedProps = this._properties;
308        const animations = [];
309        const running = target.$animations || (target.$animations = {});
310        const props = Object.keys(values);
311        const date = Date.now();
312        let i;
313        for(i = props.length - 1; i >= 0; --i){
314            const prop = props[i];
315            if (prop.charAt(0) === '$') {
316                continue;
317            }
318            if (prop === 'options') {
319                animations.push(...this._animateOptions(target, values));
320                continue;
321            }
322            const value = values[prop];
323            let animation = running[prop];
324            const cfg = animatedProps.get(prop);
325            if (animation) {
326                if (cfg && animation.active()) {
327                    animation.update(cfg, value, date);
328                    continue;
329                } else {
330                    animation.cancel();
331                }
332            }
333            if (!cfg || !cfg.duration) {
334                target[prop] = value;
335                continue;
336            }
337            running[prop] = animation = new Animation(cfg, target, prop, value);
338            animations.push(animation);
339        }
340        return animations;
341    }
342 update(target, values) {
343        if (this._properties.size === 0) {
344            Object.assign(target, values);
345            return;
346        }
347        const animations = this._createAnimations(target, values);
348        if (animations.length) {
349            animator.add(this._chart, animations);
350            return true;
351        }
352    }
353}
354function awaitAll(animations, properties) {
355    const running = [];
vendor: 3,696 bytes, lines 356-486
356    const keys = Object.keys(properties);
357    for(let i = 0; i < keys.length; i++){
358        const anim = animations[keys[i]];
359        if (anim && anim.active()) {
360            running.push(anim.wait());
361        }
362    }
363    return Promise.all(running);
364}
365function resolveTargetOptions(target, newOptions) {
366    if (!newOptions) {
367        return;
368    }
369    let options = target.options;
370    if (!options) {
371        target.options = newOptions;
372        return;
373    }
374    if (options.$shared) {
375        target.options = options = Object.assign({}, options, {
376            $shared: false,
377            $animations: {}
378        });
379    }
380    return options;
381}
382
383function scaleClip(scale, allowedOverflow) {
384    const opts = scale && scale.options || {};
385    const reverse = opts.reverse;
386    const min = opts.min === undefined ? allowedOverflow : 0;
387    const max = opts.max === undefined ? allowedOverflow : 0;
388    return {
389        start: reverse ? max : min,
390        end: reverse ? min : max
391    };
392}
393function defaultClip(xScale, yScale, allowedOverflow) {
394    if (allowedOverflow === false) {
395        return false;
396    }
397    const x = scaleClip(xScale, allowedOverflow);
398    const y = scaleClip(yScale, allowedOverflow);
399    return {
400        top: y.end,
401        right: x.end,
402        bottom: y.start,
403        left: x.start
404    };
405}
406function toClip(value) {
407    let t, r, b, l;
408    if (isObject(value)) {
409        t = value.top;
410        r = value.right;
411        b = value.bottom;
412        l = value.left;
413    } else {
414        t = r = b = l = value;
415    }
416    return {
417        top: t,
418        right: r,
419        bottom: b,
420        left: l,
421        disabled: value === false
422    };
423}
424function getSortedDatasetIndices(chart, filterVisible) {
425    const keys = [];
426    const metasets = chart._getSortedDatasetMetas(filterVisible);
427    let i, ilen;
428    for(i = 0, ilen = metasets.length; i < ilen; ++i){
429        keys.push(metasets[i].index);
430    }
431    return keys;
432}
433function applyStack(stack, value, dsIndex, options = {}) {
434    const keys = stack.keys;
435    const singleMode = options.mode === 'single';
436    let i, ilen, datasetIndex, otherValue;
437    if (value === null) {
438        return;
439    }
440    let found = false;
441    for(i = 0, ilen = keys.length; i < ilen; ++i){
442        datasetIndex = +keys[i];
443        if (datasetIndex === dsIndex) {
444            found = true;
445            if (options.all) {
446                continue;
447            }
448            break;
449        }
450        otherValue = stack.values[datasetIndex];
451        if (isNumberFinite(otherValue) && (singleMode || value === 0 || sign(value) === sign(otherValue))) {
452            value += otherValue;
453        }
454    }
455    if (!found && !options.all) {
456        return 0;
457    }
458    return value;
459}
460function convertObjectDataToArray(data, meta) {
461    const { iScale , vScale  } = meta;
462    const iAxisKey = iScale.axis === 'x' ? 'x' : 'y';
463    const vAxisKey = vScale.axis === 'x' ? 'x' : 'y';
464    const keys = Object.keys(data);
465    const adata = new Array(keys.length);
466    let i, ilen, key;
467    for(i = 0, ilen = keys.length; i < ilen; ++i){
468        key = keys[i];
469        adata[i] = {
470            [iAxisKey]: key,
471            [vAxisKey]: data[key]
472        };
473    }
474    return adata;
475}
476function isStacked(scale, meta) {
477    const stacked = scale && scale.options.stacked;
478    return stacked || stacked === undefined && meta.stack !== undefined;
479}
480function getStackKey(indexScale, valueScale, meta) {
481    return `${indexScale.id}.${valueScale.id}.${meta.stack || meta.type}`;
482}
483function getUserBounds(scale) {
484    const { min , max , minDefined , maxDefined  } = scale.getUserBounds();
485    return {
486        min: minDefined ? min : Number.NEGATIVE_INFINITY,
vendor: 4,867 bytes, lines 487-619
487        max: maxDefined ? max : Number.POSITIVE_INFINITY
488    };
489}
490function getOrCreateStack(stacks, stackKey, indexValue) {
491    const subStack = stacks[stackKey] || (stacks[stackKey] = {});
492    return subStack[indexValue] || (subStack[indexValue] = {});
493}
494function getLastIndexInStack(stack, vScale, positive, type) {
495    for (const meta of vScale.getMatchingVisibleMetas(type).reverse()){
496        const value = stack[meta.index];
497        if (positive && value > 0 || !positive && value < 0) {
498            return meta.index;
499        }
500    }
501    return null;
502}
503function updateStacks(controller, parsed) {
504    const { chart , _cachedMeta: meta  } = controller;
505    const stacks = chart._stacks || (chart._stacks = {});
506    const { iScale , vScale , index: datasetIndex  } = meta;
507    const iAxis = iScale.axis;
508    const vAxis = vScale.axis;
509    const key = getStackKey(iScale, vScale, meta);
510    const ilen = parsed.length;
511    let stack;
512    for(let i = 0; i < ilen; ++i){
513        const item = parsed[i];
514        const { [iAxis]: index , [vAxis]: value  } = item;
515        const itemStacks = item._stacks || (item._stacks = {});
516        stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index);
517        stack[datasetIndex] = value;
518        stack._top = getLastIndexInStack(stack, vScale, true, meta.type);
519        stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type);
520        const visualValues = stack._visualValues || (stack._visualValues = {});
521        visualValues[datasetIndex] = value;
522    }
523}
524function getFirstScaleId(chart, axis) {
525    const scales = chart.scales;
526    return Object.keys(scales).filter((key)=>scales[key].axis === axis).shift();
527}
528function createDatasetContext(parent, index) {
529    return createContext(parent, {
530        active: false,
531        dataset: undefined,
532        datasetIndex: index,
533        index,
534        mode: 'default',
535        type: 'dataset'
536    });
537}
538function createDataContext(parent, index, element) {
539    return createContext(parent, {
540        active: false,
541        dataIndex: index,
542        parsed: undefined,
543        raw: undefined,
544        element,
545        index,
546        mode: 'default',
547        type: 'data'
548    });
549}
550function clearStacks(meta, items) {
551    const datasetIndex = meta.controller.index;
552    const axis = meta.vScale && meta.vScale.axis;
553    if (!axis) {
554        return;
555    }
556    items = items || meta._parsed;
557    for (const parsed of items){
558        const stacks = parsed._stacks;
559        if (!stacks || stacks[axis] === undefined || stacks[axis][datasetIndex] === undefined) {
560            return;
561        }
562        delete stacks[axis][datasetIndex];
563        if (stacks[axis]._visualValues !== undefined && stacks[axis]._visualValues[datasetIndex] !== undefined) {
564            delete stacks[axis]._visualValues[datasetIndex];
565        }
566    }
567}
568const isDirectUpdateMode = (mode)=>mode === 'reset' || mode === 'none';
569const cloneIfNotShared = (cached, shared)=>shared ? cached : Object.assign({}, cached);
570const createStack = (canStack, meta, chart)=>canStack && !meta.hidden && meta._stacked && {
571        keys: getSortedDatasetIndices(chart, true),
572        values: null
573    };
574class DatasetController {
575 static defaults = {};
576 static datasetElementType = null;
577 static dataElementType = null;
578 constructor(chart, datasetIndex){
579        this.chart = chart;
580        this._ctx = chart.ctx;
581        this.index = datasetIndex;
582        this._cachedDataOpts = {};
583        this._cachedMeta = this.getMeta();
584        this._type = this._cachedMeta.type;
585        this.options = undefined;
586         this._parsing = false;
587        this._data = undefined;
588        this._objectData = undefined;
589        this._sharedOptions = undefined;
590        this._drawStart = undefined;
591        this._drawCount = undefined;
592        this.enableOptionSharing = false;
593        this.supportsDecimation = false;
594        this.$context = undefined;
595        this._syncList = [];
596        this.datasetElementType = new.target.datasetElementType;
597        this.dataElementType = new.target.dataElementType;
598        this.initialize();
599    }
600    initialize() {
601        const meta = this._cachedMeta;
602        this.configure();
603        this.linkScales();
604        meta._stacked = isStacked(meta.vScale, meta);
605        this.addElements();
606        if (this.options.fill && !this.chart.isPluginEnabled('filler')) {
607            console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options");
608        }
609    }
610    updateIndex(datasetIndex) {
611        if (this.index !== datasetIndex) {
612            clearStacks(this._cachedMeta);
613        }
614        this.index = datasetIndex;
615    }
616    linkScales() {
617        const chart = this.chart;
618        const meta = this._cachedMeta;
619        const dataset = this.getDataset();
vendor: 4,542 bytes, lines 620-736
620        const chooseId = (axis, x, y, r)=>axis === 'x' ? x : axis === 'r' ? r : y;
621        const xid = meta.xAxisID = valueOrDefault(dataset.xAxisID, getFirstScaleId(chart, 'x'));
622        const yid = meta.yAxisID = valueOrDefault(dataset.yAxisID, getFirstScaleId(chart, 'y'));
623        const rid = meta.rAxisID = valueOrDefault(dataset.rAxisID, getFirstScaleId(chart, 'r'));
624        const indexAxis = meta.indexAxis;
625        const iid = meta.iAxisID = chooseId(indexAxis, xid, yid, rid);
626        const vid = meta.vAxisID = chooseId(indexAxis, yid, xid, rid);
627        meta.xScale = this.getScaleForId(xid);
628        meta.yScale = this.getScaleForId(yid);
629        meta.rScale = this.getScaleForId(rid);
630        meta.iScale = this.getScaleForId(iid);
631        meta.vScale = this.getScaleForId(vid);
632    }
633    getDataset() {
634        return this.chart.data.datasets[this.index];
635    }
636    getMeta() {
637        return this.chart.getDatasetMeta(this.index);
638    }
639 getScaleForId(scaleID) {
640        return this.chart.scales[scaleID];
641    }
642 _getOtherScale(scale) {
643        const meta = this._cachedMeta;
644        return scale === meta.iScale ? meta.vScale : meta.iScale;
645    }
646    reset() {
647        this._update('reset');
648    }
649 _destroy() {
650        const meta = this._cachedMeta;
651        if (this._data) {
652            unlistenArrayEvents(this._data, this);
653        }
654        if (meta._stacked) {
655            clearStacks(meta);
656        }
657    }
658 _dataCheck() {
659        const dataset = this.getDataset();
660        const data = dataset.data || (dataset.data = []);
661        const _data = this._data;
662        if (isObject(data)) {
663            const meta = this._cachedMeta;
664            this._data = convertObjectDataToArray(data, meta);
665        } else if (_data !== data) {
666            if (_data) {
667                unlistenArrayEvents(_data, this);
668                const meta = this._cachedMeta;
669                clearStacks(meta);
670                meta._parsed = [];
671            }
672            if (data && Object.isExtensible(data)) {
673                listenArrayEvents(data, this);
674            }
675            this._syncList = [];
676            this._data = data;
677        }
678    }
679    addElements() {
680        const meta = this._cachedMeta;
681        this._dataCheck();
682        if (this.datasetElementType) {
683            meta.dataset = new this.datasetElementType();
684        }
685    }
686    buildOrUpdateElements(resetNewElements) {
687        const meta = this._cachedMeta;
688        const dataset = this.getDataset();
689        let stackChanged = false;
690        this._dataCheck();
691        const oldStacked = meta._stacked;
692        meta._stacked = isStacked(meta.vScale, meta);
693        if (meta.stack !== dataset.stack) {
694            stackChanged = true;
695            clearStacks(meta);
696            meta.stack = dataset.stack;
697        }
698        this._resyncElements(resetNewElements);
699        if (stackChanged || oldStacked !== meta._stacked) {
700            updateStacks(this, meta._parsed);
701            meta._stacked = isStacked(meta.vScale, meta);
702        }
703    }
704 configure() {
705        const config = this.chart.config;
706        const scopeKeys = config.datasetScopeKeys(this._type);
707        const scopes = config.getOptionScopes(this.getDataset(), scopeKeys, true);
708        this.options = config.createResolver(scopes, this.getContext());
709        this._parsing = this.options.parsing;
710        this._cachedDataOpts = {};
711    }
712 parse(start, count) {
713        const { _cachedMeta: meta , _data: data  } = this;
714        const { iScale , _stacked  } = meta;
715        const iAxis = iScale.axis;
716        let sorted = start === 0 && count === data.length ? true : meta._sorted;
717        let prev = start > 0 && meta._parsed[start - 1];
718        let i, cur, parsed;
719        if (this._parsing === false) {
720            meta._parsed = data;
721            meta._sorted = true;
722            parsed = data;
723        } else {
724            if (isArray(data[start])) {
725                parsed = this.parseArrayData(meta, data, start, count);
726            } else if (isObject(data[start])) {
727                parsed = this.parseObjectData(meta, data, start, count);
728            } else {
729                parsed = this.parsePrimitiveData(meta, data, start, count);
730            }
731            const isNotInOrderComparedToPrev = ()=>cur[iAxis] === null || prev && cur[iAxis] < prev[iAxis];
732            for(i = 0; i < count; ++i){
733                meta._parsed[i + start] = cur = parsed[i];
734                if (sorted) {
735                    if (isNotInOrderComparedToPrev()) {
736                        sorted = false;
vendor: 25,722 bytes, lines 737-1496
737                    }
738                    prev = cur;
739                }
740            }
741            meta._sorted = sorted;
742        }
743        if (_stacked) {
744            updateStacks(this, parsed);
745        }
746    }
747 parsePrimitiveData(meta, data, start, count) {
748        const { iScale , vScale  } = meta;
749        const iAxis = iScale.axis;
750        const vAxis = vScale.axis;
751        const labels = iScale.getLabels();
752        const singleScale = iScale === vScale;
753        const parsed = new Array(count);
754        let i, ilen, index;
755        for(i = 0, ilen = count; i < ilen; ++i){
756            index = i + start;
757            parsed[i] = {
758                [iAxis]: singleScale || iScale.parse(labels[index], index),
759                [vAxis]: vScale.parse(data[index], index)
760            };
761        }
762        return parsed;
763    }
764 parseArrayData(meta, data, start, count) {
765        const { xScale , yScale  } = meta;
766        const parsed = new Array(count);
767        let i, ilen, index, item;
768        for(i = 0, ilen = count; i < ilen; ++i){
769            index = i + start;
770            item = data[index];
771            parsed[i] = {
772                x: xScale.parse(item[0], index),
773                y: yScale.parse(item[1], index)
774            };
775        }
776        return parsed;
777    }
778 parseObjectData(meta, data, start, count) {
779        const { xScale , yScale  } = meta;
780        const { xAxisKey ='x' , yAxisKey ='y'  } = this._parsing;
781        const parsed = new Array(count);
782        let i, ilen, index, item;
783        for(i = 0, ilen = count; i < ilen; ++i){
784            index = i + start;
785            item = data[index];
786            parsed[i] = {
787                x: xScale.parse(resolveObjectKey(item, xAxisKey), index),
788                y: yScale.parse(resolveObjectKey(item, yAxisKey), index)
789            };
790        }
791        return parsed;
792    }
793 getParsed(index) {
794        return this._cachedMeta._parsed[index];
795    }
796 getDataElement(index) {
797        return this._cachedMeta.data[index];
798    }
799 applyStack(scale, parsed, mode) {
800        const chart = this.chart;
801        const meta = this._cachedMeta;
802        const value = parsed[scale.axis];
803        const stack = {
804            keys: getSortedDatasetIndices(chart, true),
805            values: parsed._stacks[scale.axis]._visualValues
806        };
807        return applyStack(stack, value, meta.index, {
808            mode
809        });
810    }
811 updateRangeFromParsed(range, scale, parsed, stack) {
812        const parsedValue = parsed[scale.axis];
813        let value = parsedValue === null ? NaN : parsedValue;
814        const values = stack && parsed._stacks[scale.axis];
815        if (stack && values) {
816            stack.values = values;
817            value = applyStack(stack, parsedValue, this._cachedMeta.index);
818        }
819        range.min = Math.min(range.min, value);
820        range.max = Math.max(range.max, value);
821    }
822 getMinMax(scale, canStack) {
823        const meta = this._cachedMeta;
824        const _parsed = meta._parsed;
825        const sorted = meta._sorted && scale === meta.iScale;
826        const ilen = _parsed.length;
827        const otherScale = this._getOtherScale(scale);
828        const stack = createStack(canStack, meta, this.chart);
829        const range = {
830            min: Number.POSITIVE_INFINITY,
831            max: Number.NEGATIVE_INFINITY
832        };
833        const { min: otherMin , max: otherMax  } = getUserBounds(otherScale);
834        let i, parsed;
835        function _skip() {
836            parsed = _parsed[i];
837            const otherValue = parsed[otherScale.axis];
838            return !isNumberFinite(parsed[scale.axis]) || otherMin > otherValue || otherMax < otherValue;
839        }
840        for(i = 0; i < ilen; ++i){
841            if (_skip()) {
842                continue;
843            }
844            this.updateRangeFromParsed(range, scale, parsed, stack);
845            if (sorted) {
846                break;
847            }
848        }
849        if (sorted) {
850            for(i = ilen - 1; i >= 0; --i){
851                if (_skip()) {
852                    continue;
853                }
854                this.updateRangeFromParsed(range, scale, parsed, stack);
855                break;
856            }
857        }
858        return range;
859    }
860    getAllParsedValues(scale) {
861        const parsed = this._cachedMeta._parsed;
862        const values = [];
863        let i, ilen, value;
864        for(i = 0, ilen = parsed.length; i < ilen; ++i){
865            value = parsed[i][scale.axis];
866            if (isNumberFinite(value)) {
867                values.push(value);
868            }
869        }
870        return values;
871    }
872 getMaxOverflow() {
873        return false;
874    }
875 getLabelAndValue(index) {
876        const meta = this._cachedMeta;
877        const iScale = meta.iScale;
878        const vScale = meta.vScale;
879        const parsed = this.getParsed(index);
880        return {
881            label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.axis]) : '',
882            value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.axis]) : ''
883        };
884    }
885 _update(mode) {
886        const meta = this._cachedMeta;
887        this.update(mode || 'default');
888        meta._clip = toClip(valueOrDefault(this.options.clip, defaultClip(meta.xScale, meta.yScale, this.getMaxOverflow())));
889    }
890 update(mode) {}
891    draw() {
892        const ctx = this._ctx;
893        const chart = this.chart;
894        const meta = this._cachedMeta;
895        const elements = meta.data || [];
896        const area = chart.chartArea;
897        const active = [];
898        const start = this._drawStart || 0;
899        const count = this._drawCount || elements.length - start;
900        const drawActiveElementsOnTop = this.options.drawActiveElementsOnTop;
901        let i;
902        if (meta.dataset) {
903            meta.dataset.draw(ctx, area, start, count);
904        }
905        for(i = start; i < start + count; ++i){
906            const element = elements[i];
907            if (element.hidden) {
908                continue;
909            }
910            if (element.active && drawActiveElementsOnTop) {
911                active.push(element);
912            } else {
913                element.draw(ctx, area);
914            }
915        }
916        for(i = 0; i < active.length; ++i){
917            active[i].draw(ctx, area);
918        }
919    }
920 getStyle(index, active) {
921        const mode = active ? 'active' : 'default';
922        return index === undefined && this._cachedMeta.dataset ? this.resolveDatasetElementOptions(mode) : this.resolveDataElementOptions(index || 0, mode);
923    }
924 getContext(index, active, mode) {
925        const dataset = this.getDataset();
926        let context;
927        if (index >= 0 && index < this._cachedMeta.data.length) {
928            const element = this._cachedMeta.data[index];
929            context = element.$context || (element.$context = createDataContext(this.getContext(), index, element));
930            context.parsed = this.getParsed(index);
931            context.raw = dataset.data[index];
932            context.index = context.dataIndex = index;
933        } else {
934            context = this.$context || (this.$context = createDatasetContext(this.chart.getContext(), this.index));
935            context.dataset = dataset;
936            context.index = context.datasetIndex = this.index;
937        }
938        context.active = !!active;
939        context.mode = mode;
940        return context;
941    }
942 resolveDatasetElementOptions(mode) {
943        return this._resolveElementOptions(this.datasetElementType.id, mode);
944    }
945 resolveDataElementOptions(index, mode) {
946        return this._resolveElementOptions(this.dataElementType.id, mode, index);
947    }
948 _resolveElementOptions(elementType, mode = 'default', index) {
949        const active = mode === 'active';
950        const cache = this._cachedDataOpts;
951        const cacheKey = elementType + '-' + mode;
952        const cached = cache[cacheKey];
953        const sharing = this.enableOptionSharing && defined(index);
954        if (cached) {
955            return cloneIfNotShared(cached, sharing);
956        }
957        const config = this.chart.config;
958        const scopeKeys = config.datasetElementScopeKeys(this._type, elementType);
959        const prefixes = active ? [
960            `${elementType}Hover`,
961            'hover',
962            elementType,
963            ''
964        ] : [
965            elementType,
966            ''
967        ];
968        const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);
969        const names = Object.keys(defaults.elements[elementType]);
970        const context = ()=>this.getContext(index, active, mode);
971        const values = config.resolveNamedOptions(scopes, names, context, prefixes);
972        if (values.$shared) {
973            values.$shared = sharing;
974            cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing));
975        }
976        return values;
977    }
978 _resolveAnimations(index, transition, active) {
979        const chart = this.chart;
980        const cache = this._cachedDataOpts;
981        const cacheKey = `animation-${transition}`;
982        const cached = cache[cacheKey];
983        if (cached) {
984            return cached;
985        }
986        let options;
987        if (chart.options.animation !== false) {
988            const config = this.chart.config;
989            const scopeKeys = config.datasetAnimationScopeKeys(this._type, transition);
990            const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);
991            options = config.createResolver(scopes, this.getContext(index, active, transition));
992        }
993        const animations = new Animations(chart, options && options.animations);
994        if (options && options._cacheable) {
995            cache[cacheKey] = Object.freeze(animations);
996        }
997        return animations;
998    }
999 getSharedOptions(options) {
1000        if (!options.$shared) {
1001            return;
1002        }
1003        return this._sharedOptions || (this._sharedOptions = Object.assign({}, options));
1004    }
1005 includeOptions(mode, sharedOptions) {
1006        return !sharedOptions || isDirectUpdateMode(mode) || this.chart._animationsDisabled;
1007    }
1008 _getSharedOptions(start, mode) {
1009        const firstOpts = this.resolveDataElementOptions(start, mode);
1010        const previouslySharedOptions = this._sharedOptions;
1011        const sharedOptions = this.getSharedOptions(firstOpts);
1012        const includeOptions = this.includeOptions(mode, sharedOptions) || sharedOptions !== previouslySharedOptions;
1013        this.updateSharedOptions(sharedOptions, mode, firstOpts);
1014        return {
1015            sharedOptions,
1016            includeOptions
1017        };
1018    }
1019 updateElement(element, index, properties, mode) {
1020        if (isDirectUpdateMode(mode)) {
1021            Object.assign(element, properties);
1022        } else {
1023            this._resolveAnimations(index, mode).update(element, properties);
1024        }
1025    }
1026 updateSharedOptions(sharedOptions, mode, newOptions) {
1027        if (sharedOptions && !isDirectUpdateMode(mode)) {
1028            this._resolveAnimations(undefined, mode).update(sharedOptions, newOptions);
1029        }
1030    }
1031 _setStyle(element, index, mode, active) {
1032        element.active = active;
1033        const options = this.getStyle(index, active);
1034        this._resolveAnimations(index, mode, active).update(element, {
1035            options: !active && this.getSharedOptions(options) || options
1036        });
1037    }
1038    removeHoverStyle(element, datasetIndex, index) {
1039        this._setStyle(element, index, 'active', false);
1040    }
1041    setHoverStyle(element, datasetIndex, index) {
1042        this._setStyle(element, index, 'active', true);
1043    }
1044 _removeDatasetHoverStyle() {
1045        const element = this._cachedMeta.dataset;
1046        if (element) {
1047            this._setStyle(element, undefined, 'active', false);
1048        }
1049    }
1050 _setDatasetHoverStyle() {
1051        const element = this._cachedMeta.dataset;
1052        if (element) {
1053            this._setStyle(element, undefined, 'active', true);
1054        }
1055    }
1056 _resyncElements(resetNewElements) {
1057        const data = this._data;
1058        const elements = this._cachedMeta.data;
1059        for (const [method, arg1, arg2] of this._syncList){
1060            this[method](arg1, arg2);
1061        }
1062        this._syncList = [];
1063        const numMeta = elements.length;
1064        const numData = data.length;
1065        const count = Math.min(numData, numMeta);
1066        if (count) {
1067            this.parse(0, count);
1068        }
1069        if (numData > numMeta) {
1070            this._insertElements(numMeta, numData - numMeta, resetNewElements);
1071        } else if (numData < numMeta) {
1072            this._removeElements(numData, numMeta - numData);
1073        }
1074    }
1075 _insertElements(start, count, resetNewElements = true) {
1076        const meta = this._cachedMeta;
1077        const data = meta.data;
1078        const end = start + count;
1079        let i;
1080        const move = (arr)=>{
1081            arr.length += count;
1082            for(i = arr.length - 1; i >= end; i--){
1083                arr[i] = arr[i - count];
1084            }
1085        };
1086        move(data);
1087        for(i = start; i < end; ++i){
1088            data[i] = new this.dataElementType();
1089        }
1090        if (this._parsing) {
1091            move(meta._parsed);
1092        }
1093        this.parse(start, count);
1094        if (resetNewElements) {
1095            this.updateElements(data, start, count, 'reset');
1096        }
1097    }
1098    updateElements(element, start, count, mode) {}
1099 _removeElements(start, count) {
1100        const meta = this._cachedMeta;
1101        if (this._parsing) {
1102            const removed = meta._parsed.splice(start, count);
1103            if (meta._stacked) {
1104                clearStacks(meta, removed);
1105            }
1106        }
1107        meta.data.splice(start, count);
1108    }
1109 _sync(args) {
1110        if (this._parsing) {
1111            this._syncList.push(args);
1112        } else {
1113            const [method, arg1, arg2] = args;
1114            this[method](arg1, arg2);
1115        }
1116        this.chart._dataChanges.push([
1117            this.index,
1118            ...args
1119        ]);
1120    }
1121    _onDataPush() {
1122        const count = arguments.length;
1123        this._sync([
1124            '_insertElements',
1125            this.getDataset().data.length - count,
1126            count
1127        ]);
1128    }
1129    _onDataPop() {
1130        this._sync([
1131            '_removeElements',
1132            this._cachedMeta.data.length - 1,
1133            1
1134        ]);
1135    }
1136    _onDataShift() {
1137        this._sync([
1138            '_removeElements',
1139            0,
1140            1
1141        ]);
1142    }
1143    _onDataSplice(start, count) {
1144        if (count) {
1145            this._sync([
1146                '_removeElements',
1147                start,
1148                count
1149            ]);
1150        }
1151        const newCount = arguments.length - 2;
1152        if (newCount) {
1153            this._sync([
1154                '_insertElements',
1155                start,
1156                newCount
1157            ]);
1158        }
1159    }
1160    _onDataUnshift() {
1161        this._sync([
1162            '_insertElements',
1163            0,
1164            arguments.length
1165        ]);
1166    }
1167}
1168
1169function getAllScaleValues(scale, type) {
1170    if (!scale._cache.$bar) {
1171        const visibleMetas = scale.getMatchingVisibleMetas(type);
1172        let values = [];
1173        for(let i = 0, ilen = visibleMetas.length; i < ilen; i++){
1174            values = values.concat(visibleMetas[i].controller.getAllParsedValues(scale));
1175        }
1176        scale._cache.$bar = _arrayUnique(values.sort((a, b)=>a - b));
1177    }
1178    return scale._cache.$bar;
1179}
1180 function computeMinSampleSize(meta) {
1181    const scale = meta.iScale;
1182    const values = getAllScaleValues(scale, meta.type);
1183    let min = scale._length;
1184    let i, ilen, curr, prev;
1185    const updateMinAndPrev = ()=>{
1186        if (curr === 32767 || curr === -32768) {
1187            return;
1188        }
1189        if (defined(prev)) {
1190            min = Math.min(min, Math.abs(curr - prev) || min);
1191        }
1192        prev = curr;
1193    };
1194    for(i = 0, ilen = values.length; i < ilen; ++i){
1195        curr = scale.getPixelForValue(values[i]);
1196        updateMinAndPrev();
1197    }
1198    prev = undefined;
1199    for(i = 0, ilen = scale.ticks.length; i < ilen; ++i){
1200        curr = scale.getPixelForTick(i);
1201        updateMinAndPrev();
1202    }
1203    return min;
1204}
1205 function computeFitCategoryTraits(index, ruler, options, stackCount) {
1206    const thickness = options.barThickness;
1207    let size, ratio;
1208    if (isNullOrUndef(thickness)) {
1209        size = ruler.min * options.categoryPercentage;
1210        ratio = options.barPercentage;
1211    } else {
1212        size = thickness * stackCount;
1213        ratio = 1;
1214    }
1215    return {
1216        chunk: size / stackCount,
1217        ratio,
1218        start: ruler.pixels[index] - size / 2
1219    };
1220}
1221 function computeFlexCategoryTraits(index, ruler, options, stackCount) {
1222    const pixels = ruler.pixels;
1223    const curr = pixels[index];
1224    let prev = index > 0 ? pixels[index - 1] : null;
1225    let next = index < pixels.length - 1 ? pixels[index + 1] : null;
1226    const percent = options.categoryPercentage;
1227    if (prev === null) {
1228        prev = curr - (next === null ? ruler.end - ruler.start : next - curr);
1229    }
1230    if (next === null) {
1231        next = curr + curr - prev;
1232    }
1233    const start = curr - (curr - Math.min(prev, next)) / 2 * percent;
1234    const size = Math.abs(next - prev) / 2 * percent;
1235    return {
1236        chunk: size / stackCount,
1237        ratio: options.barPercentage,
1238        start
1239    };
1240}
1241function parseFloatBar(entry, item, vScale, i) {
1242    const startValue = vScale.parse(entry[0], i);
1243    const endValue = vScale.parse(entry[1], i);
1244    const min = Math.min(startValue, endValue);
1245    const max = Math.max(startValue, endValue);
1246    let barStart = min;
1247    let barEnd = max;
1248    if (Math.abs(min) > Math.abs(max)) {
1249        barStart = max;
1250        barEnd = min;
1251    }
1252    item[vScale.axis] = barEnd;
1253    item._custom = {
1254        barStart,
1255        barEnd,
1256        start: startValue,
1257        end: endValue,
1258        min,
1259        max
1260    };
1261}
1262function parseValue(entry, item, vScale, i) {
1263    if (isArray(entry)) {
1264        parseFloatBar(entry, item, vScale, i);
1265    } else {
1266        item[vScale.axis] = vScale.parse(entry, i);
1267    }
1268    return item;
1269}
1270function parseArrayOrPrimitive(meta, data, start, count) {
1271    const iScale = meta.iScale;
1272    const vScale = meta.vScale;
1273    const labels = iScale.getLabels();
1274    const singleScale = iScale === vScale;
1275    const parsed = [];
1276    let i, ilen, item, entry;
1277    for(i = start, ilen = start + count; i < ilen; ++i){
1278        entry = data[i];
1279        item = {};
1280        item[iScale.axis] = singleScale || iScale.parse(labels[i], i);
1281        parsed.push(parseValue(entry, item, vScale, i));
1282    }
1283    return parsed;
1284}
1285function isFloatBar(custom) {
1286    return custom && custom.barStart !== undefined && custom.barEnd !== undefined;
1287}
1288function barSign(size, vScale, actualBase) {
1289    if (size !== 0) {
1290        return sign(size);
1291    }
1292    return (vScale.isHorizontal() ? 1 : -1) * (vScale.min >= actualBase ? 1 : -1);
1293}
1294function borderProps(properties) {
1295    let reverse, start, end, top, bottom;
1296    if (properties.horizontal) {
1297        reverse = properties.base > properties.x;
1298        start = 'left';
1299        end = 'right';
1300    } else {
1301        reverse = properties.base < properties.y;
1302        start = 'bottom';
1303        end = 'top';
1304    }
1305    if (reverse) {
1306        top = 'end';
1307        bottom = 'start';
1308    } else {
1309        top = 'start';
1310        bottom = 'end';
1311    }
1312    return {
1313        start,
1314        end,
1315        reverse,
1316        top,
1317        bottom
1318    };
1319}
1320function setBorderSkipped(properties, options, stack, index) {
1321    let edge = options.borderSkipped;
1322    const res = {};
1323    if (!edge) {
1324        properties.borderSkipped = res;
1325        return;
1326    }
1327    if (edge === true) {
1328        properties.borderSkipped = {
1329            top: true,
1330            right: true,
1331            bottom: true,
1332            left: true
1333        };
1334        return;
1335    }
1336    const { start , end , reverse , top , bottom  } = borderProps(properties);
1337    if (edge === 'middle' && stack) {
1338        properties.enableBorderRadius = true;
1339        if ((stack._top || 0) === index) {
1340            edge = top;
1341        } else if ((stack._bottom || 0) === index) {
1342            edge = bottom;
1343        } else {
1344            res[parseEdge(bottom, start, end, reverse)] = true;
1345            edge = top;
1346        }
1347    }
1348    res[parseEdge(edge, start, end, reverse)] = true;
1349    properties.borderSkipped = res;
1350}
1351function parseEdge(edge, a, b, reverse) {
1352    if (reverse) {
1353        edge = swap(edge, a, b);
1354        edge = startEnd(edge, b, a);
1355    } else {
1356        edge = startEnd(edge, a, b);
1357    }
1358    return edge;
1359}
1360function swap(orig, v1, v2) {
1361    return orig === v1 ? v2 : orig === v2 ? v1 : orig;
1362}
1363function startEnd(v, start, end) {
1364    return v === 'start' ? start : v === 'end' ? end : v;
1365}
1366function setInflateAmount(properties, { inflateAmount  }, ratio) {
1367    properties.inflateAmount = inflateAmount === 'auto' ? ratio === 1 ? 0.33 : 0 : inflateAmount;
1368}
1369class BarController extends DatasetController {
1370    static id = 'bar';
1371 static defaults = {
1372        datasetElementType: false,
1373        dataElementType: 'bar',
1374        categoryPercentage: 0.8,
1375        barPercentage: 0.9,
1376        grouped: true,
1377        animations: {
1378            numbers: {
1379                type: 'number',
1380                properties: [
1381                    'x',
1382                    'y',
1383                    'base',
1384                    'width',
1385                    'height'
1386                ]
1387            }
1388        }
1389    };
1390 static overrides = {
1391        scales: {
1392            _index_: {
1393                type: 'category',
1394                offset: true,
1395                grid: {
1396                    offset: true
1397                }
1398            },
1399            _value_: {
1400                type: 'linear',
1401                beginAtZero: true
1402            }
1403        }
1404    };
1405 parsePrimitiveData(meta, data, start, count) {
1406        return parseArrayOrPrimitive(meta, data, start, count);
1407    }
1408 parseArrayData(meta, data, start, count) {
1409        return parseArrayOrPrimitive(meta, data, start, count);
1410    }
1411 parseObjectData(meta, data, start, count) {
1412        const { iScale , vScale  } = meta;
1413        const { xAxisKey ='x' , yAxisKey ='y'  } = this._parsing;
1414        const iAxisKey = iScale.axis === 'x' ? xAxisKey : yAxisKey;
1415        const vAxisKey = vScale.axis === 'x' ? xAxisKey : yAxisKey;
1416        const parsed = [];
1417        let i, ilen, item, obj;
1418        for(i = start, ilen = start + count; i < ilen; ++i){
1419            obj = data[i];
1420            item = {};
1421            item[iScale.axis] = iScale.parse(resolveObjectKey(obj, iAxisKey), i);
1422            parsed.push(parseValue(resolveObjectKey(obj, vAxisKey), item, vScale, i));
1423        }
1424        return parsed;
1425    }
1426 updateRangeFromParsed(range, scale, parsed, stack) {
1427        super.updateRangeFromParsed(range, scale, parsed, stack);
1428        const custom = parsed._custom;
1429        if (custom && scale === this._cachedMeta.vScale) {
1430            range.min = Math.min(range.min, custom.min);
1431            range.max = Math.max(range.max, custom.max);
1432        }
1433    }
1434 getMaxOverflow() {
1435        return 0;
1436    }
1437 getLabelAndValue(index) {
1438        const meta = this._cachedMeta;
1439        const { iScale , vScale  } = meta;
1440        const parsed = this.getParsed(index);
1441        const custom = parsed._custom;
1442        const value = isFloatBar(custom) ? '[' + custom.start + ', ' + custom.end + ']' : '' + vScale.getLabelForValue(parsed[vScale.axis]);
1443        return {
1444            label: '' + iScale.getLabelForValue(parsed[iScale.axis]),
1445            value
1446        };
1447    }
1448    initialize() {
1449        this.enableOptionSharing = true;
1450        super.initialize();
1451        const meta = this._cachedMeta;
1452        meta.stack = this.getDataset().stack;
1453    }
1454    update(mode) {
1455        const meta = this._cachedMeta;
1456        this.updateElements(meta.data, 0, meta.data.length, mode);
1457    }
1458    updateElements(bars, start, count, mode) {
1459        const reset = mode === 'reset';
1460        const { index , _cachedMeta: { vScale  }  } = this;
1461        const base = vScale.getBasePixel();
1462        const horizontal = vScale.isHorizontal();
1463        const ruler = this._getRuler();
1464        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);
1465        for(let i = start; i < start + count; i++){
1466            const parsed = this.getParsed(i);
1467            const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {
1468                base,
1469                head: base
1470            } : this._calculateBarValuePixels(i);
1471            const ipixels = this._calculateBarIndexPixels(i, ruler);
1472            const stack = (parsed._stacks || {})[vScale.axis];
1473            const properties = {
1474                horizontal,
1475                base: vpixels.base,
1476                enableBorderRadius: !stack || isFloatBar(parsed._custom) || index === stack._top || index === stack._bottom,
1477                x: horizontal ? vpixels.head : ipixels.center,
1478                y: horizontal ? ipixels.center : vpixels.head,
1479                height: horizontal ? ipixels.size : Math.abs(vpixels.size),
1480                width: horizontal ? Math.abs(vpixels.size) : ipixels.size
1481            };
1482            if (includeOptions) {
1483                properties.options = sharedOptions || this.resolveDataElementOptions(i, bars[i].active ? 'active' : mode);
1484            }
1485            const options = properties.options || bars[i].options;
1486            setBorderSkipped(properties, options, stack, index);
1487            setInflateAmount(properties, options, ruler.ratio);
1488            this.updateElement(bars[i], i, properties, mode);
1489        }
1490    }
1491 _getStacks(last, dataIndex) {
1492        const { iScale  } = this._cachedMeta;
1493        const metasets = iScale.getMatchingVisibleMetas(this._type).filter((meta)=>meta.controller.options.grouped);
1494        const stacked = iScale.options.stacked;
1495        const stacks = [];
1496        const 
1496currentParsed = this._cachedMeta.controller.getParsed(dataIndex);
1497        const iScaleValue = currentParsed && currentParsed[iScale.axis];
1498        const skipNull = (meta)=>{
1499            const parsed = meta._parsed.find((item)=>item[iScale.axis] === iScaleValue
vendor: 5,501 bytes, lines 1499-1638
1499);
1500            const val = parsed && parsed[meta.vScale.axis];
1501            if (isNullOrUndef(val) || isNaN(val)) {
1502                return true;
1503            }
1504        };
1505        for (const meta of metasets){
1506            if (dataIndex !== undefined && skipNull(meta)) {
1507                continue;
1508            }
1509            if (stacked === false || stacks.indexOf(meta.stack) === -1 || stacked === undefined && meta.stack === undefined) {
1510                stacks.push(meta.stack);
1511            }
1512            if (meta.index === last) {
1513                break;
1514            }
1515        }
1516        if (!stacks.length) {
1517            stacks.push(undefined);
1518        }
1519        return stacks;
1520    }
1521 _getStackCount(index) {
1522        return this._getStacks(undefined, index).length;
1523    }
1524 _getStackIndex(datasetIndex, name, dataIndex) {
1525        const stacks = this._getStacks(datasetIndex, dataIndex);
1526        const index = name !== undefined ? stacks.indexOf(name) : -1;
1527        return index === -1 ? stacks.length - 1 : index;
1528    }
1529 _getRuler() {
1530        const opts = this.options;
1531        const meta = this._cachedMeta;
1532        const iScale = meta.iScale;
1533        const pixels = [];
1534        let i, ilen;
1535        for(i = 0, ilen = meta.data.length; i < ilen; ++i){
1536            pixels.push(iScale.getPixelForValue(this.getParsed(i)[iScale.axis], i));
1537        }
1538        const barThickness = opts.barThickness;
1539        const min = barThickness || computeMinSampleSize(meta);
1540        return {
1541            min,
1542            pixels,
1543            start: iScale._startPixel,
1544            end: iScale._endPixel,
1545            stackCount: this._getStackCount(),
1546            scale: iScale,
1547            grouped: opts.grouped,
1548            ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage
1549        };
1550    }
1551 _calculateBarValuePixels(index) {
1552        const { _cachedMeta: { vScale , _stacked , index: datasetIndex  } , options: { base: baseValue , minBarLength  }  } = this;
1553        const actualBase = baseValue || 0;
1554        const parsed = this.getParsed(index);
1555        const custom = parsed._custom;
1556        const floating = isFloatBar(custom);
1557        let value = parsed[vScale.axis];
1558        let start = 0;
1559        let length = _stacked ? this.applyStack(vScale, parsed, _stacked) : value;
1560        let head, size;
1561        if (length !== value) {
1562            start = length - value;
1563            length = value;
1564        }
1565        if (floating) {
1566            value = custom.barStart;
1567            length = custom.barEnd - custom.barStart;
1568            if (value !== 0 && sign(value) !== sign(custom.barEnd)) {
1569                start = 0;
1570            }
1571            start += value;
1572        }
1573        const startValue = !isNullOrUndef(baseValue) && !floating ? baseValue : start;
1574        let base = vScale.getPixelForValue(startValue);
1575        if (this.chart.getDataVisibility(index)) {
1576            head = vScale.getPixelForValue(start + length);
1577        } else {
1578            head = base;
1579        }
1580        size = head - base;
1581        if (Math.abs(size) < minBarLength) {
1582            size = barSign(size, vScale, actualBase) * minBarLength;
1583            if (value === actualBase) {
1584                base -= size / 2;
1585            }
1586            const startPixel = vScale.getPixelForDecimal(0);
1587            const endPixel = vScale.getPixelForDecimal(1);
1588            const min = Math.min(startPixel, endPixel);
1589            const max = Math.max(startPixel, endPixel);
1590            base = Math.max(Math.min(base, max), min);
1591            head = base + size;
1592            if (_stacked && !floating) {
1593                parsed._stacks[vScale.axis]._visualValues[datasetIndex] = vScale.getValueForPixel(head) - vScale.getValueForPixel(base);
1594            }
1595        }
1596        if (base === vScale.getPixelForValue(actualBase)) {
1597            const halfGrid = sign(size) * vScale.getLineWidthForValue(actualBase) / 2;
1598            base += halfGrid;
1599            size -= halfGrid;
1600        }
1601        return {
1602            size,
1603            base,
1604            head,
1605            center: head + size / 2
1606        };
1607    }
1608 _calculateBarIndexPixels(index, ruler) {
1609        const scale = ruler.scale;
1610        const options = this.options;
1611        const skipNull = options.skipNull;
1612        const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity);
1613        let center, size;
1614        if (ruler.grouped) {
1615            const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount;
1616            const range = options.barThickness === 'flex' ? computeFlexCategoryTraits(index, ruler, options, stackCount) : computeFitCategoryTraits(index, ruler, options, stackCount);
1617            const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined);
1618            center = range.start + range.chunk * stackIndex + range.chunk / 2;
1619            size = Math.min(maxBarThickness, range.chunk * range.ratio);
1620        } else {
1621            center = scale.getPixelForValue(this.getParsed(index)[scale.axis], index);
1622            size = Math.min(maxBarThickness, ruler.min * ruler.ratio);
1623        }
1624        return {
1625            base: center - size / 2,
1626            head: center + size / 2,
1627            center,
1628            size
1629        };
1630    }
1631    draw() {
1632        const meta = this._cachedMeta;
1633        const vScale = meta.vScale;
1634        const rects = meta.data;
1635        const ilen = rects.length;
1636        let i = 0;
1637        for(; i < ilen; ++i){
1638            if (this.getParsed(i)[vScale.axis] !== null
1638 && !rects[i].hidden
vendor: 27,670 bytes, lines 1638-2387
1638) {
1639                rects[i].draw(this._ctx);
1640            }
1641        }
1642    }
1643}
1644
1645class BubbleController extends DatasetController {
1646    static id = 'bubble';
1647 static defaults = {
1648        datasetElementType: false,
1649        dataElementType: 'point',
1650        animations: {
1651            numbers: {
1652                type: 'number',
1653                properties: [
1654                    'x',
1655                    'y',
1656                    'borderWidth',
1657                    'radius'
1658                ]
1659            }
1660        }
1661    };
1662 static overrides = {
1663        scales: {
1664            x: {
1665                type: 'linear'
1666            },
1667            y: {
1668                type: 'linear'
1669            }
1670        }
1671    };
1672    initialize() {
1673        this.enableOptionSharing = true;
1674        super.initialize();
1675    }
1676 parsePrimitiveData(meta, data, start, count) {
1677        const parsed = super.parsePrimitiveData(meta, data, start, count);
1678        for(let i = 0; i < parsed.length; i++){
1679            parsed[i]._custom = this.resolveDataElementOptions(i + start).radius;
1680        }
1681        return parsed;
1682    }
1683 parseArrayData(meta, data, start, count) {
1684        const parsed = super.parseArrayData(meta, data, start, count);
1685        for(let i = 0; i < parsed.length; i++){
1686            const item = data[start + i];
1687            parsed[i]._custom = valueOrDefault(item[2], this.resolveDataElementOptions(i + start).radius);
1688        }
1689        return parsed;
1690    }
1691 parseObjectData(meta, data, start, count) {
1692        const parsed = super.parseObjectData(meta, data, start, count);
1693        for(let i = 0; i < parsed.length; i++){
1694            const item = data[start + i];
1695            parsed[i]._custom = valueOrDefault(item && item.r && +item.r, this.resolveDataElementOptions(i + start).radius);
1696        }
1697        return parsed;
1698    }
1699 getMaxOverflow() {
1700        const data = this._cachedMeta.data;
1701        let max = 0;
1702        for(let i = data.length - 1; i >= 0; --i){
1703            max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);
1704        }
1705        return max > 0 && max;
1706    }
1707 getLabelAndValue(index) {
1708        const meta = this._cachedMeta;
1709        const labels = this.chart.data.labels || [];
1710        const { xScale , yScale  } = meta;
1711        const parsed = this.getParsed(index);
1712        const x = xScale.getLabelForValue(parsed.x);
1713        const y = yScale.getLabelForValue(parsed.y);
1714        const r = parsed._custom;
1715        return {
1716            label: labels[index] || '',
1717            value: '(' + x + ', ' + y + (r ? ', ' + r : '') + ')'
1718        };
1719    }
1720    update(mode) {
1721        const points = this._cachedMeta.data;
1722        this.updateElements(points, 0, points.length, mode);
1723    }
1724    updateElements(points, start, count, mode) {
1725        const reset = mode === 'reset';
1726        const { iScale , vScale  } = this._cachedMeta;
1727        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);
1728        const iAxis = iScale.axis;
1729        const vAxis = vScale.axis;
1730        for(let i = start; i < start + count; i++){
1731            const point = points[i];
1732            const parsed = !reset && this.getParsed(i);
1733            const properties = {};
1734            const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]);
1735            const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]);
1736            properties.skip = isNaN(iPixel) || isNaN(vPixel);
1737            if (includeOptions) {
1738                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);
1739                if (reset) {
1740                    properties.options.radius = 0;
1741                }
1742            }
1743            this.updateElement(point, i, properties, mode);
1744        }
1745    }
1746 resolveDataElementOptions(index, mode) {
1747        const parsed = this.getParsed(index);
1748        let values = super.resolveDataElementOptions(index, mode);
1749        if (values.$shared) {
1750            values = Object.assign({}, values, {
1751                $shared: false
1752            });
1753        }
1754        const radius = values.radius;
1755        if (mode !== 'active') {
1756            values.radius = 0;
1757        }
1758        values.radius += valueOrDefault(parsed && parsed._custom, radius);
1759        return values;
1760    }
1761}
1762
1763function getRatioAndOffset(rotation, circumference, cutout) {
1764    let ratioX = 1;
1765    let ratioY = 1;
1766    let offsetX = 0;
1767    let offsetY = 0;
1768    if (circumference < TAU) {
1769        const startAngle = rotation;
1770        const endAngle = startAngle + circumference;
1771        const startX = Math.cos(startAngle);
1772        const startY = Math.sin(startAngle);
1773        const endX = Math.cos(endAngle);
1774        const endY = Math.sin(endAngle);
1775        const calcMax = (angle, a, b)=>_angleBetween(angle, startAngle, endAngle, true) ? 1 : Math.max(a, a * cutout, b, b * cutout);
1776        const calcMin = (angle, a, b)=>_angleBetween(angle, startAngle, endAngle, true) ? -1 : Math.min(a, a * cutout, b, b * cutout);
1777        const maxX = calcMax(0, startX, endX);
1778        const maxY = calcMax(HALF_PI, startY, endY);
1779        const minX = calcMin(PI, startX, endX);
1780        const minY = calcMin(PI + HALF_PI, startY, endY);
1781        ratioX = (maxX - minX) / 2;
1782        ratioY = (maxY - minY) / 2;
1783        offsetX = -(maxX + minX) / 2;
1784        offsetY = -(maxY + minY) / 2;
1785    }
1786    return {
1787        ratioX,
1788        ratioY,
1789        offsetX,
1790        offsetY
1791    };
1792}
1793class DoughnutController extends DatasetController {
1794    static id = 'doughnut';
1795 static defaults = {
1796        datasetElementType: false,
1797        dataElementType: 'arc',
1798        animation: {
1799            animateRotate: true,
1800            animateScale: false
1801        },
1802        animations: {
1803            numbers: {
1804                type: 'number',
1805                properties: [
1806                    'circumference',
1807                    'endAngle',
1808                    'innerRadius',
1809                    'outerRadius',
1810                    'startAngle',
1811                    'x',
1812                    'y',
1813                    'offset',
1814                    'borderWidth',
1815                    'spacing'
1816                ]
1817            }
1818        },
1819        cutout: '50%',
1820        rotation: 0,
1821        circumference: 360,
1822        radius: '100%',
1823        spacing: 0,
1824        indexAxis: 'r'
1825    };
1826    static descriptors = {
1827        _scriptable: (name)=>name !== 'spacing',
1828        _indexable: (name)=>name !== 'spacing' && !name.startsWith('borderDash') && !name.startsWith('hoverBorderDash')
1829    };
1830 static overrides = {
1831        aspectRatio: 1,
1832        plugins: {
1833            legend: {
1834                labels: {
1835                    generateLabels (chart) {
1836                        const data = chart.data;
1837                        if (data.labels.length && data.datasets.length) {
1838                            const { labels: { pointStyle , color  }  } = chart.legend.options;
1839                            return data.labels.map((label, i)=>{
1840                                const meta = chart.getDatasetMeta(0);
1841                                const style = meta.controller.getStyle(i);
1842                                return {
1843                                    text: label,
1844                                    fillStyle: style.backgroundColor,
1845                                    strokeStyle: style.borderColor,
1846                                    fontColor: color,
1847                                    lineWidth: style.borderWidth,
1848                                    pointStyle: pointStyle,
1849                                    hidden: !chart.getDataVisibility(i),
1850                                    index: i
1851                                };
1852                            });
1853                        }
1854                        return [];
1855                    }
1856                },
1857                onClick (e, legendItem, legend) {
1858                    legend.chart.toggleDataVisibility(legendItem.index);
1859                    legend.chart.update();
1860                }
1861            }
1862        }
1863    };
1864    constructor(chart, datasetIndex){
1865        super(chart, datasetIndex);
1866        this.enableOptionSharing = true;
1867        this.innerRadius = undefined;
1868        this.outerRadius = undefined;
1869        this.offsetX = undefined;
1870        this.offsetY = undefined;
1871    }
1872    linkScales() {}
1873 parse(start, count) {
1874        const data = this.getDataset().data;
1875        const meta = this._cachedMeta;
1876        if (this._parsing === false) {
1877            meta._parsed = data;
1878        } else {
1879            let getter = (i)=>+data[i];
1880            if (isObject(data[start])) {
1881                const { key ='value'  } = this._parsing;
1882                getter = (i)=>+resolveObjectKey(data[i], key);
1883            }
1884            let i, ilen;
1885            for(i = start, ilen = start + count; i < ilen; ++i){
1886                meta._parsed[i] = getter(i);
1887            }
1888        }
1889    }
1890 _getRotation() {
1891        return toRadians(this.options.rotation - 90);
1892    }
1893 _getCircumference() {
1894        return toRadians(this.options.circumference);
1895    }
1896 _getRotationExtents() {
1897        let min = TAU;
1898        let max = -TAU;
1899        for(let i = 0; i < this.chart.data.datasets.length; ++i){
1900            if (this.chart.isDatasetVisible(i) && this.chart.getDatasetMeta(i).type === this._type) {
1901                const controller = this.chart.getDatasetMeta(i).controller;
1902                const rotation = controller._getRotation();
1903                const circumference = controller._getCircumference();
1904                min = Math.min(min, rotation);
1905                max = Math.max(max, rotation + circumference);
1906            }
1907        }
1908        return {
1909            rotation: min,
1910            circumference: max - min
1911        };
1912    }
1913 update(mode) {
1914        const chart = this.chart;
1915        const { chartArea  } = chart;
1916        const meta = this._cachedMeta;
1917        const arcs = meta.data;
1918        const spacing = this.getMaxBorderWidth() + this.getMaxOffset(arcs) + this.options.spacing;
1919        const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0);
1920        const cutout = Math.min(toPercentage(this.options.cutout, maxSize), 1);
1921        const chartWeight = this._getRingWeight(this.index);
1922        const { circumference , rotation  } = this._getRotationExtents();
1923        const { ratioX , ratioY , offsetX , offsetY  } = getRatioAndOffset(rotation, circumference, cutout);
1924        const maxWidth = (chartArea.width - spacing) / ratioX;
1925        const maxHeight = (chartArea.height - spacing) / ratioY;
1926        const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
1927        const outerRadius = toDimension(this.options.radius, maxRadius);
1928        const innerRadius = Math.max(outerRadius * cutout, 0);
1929        const radiusLength = (outerRadius - innerRadius) / this._getVisibleDatasetWeightTotal();
1930        this.offsetX = offsetX * outerRadius;
1931        this.offsetY = offsetY * outerRadius;
1932        meta.total = this.calculateTotal();
1933        this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index);
1934        this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0);
1935        this.updateElements(arcs, 0, arcs.length, mode);
1936    }
1937 _circumference(i, reset) {
1938        const opts = this.options;
1939        const meta = this._cachedMeta;
1940        const circumference = this._getCircumference();
1941        if (reset && opts.animation.animateRotate || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) {
1942            return 0;
1943        }
1944        return this.calculateCircumference(meta._parsed[i] * circumference / TAU);
1945    }
1946    updateElements(arcs, start, count, mode) {
1947        const reset = mode === 'reset';
1948        const chart = this.chart;
1949        const chartArea = chart.chartArea;
1950        const opts = chart.options;
1951        const animationOpts = opts.animation;
1952        const centerX = (chartArea.left + chartArea.right) / 2;
1953        const centerY = (chartArea.top + chartArea.bottom) / 2;
1954        const animateScale = reset && animationOpts.animateScale;
1955        const innerRadius = animateScale ? 0 : this.innerRadius;
1956        const outerRadius = animateScale ? 0 : this.outerRadius;
1957        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);
1958        let startAngle = this._getRotation();
1959        let i;
1960        for(i = 0; i < start; ++i){
1961            startAngle += this._circumference(i, reset);
1962        }
1963        for(i = start; i < start + count; ++i){
1964            const circumference = this._circumference(i, reset);
1965            const arc = arcs[i];
1966            const properties = {
1967                x: centerX + this.offsetX,
1968                y: centerY + this.offsetY,
1969                startAngle,
1970                endAngle: startAngle + circumference,
1971                circumference,
1972                outerRadius,
1973                innerRadius
1974            };
1975            if (includeOptions) {
1976                properties.options = sharedOptions || this.resolveDataElementOptions(i, arc.active ? 'active' : mode);
1977            }
1978            startAngle += circumference;
1979            this.updateElement(arc, i, properties, mode);
1980        }
1981    }
1982    calculateTotal() {
1983        const meta = this._cachedMeta;
1984        const metaData = meta.data;
1985        let total = 0;
1986        let i;
1987        for(i = 0; i < metaData.length; i++){
1988            const value = meta._parsed[i];
1989            if (value !== null && !isNaN(value) && this.chart.getDataVisibility(i) && !metaData[i].hidden) {
1990                total += Math.abs(value);
1991            }
1992        }
1993        return total;
1994    }
1995    calculateCircumference(value) {
1996        const total = this._cachedMeta.total;
1997        if (total > 0 && !isNaN(value)) {
1998            return TAU * (Math.abs(value) / total);
1999        }
2000        return 0;
2001    }
2002    getLabelAndValue(index) {
2003        const meta = this._cachedMeta;
2004        const chart = this.chart;
2005        const labels = chart.data.labels || [];
2006        const value = formatNumber(meta._parsed[index], chart.options.locale);
2007        return {
2008            label: labels[index] || '',
2009            value
2010        };
2011    }
2012    getMaxBorderWidth(arcs) {
2013        let max = 0;
2014        const chart = this.chart;
2015        let i, ilen, meta, controller, options;
2016        if (!arcs) {
2017            for(i = 0, ilen = chart.data.datasets.length; i < ilen; ++i){
2018                if (chart.isDatasetVisible(i)) {
2019                    meta = chart.getDatasetMeta(i);
2020                    arcs = meta.data;
2021                    controller = meta.controller;
2022                    break;
2023                }
2024            }
2025        }
2026        if (!arcs) {
2027            return 0;
2028        }
2029        for(i = 0, ilen = arcs.length; i < ilen; ++i){
2030            options = controller.resolveDataElementOptions(i);
2031            if (options.borderAlign !== 'inner') {
2032                max = Math.max(max, options.borderWidth || 0, options.hoverBorderWidth || 0);
2033            }
2034        }
2035        return max;
2036    }
2037    getMaxOffset(arcs) {
2038        let max = 0;
2039        for(let i = 0, ilen = arcs.length; i < ilen; ++i){
2040            const options = this.resolveDataElementOptions(i);
2041            max = Math.max(max, options.offset || 0, options.hoverOffset || 0);
2042        }
2043        return max;
2044    }
2045 _getRingWeightOffset(datasetIndex) {
2046        let ringWeightOffset = 0;
2047        for(let i = 0; i < datasetIndex; ++i){
2048            if (this.chart.isDatasetVisible(i)) {
2049                ringWeightOffset += this._getRingWeight(i);
2050            }
2051        }
2052        return ringWeightOffset;
2053    }
2054 _getRingWeight(datasetIndex) {
2055        return Math.max(valueOrDefault(this.chart.data.datasets[datasetIndex].weight, 1), 0);
2056    }
2057 _getVisibleDatasetWeightTotal() {
2058        return this._getRingWeightOffset(this.chart.data.datasets.length) || 1;
2059    }
2060}
2061
2062class LineController extends DatasetController {
2063    static id = 'line';
2064 static defaults = {
2065        datasetElementType: 'line',
2066        dataElementType: 'point',
2067        showLine: true,
2068        spanGaps: false
2069    };
2070 static overrides = {
2071        scales: {
2072            _index_: {
2073                type: 'category'
2074            },
2075            _value_: {
2076                type: 'linear'
2077            }
2078        }
2079    };
2080    initialize() {
2081        this.enableOptionSharing = true;
2082        this.supportsDecimation = true;
2083        super.initialize();
2084    }
2085    update(mode) {
2086        const meta = this._cachedMeta;
2087        const { dataset: line , data: points = [] , _dataset  } = meta;
2088        const animationsDisabled = this.chart._animationsDisabled;
2089        let { start , count  } = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
2090        this._drawStart = start;
2091        this._drawCount = count;
2092        if (_scaleRangesChanged(meta)) {
2093            start = 0;
2094            count = points.length;
2095        }
2096        line._chart = this.chart;
2097        line._datasetIndex = this.index;
2098        line._decimated = !!_dataset._decimated;
2099        line.points = points;
2100        const options = this.resolveDatasetElementOptions(mode);
2101        if (!this.options.showLine) {
2102            options.borderWidth = 0;
2103        }
2104        options.segment = this.options.segment;
2105        this.updateElement(line, undefined, {
2106            animated: !animationsDisabled,
2107            options
2108        }, mode);
2109        this.updateElements(points, start, count, mode);
2110    }
2111    updateElements(points, start, count, mode) {
2112        const reset = mode === 'reset';
2113        const { iScale , vScale , _stacked , _dataset  } = this._cachedMeta;
2114        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);
2115        const iAxis = iScale.axis;
2116        const vAxis = vScale.axis;
2117        const { spanGaps , segment  } = this.options;
2118        const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
2119        const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';
2120        const end = start + count;
2121        const pointsCount = points.length;
2122        let prevParsed = start > 0 && this.getParsed(start - 1);
2123        for(let i = 0; i < pointsCount; ++i){
2124            const point = points[i];
2125            const properties = directUpdate ? point : {};
2126            if (i < start || i >= end) {
2127                properties.skip = true;
2128                continue;
2129            }
2130            const parsed = this.getParsed(i);
2131            const nullData = isNullOrUndef(parsed[vAxis]);
2132            const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);
2133            const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);
2134            properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;
2135            properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;
2136            if (segment) {
2137                properties.parsed = parsed;
2138                properties.raw = _dataset.data[i];
2139            }
2140            if (includeOptions) {
2141                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);
2142            }
2143            if (!directUpdate) {
2144                this.updateElement(point, i, properties, mode);
2145            }
2146            prevParsed = parsed;
2147        }
2148    }
2149 getMaxOverflow() {
2150        const meta = this._cachedMeta;
2151        const dataset = meta.dataset;
2152        const border = dataset.options && dataset.options.borderWidth || 0;
2153        const data = meta.data || [];
2154        if (!data.length) {
2155            return border;
2156        }
2157        const firstPoint = data[0].size(this.resolveDataElementOptions(0));
2158        const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));
2159        return Math.max(border, firstPoint, lastPoint) / 2;
2160    }
2161    draw() {
2162        const meta = this._cachedMeta;
2163        meta.dataset.updateControlPoints(this.chart.chartArea, meta.iScale.axis);
2164        super.draw();
2165    }
2166}
2167
2168class PolarAreaController extends DatasetController {
2169    static id = 'polarArea';
2170 static defaults = {
2171        dataElementType: 'arc',
2172        animation: {
2173            animateRotate: true,
2174            animateScale: true
2175        },
2176        animations: {
2177            numbers: {
2178                type: 'number',
2179                properties: [
2180                    'x',
2181                    'y',
2182                    'startAngle',
2183                    'endAngle',
2184                    'innerRadius',
2185                    'outerRadius'
2186                ]
2187            }
2188        },
2189        indexAxis: 'r',
2190        startAngle: 0
2191    };
2192 static overrides = {
2193        aspectRatio: 1,
2194        plugins: {
2195            legend: {
2196                labels: {
2197                    generateLabels (chart) {
2198                        const data = chart.data;
2199                        if (data.labels.length && data.datasets.length) {
2200                            const { labels: { pointStyle , color  }  } = chart.legend.options;
2201                            return data.labels.map((label, i)=>{
2202                                const meta = chart.getDatasetMeta(0);
2203                                const style = meta.controller.getStyle(i);
2204                                return {
2205                                    text: label,
2206                                    fillStyle: style.backgroundColor,
2207                                    strokeStyle: style.borderColor,
2208                                    fontColor: color,
2209                                    lineWidth: style.borderWidth,
2210                                    pointStyle: pointStyle,
2211                                    hidden: !chart.getDataVisibility(i),
2212                                    index: i
2213                                };
2214                            });
2215                        }
2216                        return [];
2217                    }
2218                },
2219                onClick (e, legendItem, legend) {
2220                    legend.chart.toggleDataVisibility(legendItem.index);
2221                    legend.chart.update();
2222                }
2223            }
2224        },
2225        scales: {
2226            r: {
2227                type: 'radialLinear',
2228                angleLines: {
2229                    display: false
2230                },
2231                beginAtZero: true,
2232                grid: {
2233                    circular: true
2234                },
2235                pointLabels: {
2236                    display: false
2237                },
2238                startAngle: 0
2239            }
2240        }
2241    };
2242    constructor(chart, datasetIndex){
2243        super(chart, datasetIndex);
2244        this.innerRadius = undefined;
2245        this.outerRadius = undefined;
2246    }
2247    getLabelAndValue(index) {
2248        const meta = this._cachedMeta;
2249        const chart = this.chart;
2250        const labels = chart.data.labels || [];
2251        const value = formatNumber(meta._parsed[index].r, chart.options.locale);
2252        return {
2253            label: labels[index] || '',
2254            value
2255        };
2256    }
2257    parseObjectData(meta, data, start, count) {
2258        return _parseObjectDataRadialScale.bind(this)(meta, data, start, count);
2259    }
2260    update(mode) {
2261        const arcs = this._cachedMeta.data;
2262        this._updateRadius();
2263        this.updateElements(arcs, 0, arcs.length, mode);
2264    }
2265 getMinMax() {
2266        const meta = this._cachedMeta;
2267        const range = {
2268            min: Number.POSITIVE_INFINITY,
2269            max: Number.NEGATIVE_INFINITY
2270        };
2271        meta.data.forEach((element, index)=>{
2272            const parsed = this.getParsed(index).r;
2273            if (!isNaN(parsed) && this.chart.getDataVisibility(index)) {
2274                if (parsed < range.min) {
2275                    range.min = parsed;
2276                }
2277                if (parsed > range.max) {
2278                    range.max = parsed;
2279                }
2280            }
2281        });
2282        return range;
2283    }
2284 _updateRadius() {
2285        const chart = this.chart;
2286        const chartArea = chart.chartArea;
2287        const opts = chart.options;
2288        const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
2289        const outerRadius = Math.max(minSize / 2, 0);
2290        const innerRadius = Math.max(opts.cutoutPercentage ? outerRadius / 100 * opts.cutoutPercentage : 1, 0);
2291        const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount();
2292        this.outerRadius = outerRadius - radiusLength * this.index;
2293        this.innerRadius = this.outerRadius - radiusLength;
2294    }
2295    updateElements(arcs, start, count, mode) {
2296        const reset = mode === 'reset';
2297        const chart = this.chart;
2298        const opts = chart.options;
2299        const animationOpts = opts.animation;
2300        const scale = this._cachedMeta.rScale;
2301        const centerX = scale.xCenter;
2302        const centerY = scale.yCenter;
2303        const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI;
2304        let angle = datasetStartAngle;
2305        let i;
2306        const defaultAngle = 360 / this.countVisibleElements();
2307        for(i = 0; i < start; ++i){
2308            angle += this._computeAngle(i, mode, defaultAngle);
2309        }
2310        for(i = start; i < start + count; i++){
2311            const arc = arcs[i];
2312            let startAngle = angle;
2313            let endAngle = angle + this._computeAngle(i, mode, defaultAngle);
2314            let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(this.getParsed(i).r) : 0;
2315            angle = endAngle;
2316            if (reset) {
2317                if (animationOpts.animateScale) {
2318                    outerRadius = 0;
2319                }
2320                if (animationOpts.animateRotate) {
2321                    startAngle = endAngle = datasetStartAngle;
2322                }
2323            }
2324            const properties = {
2325                x: centerX,
2326                y: centerY,
2327                innerRadius: 0,
2328                outerRadius,
2329                startAngle,
2330                endAngle,
2331                options: this.resolveDataElementOptions(i, arc.active ? 'active' : mode)
2332            };
2333            this.updateElement(arc, i, properties, mode);
2334        }
2335    }
2336    countVisibleElements() {
2337        const meta = this._cachedMeta;
2338        let count = 0;
2339        meta.data.forEach((element, index)=>{
2340            if (!isNaN(this.getParsed(index).r) && this.chart.getDataVisibility(index)) {
2341                count++;
2342            }
2343        });
2344        return count;
2345    }
2346 _computeAngle(index, mode, defaultAngle) {
2347        return this.chart.getDataVisibility(index) ? toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle) : 0;
2348    }
2349}
2350
2351class PieController extends DoughnutController {
2352    static id = 'pie';
2353 static defaults = {
2354        cutout: 0,
2355        rotation: 0,
2356        circumference: 360,
2357        radius: '100%'
2358    };
2359}
2360
2361class RadarController extends DatasetController {
2362    static id = 'radar';
2363 static defaults = {
2364        datasetElementType: 'line',
2365        dataElementType: 'point',
2366        indexAxis: 'r',
2367        showLine: true,
2368        elements: {
2369            line: {
2370                fill: 'start'
2371            }
2372        }
2373    };
2374 static overrides = {
2375        aspectRatio: 1,
2376        scales: {
2377            r: {
2378                type: 'radialLinear'
2379            }
2380        }
2381    };
2382 getLabelAndValue(index) {
2383        const vScale = this._cachedMeta.vScale;
2384        const parsed = this.getParsed(index);
2385        return {
2386            label: vScale.getLabels()[index],
2387            value: '' + vScale.getLabelForValue(parsed[vScale.axis
vendor: 20,186 bytes, lines 2387-2933
2387])
2388        };
2389    }
2390    parseObjectData(meta, data, start, count) {
2391        return _parseObjectDataRadialScale.bind(this)(meta, data, start, count);
2392    }
2393    update(mode) {
2394        const meta = this._cachedMeta;
2395        const line = meta.dataset;
2396        const points = meta.data || [];
2397        const labels = meta.iScale.getLabels();
2398        line.points = points;
2399        if (mode !== 'resize') {
2400            const options = this.resolveDatasetElementOptions(mode);
2401            if (!this.options.showLine) {
2402                options.borderWidth = 0;
2403            }
2404            const properties = {
2405                _loop: true,
2406                _fullLoop: labels.length === points.length,
2407                options
2408            };
2409            this.updateElement(line, undefined, properties, mode);
2410        }
2411        this.updateElements(points, 0, points.length, mode);
2412    }
2413    updateElements(points, start, count, mode) {
2414        const scale = this._cachedMeta.rScale;
2415        const reset = mode === 'reset';
2416        for(let i = start; i < start + count; i++){
2417            const point = points[i];
2418            const options = this.resolveDataElementOptions(i, point.active ? 'active' : mode);
2419            const pointPosition = scale.getPointPositionForValue(i, this.getParsed(i).r);
2420            const x = reset ? scale.xCenter : pointPosition.x;
2421            const y = reset ? scale.yCenter : pointPosition.y;
2422            const properties = {
2423                x,
2424                y,
2425                angle: pointPosition.angle,
2426                skip: isNaN(x) || isNaN(y),
2427                options
2428            };
2429            this.updateElement(point, i, properties, mode);
2430        }
2431    }
2432}
2433
2434class ScatterController extends DatasetController {
2435    static id = 'scatter';
2436 static defaults = {
2437        datasetElementType: false,
2438        dataElementType: 'point',
2439        showLine: false,
2440        fill: false
2441    };
2442 static overrides = {
2443        interaction: {
2444            mode: 'point'
2445        },
2446        scales: {
2447            x: {
2448                type: 'linear'
2449            },
2450            y: {
2451                type: 'linear'
2452            }
2453        }
2454    };
2455 getLabelAndValue(index) {
2456        const meta = this._cachedMeta;
2457        const labels = this.chart.data.labels || [];
2458        const { xScale , yScale  } = meta;
2459        const parsed = this.getParsed(index);
2460        const x = xScale.getLabelForValue(parsed.x);
2461        const y = yScale.getLabelForValue(parsed.y);
2462        return {
2463            label: labels[index] || '',
2464            value: '(' + x + ', ' + y + ')'
2465        };
2466    }
2467    update(mode) {
2468        const meta = this._cachedMeta;
2469        const { data: points = []  } = meta;
2470        const animationsDisabled = this.chart._animationsDisabled;
2471        let { start , count  } = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
2472        this._drawStart = start;
2473        this._drawCount = count;
2474        if (_scaleRangesChanged(meta)) {
2475            start = 0;
2476            count = points.length;
2477        }
2478        if (this.options.showLine) {
2479            if (!this.datasetElementType) {
2480                this.addElements();
2481            }
2482            const { dataset: line , _dataset  } = meta;
2483            line._chart = this.chart;
2484            line._datasetIndex = this.index;
2485            line._decimated = !!_dataset._decimated;
2486            line.points = points;
2487            const options = this.resolveDatasetElementOptions(mode);
2488            options.segment = this.options.segment;
2489            this.updateElement(line, undefined, {
2490                animated: !animationsDisabled,
2491                options
2492            }, mode);
2493        } else if (this.datasetElementType) {
2494            delete meta.dataset;
2495            this.datasetElementType = false;
2496        }
2497        this.updateElements(points, start, count, mode);
2498    }
2499    addElements() {
2500        const { showLine  } = this.options;
2501        if (!this.datasetElementType && showLine) {
2502            this.datasetElementType = this.chart.registry.getElement('line');
2503        }
2504        super.addElements();
2505    }
2506    updateElements(points, start, count, mode) {
2507        const reset = mode === 'reset';
2508        const { iScale , vScale , _stacked , _dataset  } = this._cachedMeta;
2509        const firstOpts = this.resolveDataElementOptions(start, mode);
2510        const sharedOptions = this.getSharedOptions(firstOpts);
2511        const includeOptions = this.includeOptions(mode, sharedOptions);
2512        const iAxis = iScale.axis;
2513        const vAxis = vScale.axis;
2514        const { spanGaps , segment  } = this.options;
2515        const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
2516        const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';
2517        let prevParsed = start > 0 && this.getParsed(start - 1);
2518        for(let i = start; i < start + count; ++i){
2519            const point = points[i];
2520            const parsed = this.getParsed(i);
2521            const properties = directUpdate ? point : {};
2522            const nullData = isNullOrUndef(parsed[vAxis]);
2523            const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);
2524            const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);
2525            properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;
2526            properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;
2527            if (segment) {
2528                properties.parsed = parsed;
2529                properties.raw = _dataset.data[i];
2530            }
2531            if (includeOptions) {
2532                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);
2533            }
2534            if (!directUpdate) {
2535                this.updateElement(point, i, properties, mode);
2536            }
2537            prevParsed = parsed;
2538        }
2539        this.updateSharedOptions(sharedOptions, mode, firstOpts);
2540    }
2541 getMaxOverflow() {
2542        const meta = this._cachedMeta;
2543        const data = meta.data || [];
2544        if (!this.options.showLine) {
2545            let max = 0;
2546            for(let i = data.length - 1; i >= 0; --i){
2547                max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);
2548            }
2549            return max > 0 && max;
2550        }
2551        const dataset = meta.dataset;
2552        const border = dataset.options && dataset.options.borderWidth || 0;
2553        if (!data.length) {
2554            return border;
2555        }
2556        const firstPoint = data[0].size(this.resolveDataElementOptions(0));
2557        const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));
2558        return Math.max(border, firstPoint, lastPoint) / 2;
2559    }
2560}
2561
2562var controllers = /*#__PURE__*/Object.freeze({
2563__proto__: null,
2564BarController: BarController,
2565BubbleController: BubbleController,
2566DoughnutController: DoughnutController,
2567LineController: LineController,
2568PieController: PieController,
2569PolarAreaController: PolarAreaController,
2570RadarController: RadarController,
2571ScatterController: ScatterController
2572});
2573
2574/**
2575 * @namespace Chart._adapters
2576 * @since 2.8.0
2577 * @private
2578 */ function abstract() {
2579    throw new Error('This method is not implemented: Check that a complete date adapter is provided.');
2580}
2581/**
2582 * Date adapter (current used by the time scale)
2583 * @namespace Chart._adapters._date
2584 * @memberof Chart._adapters
2585 * @private
2586 */ class DateAdapterBase {
2587    /**
2588   * Override default date adapter methods.
2589   * Accepts type parameter to define options type.
2590   * @example
2591   * Chart._adapters._date.override<{myAdapterOption: string}>({
2592   *   init() {
2593   *     console.log(this.options.myAdapterOption);
2594   *   }
2595   * })
2596   */ static override(members) {
2597        Object.assign(DateAdapterBase.prototype, members);
2598    }
2599    options;
2600    constructor(options){
2601        this.options = options || {};
2602    }
2603    // eslint-disable-next-line @typescript-eslint/no-empty-function
2604    init() {}
2605    formats() {
2606        return abstract();
2607    }
2608    parse() {
2609        return abstract();
2610    }
2611    format() {
2612        return abstract();
2613    }
2614    add() {
2615        return abstract();
2616    }
2617    diff() {
2618        return abstract();
2619    }
2620    startOf() {
2621        return abstract();
2622    }
2623    endOf() {
2624        return abstract();
2625    }
2626}
2627var adapters = {
2628    _date: DateAdapterBase
2629};
2630
2631function binarySearch(metaset, axis, value, intersect) {
2632    const { controller , data , _sorted  } = metaset;
2633    const iScale = controller._cachedMeta.iScale;
2634    const spanGaps = metaset.dataset ? metaset.dataset.options ? metaset.dataset.options.spanGaps : null : null;
2635    if (iScale && axis === iScale.axis && axis !== 'r' && _sorted && data.length) {
2636        const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey;
2637        if (!intersect) {
2638            const result = lookupMethod(data, axis, value);
2639            if (spanGaps) {
2640                const { vScale  } = controller._cachedMeta;
2641                const { _parsed  } = metaset;
2642                const distanceToDefinedLo = _parsed.slice(0, result.lo + 1).reverse().findIndex((point)=>!isNullOrUndef(point[vScale.axis]));
2643                result.lo -= Math.max(0, distanceToDefinedLo);
2644                const distanceToDefinedHi = _parsed.slice(result.hi).findIndex((point)=>!isNullOrUndef(point[vScale.axis]));
2645                result.hi += Math.max(0, distanceToDefinedHi);
2646            }
2647            return result;
2648        } else if (controller._sharedOptions) {
2649            const el = data[0];
2650            const range = typeof el.getRange === 'function' && el.getRange(axis);
2651            if (range) {
2652                const start = lookupMethod(data, axis, value - range);
2653                const end = lookupMethod(data, axis, value + range);
2654                return {
2655                    lo: start.lo,
2656                    hi: end.hi
2657                };
2658            }
2659        }
2660    }
2661    return {
2662        lo: 0,
2663        hi: data.length - 1
2664    };
2665}
2666 function evaluateInteractionItems(chart, axis, position, handler, intersect) {
2667    const metasets = chart.getSortedVisibleDatasetMetas();
2668    const value = position[axis];
2669    for(let i = 0, ilen = metasets.length; i < ilen; ++i){
2670        const { index , data  } = metasets[i];
2671        const { lo , hi  } = binarySearch(metasets[i], axis, value, intersect);
2672        for(let j = lo; j <= hi; ++j){
2673            const element = data[j];
2674            if (!element.skip) {
2675                handler(element, index, j);
2676            }
2677        }
2678    }
2679}
2680 function getDistanceMetricForAxis(axis) {
2681    const useX = axis.indexOf('x') !== -1;
2682    const useY = axis.indexOf('y') !== -1;
2683    return function(pt1, pt2) {
2684        const deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
2685        const deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
2686        return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
2687    };
2688}
2689 function getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) {
2690    const items = [];
2691    if (!includeInvisible && !chart.isPointInArea(position)) {
2692        return items;
2693    }
2694    const evaluationFunc = function(element, datasetIndex, index) {
2695        if (!includeInvisible && !_isPointInArea(element, chart.chartArea, 0)) {
2696            return;
2697        }
2698        if (element.inRange(position.x, position.y, useFinalPosition)) {
2699            items.push({
2700                element,
2701                datasetIndex,
2702                index
2703            });
2704        }
2705    };
2706    evaluateInteractionItems(chart, axis, position, evaluationFunc, true);
2707    return items;
2708}
2709 function getNearestRadialItems(chart, position, axis, useFinalPosition) {
2710    let items = [];
2711    function evaluationFunc(element, datasetIndex, index) {
2712        const { startAngle , endAngle  } = element.getProps([
2713            'startAngle',
2714            'endAngle'
2715        ], useFinalPosition);
2716        const { angle  } = getAngleFromPoint(element, {
2717            x: position.x,
2718            y: position.y
2719        });
2720        if (_angleBetween(angle, startAngle, endAngle)) {
2721            items.push({
2722                element,
2723                datasetIndex,
2724                index
2725            });
2726        }
2727    }
2728    evaluateInteractionItems(chart, axis, position, evaluationFunc);
2729    return items;
2730}
2731 function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {
2732    let items = [];
2733    const distanceMetric = getDistanceMetricForAxis(axis);
2734    let minDistance = Number.POSITIVE_INFINITY;
2735    function evaluationFunc(element, datasetIndex, index) {
2736        const inRange = element.inRange(position.x, position.y, useFinalPosition);
2737        if (intersect && !inRange) {
2738            return;
2739        }
2740        const center = element.getCenterPoint(useFinalPosition);
2741        const pointInArea = !!includeInvisible || chart.isPointInArea(center);
2742        if (!pointInArea && !inRange) {
2743            return;
2744        }
2745        const distance = distanceMetric(position, center);
2746        if (distance < minDistance) {
2747            items = [
2748                {
2749                    element,
2750                    datasetIndex,
2751                    index
2752                }
2753            ];
2754            minDistance = distance;
2755        } else if (distance === minDistance) {
2756            items.push({
2757                element,
2758                datasetIndex,
2759                index
2760            });
2761        }
2762    }
2763    evaluateInteractionItems(chart, axis, position, evaluationFunc);
2764    return items;
2765}
2766 function getNearestItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {
2767    if (!includeInvisible && !chart.isPointInArea(position)) {
2768        return [];
2769    }
2770    return axis === 'r' && !intersect ? getNearestRadialItems(chart, position, axis, useFinalPosition) : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible);
2771}
2772 function getAxisItems(chart, position, axis, intersect, useFinalPosition) {
2773    const items = [];
2774    const rangeMethod = axis === 'x' ? 'inXRange' : 'inYRange';
2775    let intersectsItem = false;
2776    evaluateInteractionItems(chart, axis, position, (element, datasetIndex, index)=>{
2777        if (element[rangeMethod] && element[rangeMethod](position[axis], useFinalPosition)) {
2778            items.push({
2779                element,
2780                datasetIndex,
2781                index
2782            });
2783            intersectsItem = intersectsItem || element.inRange(position.x, position.y, useFinalPosition);
2784        }
2785    });
2786    if (intersect && !intersectsItem) {
2787        return [];
2788    }
2789    return items;
2790}
2791 var Interaction = {
2792    evaluateInteractionItems,
2793    modes: {
2794 index (chart, e, options, useFinalPosition) {
2795            const position = getRelativePosition(e, chart);
2796            const axis = options.axis || 'x';
2797            const includeInvisible = options.includeInvisible || false;
2798            const items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);
2799            const elements = [];
2800            if (!items.length) {
2801                return [];
2802            }
2803            chart.getSortedVisibleDatasetMetas().forEach((meta)=>{
2804                const index = items[0].index;
2805                const element = meta.data[index];
2806                if (element && !element.skip) {
2807                    elements.push({
2808                        element,
2809                        datasetIndex: meta.index,
2810                        index
2811                    });
2812                }
2813            });
2814            return elements;
2815        },
2816 dataset (chart, e, options, useFinalPosition) {
2817            const position = getRelativePosition(e, chart);
2818            const axis = options.axis || 'xy';
2819            const includeInvisible = options.includeInvisible || false;
2820            let items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);
2821            if (items.length > 0) {
2822                const datasetIndex = items[0].datasetIndex;
2823                const data = chart.getDatasetMeta(datasetIndex).data;
2824                items = [];
2825                for(let i = 0; i < data.length; ++i){
2826                    items.push({
2827                        element: data[i],
2828                        datasetIndex,
2829                        index: i
2830                    });
2831                }
2832            }
2833            return items;
2834        },
2835 point (chart, e, options, useFinalPosition) {
2836            const position = getRelativePosition(e, chart);
2837            const axis = options.axis || 'xy';
2838            const includeInvisible = options.includeInvisible || false;
2839            return getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible);
2840        },
2841 nearest (chart, e, options, useFinalPosition) {
2842            const position = getRelativePosition(e, chart);
2843            const axis = options.axis || 'xy';
2844            const includeInvisible = options.includeInvisible || false;
2845            return getNearestItems(chart, position, axis, options.intersect, useFinalPosition, includeInvisible);
2846        },
2847 x (chart, e, options, useFinalPosition) {
2848            const position = getRelativePosition(e, chart);
2849            return getAxisItems(chart, position, 'x', options.intersect, useFinalPosition);
2850        },
2851 y (chart, e, options, useFinalPosition) {
2852            const position = getRelativePosition(e, chart);
2853            return getAxisItems(chart, position, 'y', options.intersect, useFinalPosition);
2854        }
2855    }
2856};
2857
2858const STATIC_POSITIONS = [
2859    'left',
2860    'top',
2861    'right',
2862    'bottom'
2863];
2864function filterByPosition(array, position) {
2865    return array.filter((v)=>v.pos === position);
2866}
2867function filterDynamicPositionByAxis(array, axis) {
2868    return array.filter((v)=>STATIC_POSITIONS.indexOf(v.pos) === -1 && v.box.axis === axis);
2869}
2870function sortByWeight(array, reverse) {
2871    return array.sort((a, b)=>{
2872        const v0 = reverse ? b : a;
2873        const v1 = reverse ? a : b;
2874        return v0.weight === v1.weight ? v0.index - v1.index : v0.weight - v1.weight;
2875    });
2876}
2877function wrapBoxes(boxes) {
2878    const layoutBoxes = [];
2879    let i, ilen, box, pos, stack, stackWeight;
2880    for(i = 0, ilen = (boxes || []).length; i < ilen; ++i){
2881        box = boxes[i];
2882        ({ position: pos , options: { stack , stackWeight =1  }  } = box);
2883        layoutBoxes.push({
2884            index: i,
2885            box,
2886            pos,
2887            horizontal: box.isHorizontal(),
2888            weight: box.weight,
2889            stack: stack && pos + stack,
2890            stackWeight
2891        });
2892    }
2893    return layoutBoxes;
2894}
2895function buildStacks(layouts) {
2896    const stacks = {};
2897    for (const wrap of layouts){
2898        const { stack , pos , stackWeight  } = wrap;
2899        if (!stack || !STATIC_POSITIONS.includes(pos)) {
2900            continue;
2901        }
2902        const _stack = stacks[stack] || (stacks[stack] = {
2903            count: 0,
2904            placed: 0,
2905            weight: 0,
2906            size: 0
2907        });
2908        _stack.count++;
2909        _stack.weight += stackWeight;
2910    }
2911    return stacks;
2912}
2913 function setLayoutDims(layouts, params) {
2914    const stacks = buildStacks(layouts);
2915    const { vBoxMaxWidth , hBoxMaxHeight  } = params;
2916    let i, ilen, layout;
2917    for(i = 0, ilen = layouts.length; i < ilen; ++i){
2918        layout = layouts[i];
2919        const { fullSize  } = layout.box;
2920        const stack = stacks[layout.stack];
2921        const factor = stack && layout.stackWeight / stack.weight;
2922        if (layout.horizontal) {
2923            layout.width = factor ? factor * vBoxMaxWidth : fullSize && params.availableWidth;
2924            layout.height = hBoxMaxHeight;
2925        } else {
2926            layout.width = vBoxMaxWidth;
2927            layout.height = factor ? factor * hBoxMaxHeight : fullSize && params.availableHeight;
2928        }
2929    }
2930    return stacks;
2931}
2932function buildLayoutBoxes(boxes) {
2933    const layoutBoxes = wrapBoxes(boxes);
vendor: 11,843 bytes, lines 2934-3272
2934    const fullSize = sortByWeight(layoutBoxes.filter((wrap)=>wrap.box.fullSize), true);
2935    const left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);
2936    const right = sortByWeight(filterByPosition(layoutBoxes, 'right'));
2937    const top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);
2938    const bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom'));
2939    const centerHorizontal = filterDynamicPositionByAxis(layoutBoxes, 'x');
2940    const centerVertical = filterDynamicPositionByAxis(layoutBoxes, 'y');
2941    return {
2942        fullSize,
2943        leftAndTop: left.concat(top),
2944        rightAndBottom: right.concat(centerVertical).concat(bottom).concat(centerHorizontal),
2945        chartArea: filterByPosition(layoutBoxes, 'chartArea'),
2946        vertical: left.concat(right).concat(centerVertical),
2947        horizontal: top.concat(bottom).concat(centerHorizontal)
2948    };
2949}
2950function getCombinedMax(maxPadding, chartArea, a, b) {
2951    return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);
2952}
2953function updateMaxPadding(maxPadding, boxPadding) {
2954    maxPadding.top = Math.max(maxPadding.top, boxPadding.top);
2955    maxPadding.left = Math.max(maxPadding.left, boxPadding.left);
2956    maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);
2957    maxPadding.right = Math.max(maxPadding.right, boxPadding.right);
2958}
2959function updateDims(chartArea, params, layout, stacks) {
2960    const { pos , box  } = layout;
2961    const maxPadding = chartArea.maxPadding;
2962    if (!isObject(pos)) {
2963        if (layout.size) {
2964            chartArea[pos] -= layout.size;
2965        }
2966        const stack = stacks[layout.stack] || {
2967            size: 0,
2968            count: 1
2969        };
2970        stack.size = Math.max(stack.size, layout.horizontal ? box.height : box.width);
2971        layout.size = stack.size / stack.count;
2972        chartArea[pos] += layout.size;
2973    }
2974    if (box.getPadding) {
2975        updateMaxPadding(maxPadding, box.getPadding());
2976    }
2977    const newWidth = Math.max(0, params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right'));
2978    const newHeight = Math.max(0, params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom'));
2979    const widthChanged = newWidth !== chartArea.w;
2980    const heightChanged = newHeight !== chartArea.h;
2981    chartArea.w = newWidth;
2982    chartArea.h = newHeight;
2983    return layout.horizontal ? {
2984        same: widthChanged,
2985        other: heightChanged
2986    } : {
2987        same: heightChanged,
2988        other: widthChanged
2989    };
2990}
2991function handleMaxPadding(chartArea) {
2992    const maxPadding = chartArea.maxPadding;
2993    function updatePos(pos) {
2994        const change = Math.max(maxPadding[pos] - chartArea[pos], 0);
2995        chartArea[pos] += change;
2996        return change;
2997    }
2998    chartArea.y += updatePos('top');
2999    chartArea.x += updatePos('left');
3000    updatePos('right');
3001    updatePos('bottom');
3002}
3003function getMargins(horizontal, chartArea) {
3004    const maxPadding = chartArea.maxPadding;
3005    function marginForPositions(positions) {
3006        const margin = {
3007            left: 0,
3008            top: 0,
3009            right: 0,
3010            bottom: 0
3011        };
3012        positions.forEach((pos)=>{
3013            margin[pos] = Math.max(chartArea[pos], maxPadding[pos]);
3014        });
3015        return margin;
3016    }
3017    return horizontal ? marginForPositions([
3018        'left',
3019        'right'
3020    ]) : marginForPositions([
3021        'top',
3022        'bottom'
3023    ]);
3024}
3025function fitBoxes(boxes, chartArea, params, stacks) {
3026    const refitBoxes = [];
3027    let i, ilen, layout, box, refit, changed;
3028    for(i = 0, ilen = boxes.length, refit = 0; i < ilen; ++i){
3029        layout = boxes[i];
3030        box = layout.box;
3031        box.update(layout.width || chartArea.w, layout.height || chartArea.h, getMargins(layout.horizontal, chartArea));
3032        const { same , other  } = updateDims(chartArea, params, layout, stacks);
3033        refit |= same && refitBoxes.length;
3034        changed = changed || other;
3035        if (!box.fullSize) {
3036            refitBoxes.push(layout);
3037        }
3038    }
3039    return refit && fitBoxes(refitBoxes, chartArea, params, stacks) || changed;
3040}
3041function setBoxDims(box, left, top, width, height) {
3042    box.top = top;
3043    box.left = left;
3044    box.right = left + width;
3045    box.bottom = top + height;
3046    box.width = width;
3047    box.height = height;
3048}
3049function placeBoxes(boxes, chartArea, params, stacks) {
3050    const userPadding = params.padding;
3051    let { x , y  } = chartArea;
3052    for (const layout of boxes){
3053        const box = layout.box;
3054        const stack = stacks[layout.stack] || {
3055            count: 1,
3056            placed: 0,
3057            weight: 1
3058        };
3059        const weight = layout.stackWeight / stack.weight || 1;
3060        if (layout.horizontal) {
3061            const width = chartArea.w * weight;
3062            const height = stack.size || box.height;
3063            if (defined(stack.start)) {
3064                y = stack.start;
3065            }
3066            if (box.fullSize) {
3067                setBoxDims(box, userPadding.left, y, params.outerWidth - userPadding.right - userPadding.left, height);
3068            } else {
3069                setBoxDims(box, chartArea.left + stack.placed, y, width, height);
3070            }
3071            stack.start = y;
3072            stack.placed += width;
3073            y = box.bottom;
3074        } else {
3075            const height = chartArea.h * weight;
3076            const width = stack.size || box.width;
3077            if (defined(stack.start)) {
3078                x = stack.start;
3079            }
3080            if (box.fullSize) {
3081                setBoxDims(box, x, userPadding.top, width, params.outerHeight - userPadding.bottom - userPadding.top);
3082            } else {
3083                setBoxDims(box, x, chartArea.top + stack.placed, width, height);
3084            }
3085            stack.start = x;
3086            stack.placed += height;
3087            x = box.right;
3088        }
3089    }
3090    chartArea.x = x;
3091    chartArea.y = y;
3092}
3093var layouts = {
3094 addBox (chart, item) {
3095        if (!chart.boxes) {
3096            chart.boxes = [];
3097        }
3098        item.fullSize = item.fullSize || false;
3099        item.position = item.position || 'top';
3100        item.weight = item.weight || 0;
3101        item._layers = item._layers || function() {
3102            return [
3103                {
3104                    z: 0,
3105                    draw (chartArea) {
3106                        item.draw(chartArea);
3107                    }
3108                }
3109            ];
3110        };
3111        chart.boxes.push(item);
3112    },
3113 removeBox (chart, layoutItem) {
3114        const index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
3115        if (index !== -1) {
3116            chart.boxes.splice(index, 1);
3117        }
3118    },
3119 configure (chart, item, options) {
3120        item.fullSize = options.fullSize;
3121        item.position = options.position;
3122        item.weight = options.weight;
3123    },
3124 update (chart, width, height, minPadding) {
3125        if (!chart) {
3126            return;
3127        }
3128        const padding = toPadding(chart.options.layout.padding);
3129        const availableWidth = Math.max(width - padding.width, 0);
3130        const availableHeight = Math.max(height - padding.height, 0);
3131        const boxes = buildLayoutBoxes(chart.boxes);
3132        const verticalBoxes = boxes.vertical;
3133        const horizontalBoxes = boxes.horizontal;
3134        each(chart.boxes, (box)=>{
3135            if (typeof box.beforeLayout === 'function') {
3136                box.beforeLayout();
3137            }
3138        });
3139        const visibleVerticalBoxCount = verticalBoxes.reduce((total, wrap)=>wrap.box.options && wrap.box.options.display === false ? total : total + 1, 0) || 1;
3140        const params = Object.freeze({
3141            outerWidth: width,
3142            outerHeight: height,
3143            padding,
3144            availableWidth,
3145            availableHeight,
3146            vBoxMaxWidth: availableWidth / 2 / visibleVerticalBoxCount,
3147            hBoxMaxHeight: availableHeight / 2
3148        });
3149        const maxPadding = Object.assign({}, padding);
3150        updateMaxPadding(maxPadding, toPadding(minPadding));
3151        const chartArea = Object.assign({
3152            maxPadding,
3153            w: availableWidth,
3154            h: availableHeight,
3155            x: padding.left,
3156            y: padding.top
3157        }, padding);
3158        const stacks = setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);
3159        fitBoxes(boxes.fullSize, chartArea, params, stacks);
3160        fitBoxes(verticalBoxes, chartArea, params, stacks);
3161        if (fitBoxes(horizontalBoxes, chartArea, params, stacks)) {
3162            fitBoxes(verticalBoxes, chartArea, params, stacks);
3163        }
3164        handleMaxPadding(chartArea);
3165        placeBoxes(boxes.leftAndTop, chartArea, params, stacks);
3166        chartArea.x += chartArea.w;
3167        chartArea.y += chartArea.h;
3168        placeBoxes(boxes.rightAndBottom, chartArea, params, stacks);
3169        chart.chartArea = {
3170            left: chartArea.left,
3171            top: chartArea.top,
3172            right: chartArea.left + chartArea.w,
3173            bottom: chartArea.top + chartArea.h,
3174            height: chartArea.h,
3175            width: chartArea.w
3176        };
3177        each(boxes.chartArea, (layout)=>{
3178            const box = layout.box;
3179            Object.assign(box, chart.chartArea);
3180            box.update(chartArea.w, chartArea.h, {
3181                left: 0,
3182                top: 0,
3183                right: 0,
3184                bottom: 0
3185            });
3186        });
3187    }
3188};
3189
3190class BasePlatform {
3191 acquireContext(canvas, aspectRatio) {}
3192 releaseContext(context) {
3193        return false;
3194    }
3195 addEventListener(chart, type, listener) {}
3196 removeEventListener(chart, type, listener) {}
3197 getDevicePixelRatio() {
3198        return 1;
3199    }
3200 getMaximumSize(element, width, height, aspectRatio) {
3201        width = Math.max(0, width || element.width);
3202        height = height || element.height;
3203        return {
3204            width,
3205            height: Math.max(0, aspectRatio ? Math.floor(width / aspectRatio) : height)
3206        };
3207    }
3208 isAttached(canvas) {
3209        return true;
3210    }
3211 updateConfig(config) {
3212    }
3213}
3214
3215class BasicPlatform extends BasePlatform {
3216    acquireContext(item) {
3217        return item && item.getContext && item.getContext('2d') || null;
3218    }
3219    updateConfig(config) {
3220        config.options.animation = false;
3221    }
3222}
3223
3224const EXPANDO_KEY = '$chartjs';
3225 const EVENT_TYPES = {
3226    touchstart: 'mousedown',
3227    touchmove: 'mousemove',
3228    touchend: 'mouseup',
3229    pointerenter: 'mouseenter',
3230    pointerdown: 'mousedown',
3231    pointermove: 'mousemove',
3232    pointerup: 'mouseup',
3233    pointerleave: 'mouseout',
3234    pointerout: 'mouseout'
3235};
3236const isNullOrEmpty = (value)=>value === null || value === '';
3237 function initCanvas(canvas, aspectRatio) {
3238    const style = canvas.style;
3239    const renderHeight = canvas.getAttribute('height');
3240    const renderWidth = canvas.getAttribute('width');
3241    canvas[EXPANDO_KEY] = {
3242        initial: {
3243            height: renderHeight,
3244            width: renderWidth,
3245            style: {
3246                display: style.display,
3247                height: style.height,
3248                width: style.width
3249            }
3250        }
3251    };
3252    style.display = style.display || 'block';
3253    style.boxSizing = style.boxSizing || 'border-box';
3254    if (isNullOrEmpty(renderWidth)) {
3255        const displayWidth = readUsedSize(canvas, 'width');
3256        if (displayWidth !== undefined) {
3257            canvas.width = displayWidth;
3258        }
3259    }
3260    if (isNullOrEmpty(renderHeight)) {
3261        if (canvas.style.height === '') {
3262            canvas.height = canvas.width / (aspectRatio || 2);
3263        } else {
3264            const displayHeight = readUsedSize(canvas, 'height');
3265            if (displayHeight !== undefined) {
3266                canvas.height = displayHeight;
3267            }
3268        }
3269    }
3270    return canvas;
3271}
3272const eventListenerOptions = supportsEventListenerOptions ? {
vendor: 9,740 bytes, lines 3273-3582
3273    passive: true
3274} : false;
3275function addListener(node, type, listener) {
3276    if (node) {
3277        node.addEventListener(type, listener, eventListenerOptions);
3278    }
3279}
3280function removeListener(chart, type, listener) {
3281    if (chart && chart.canvas) {
3282        chart.canvas.removeEventListener(type, listener, eventListenerOptions);
3283    }
3284}
3285function fromNativeEvent(event, chart) {
3286    const type = EVENT_TYPES[event.type] || event.type;
3287    const { x , y  } = getRelativePosition(event, chart);
3288    return {
3289        type,
3290        chart,
3291        native: event,
3292        x: x !== undefined ? x : null,
3293        y: y !== undefined ? y : null
3294    };
3295}
3296function nodeListContains(nodeList, canvas) {
3297    for (const node of nodeList){
3298        if (node === canvas || node.contains(canvas)) {
3299            return true;
3300        }
3301    }
3302}
3303function createAttachObserver(chart, type, listener) {
3304    const canvas = chart.canvas;
3305    const observer = new MutationObserver((entries)=>{
3306        let trigger = false;
3307        for (const entry of entries){
3308            trigger = trigger || nodeListContains(entry.addedNodes, canvas);
3309            trigger = trigger && !nodeListContains(entry.removedNodes, canvas);
3310        }
3311        if (trigger) {
3312            listener();
3313        }
3314    });
3315    observer.observe(document, {
3316        childList: true,
3317        subtree: true
3318    });
3319    return observer;
3320}
3321function createDetachObserver(chart, type, listener) {
3322    const canvas = chart.canvas;
3323    const observer = new MutationObserver((entries)=>{
3324        let trigger = false;
3325        for (const entry of entries){
3326            trigger = trigger || nodeListContains(entry.removedNodes, canvas);
3327            trigger = trigger && !nodeListContains(entry.addedNodes, canvas);
3328        }
3329        if (trigger) {
3330            listener();
3331        }
3332    });
3333    observer.observe(document, {
3334        childList: true,
3335        subtree: true
3336    });
3337    return observer;
3338}
3339const drpListeningCharts = new Map();
3340let oldDevicePixelRatio = 0;
3341function onWindowResize() {
3342    const dpr = window.devicePixelRatio;
3343    if (dpr === oldDevicePixelRatio) {
3344        return;
3345    }
3346    oldDevicePixelRatio = dpr;
3347    drpListeningCharts.forEach((resize, chart)=>{
3348        if (chart.currentDevicePixelRatio !== dpr) {
3349            resize();
3350        }
3351    });
3352}
3353function listenDevicePixelRatioChanges(chart, resize) {
3354    if (!drpListeningCharts.size) {
3355        window.addEventListener('resize', onWindowResize);
3356    }
3357    drpListeningCharts.set(chart, resize);
3358}
3359function unlistenDevicePixelRatioChanges(chart) {
3360    drpListeningCharts.delete(chart);
3361    if (!drpListeningCharts.size) {
3362        window.removeEventListener('resize', onWindowResize);
3363    }
3364}
3365function createResizeObserver(chart, type, listener) {
3366    const canvas = chart.canvas;
3367    const container = canvas && _getParentNode(canvas);
3368    if (!container) {
3369        return;
3370    }
3371    const resize = throttled((width, height)=>{
3372        const w = container.clientWidth;
3373        listener(width, height);
3374        if (w < container.clientWidth) {
3375            listener();
3376        }
3377    }, window);
3378    const observer = new ResizeObserver((entries)=>{
3379        const entry = entries[0];
3380        const width = entry.contentRect.width;
3381        const height = entry.contentRect.height;
3382        if (width === 0 && height === 0) {
3383            return;
3384        }
3385        resize(width, height);
3386    });
3387    observer.observe(container);
3388    listenDevicePixelRatioChanges(chart, resize);
3389    return observer;
3390}
3391function releaseObserver(chart, type, observer) {
3392    if (observer) {
3393        observer.disconnect();
3394    }
3395    if (type === 'resize') {
3396        unlistenDevicePixelRatioChanges(chart);
3397    }
3398}
3399function createProxyAndListen(chart, type, listener) {
3400    const canvas = chart.canvas;
3401    const proxy = throttled((event)=>{
3402        if (chart.ctx !== null) {
3403            listener(fromNativeEvent(event, chart));
3404        }
3405    }, chart);
3406    addListener(canvas, type, proxy);
3407    return proxy;
3408}
3409 class DomPlatform extends BasePlatform {
3410 acquireContext(canvas, aspectRatio) {
3411        const context = canvas && canvas.getContext && canvas.getContext('2d');
3412        if (context && context.canvas === canvas) {
3413            initCanvas(canvas, aspectRatio);
3414            return context;
3415        }
3416        return null;
3417    }
3418 releaseContext(context) {
3419        const canvas = context.canvas;
3420        if (!canvas[EXPANDO_KEY]) {
3421            return false;
3422        }
3423        const initial = canvas[EXPANDO_KEY].initial;
3424        [
3425            'height',
3426            'width'
3427        ].forEach((prop)=>{
3428            const value = initial[prop];
3429            if (isNullOrUndef(value)) {
3430                canvas.removeAttribute(prop);
3431            } else {
3432                canvas.setAttribute(prop, value);
3433            }
3434        });
3435        const style = initial.style || {};
3436        Object.keys(style).forEach((key)=>{
3437            canvas.style[key] = style[key];
3438        });
3439        canvas.width = canvas.width;
3440        delete canvas[EXPANDO_KEY];
3441        return true;
3442    }
3443 addEventListener(chart, type, listener) {
3444        this.removeEventListener(chart, type);
3445        const proxies = chart.$proxies || (chart.$proxies = {});
3446        const handlers = {
3447            attach: createAttachObserver,
3448            detach: createDetachObserver,
3449            resize: createResizeObserver
3450        };
3451        const handler = handlers[type] || createProxyAndListen;
3452        proxies[type] = handler(chart, type, listener);
3453    }
3454 removeEventListener(chart, type) {
3455        const proxies = chart.$proxies || (chart.$proxies = {});
3456        const proxy = proxies[type];
3457        if (!proxy) {
3458            return;
3459        }
3460        const handlers = {
3461            attach: releaseObserver,
3462            detach: releaseObserver,
3463            resize: releaseObserver
3464        };
3465        const handler = handlers[type] || removeListener;
3466        handler(chart, type, proxy);
3467        proxies[type] = undefined;
3468    }
3469    getDevicePixelRatio() {
3470        return window.devicePixelRatio;
3471    }
3472 getMaximumSize(canvas, width, height, aspectRatio) {
3473        return getMaximumSize(canvas, width, height, aspectRatio);
3474    }
3475 isAttached(canvas) {
3476        const container = canvas && _getParentNode(canvas);
3477        return !!(container && container.isConnected);
3478    }
3479}
3480
3481function _detectPlatform(canvas) {
3482    if (!_isDomSupported() || typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
3483        return BasicPlatform;
3484    }
3485    return DomPlatform;
3486}
3487
3488class Element {
3489    static defaults = {};
3490    static defaultRoutes = undefined;
3491    x;
3492    y;
3493    active = false;
3494    options;
3495    $animations;
3496    tooltipPosition(useFinalPosition) {
3497        const { x , y  } = this.getProps([
3498            'x',
3499            'y'
3500        ], useFinalPosition);
3501        return {
3502            x,
3503            y
3504        };
3505    }
3506    hasValue() {
3507        return isNumber(this.x) && isNumber(this.y);
3508    }
3509    getProps(props, final) {
3510        const anims = this.$animations;
3511        if (!final || !anims) {
3512            // let's not create an object, if not needed
3513            return this;
3514        }
3515        const ret = {};
3516        props.forEach((prop)=>{
3517            ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : this[prop];
3518        });
3519        return ret;
3520    }
3521}
3522
3523function autoSkip(scale, ticks) {
3524    const tickOpts = scale.options.ticks;
3525    const determinedMaxTicks = determineMaxTicks(scale);
3526    const ticksLimit = Math.min(tickOpts.maxTicksLimit || determinedMaxTicks, determinedMaxTicks);
3527    const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];
3528    const numMajorIndices = majorIndices.length;
3529    const first = majorIndices[0];
3530    const last = majorIndices[numMajorIndices - 1];
3531    const newTicks = [];
3532    if (numMajorIndices > ticksLimit) {
3533        skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit);
3534        return newTicks;
3535    }
3536    const spacing = calculateSpacing(majorIndices, ticks, ticksLimit);
3537    if (numMajorIndices > 0) {
3538        let i, ilen;
3539        const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null;
3540        skip(ticks, newTicks, spacing, isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
3541        for(i = 0, ilen = numMajorIndices - 1; i < ilen; i++){
3542            skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]);
3543        }
3544        skip(ticks, newTicks, spacing, last, isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
3545        return newTicks;
3546    }
3547    skip(ticks, newTicks, spacing);
3548    return newTicks;
3549}
3550function determineMaxTicks(scale) {
3551    const offset = scale.options.offset;
3552    const tickLength = scale._tickSize();
3553    const maxScale = scale._length / tickLength + (offset ? 0 : 1);
3554    const maxChart = scale._maxLength / tickLength;
3555    return Math.floor(Math.min(maxScale, maxChart));
3556}
3557 function calculateSpacing(majorIndices, ticks, ticksLimit) {
3558    const evenMajorSpacing = getEvenSpacing(majorIndices);
3559    const spacing = ticks.length / ticksLimit;
3560    if (!evenMajorSpacing) {
3561        return Math.max(spacing, 1);
3562    }
3563    const factors = _factorize(evenMajorSpacing);
3564    for(let i = 0, ilen = factors.length - 1; i < ilen; i++){
3565        const factor = factors[i];
3566        if (factor > spacing) {
3567            return factor;
3568        }
3569    }
3570    return Math.max(spacing, 1);
3571}
3572 function getMajorIndices(ticks) {
3573    const result = [];
3574    let i, ilen;
3575    for(i = 0, ilen = ticks.length; i < ilen; i++){
3576        if (ticks[i].major) {
3577            result.push(i);
3578        }
3579    }
3580    return result;
3581}
3582 function skipMajors(ticks, newTicks, majorIndices, spacing) {
vendor: 23,446 bytes, lines 3583-4227
3583    let count = 0;
3584    let next = majorIndices[0];
3585    let i;
3586    spacing = Math.ceil(spacing);
3587    for(i = 0; i < ticks.length; i++){
3588        if (i === next) {
3589            newTicks.push(ticks[i]);
3590            count++;
3591            next = majorIndices[count * spacing];
3592        }
3593    }
3594}
3595 function skip(ticks, newTicks, spacing, majorStart, majorEnd) {
3596    const start = valueOrDefault(majorStart, 0);
3597    const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length);
3598    let count = 0;
3599    let length, i, next;
3600    spacing = Math.ceil(spacing);
3601    if (majorEnd) {
3602        length = majorEnd - majorStart;
3603        spacing = length / Math.floor(length / spacing);
3604    }
3605    next = start;
3606    while(next < 0){
3607        count++;
3608        next = Math.round(start + count * spacing);
3609    }
3610    for(i = Math.max(start, 0); i < end; i++){
3611        if (i === next) {
3612            newTicks.push(ticks[i]);
3613            count++;
3614            next = Math.round(start + count * spacing);
3615        }
3616    }
3617}
3618 function getEvenSpacing(arr) {
3619    const len = arr.length;
3620    let i, diff;
3621    if (len < 2) {
3622        return false;
3623    }
3624    for(diff = arr[0], i = 1; i < len; ++i){
3625        if (arr[i] - arr[i - 1] !== diff) {
3626            return false;
3627        }
3628    }
3629    return diff;
3630}
3631
3632const reverseAlign = (align)=>align === 'left' ? 'right' : align === 'right' ? 'left' : align;
3633const offsetFromEdge = (scale, edge, offset)=>edge === 'top' || edge === 'left' ? scale[edge] + offset : scale[edge] - offset;
3634const getTicksLimit = (ticksLength, maxTicksLimit)=>Math.min(maxTicksLimit || ticksLength, ticksLength);
3635 function sample(arr, numItems) {
3636    const result = [];
3637    const increment = arr.length / numItems;
3638    const len = arr.length;
3639    let i = 0;
3640    for(; i < len; i += increment){
3641        result.push(arr[Math.floor(i)]);
3642    }
3643    return result;
3644}
3645 function getPixelForGridLine(scale, index, offsetGridLines) {
3646    const length = scale.ticks.length;
3647    const validIndex = Math.min(index, length - 1);
3648    const start = scale._startPixel;
3649    const end = scale._endPixel;
3650    const epsilon = 1e-6;
3651    let lineValue = scale.getPixelForTick(validIndex);
3652    let offset;
3653    if (offsetGridLines) {
3654        if (length === 1) {
3655            offset = Math.max(lineValue - start, end - lineValue);
3656        } else if (index === 0) {
3657            offset = (scale.getPixelForTick(1) - lineValue) / 2;
3658        } else {
3659            offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2;
3660        }
3661        lineValue += validIndex < index ? offset : -offset;
3662        if (lineValue < start - epsilon || lineValue > end + epsilon) {
3663            return;
3664        }
3665    }
3666    return lineValue;
3667}
3668 function garbageCollect(caches, length) {
3669    each(caches, (cache)=>{
3670        const gc = cache.gc;
3671        const gcLen = gc.length / 2;
3672        let i;
3673        if (gcLen > length) {
3674            for(i = 0; i < gcLen; ++i){
3675                delete cache.data[gc[i]];
3676            }
3677            gc.splice(0, gcLen);
3678        }
3679    });
3680}
3681 function getTickMarkLength(options) {
3682    return options.drawTicks ? options.tickLength : 0;
3683}
3684 function getTitleHeight(options, fallback) {
3685    if (!options.display) {
3686        return 0;
3687    }
3688    const font = toFont(options.font, fallback);
3689    const padding = toPadding(options.padding);
3690    const lines = isArray(options.text) ? options.text.length : 1;
3691    return lines * font.lineHeight + padding.height;
3692}
3693function createScaleContext(parent, scale) {
3694    return createContext(parent, {
3695        scale,
3696        type: 'scale'
3697    });
3698}
3699function createTickContext(parent, index, tick) {
3700    return createContext(parent, {
3701        tick,
3702        index,
3703        type: 'tick'
3704    });
3705}
3706function titleAlign(align, position, reverse) {
3707     let ret = _toLeftRightCenter(align);
3708    if (reverse && position !== 'right' || !reverse && position === 'right') {
3709        ret = reverseAlign(ret);
3710    }
3711    return ret;
3712}
3713function titleArgs(scale, offset, position, align) {
3714    const { top , left , bottom , right , chart  } = scale;
3715    const { chartArea , scales  } = chart;
3716    let rotation = 0;
3717    let maxWidth, titleX, titleY;
3718    const height = bottom - top;
3719    const width = right - left;
3720    if (scale.isHorizontal()) {
3721        titleX = _alignStartEnd(align, left, right);
3722        if (isObject(position)) {
3723            const positionAxisID = Object.keys(position)[0];
3724            const value = position[positionAxisID];
3725            titleY = scales[positionAxisID].getPixelForValue(value) + height - offset;
3726        } else if (position === 'center') {
3727            titleY = (chartArea.bottom + chartArea.top) / 2 + height - offset;
3728        } else {
3729            titleY = offsetFromEdge(scale, position, offset);
3730        }
3731        maxWidth = right - left;
3732    } else {
3733        if (isObject(position)) {
3734            const positionAxisID = Object.keys(position)[0];
3735            const value = position[positionAxisID];
3736            titleX = scales[positionAxisID].getPixelForValue(value) - width + offset;
3737        } else if (position === 'center') {
3738            titleX = (chartArea.left + chartArea.right) / 2 - width + offset;
3739        } else {
3740            titleX = offsetFromEdge(scale, position, offset);
3741        }
3742        titleY = _alignStartEnd(align, bottom, top);
3743        rotation = position === 'left' ? -HALF_PI : HALF_PI;
3744    }
3745    return {
3746        titleX,
3747        titleY,
3748        maxWidth,
3749        rotation
3750    };
3751}
3752class Scale extends Element {
3753    constructor(cfg){
3754        super();
3755         this.id = cfg.id;
3756         this.type = cfg.type;
3757         this.options = undefined;
3758         this.ctx = cfg.ctx;
3759         this.chart = cfg.chart;
3760         this.top = undefined;
3761         this.bottom = undefined;
3762         this.left = undefined;
3763         this.right = undefined;
3764         this.width = undefined;
3765         this.height = undefined;
3766        this._margins = {
3767            left: 0,
3768            right: 0,
3769            top: 0,
3770            bottom: 0
3771        };
3772         this.maxWidth = undefined;
3773         this.maxHeight = undefined;
3774         this.paddingTop = undefined;
3775         this.paddingBottom = undefined;
3776         this.paddingLeft = undefined;
3777         this.paddingRight = undefined;
3778         this.axis = undefined;
3779         this.labelRotation = undefined;
3780        this.min = undefined;
3781        this.max = undefined;
3782        this._range = undefined;
3783         this.ticks = [];
3784         this._gridLineItems = null;
3785         this._labelItems = null;
3786         this._labelSizes = null;
3787        this._length = 0;
3788        this._maxLength = 0;
3789        this._longestTextCache = {};
3790         this._startPixel = undefined;
3791         this._endPixel = undefined;
3792        this._reversePixels = false;
3793        this._userMax = undefined;
3794        this._userMin = undefined;
3795        this._suggestedMax = undefined;
3796        this._suggestedMin = undefined;
3797        this._ticksLength = 0;
3798        this._borderValue = 0;
3799        this._cache = {};
3800        this._dataLimitsCached = false;
3801        this.$context = undefined;
3802    }
3803 init(options) {
3804        this.options = options.setContext(this.getContext());
3805        this.axis = options.axis;
3806        this._userMin = this.parse(options.min);
3807        this._userMax = this.parse(options.max);
3808        this._suggestedMin = this.parse(options.suggestedMin);
3809        this._suggestedMax = this.parse(options.suggestedMax);
3810    }
3811 parse(raw, index) {
3812        return raw;
3813    }
3814 getUserBounds() {
3815        let { _userMin , _userMax , _suggestedMin , _suggestedMax  } = this;
3816        _userMin = finiteOrDefault(_userMin, Number.POSITIVE_INFINITY);
3817        _userMax = finiteOrDefault(_userMax, Number.NEGATIVE_INFINITY);
3818        _suggestedMin = finiteOrDefault(_suggestedMin, Number.POSITIVE_INFINITY);
3819        _suggestedMax = finiteOrDefault(_suggestedMax, Number.NEGATIVE_INFINITY);
3820        return {
3821            min: finiteOrDefault(_userMin, _suggestedMin),
3822            max: finiteOrDefault(_userMax, _suggestedMax),
3823            minDefined: isNumberFinite(_userMin),
3824            maxDefined: isNumberFinite(_userMax)
3825        };
3826    }
3827 getMinMax(canStack) {
3828        let { min , max , minDefined , maxDefined  } = this.getUserBounds();
3829        let range;
3830        if (minDefined && maxDefined) {
3831            return {
3832                min,
3833                max
3834            };
3835        }
3836        const metas = this.getMatchingVisibleMetas();
3837        for(let i = 0, ilen = metas.length; i < ilen; ++i){
3838            range = metas[i].controller.getMinMax(this, canStack);
3839            if (!minDefined) {
3840                min = Math.min(min, range.min);
3841            }
3842            if (!maxDefined) {
3843                max = Math.max(max, range.max);
3844            }
3845        }
3846        min = maxDefined && min > max ? max : min;
3847        max = minDefined && min > max ? min : max;
3848        return {
3849            min: finiteOrDefault(min, finiteOrDefault(max, min)),
3850            max: finiteOrDefault(max, finiteOrDefault(min, max))
3851        };
3852    }
3853 getPadding() {
3854        return {
3855            left: this.paddingLeft || 0,
3856            top: this.paddingTop || 0,
3857            right: this.paddingRight || 0,
3858            bottom: this.paddingBottom || 0
3859        };
3860    }
3861 getTicks() {
3862        return this.ticks;
3863    }
3864 getLabels() {
3865        const data = this.chart.data;
3866        return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || [];
3867    }
3868 getLabelItems(chartArea = this.chart.chartArea) {
3869        const items = this._labelItems || (this._labelItems = this._computeLabelItems(chartArea));
3870        return items;
3871    }
3872    beforeLayout() {
3873        this._cache = {};
3874        this._dataLimitsCached = false;
3875    }
3876    beforeUpdate() {
3877        callback(this.options.beforeUpdate, [
3878            this
3879        ]);
3880    }
3881 update(maxWidth, maxHeight, margins) {
3882        const { beginAtZero , grace , ticks: tickOpts  } = this.options;
3883        const sampleSize = tickOpts.sampleSize;
3884        this.beforeUpdate();
3885        this.maxWidth = maxWidth;
3886        this.maxHeight = maxHeight;
3887        this._margins = margins = Object.assign({
3888            left: 0,
3889            right: 0,
3890            top: 0,
3891            bottom: 0
3892        }, margins);
3893        this.ticks = null;
3894        this._labelSizes = null;
3895        this._gridLineItems = null;
3896        this._labelItems = null;
3897        this.beforeSetDimensions();
3898        this.setDimensions();
3899        this.afterSetDimensions();
3900        this._maxLength = this.isHorizontal() ? this.width + margins.left + margins.right : this.height + margins.top + margins.bottom;
3901        if (!this._dataLimitsCached) {
3902            this.beforeDataLimits();
3903            this.determineDataLimits();
3904            this.afterDataLimits();
3905            this._range = _addGrace(this, grace, beginAtZero);
3906            this._dataLimitsCached = true;
3907        }
3908        this.beforeBuildTicks();
3909        this.ticks = this.buildTicks() || [];
3910        this.afterBuildTicks();
3911        const samplingEnabled = sampleSize < this.ticks.length;
3912        this._convertTicksToLabels(samplingEnabled ? sample(this.ticks, sampleSize) : this.ticks);
3913        this.configure();
3914        this.beforeCalculateLabelRotation();
3915        this.calculateLabelRotation();
3916        this.afterCalculateLabelRotation();
3917        if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto')) {
3918            this.ticks = autoSkip(this, this.ticks);
3919            this._labelSizes = null;
3920            this.afterAutoSkip();
3921        }
3922        if (samplingEnabled) {
3923            this._convertTicksToLabels(this.ticks);
3924        }
3925        this.beforeFit();
3926        this.fit();
3927        this.afterFit();
3928        this.afterUpdate();
3929    }
3930 configure() {
3931        let reversePixels = this.options.reverse;
3932        let startPixel, endPixel;
3933        if (this.isHorizontal()) {
3934            startPixel = this.left;
3935            endPixel = this.right;
3936        } else {
3937            startPixel = this.top;
3938            endPixel = this.bottom;
3939            reversePixels = !reversePixels;
3940        }
3941        this._startPixel = startPixel;
3942        this._endPixel = endPixel;
3943        this._reversePixels = reversePixels;
3944        this._length = endPixel - startPixel;
3945        this._alignToPixels = this.options.alignToPixels;
3946    }
3947    afterUpdate() {
3948        callback(this.options.afterUpdate, [
3949            this
3950        ]);
3951    }
3952    beforeSetDimensions() {
3953        callback(this.options.beforeSetDimensions, [
3954            this
3955        ]);
3956    }
3957    setDimensions() {
3958        if (this.isHorizontal()) {
3959            this.width = this.maxWidth;
3960            this.left = 0;
3961            this.right = this.width;
3962        } else {
3963            this.height = this.maxHeight;
3964            this.top = 0;
3965            this.bottom = this.height;
3966        }
3967        this.paddingLeft = 0;
3968        this.paddingTop = 0;
3969        this.paddingRight = 0;
3970        this.paddingBottom = 0;
3971    }
3972    afterSetDimensions() {
3973        callback(this.options.afterSetDimensions, [
3974            this
3975        ]);
3976    }
3977    _callHooks(name) {
3978        this.chart.notifyPlugins(name, this.getContext());
3979        callback(this.options[name], [
3980            this
3981        ]);
3982    }
3983    beforeDataLimits() {
3984        this._callHooks('beforeDataLimits');
3985    }
3986    determineDataLimits() {}
3987    afterDataLimits() {
3988        this._callHooks('afterDataLimits');
3989    }
3990    beforeBuildTicks() {
3991        this._callHooks('beforeBuildTicks');
3992    }
3993 buildTicks() {
3994        return [];
3995    }
3996    afterBuildTicks() {
3997        this._callHooks('afterBuildTicks');
3998    }
3999    beforeTickToLabelConversion() {
4000        callback(this.options.beforeTickToLabelConversion, [
4001            this
4002        ]);
4003    }
4004 generateTickLabels(ticks) {
4005        const tickOpts = this.options.ticks;
4006        let i, ilen, tick;
4007        for(i = 0, ilen = ticks.length; i < ilen; i++){
4008            tick = ticks[i];
4009            tick.label = callback(tickOpts.callback, [
4010                tick.value,
4011                i,
4012                ticks
4013            ], this);
4014        }
4015    }
4016    afterTickToLabelConversion() {
4017        callback(this.options.afterTickToLabelConversion, [
4018            this
4019        ]);
4020    }
4021    beforeCalculateLabelRotation() {
4022        callback(this.options.beforeCalculateLabelRotation, [
4023            this
4024        ]);
4025    }
4026    calculateLabelRotation() {
4027        const options = this.options;
4028        const tickOpts = options.ticks;
4029        const numTicks = getTicksLimit(this.ticks.length, options.ticks.maxTicksLimit);
4030        const minRotation = tickOpts.minRotation || 0;
4031        const maxRotation = tickOpts.maxRotation;
4032        let labelRotation = minRotation;
4033        let tickWidth, maxHeight, maxLabelDiagonal;
4034        if (!this._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !this.isHorizontal()) {
4035            this.labelRotation = minRotation;
4036            return;
4037        }
4038        const labelSizes = this._getLabelSizes();
4039        const maxLabelWidth = labelSizes.widest.width;
4040        const maxLabelHeight = labelSizes.highest.height;
4041        const maxWidth = _limitValue(this.chart.width - maxLabelWidth, 0, this.maxWidth);
4042        tickWidth = options.offset ? this.maxWidth / numTicks : maxWidth / (numTicks - 1);
4043        if (maxLabelWidth + 6 > tickWidth) {
4044            tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1));
4045            maxHeight = this.maxHeight - getTickMarkLength(options.grid) - tickOpts.padding - getTitleHeight(options.title, this.chart.options.font);
4046            maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight);
4047            labelRotation = toDegrees(Math.min(Math.asin(_limitValue((labelSizes.highest.height + 6) / tickWidth, -1, 1)), Math.asin(_limitValue(maxHeight / maxLabelDiagonal, -1, 1)) - Math.asin(_limitValue(maxLabelHeight / maxLabelDiagonal, -1, 1))));
4048            labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation));
4049        }
4050        this.labelRotation = labelRotation;
4051    }
4052    afterCalculateLabelRotation() {
4053        callback(this.options.afterCalculateLabelRotation, [
4054            this
4055        ]);
4056    }
4057    afterAutoSkip() {}
4058    beforeFit() {
4059        callback(this.options.beforeFit, [
4060            this
4061        ]);
4062    }
4063    fit() {
4064        const minSize = {
4065            width: 0,
4066            height: 0
4067        };
4068        const { chart , options: { ticks: tickOpts , title: titleOpts , grid: gridOpts  }  } = this;
4069        const display = this._isVisible();
4070        const isHorizontal = this.isHorizontal();
4071        if (display) {
4072            const titleHeight = getTitleHeight(titleOpts, chart.options.font);
4073            if (isHorizontal) {
4074                minSize.width = this.maxWidth;
4075                minSize.height = getTickMarkLength(gridOpts) + titleHeight;
4076            } else {
4077                minSize.height = this.maxHeight;
4078                minSize.width = getTickMarkLength(gridOpts) + titleHeight;
4079            }
4080            if (tickOpts.display && this.ticks.length) {
4081                const { first , last , widest , highest  } = this._getLabelSizes();
4082                const tickPadding = tickOpts.padding * 2;
4083                const angleRadians = toRadians(this.labelRotation);
4084                const cos = Math.cos(angleRadians);
4085                const sin = Math.sin(angleRadians);
4086                if (isHorizontal) {
4087                    const labelHeight = tickOpts.mirror ? 0 : sin * widest.width + cos * highest.height;
4088                    minSize.height = Math.min(this.maxHeight, minSize.height + labelHeight + tickPadding);
4089                } else {
4090                    const labelWidth = tickOpts.mirror ? 0 : cos * widest.width + sin * highest.height;
4091                    minSize.width = Math.min(this.maxWidth, minSize.width + labelWidth + tickPadding);
4092                }
4093                this._calculatePadding(first, last, sin, cos);
4094            }
4095        }
4096        this._handleMargins();
4097        if (isHorizontal) {
4098            this.width = this._length = chart.width - this._margins.left - this._margins.right;
4099            this.height = minSize.height;
4100        } else {
4101            this.width = minSize.width;
4102            this.height = this._length = chart.height - this._margins.top - this._margins.bottom;
4103        }
4104    }
4105    _calculatePadding(first, last, sin, cos) {
4106        const { ticks: { align , padding  } , position  } = this.options;
4107        const isRotated = this.labelRotation !== 0;
4108        const labelsBelowTicks = position !== 'top' && this.axis === 'x';
4109        if (this.isHorizontal()) {
4110            const offsetLeft = this.getPixelForTick(0) - this.left;
4111            const offsetRight = this.right - this.getPixelForTick(this.ticks.length - 1);
4112            let paddingLeft = 0;
4113            let paddingRight = 0;
4114            if (isRotated) {
4115                if (labelsBelowTicks) {
4116                    paddingLeft = cos * first.width;
4117                    paddingRight = sin * last.height;
4118                } else {
4119                    paddingLeft = sin * first.height;
4120                    paddingRight = cos * last.width;
4121                }
4122            } else if (align === 'start') {
4123                paddingRight = last.width;
4124            } else if (align === 'end') {
4125                paddingLeft = first.width;
4126            } else if (align !== 'inner') {
4127                paddingLeft = first.width / 2;
4128                paddingRight = last.width / 2;
4129            }
4130            this.paddingLeft = Math.max((paddingLeft - offsetLeft + padding) * this.width / (this.width - offsetLeft), 0);
4131            this.paddingRight = Math.max((paddingRight - offsetRight + padding) * this.width / (this.width - offsetRight), 0);
4132        } else {
4133            let paddingTop = last.height / 2;
4134            let paddingBottom = first.height / 2;
4135            if (align === 'start') {
4136                paddingTop = 0;
4137                paddingBottom = first.height;
4138            } else if (align === 'end') {
4139                paddingTop = last.height;
4140                paddingBottom = 0;
4141            }
4142            this.paddingTop = paddingTop + padding;
4143            this.paddingBottom = paddingBottom + padding;
4144        }
4145    }
4146 _handleMargins() {
4147        if (this._margins) {
4148            this._margins.left = Math.max(this.paddingLeft, this._margins.left);
4149            this._margins.top = Math.max(this.paddingTop, this._margins.top);
4150            this._margins.right = Math.max(this.paddingRight, this._margins.right);
4151            this._margins.bottom = Math.max(this.paddingBottom, this._margins.bottom);
4152        }
4153    }
4154    afterFit() {
4155        callback(this.options.afterFit, [
4156            this
4157        ]);
4158    }
4159 isHorizontal() {
4160        const { axis , position  } = this.options;
4161        return position === 'top' || position === 'bottom' || axis === 'x';
4162    }
4163 isFullSize() {
4164        return this.options.fullSize;
4165    }
4166 _convertTicksToLabels(ticks) {
4167        this.beforeTickToLabelConversion();
4168        this.generateTickLabels(ticks);
4169        let i, ilen;
4170        for(i = 0, ilen = ticks.length; i < ilen; i++){
4171            if (isNullOrUndef(ticks[i].label)) {
4172                ticks.splice(i, 1);
4173                ilen--;
4174                i--;
4175            }
4176        }
4177        this.afterTickToLabelConversion();
4178    }
4179 _getLabelSizes() {
4180        let labelSizes = this._labelSizes;
4181        if (!labelSizes) {
4182            const sampleSize = this.options.ticks.sampleSize;
4183            let ticks = this.ticks;
4184            if (sampleSize < ticks.length) {
4185                ticks = sample(ticks, sampleSize);
4186            }
4187            this._labelSizes = labelSizes = this._computeLabelSizes(ticks, ticks.length, this.options.ticks.maxTicksLimit);
4188        }
4189        return labelSizes;
4190    }
4191 _computeLabelSizes(ticks, length, maxTicksLimit) {
4192        const { ctx , _longestTextCache: caches  } = this;
4193        const widths = [];
4194        const heights = [];
4195        const increment = Math.floor(length / getTicksLimit(length, maxTicksLimit));
4196        let widestLabelSize = 0;
4197        let highestLabelSize = 0;
4198        let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel;
4199        for(i = 0; i < length; i += increment){
4200            label = ticks[i].label;
4201            tickFont = this._resolveTickFontOptions(i);
4202            ctx.font = fontString = tickFont.string;
4203            cache = caches[fontString] = caches[fontString] || {
4204                data: {},
4205                gc: []
4206            };
4207            lineHeight = tickFont.lineHeight;
4208            width = height = 0;
4209            if (!isNullOrUndef(label) && !isArray(label)) {
4210                width = _measureText(ctx, cache.data, cache.gc, width, label);
4211                height = lineHeight;
4212            } else if (isArray(label)) {
4213                for(j = 0, jlen = label.length; j < jlen; ++j){
4214                    nestedLabel =  label[j];
4215                    if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) {
4216                        width = _measureText(ctx, cache.data, cache.gc, width, nestedLabel);
4217                        height += lineHeight;
4218                    }
4219                }
4220            }
4221            widths.push(width);
4222            heights.push(height);
4223            widestLabelSize = Math.max(width, widestLabelSize);
4224            highestLabelSize = Math.max(height, highestLabelSize);
4225        }
4226        garbageCollect(caches, length);
4227        const widest = widths.indexOf(widestLabelSize);
vendor: 21,373 bytes, lines 4228-4778
4228        const highest = heights.indexOf(highestLabelSize);
4229        const valueAt = (idx)=>({
4230                width: widths[idx] || 0,
4231                height: heights[idx] || 0
4232            });
4233        return {
4234            first: valueAt(0),
4235            last: valueAt(length - 1),
4236            widest: valueAt(widest),
4237            highest: valueAt(highest),
4238            widths,
4239            heights
4240        };
4241    }
4242 getLabelForValue(value) {
4243        return value;
4244    }
4245 getPixelForValue(value, index) {
4246        return NaN;
4247    }
4248 getValueForPixel(pixel) {}
4249 getPixelForTick(index) {
4250        const ticks = this.ticks;
4251        if (index < 0 || index > ticks.length - 1) {
4252            return null;
4253        }
4254        return this.getPixelForValue(ticks[index].value);
4255    }
4256 getPixelForDecimal(decimal) {
4257        if (this._reversePixels) {
4258            decimal = 1 - decimal;
4259        }
4260        const pixel = this._startPixel + decimal * this._length;
4261        return _int16Range(this._alignToPixels ? _alignPixel(this.chart, pixel, 0) : pixel);
4262    }
4263 getDecimalForPixel(pixel) {
4264        const decimal = (pixel - this._startPixel) / this._length;
4265        return this._reversePixels ? 1 - decimal : decimal;
4266    }
4267 getBasePixel() {
4268        return this.getPixelForValue(this.getBaseValue());
4269    }
4270 getBaseValue() {
4271        const { min , max  } = this;
4272        return min < 0 && max < 0 ? max : min > 0 && max > 0 ? min : 0;
4273    }
4274 getContext(index) {
4275        const ticks = this.ticks || [];
4276        if (index >= 0 && index < ticks.length) {
4277            const tick = ticks[index];
4278            return tick.$context || (tick.$context = createTickContext(this.getContext(), index, tick));
4279        }
4280        return this.$context || (this.$context = createScaleContext(this.chart.getContext(), this));
4281    }
4282 _tickSize() {
4283        const optionTicks = this.options.ticks;
4284        const rot = toRadians(this.labelRotation);
4285        const cos = Math.abs(Math.cos(rot));
4286        const sin = Math.abs(Math.sin(rot));
4287        const labelSizes = this._getLabelSizes();
4288        const padding = optionTicks.autoSkipPadding || 0;
4289        const w = labelSizes ? labelSizes.widest.width + padding : 0;
4290        const h = labelSizes ? labelSizes.highest.height + padding : 0;
4291        return this.isHorizontal() ? h * cos > w * sin ? w / cos : h / sin : h * sin < w * cos ? h / cos : w / sin;
4292    }
4293 _isVisible() {
4294        const display = this.options.display;
4295        if (display !== 'auto') {
4296            return !!display;
4297        }
4298        return this.getMatchingVisibleMetas().length > 0;
4299    }
4300 _computeGridLineItems(chartArea) {
4301        const axis = this.axis;
4302        const chart = this.chart;
4303        const options = this.options;
4304        const { grid , position , border  } = options;
4305        const offset = grid.offset;
4306        const isHorizontal = this.isHorizontal();
4307        const ticks = this.ticks;
4308        const ticksLength = ticks.length + (offset ? 1 : 0);
4309        const tl = getTickMarkLength(grid);
4310        const items = [];
4311        const borderOpts = border.setContext(this.getContext());
4312        const axisWidth = borderOpts.display ? borderOpts.width : 0;
4313        const axisHalfWidth = axisWidth / 2;
4314        const alignBorderValue = function(pixel) {
4315            return _alignPixel(chart, pixel, axisWidth);
4316        };
4317        let borderValue, i, lineValue, alignedLineValue;
4318        let tx1, ty1, tx2, ty2, x1, y1, x2, y2;
4319        if (position === 'top') {
4320            borderValue = alignBorderValue(this.bottom);
4321            ty1 = this.bottom - tl;
4322            ty2 = borderValue - axisHalfWidth;
4323            y1 = alignBorderValue(chartArea.top) + axisHalfWidth;
4324            y2 = chartArea.bottom;
4325        } else if (position === 'bottom') {
4326            borderValue = alignBorderValue(this.top);
4327            y1 = chartArea.top;
4328            y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth;
4329            ty1 = borderValue + axisHalfWidth;
4330            ty2 = this.top + tl;
4331        } else if (position === 'left') {
4332            borderValue = alignBorderValue(this.right);
4333            tx1 = this.right - tl;
4334            tx2 = borderValue - axisHalfWidth;
4335            x1 = alignBorderValue(chartArea.left) + axisHalfWidth;
4336            x2 = chartArea.right;
4337        } else if (position === 'right') {
4338            borderValue = alignBorderValue(this.left);
4339            x1 = chartArea.left;
4340            x2 = alignBorderValue(chartArea.right) - axisHalfWidth;
4341            tx1 = borderValue + axisHalfWidth;
4342            tx2 = this.left + tl;
4343        } else if (axis === 'x') {
4344            if (position === 'center') {
4345                borderValue = alignBorderValue((chartArea.top + chartArea.bottom) / 2 + 0.5);
4346            } else if (isObject(position)) {
4347                const positionAxisID = Object.keys(position)[0];
4348                const value = position[positionAxisID];
4349                borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));
4350            }
4351            y1 = chartArea.top;
4352            y2 = chartArea.bottom;
4353            ty1 = borderValue + axisHalfWidth;
4354            ty2 = ty1 + tl;
4355        } else if (axis === 'y') {
4356            if (position === 'center') {
4357                borderValue = alignBorderValue((chartArea.left + chartArea.right) / 2);
4358            } else if (isObject(position)) {
4359                const positionAxisID = Object.keys(position)[0];
4360                const value = position[positionAxisID];
4361                borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));
4362            }
4363            tx1 = borderValue - axisHalfWidth;
4364            tx2 = tx1 - tl;
4365            x1 = chartArea.left;
4366            x2 = chartArea.right;
4367        }
4368        const limit = valueOrDefault(options.ticks.maxTicksLimit, ticksLength);
4369        const step = Math.max(1, Math.ceil(ticksLength / limit));
4370        for(i = 0; i < ticksLength; i += step){
4371            const context = this.getContext(i);
4372            const optsAtIndex = grid.setContext(context);
4373            const optsAtIndexBorder = border.setContext(context);
4374            const lineWidth = optsAtIndex.lineWidth;
4375            const lineColor = optsAtIndex.color;
4376            const borderDash = optsAtIndexBorder.dash || [];
4377            const borderDashOffset = optsAtIndexBorder.dashOffset;
4378            const tickWidth = optsAtIndex.tickWidth;
4379            const tickColor = optsAtIndex.tickColor;
4380            const tickBorderDash = optsAtIndex.tickBorderDash || [];
4381            const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset;
4382            lineValue = getPixelForGridLine(this, i, offset);
4383            if (lineValue === undefined) {
4384                continue;
4385            }
4386            alignedLineValue = _alignPixel(chart, lineValue, lineWidth);
4387            if (isHorizontal) {
4388                tx1 = tx2 = x1 = x2 = alignedLineValue;
4389            } else {
4390                ty1 = ty2 = y1 = y2 = alignedLineValue;
4391            }
4392            items.push({
4393                tx1,
4394                ty1,
4395                tx2,
4396                ty2,
4397                x1,
4398                y1,
4399                x2,
4400                y2,
4401                width: lineWidth,
4402                color: lineColor,
4403                borderDash,
4404                borderDashOffset,
4405                tickWidth,
4406                tickColor,
4407                tickBorderDash,
4408                tickBorderDashOffset
4409            });
4410        }
4411        this._ticksLength = ticksLength;
4412        this._borderValue = borderValue;
4413        return items;
4414    }
4415 _computeLabelItems(chartArea) {
4416        const axis = this.axis;
4417        const options = this.options;
4418        const { position , ticks: optionTicks  } = options;
4419        const isHorizontal = this.isHorizontal();
4420        const ticks = this.ticks;
4421        const { align , crossAlign , padding , mirror  } = optionTicks;
4422        const tl = getTickMarkLength(options.grid);
4423        const tickAndPadding = tl + padding;
4424        const hTickAndPadding = mirror ? -padding : tickAndPadding;
4425        const rotation = -toRadians(this.labelRotation);
4426        const items = [];
4427        let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;
4428        let textBaseline = 'middle';
4429        if (position === 'top') {
4430            y = this.bottom - hTickAndPadding;
4431            textAlign = this._getXAxisLabelAlignment();
4432        } else if (position === 'bottom') {
4433            y = this.top + hTickAndPadding;
4434            textAlign = this._getXAxisLabelAlignment();
4435        } else if (position === 'left') {
4436            const ret = this._getYAxisLabelAlignment(tl);
4437            textAlign = ret.textAlign;
4438            x = ret.x;
4439        } else if (position === 'right') {
4440            const ret = this._getYAxisLabelAlignment(tl);
4441            textAlign = ret.textAlign;
4442            x = ret.x;
4443        } else if (axis === 'x') {
4444            if (position === 'center') {
4445                y = (chartArea.top + chartArea.bottom) / 2 + tickAndPadding;
4446            } else if (isObject(position)) {
4447                const positionAxisID = Object.keys(position)[0];
4448                const value = position[positionAxisID];
4449                y = this.chart.scales[positionAxisID].getPixelForValue(value) + tickAndPadding;
4450            }
4451            textAlign = this._getXAxisLabelAlignment();
4452        } else if (axis === 'y') {
4453            if (position === 'center') {
4454                x = (chartArea.left + chartArea.right) / 2 - tickAndPadding;
4455            } else if (isObject(position)) {
4456                const positionAxisID = Object.keys(position)[0];
4457                const value = position[positionAxisID];
4458                x = this.chart.scales[positionAxisID].getPixelForValue(value);
4459            }
4460            textAlign = this._getYAxisLabelAlignment(tl).textAlign;
4461        }
4462        if (axis === 'y') {
4463            if (align === 'start') {
4464                textBaseline = 'top';
4465            } else if (align === 'end') {
4466                textBaseline = 'bottom';
4467            }
4468        }
4469        const labelSizes = this._getLabelSizes();
4470        for(i = 0, ilen = ticks.length; i < ilen; ++i){
4471            tick = ticks[i];
4472            label = tick.label;
4473            const optsAtIndex = optionTicks.setContext(this.getContext(i));
4474            pixel = this.getPixelForTick(i) + optionTicks.labelOffset;
4475            font = this._resolveTickFontOptions(i);
4476            lineHeight = font.lineHeight;
4477            lineCount = isArray(label) ? label.length : 1;
4478            const halfCount = lineCount / 2;
4479            const color = optsAtIndex.color;
4480            const strokeColor = optsAtIndex.textStrokeColor;
4481            const strokeWidth = optsAtIndex.textStrokeWidth;
4482            let tickTextAlign = textAlign;
4483            if (isHorizontal) {
4484                x = pixel;
4485                if (textAlign === 'inner') {
4486                    if (i === ilen - 1) {
4487                        tickTextAlign = !this.options.reverse ? 'right' : 'left';
4488                    } else if (i === 0) {
4489                        tickTextAlign = !this.options.reverse ? 'left' : 'right';
4490                    } else {
4491                        tickTextAlign = 'center';
4492                    }
4493                }
4494                if (position === 'top') {
4495                    if (crossAlign === 'near' || rotation !== 0) {
4496                        textOffset = -lineCount * lineHeight + lineHeight / 2;
4497                    } else if (crossAlign === 'center') {
4498                        textOffset = -labelSizes.highest.height / 2 - halfCount * lineHeight + lineHeight;
4499                    } else {
4500                        textOffset = -labelSizes.highest.height + lineHeight / 2;
4501                    }
4502                } else {
4503                    if (crossAlign === 'near' || rotation !== 0) {
4504                        textOffset = lineHeight / 2;
4505                    } else if (crossAlign === 'center') {
4506                        textOffset = labelSizes.highest.height / 2 - halfCount * lineHeight;
4507                    } else {
4508                        textOffset = labelSizes.highest.height - lineCount * lineHeight;
4509                    }
4510                }
4511                if (mirror) {
4512                    textOffset *= -1;
4513                }
4514                if (rotation !== 0 && !optsAtIndex.showLabelBackdrop) {
4515                    x += lineHeight / 2 * Math.sin(rotation);
4516                }
4517            } else {
4518                y = pixel;
4519                textOffset = (1 - lineCount) * lineHeight / 2;
4520            }
4521            let backdrop;
4522            if (optsAtIndex.showLabelBackdrop) {
4523                const labelPadding = toPadding(optsAtIndex.backdropPadding);
4524                const height = labelSizes.heights[i];
4525                const width = labelSizes.widths[i];
4526                let top = textOffset - labelPadding.top;
4527                let left = 0 - labelPadding.left;
4528                switch(textBaseline){
4529                    case 'middle':
4530                        top -= height / 2;
4531                        break;
4532                    case 'bottom':
4533                        top -= height;
4534                        break;
4535                }
4536                switch(textAlign){
4537                    case 'center':
4538                        left -= width / 2;
4539                        break;
4540                    case 'right':
4541                        left -= width;
4542                        break;
4543                    case 'inner':
4544                        if (i === ilen - 1) {
4545                            left -= width;
4546                        } else if (i > 0) {
4547                            left -= width / 2;
4548                        }
4549                        break;
4550                }
4551                backdrop = {
4552                    left,
4553                    top,
4554                    width: width + labelPadding.width,
4555                    height: height + labelPadding.height,
4556                    color: optsAtIndex.backdropColor
4557                };
4558            }
4559            items.push({
4560                label,
4561                font,
4562                textOffset,
4563                options: {
4564                    rotation,
4565                    color,
4566                    strokeColor,
4567                    strokeWidth,
4568                    textAlign: tickTextAlign,
4569                    textBaseline,
4570                    translation: [
4571                        x,
4572                        y
4573                    ],
4574                    backdrop
4575                }
4576            });
4577        }
4578        return items;
4579    }
4580    _getXAxisLabelAlignment() {
4581        const { position , ticks  } = this.options;
4582        const rotation = -toRadians(this.labelRotation);
4583        if (rotation) {
4584            return position === 'top' ? 'left' : 'right';
4585        }
4586        let align = 'center';
4587        if (ticks.align === 'start') {
4588            align = 'left';
4589        } else if (ticks.align === 'end') {
4590            align = 'right';
4591        } else if (ticks.align === 'inner') {
4592            align = 'inner';
4593        }
4594        return align;
4595    }
4596    _getYAxisLabelAlignment(tl) {
4597        const { position , ticks: { crossAlign , mirror , padding  }  } = this.options;
4598        const labelSizes = this._getLabelSizes();
4599        const tickAndPadding = tl + padding;
4600        const widest = labelSizes.widest.width;
4601        let textAlign;
4602        let x;
4603        if (position === 'left') {
4604            if (mirror) {
4605                x = this.right + padding;
4606                if (crossAlign === 'near') {
4607                    textAlign = 'left';
4608                } else if (crossAlign === 'center') {
4609                    textAlign = 'center';
4610                    x += widest / 2;
4611                } else {
4612                    textAlign = 'right';
4613                    x += widest;
4614                }
4615            } else {
4616                x = this.right - tickAndPadding;
4617                if (crossAlign === 'near') {
4618                    textAlign = 'right';
4619                } else if (crossAlign === 'center') {
4620                    textAlign = 'center';
4621                    x -= widest / 2;
4622                } else {
4623                    textAlign = 'left';
4624                    x = this.left;
4625                }
4626            }
4627        } else if (position === 'right') {
4628            if (mirror) {
4629                x = this.left + padding;
4630                if (crossAlign === 'near') {
4631                    textAlign = 'right';
4632                } else if (crossAlign === 'center') {
4633                    textAlign = 'center';
4634                    x -= widest / 2;
4635                } else {
4636                    textAlign = 'left';
4637                    x -= widest;
4638                }
4639            } else {
4640                x = this.left + tickAndPadding;
4641                if (crossAlign === 'near') {
4642                    textAlign = 'left';
4643                } else if (crossAlign === 'center') {
4644                    textAlign = 'center';
4645                    x += widest / 2;
4646                } else {
4647                    textAlign = 'right';
4648                    x = this.right;
4649                }
4650            }
4651        } else {
4652            textAlign = 'right';
4653        }
4654        return {
4655            textAlign,
4656            x
4657        };
4658    }
4659 _computeLabelArea() {
4660        if (this.options.ticks.mirror) {
4661            return;
4662        }
4663        const chart = this.chart;
4664        const position = this.options.position;
4665        if (position === 'left' || position === 'right') {
4666            return {
4667                top: 0,
4668                left: this.left,
4669                bottom: chart.height,
4670                right: this.right
4671            };
4672        }
4673        if (position === 'top' || position === 'bottom') {
4674            return {
4675                top: this.top,
4676                left: 0,
4677                bottom: this.bottom,
4678                right: chart.width
4679            };
4680        }
4681    }
4682 drawBackground() {
4683        const { ctx , options: { backgroundColor  } , left , top , width , height  } = this;
4684        if (backgroundColor) {
4685            ctx.save();
4686            ctx.fillStyle = backgroundColor;
4687            ctx.fillRect(left, top, width, height);
4688            ctx.restore();
4689        }
4690    }
4691    getLineWidthForValue(value) {
4692        const grid = this.options.grid;
4693        if (!this._isVisible() || !grid.display) {
4694            return 0;
4695        }
4696        const ticks = this.ticks;
4697        const index = ticks.findIndex((t)=>t.value === value);
4698        if (index >= 0) {
4699            const opts = grid.setContext(this.getContext(index));
4700            return opts.lineWidth;
4701        }
4702        return 0;
4703    }
4704 drawGrid(chartArea) {
4705        const grid = this.options.grid;
4706        const ctx = this.ctx;
4707        const items = this._gridLineItems || (this._gridLineItems = this._computeGridLineItems(chartArea));
4708        let i, ilen;
4709        const drawLine = (p1, p2, style)=>{
4710            if (!style.width || !style.color) {
4711                return;
4712            }
4713            ctx.save();
4714            ctx.lineWidth = style.width;
4715            ctx.strokeStyle = style.color;
4716            ctx.setLineDash(style.borderDash || []);
4717            ctx.lineDashOffset = style.borderDashOffset;
4718            ctx.beginPath();
4719            ctx.moveTo(p1.x, p1.y);
4720            ctx.lineTo(p2.x, p2.y);
4721            ctx.stroke();
4722            ctx.restore();
4723        };
4724        if (grid.display) {
4725            for(i = 0, ilen = items.length; i < ilen; ++i){
4726                const item = items[i];
4727                if (grid.drawOnChartArea) {
4728                    drawLine({
4729                        x: item.x1,
4730                        y: item.y1
4731                    }, {
4732                        x: item.x2,
4733                        y: item.y2
4734                    }, item);
4735                }
4736                if (grid.drawTicks) {
4737                    drawLine({
4738                        x: item.tx1,
4739                        y: item.ty1
4740                    }, {
4741                        x: item.tx2,
4742                        y: item.ty2
4743                    }, {
4744                        color: item.tickColor,
4745                        width: item.tickWidth,
4746                        borderDash: item.tickBorderDash,
4747                        borderDashOffset: item.tickBorderDashOffset
4748                    });
4749                }
4750            }
4751        }
4752    }
4753 drawBorder() {
4754        const { chart , ctx , options: { border , grid  }  } = this;
4755        const borderOpts = border.setContext(this.getContext());
4756        const axisWidth = border.display ? borderOpts.width : 0;
4757        if (!axisWidth) {
4758            return;
4759        }
4760        const lastLineWidth = grid.setContext(this.getContext(0)).lineWidth;
4761        const borderValue = this._borderValue;
4762        let x1, x2, y1, y2;
4763        if (this.isHorizontal()) {
4764            x1 = _alignPixel(chart, this.left, axisWidth) - axisWidth / 2;
4765            x2 = _alignPixel(chart, this.right, lastLineWidth) + lastLineWidth / 2;
4766            y1 = y2 = borderValue;
4767        } else {
4768            y1 = _alignPixel(chart, this.top, axisWidth) - axisWidth / 2;
4769            y2 = _alignPixel(chart, this.bottom, lastLineWidth) + lastLineWidth / 2;
4770            x1 = x2 = borderValue;
4771        }
4772        ctx.save();
4773        ctx.lineWidth = borderOpts.width;
4774        ctx.strokeStyle = borderOpts.color;
4775        ctx.beginPath();
4776        ctx.moveTo(x1, y1);
4777        ctx.lineTo(x2, y2);
4778        ctx.stroke();
vendor: 23,306 bytes, lines 4779-5521
4779        ctx.restore();
4780    }
4781 drawLabels(chartArea) {
4782        const optionTicks = this.options.ticks;
4783        if (!optionTicks.display) {
4784            return;
4785        }
4786        const ctx = this.ctx;
4787        const area = this._computeLabelArea();
4788        if (area) {
4789            clipArea(ctx, area);
4790        }
4791        const items = this.getLabelItems(chartArea);
4792        for (const item of items){
4793            const renderTextOptions = item.options;
4794            const tickFont = item.font;
4795            const label = item.label;
4796            const y = item.textOffset;
4797            renderText(ctx, label, 0, y, tickFont, renderTextOptions);
4798        }
4799        if (area) {
4800            unclipArea(ctx);
4801        }
4802    }
4803 drawTitle() {
4804        const { ctx , options: { position , title , reverse  }  } = this;
4805        if (!title.display) {
4806            return;
4807        }
4808        const font = toFont(title.font);
4809        const padding = toPadding(title.padding);
4810        const align = title.align;
4811        let offset = font.lineHeight / 2;
4812        if (position === 'bottom' || position === 'center' || isObject(position)) {
4813            offset += padding.bottom;
4814            if (isArray(title.text)) {
4815                offset += font.lineHeight * (title.text.length - 1);
4816            }
4817        } else {
4818            offset += padding.top;
4819        }
4820        const { titleX , titleY , maxWidth , rotation  } = titleArgs(this, offset, position, align);
4821        renderText(ctx, title.text, 0, 0, font, {
4822            color: title.color,
4823            maxWidth,
4824            rotation,
4825            textAlign: titleAlign(align, position, reverse),
4826            textBaseline: 'middle',
4827            translation: [
4828                titleX,
4829                titleY
4830            ]
4831        });
4832    }
4833    draw(chartArea) {
4834        if (!this._isVisible()) {
4835            return;
4836        }
4837        this.drawBackground();
4838        this.drawGrid(chartArea);
4839        this.drawBorder();
4840        this.drawTitle();
4841        this.drawLabels(chartArea);
4842    }
4843 _layers() {
4844        const opts = this.options;
4845        const tz = opts.ticks && opts.ticks.z || 0;
4846        const gz = valueOrDefault(opts.grid && opts.grid.z, -1);
4847        const bz = valueOrDefault(opts.border && opts.border.z, 0);
4848        if (!this._isVisible() || this.draw !== Scale.prototype.draw) {
4849            return [
4850                {
4851                    z: tz,
4852                    draw: (chartArea)=>{
4853                        this.draw(chartArea);
4854                    }
4855                }
4856            ];
4857        }
4858        return [
4859            {
4860                z: gz,
4861                draw: (chartArea)=>{
4862                    this.drawBackground();
4863                    this.drawGrid(chartArea);
4864                    this.drawTitle();
4865                }
4866            },
4867            {
4868                z: bz,
4869                draw: ()=>{
4870                    this.drawBorder();
4871                }
4872            },
4873            {
4874                z: tz,
4875                draw: (chartArea)=>{
4876                    this.drawLabels(chartArea);
4877                }
4878            }
4879        ];
4880    }
4881 getMatchingVisibleMetas(type) {
4882        const metas = this.chart.getSortedVisibleDatasetMetas();
4883        const axisID = this.axis + 'AxisID';
4884        const result = [];
4885        let i, ilen;
4886        for(i = 0, ilen = metas.length; i < ilen; ++i){
4887            const meta = metas[i];
4888            if (meta[axisID] === this.id && (!type || meta.type === type)) {
4889                result.push(meta);
4890            }
4891        }
4892        return result;
4893    }
4894 _resolveTickFontOptions(index) {
4895        const opts = this.options.ticks.setContext(this.getContext(index));
4896        return toFont(opts.font);
4897    }
4898 _maxDigits() {
4899        const fontSize = this._resolveTickFontOptions(0).lineHeight;
4900        return (this.isHorizontal() ? this.width : this.height) / fontSize;
4901    }
4902}
4903
4904class TypedRegistry {
4905    constructor(type, scope, override){
4906        this.type = type;
4907        this.scope = scope;
4908        this.override = override;
4909        this.items = Object.create(null);
4910    }
4911    isForType(type) {
4912        return Object.prototype.isPrototypeOf.call(this.type.prototype, type.prototype);
4913    }
4914 register(item) {
4915        const proto = Object.getPrototypeOf(item);
4916        let parentScope;
4917        if (isIChartComponent(proto)) {
4918            parentScope = this.register(proto);
4919        }
4920        const items = this.items;
4921        const id = item.id;
4922        const scope = this.scope + '.' + id;
4923        if (!id) {
4924            throw new Error('class does not have id: ' + item);
4925        }
4926        if (id in items) {
4927            return scope;
4928        }
4929        items[id] = item;
4930        registerDefaults(item, scope, parentScope);
4931        if (this.override) {
4932            defaults.override(item.id, item.overrides);
4933        }
4934        return scope;
4935    }
4936 get(id) {
4937        return this.items[id];
4938    }
4939 unregister(item) {
4940        const items = this.items;
4941        const id = item.id;
4942        const scope = this.scope;
4943        if (id in items) {
4944            delete items[id];
4945        }
4946        if (scope && id in defaults[scope]) {
4947            delete defaults[scope][id];
4948            if (this.override) {
4949                delete overrides[id];
4950            }
4951        }
4952    }
4953}
4954function registerDefaults(item, scope, parentScope) {
4955    const itemDefaults = merge(Object.create(null), [
4956        parentScope ? defaults.get(parentScope) : {},
4957        defaults.get(scope),
4958        item.defaults
4959    ]);
4960    defaults.set(scope, itemDefaults);
4961    if (item.defaultRoutes) {
4962        routeDefaults(scope, item.defaultRoutes);
4963    }
4964    if (item.descriptors) {
4965        defaults.describe(scope, item.descriptors);
4966    }
4967}
4968function routeDefaults(scope, routes) {
4969    Object.keys(routes).forEach((property)=>{
4970        const propertyParts = property.split('.');
4971        const sourceName = propertyParts.pop();
4972        const sourceScope = [
4973            scope
4974        ].concat(propertyParts).join('.');
4975        const parts = routes[property].split('.');
4976        const targetName = parts.pop();
4977        const targetScope = parts.join('.');
4978        defaults.route(sourceScope, sourceName, targetScope, targetName);
4979    });
4980}
4981function isIChartComponent(proto) {
4982    return 'id' in proto && 'defaults' in proto;
4983}
4984
4985class Registry {
4986    constructor(){
4987        this.controllers = new TypedRegistry(DatasetController, 'datasets', true);
4988        this.elements = new TypedRegistry(Element, 'elements');
4989        this.plugins = new TypedRegistry(Object, 'plugins');
4990        this.scales = new TypedRegistry(Scale, 'scales');
4991        this._typedRegistries = [
4992            this.controllers,
4993            this.scales,
4994            this.elements
4995        ];
4996    }
4997 add(...args) {
4998        this._each('register', args);
4999    }
5000    remove(...args) {
5001        this._each('unregister', args);
5002    }
5003 addControllers(...args) {
5004        this._each('register', args, this.controllers);
5005    }
5006 addElements(...args) {
5007        this._each('register', args, this.elements);
5008    }
5009 addPlugins(...args) {
5010        this._each('register', args, this.plugins);
5011    }
5012 addScales(...args) {
5013        this._each('register', args, this.scales);
5014    }
5015 getController(id) {
5016        return this._get(id, this.controllers, 'controller');
5017    }
5018 getElement(id) {
5019        return this._get(id, this.elements, 'element');
5020    }
5021 getPlugin(id) {
5022        return this._get(id, this.plugins, 'plugin');
5023    }
5024 getScale(id) {
5025        return this._get(id, this.scales, 'scale');
5026    }
5027 removeControllers(...args) {
5028        this._each('unregister', args, this.controllers);
5029    }
5030 removeElements(...args) {
5031        this._each('unregister', args, this.elements);
5032    }
5033 removePlugins(...args) {
5034        this._each('unregister', args, this.plugins);
5035    }
5036 removeScales(...args) {
5037        this._each('unregister', args, this.scales);
5038    }
5039 _each(method, args, typedRegistry) {
5040        [
5041            ...args
5042        ].forEach((arg)=>{
5043            const reg = typedRegistry || this._getRegistryForType(arg);
5044            if (typedRegistry || reg.isForType(arg) || reg === this.plugins && arg.id) {
5045                this._exec(method, reg, arg);
5046            } else {
5047                each(arg, (item)=>{
5048                    const itemReg = typedRegistry || this._getRegistryForType(item);
5049                    this._exec(method, itemReg, item);
5050                });
5051            }
5052        });
5053    }
5054 _exec(method, registry, component) {
5055        const camelMethod = _capitalize(method);
5056        callback(component['before' + camelMethod], [], component);
5057        registry[method](component);
5058        callback(component['after' + camelMethod], [], component);
5059    }
5060 _getRegistryForType(type) {
5061        for(let i = 0; i < this._typedRegistries.length; i++){
5062            const reg = this._typedRegistries[i];
5063            if (reg.isForType(type)) {
5064                return reg;
5065            }
5066        }
5067        return this.plugins;
5068    }
5069 _get(id, typedRegistry, type) {
5070        const item = typedRegistry.get(id);
5071        if (item === undefined) {
5072            throw new Error('"' + id + '" is not a registered ' + type + '.');
5073        }
5074        return item;
5075    }
5076}
5077var registry = /* #__PURE__ */ new Registry();
5078
5079class PluginService {
5080    constructor(){
5081        this._init = [];
5082    }
5083 notify(chart, hook, args, filter) {
5084        if (hook === 'beforeInit') {
5085            this._init = this._createDescriptors(chart, true);
5086            this._notify(this._init, chart, 'install');
5087        }
5088        const descriptors = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart);
5089        const result = this._notify(descriptors, chart, hook, args);
5090        if (hook === 'afterDestroy') {
5091            this._notify(descriptors, chart, 'stop');
5092            this._notify(this._init, chart, 'uninstall');
5093        }
5094        return result;
5095    }
5096 _notify(descriptors, chart, hook, args) {
5097        args = args || {};
5098        for (const descriptor of descriptors){
5099            const plugin = descriptor.plugin;
5100            const method = plugin[hook];
5101            const params = [
5102                chart,
5103                args,
5104                descriptor.options
5105            ];
5106            if (callback(method, params, plugin) === false && args.cancelable) {
5107                return false;
5108            }
5109        }
5110        return true;
5111    }
5112    invalidate() {
5113        if (!isNullOrUndef(this._cache)) {
5114            this._oldCache = this._cache;
5115            this._cache = undefined;
5116        }
5117    }
5118 _descriptors(chart) {
5119        if (this._cache) {
5120            return this._cache;
5121        }
5122        const descriptors = this._cache = this._createDescriptors(chart);
5123        this._notifyStateChanges(chart);
5124        return descriptors;
5125    }
5126    _createDescriptors(chart, all) {
5127        const config = chart && chart.config;
5128        const options = valueOrDefault(config.options && config.options.plugins, {});
5129        const plugins = allPlugins(config);
5130        return options === false && !all ? [] : createDescriptors(chart, plugins, options, all);
5131    }
5132 _notifyStateChanges(chart) {
5133        const previousDescriptors = this._oldCache || [];
5134        const descriptors = this._cache;
5135        const diff = (a, b)=>a.filter((x)=>!b.some((y)=>x.plugin.id === y.plugin.id));
5136        this._notify(diff(previousDescriptors, descriptors), chart, 'stop');
5137        this._notify(diff(descriptors, previousDescriptors), chart, 'start');
5138    }
5139}
5140 function allPlugins(config) {
5141    const localIds = {};
5142    const plugins = [];
5143    const keys = Object.keys(registry.plugins.items);
5144    for(let i = 0; i < keys.length; i++){
5145        plugins.push(registry.getPlugin(keys[i]));
5146    }
5147    const local = config.plugins || [];
5148    for(let i = 0; i < local.length; i++){
5149        const plugin = local[i];
5150        if (plugins.indexOf(plugin) === -1) {
5151            plugins.push(plugin);
5152            localIds[plugin.id] = true;
5153        }
5154    }
5155    return {
5156        plugins,
5157        localIds
5158    };
5159}
5160function getOpts(options, all) {
5161    if (!all && options === false) {
5162        return null;
5163    }
5164    if (options === true) {
5165        return {};
5166    }
5167    return options;
5168}
5169function createDescriptors(chart, { plugins , localIds  }, options, all) {
5170    const result = [];
5171    const context = chart.getContext();
5172    for (const plugin of plugins){
5173        const id = plugin.id;
5174        const opts = getOpts(options[id], all);
5175        if (opts === null) {
5176            continue;
5177        }
5178        result.push({
5179            plugin,
5180            options: pluginOpts(chart.config, {
5181                plugin,
5182                local: localIds[id]
5183            }, opts, context)
5184        });
5185    }
5186    return result;
5187}
5188function pluginOpts(config, { plugin , local  }, opts, context) {
5189    const keys = config.pluginScopeKeys(plugin);
5190    const scopes = config.getOptionScopes(opts, keys);
5191    if (local && plugin.defaults) {
5192        scopes.push(plugin.defaults);
5193    }
5194    return config.createResolver(scopes, context, [
5195        ''
5196    ], {
5197        scriptable: false,
5198        indexable: false,
5199        allKeys: true
5200    });
5201}
5202
5203function getIndexAxis(type, options) {
5204    const datasetDefaults = defaults.datasets[type] || {};
5205    const datasetOptions = (options.datasets || {})[type] || {};
5206    return datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || 'x';
5207}
5208function getAxisFromDefaultScaleID(id, indexAxis) {
5209    let axis = id;
5210    if (id === '_index_') {
5211        axis = indexAxis;
5212    } else if (id === '_value_') {
5213        axis = indexAxis === 'x' ? 'y' : 'x';
5214    }
5215    return axis;
5216}
5217function getDefaultScaleIDFromAxis(axis, indexAxis) {
5218    return axis === indexAxis ? '_index_' : '_value_';
5219}
5220function idMatchesAxis(id) {
5221    if (id === 'x' || id === 'y' || id === 'r') {
5222        return id;
5223    }
5224}
5225function axisFromPosition(position) {
5226    if (position === 'top' || position === 'bottom') {
5227        return 'x';
5228    }
5229    if (position === 'left' || position === 'right') {
5230        return 'y';
5231    }
5232}
5233function determineAxis(id, ...scaleOptions) {
5234    if (idMatchesAxis(id)) {
5235        return id;
5236    }
5237    for (const opts of scaleOptions){
5238        const axis = opts.axis || axisFromPosition(opts.position) || id.length > 1 && idMatchesAxis(id[0].toLowerCase());
5239        if (axis) {
5240            return axis;
5241        }
5242    }
5243    throw new Error(`Cannot determine type of '${id}' axis. Please provide 'axis' or 'position' option.`);
5244}
5245function getAxisFromDataset(id, axis, dataset) {
5246    if (dataset[axis + 'AxisID'] === id) {
5247        return {
5248            axis
5249        };
5250    }
5251}
5252function retrieveAxisFromDatasets(id, config) {
5253    if (config.data && config.data.datasets) {
5254        const boundDs = config.data.datasets.filter((d)=>d.xAxisID === id || d.yAxisID === id);
5255        if (boundDs.length) {
5256            return getAxisFromDataset(id, 'x', boundDs[0]) || getAxisFromDataset(id, 'y', boundDs[0]);
5257        }
5258    }
5259    return {};
5260}
5261function mergeScaleConfig(config, options) {
5262    const chartDefaults = overrides[config.type] || {
5263        scales: {}
5264    };
5265    const configScales = options.scales || {};
5266    const chartIndexAxis = getIndexAxis(config.type, options);
5267    const scales = Object.create(null);
5268    Object.keys(configScales).forEach((id)=>{
5269        const scaleConf = configScales[id];
5270        if (!isObject(scaleConf)) {
5271            return console.error(`Invalid scale configuration for scale: ${id}`);
5272        }
5273        if (scaleConf._proxy) {
5274            return console.warn(`Ignoring resolver passed as options for scale: ${id}`);
5275        }
5276        const axis = determineAxis(id, scaleConf, retrieveAxisFromDatasets(id, config), defaults.scales[scaleConf.type]);
5277        const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis);
5278        const defaultScaleOptions = chartDefaults.scales || {};
5279        scales[id] = mergeIf(Object.create(null), [
5280            {
5281                axis
5282            },
5283            scaleConf,
5284            defaultScaleOptions[axis],
5285            defaultScaleOptions[defaultId]
5286        ]);
5287    });
5288    config.data.datasets.forEach((dataset)=>{
5289        const type = dataset.type || config.type;
5290        const indexAxis = dataset.indexAxis || getIndexAxis(type, options);
5291        const datasetDefaults = overrides[type] || {};
5292        const defaultScaleOptions = datasetDefaults.scales || {};
5293        Object.keys(defaultScaleOptions).forEach((defaultID)=>{
5294            const axis = getAxisFromDefaultScaleID(defaultID, indexAxis);
5295            const id = dataset[axis + 'AxisID'] || axis;
5296            scales[id] = scales[id] || Object.create(null);
5297            mergeIf(scales[id], [
5298                {
5299                    axis
5300                },
5301                configScales[id],
5302                defaultScaleOptions[defaultID]
5303            ]);
5304        });
5305    });
5306    Object.keys(scales).forEach((key)=>{
5307        const scale = scales[key];
5308        mergeIf(scale, [
5309            defaults.scales[scale.type],
5310            defaults.scale
5311        ]);
5312    });
5313    return scales;
5314}
5315function initOptions(config) {
5316    const options = config.options || (config.options = {});
5317    options.plugins = valueOrDefault(options.plugins, {});
5318    options.scales = mergeScaleConfig(config, options);
5319}
5320function initData(data) {
5321    data = data || {};
5322    data.datasets = data.datasets || [];
5323    data.labels = data.labels || [];
5324    return data;
5325}
5326function initConfig(config) {
5327    config = config || {};
5328    config.data = initData(config.data);
5329    initOptions(config);
5330    return config;
5331}
5332const keyCache = new Map();
5333const keysCached = new Set();
5334function cachedKeys(cacheKey, generate) {
5335    let keys = keyCache.get(cacheKey);
5336    if (!keys) {
5337        keys = generate();
5338        keyCache.set(cacheKey, keys);
5339        keysCached.add(keys);
5340    }
5341    return keys;
5342}
5343const addIfFound = (set, obj, key)=>{
5344    const opts = resolveObjectKey(obj, key);
5345    if (opts !== undefined) {
5346        set.add(opts);
5347    }
5348};
5349class Config {
5350    constructor(config){
5351        this._config = initConfig(config);
5352        this._scopeCache = new Map();
5353        this._resolverCache = new Map();
5354    }
5355    get platform() {
5356        return this._config.platform;
5357    }
5358    get type() {
5359        return this._config.type;
5360    }
5361    set type(type) {
5362        this._config.type = type;
5363    }
5364    get data() {
5365        return this._config.data;
5366    }
5367    set data(data) {
5368        this._config.data = initData(data);
5369    }
5370    get options() {
5371        return this._config.options;
5372    }
5373    set options(options) {
5374        this._config.options = options;
5375    }
5376    get plugins() {
5377        return this._config.plugins;
5378    }
5379    update() {
5380        const config = this._config;
5381        this.clearCache();
5382        initOptions(config);
5383    }
5384    clearCache() {
5385        this._scopeCache.clear();
5386        this._resolverCache.clear();
5387    }
5388 datasetScopeKeys(datasetType) {
5389        return cachedKeys(datasetType, ()=>[
5390                [
5391                    `datasets.${datasetType}`,
5392                    ''
5393                ]
5394            ]);
5395    }
5396 datasetAnimationScopeKeys(datasetType, transition) {
5397        return cachedKeys(`${datasetType}.transition.${transition}`, ()=>[
5398                [
5399                    `datasets.${datasetType}.transitions.${transition}`,
5400                    `transitions.${transition}`
5401                ],
5402                [
5403                    `datasets.${datasetType}`,
5404                    ''
5405                ]
5406            ]);
5407    }
5408 datasetElementScopeKeys(datasetType, elementType) {
5409        return cachedKeys(`${datasetType}-${elementType}`, ()=>[
5410                [
5411                    `datasets.${datasetType}.elements.${elementType}`,
5412                    `datasets.${datasetType}`,
5413                    `elements.${elementType}`,
5414                    ''
5415                ]
5416            ]);
5417    }
5418 pluginScopeKeys(plugin) {
5419        const id = plugin.id;
5420        const type = this.type;
5421        return cachedKeys(`${type}-plugin-${id}`, ()=>[
5422                [
5423                    `plugins.${id}`,
5424                    ...plugin.additionalOptionScopes || []
5425                ]
5426            ]);
5427    }
5428 _cachedScopes(mainScope, resetCache) {
5429        const _scopeCache = this._scopeCache;
5430        let cache = _scopeCache.get(mainScope);
5431        if (!cache || resetCache) {
5432            cache = new Map();
5433            _scopeCache.set(mainScope, cache);
5434        }
5435        return cache;
5436    }
5437 getOptionScopes(mainScope, keyLists, resetCache) {
5438        const { options , type  } = this;
5439        const cache = this._cachedScopes(mainScope, resetCache);
5440        const cached = cache.get(keyLists);
5441        if (cached) {
5442            return cached;
5443        }
5444        const scopes = new Set();
5445        keyLists.forEach((keys)=>{
5446            if (mainScope) {
5447                scopes.add(mainScope);
5448                keys.forEach((key)=>addIfFound(scopes, mainScope, key));
5449            }
5450            keys.forEach((key)=>addIfFound(scopes, options, key));
5451            keys.forEach((key)=>addIfFound(scopes, overrides[type] || {}, key));
5452            keys.forEach((key)=>addIfFound(scopes, defaults, key));
5453            keys.forEach((key)=>addIfFound(scopes, descriptors, key));
5454        });
5455        const array = Array.from(scopes);
5456        if (array.length === 0) {
5457            array.push(Object.create(null));
5458        }
5459        if (keysCached.has(keyLists)) {
5460            cache.set(keyLists, array);
5461        }
5462        return array;
5463    }
5464 chartOptionScopes() {
5465        const { options , type  } = this;
5466        return [
5467            options,
5468            overrides[type] || {},
5469            defaults.datasets[type] || {},
5470            {
5471                type
5472            },
5473            defaults,
5474            descriptors
5475        ];
5476    }
5477 resolveNamedOptions(scopes, names, context, prefixes = [
5478        ''
5479    ]) {
5480        const result = {
5481            $shared: true
5482        };
5483        const { resolver , subPrefixes  } = getResolver(this._resolverCache, scopes, prefixes);
5484        let options = resolver;
5485        if (needContext(resolver, names)) {
5486            result.$shared = false;
5487            context = isFunction(context) ? context() : context;
5488            const subResolver = this.createResolver(scopes, context, subPrefixes);
5489            options = _attachContext(resolver, context, subResolver);
5490        }
5491        for (const prop of names){
5492            result[prop] = options[prop];
5493        }
5494        return result;
5495    }
5496 createResolver(scopes, context, prefixes = [
5497        ''
5498    ], descriptorDefaults) {
5499        const { resolver  } = getResolver(this._resolverCache, scopes, prefixes);
5500        return isObject(context) ? _attachContext(resolver, context, undefined, descriptorDefaults) : resolver;
5501    }
5502}
5503function getResolver(resolverCache, scopes, prefixes) {
5504    let cache = resolverCache.get(scopes);
5505    if (!cache) {
5506        cache = new Map();
5507        resolverCache.set(scopes, cache);
5508    }
5509    const cacheKey = prefixes.join();
5510    let cached = cache.get(cacheKey);
5511    if (!cached) {
5512        const resolver = _createResolver(scopes, prefixes);
5513        cached = {
5514            resolver,
5515            subPrefixes: prefixes.filter((p)=>!p.toLowerCase().includes('hover'))
5516        };
5517        cache.set(cacheKey, cached);
5518    }
5519    return cached;
5520}
5521const hasFunction = (value)=>isObject(value) && Object.getOwnPropertyNames(value).
vendor: 2,939 bytes, lines 5521-5618
5521some((key)=>isFunction(value[key]));
5522function needContext(proxy, names) {
5523    const { isScriptable , isIndexable  } = _descriptors(proxy);
5524    for (const prop of names){
5525        const scriptable = isScriptable(prop);
5526        const indexable = isIndexable(prop);
5527        const value = (indexable || scriptable) && proxy[prop];
5528        if (scriptable && (isFunction(value) || hasFunction(value)) || indexable && isArray(value)) {
5529            return true;
5530        }
5531    }
5532    return false;
5533}
5534
5535var version = "4.4.8";
5536
5537const KNOWN_POSITIONS = [
5538    'top',
5539    'bottom',
5540    'left',
5541    'right',
5542    'chartArea'
5543];
5544function positionIsHorizontal(position, axis) {
5545    return position === 'top' || position === 'bottom' || KNOWN_POSITIONS.indexOf(position) === -1 && axis === 'x';
5546}
5547function compare2Level(l1, l2) {
5548    return function(a, b) {
5549        return a[l1] === b[l1] ? a[l2] - b[l2] : a[l1] - b[l1];
5550    };
5551}
5552function onAnimationsComplete(context) {
5553    const chart = context.chart;
5554    const animationOptions = chart.options.animation;
5555    chart.notifyPlugins('afterRender');
5556    callback(animationOptions && animationOptions.onComplete, [
5557        context
5558    ], chart);
5559}
5560function onAnimationProgress(context) {
5561    const chart = context.chart;
5562    const animationOptions = chart.options.animation;
5563    callback(animationOptions && animationOptions.onProgress, [
5564        context
5565    ], chart);
5566}
5567 function getCanvas(item) {
5568    if (_isDomSupported() && typeof item === 'string') {
5569        item = document.getElementById(item);
5570    } else if (item && item.length) {
5571        item = item[0];
5572    }
5573    if (item && item.canvas) {
5574        item = item.canvas;
5575    }
5576    return item;
5577}
5578const instances = {};
5579const getChart = (key)=>{
5580    const canvas = getCanvas(key);
5581    return Object.values(instances).filter((c)=>c.canvas === canvas).pop();
5582};
5583function moveNumericKeys(obj, start, move) {
5584    const keys = Object.keys(obj);
5585    for (const key of keys){
5586        const intKey = +key;
5587        if (intKey >= start) {
5588            const value = obj[key];
5589            delete obj[key];
5590            if (move > 0 || intKey > start) {
5591                obj[intKey + move] = value;
5592            }
5593        }
5594    }
5595}
5596 function determineLastEvent(e, lastEvent, inChartArea, isClick) {
5597    if (!inChartArea || e.type === 'mouseout') {
5598        return null;
5599    }
5600    if (isClick) {
5601        return lastEvent;
5602    }
5603    return e;
5604}
5605function getSizeForArea(scale, chartArea, field) {
5606    return scale.options.clip ? scale[field] : chartArea[field];
5607}
5608function getDatasetArea(meta, chartArea) {
5609    const { xScale , yScale  } = meta;
5610    if (xScale && yScale) {
5611        return {
5612            left: getSizeForArea(xScale, chartArea, 'left'),
5613            right: getSizeForArea(xScale, chartArea, 'right'),
5614            top: getSizeForArea(yScale, chartArea, 'top'),
5615            bottom: getSizeForArea(yScale, chartArea, 'bottom')
5616        };
5617    }
5618    return chartArea;
vendor: 15,939 bytes, lines 5618-6067
5618
5619}
5620class Chart {
5621    static defaults = defaults;
5622    static instances = instances;
5623    static overrides = overrides;
5624    static registry = registry;
5625    static version = version;
5626    static getChart = getChart;
5627    static register(...items) {
5628        registry.add(...items);
5629        invalidatePlugins();
5630    }
5631    static unregister(...items) {
5632        registry.remove(...items);
5633        invalidatePlugins();
5634    }
5635    constructor(item, userConfig){
5636        const config = this.config = new Config(userConfig);
5637        const initialCanvas = getCanvas(item);
5638        const existingChart = getChart(initialCanvas);
5639        if (existingChart) {
5640            throw new Error('Canvas is already in use. Chart with ID \'' + existingChart.id + '\'' + ' must be destroyed before the canvas with ID \'' + existingChart.canvas.id + '\' can be reused.');
5641        }
5642        const options = config.createResolver(config.chartOptionScopes(), this.getContext());
5643        this.platform = new (config.platform || _detectPlatform(initialCanvas))();
5644        this.platform.updateConfig(config);
5645        const context = this.platform.acquireContext(initialCanvas, options.aspectRatio);
5646        const canvas = context && context.canvas;
5647        const height = canvas && canvas.height;
5648        const width = canvas && canvas.width;
5649        this.id = uid();
5650        this.ctx = context;
5651        this.canvas = canvas;
5652        this.width = width;
5653        this.height = height;
5654        this._options = options;
5655        this._aspectRatio = this.aspectRatio;
5656        this._layers = [];
5657        this._metasets = [];
5658        this._stacks = undefined;
5659        this.boxes = [];
5660        this.currentDevicePixelRatio = undefined;
5661        this.chartArea = undefined;
5662        this._active = [];
5663        this._lastEvent = undefined;
5664        this._listeners = {};
5665         this._responsiveListeners = undefined;
5666        this._sortedMetasets = [];
5667        this.scales = {};
5668        this._plugins = new PluginService();
5669        this.$proxies = {};
5670        this._hiddenIndices = {};
5671        this.attached = false;
5672        this._animationsDisabled = undefined;
5673        this.$context = undefined;
5674        this._doResize = debounce((mode)=>this.update(mode), options.resizeDelay || 0);
5675        this._dataChanges = [];
5676        instances[this.id] = this;
5677        if (!context || !canvas) {
5678            console.error("Failed to create chart: can't acquire context from the given item");
5679            return;
5680        }
5681        animator.listen(this, 'complete', onAnimationsComplete);
5682        animator.listen(this, 'progress', onAnimationProgress);
5683        this._initialize();
5684        if (this.attached) {
5685            this.update();
5686        }
5687    }
5688    get aspectRatio() {
5689        const { options: { aspectRatio , maintainAspectRatio  } , width , height , _aspectRatio  } = this;
5690        if (!isNullOrUndef(aspectRatio)) {
5691            return aspectRatio;
5692        }
5693        if (maintainAspectRatio && _aspectRatio) {
5694            return _aspectRatio;
5695        }
5696        return height ? width / height : null;
5697    }
5698    get data() {
5699        return this.config.data;
5700    }
5701    set data(data) {
5702        this.config.data = data;
5703    }
5704    get options() {
5705        return this._options;
5706    }
5707    set options(options) {
5708        this.config.options = options;
5709    }
5710    get registry() {
5711        return registry;
5712    }
5713 _initialize() {
5714        this.notifyPlugins('beforeInit');
5715        if (this.options.responsive) {
5716            this.resize();
5717        } else {
5718            retinaScale(this, this.options.devicePixelRatio);
5719        }
5720        this.bindEvents();
5721        this.notifyPlugins('afterInit');
5722        return this;
5723    }
5724    clear() {
5725        clearCanvas(this.canvas, this.ctx);
5726        return this;
5727    }
5728    stop() {
5729        animator.stop(this);
5730        return this;
5731    }
5732 resize(width, height) {
5733        if (!animator.running(this)) {
5734            this._resize(width, height);
5735        } else {
5736            this._resizeBeforeDraw = {
5737                width,
5738                height
5739            };
5740        }
5741    }
5742    _resize(width, height) {
5743        const options = this.options;
5744        const canvas = this.canvas;
5745        const aspectRatio = options.maintainAspectRatio && this.aspectRatio;
5746        const newSize = this.platform.getMaximumSize(canvas, width, height, aspectRatio);
5747        const newRatio = options.devicePixelRatio || this.platform.getDevicePixelRatio();
5748        const mode = this.width ? 'resize' : 'attach';
5749        this.width = newSize.width;
5750        this.height = newSize.height;
5751        this._aspectRatio = this.aspectRatio;
5752        if (!retinaScale(this, newRatio, true)) {
5753            return;
5754        }
5755        this.notifyPlugins('resize', {
5756            size: newSize
5757        });
5758        callback(options.onResize, [
5759            this,
5760            newSize
5761        ], this);
5762        if (this.attached) {
5763            if (this._doResize(mode)) {
5764                this.render();
5765            }
5766        }
5767    }
5768    ensureScalesHaveIDs() {
5769        const options = this.options;
5770        const scalesOptions = options.scales || {};
5771        each(scalesOptions, (axisOptions, axisID)=>{
5772            axisOptions.id = axisID;
5773        });
5774    }
5775 buildOrUpdateScales() {
5776        const options = this.options;
5777        const scaleOpts = options.scales;
5778        const scales = this.scales;
5779        const updated = Object.keys(scales).reduce((obj, id)=>{
5780            obj[id] = false;
5781            return obj;
5782        }, {});
5783        let items = [];
5784        if (scaleOpts) {
5785            items = items.concat(Object.keys(scaleOpts).map((id)=>{
5786                const scaleOptions = scaleOpts[id];
5787                const axis = determineAxis(id, scaleOptions);
5788                const isRadial = axis === 'r';
5789                const isHorizontal = axis === 'x';
5790                return {
5791                    options: scaleOptions,
5792                    dposition: isRadial ? 'chartArea' : isHorizontal ? 'bottom' : 'left',
5793                    dtype: isRadial ? 'radialLinear' : isHorizontal ? 'category' : 'linear'
5794                };
5795            }));
5796        }
5797        each(items, (item)=>{
5798            const scaleOptions = item.options;
5799            const id = scaleOptions.id;
5800            const axis = determineAxis(id, scaleOptions);
5801            const scaleType = valueOrDefault(scaleOptions.type, item.dtype);
5802            if (scaleOptions.position === undefined || positionIsHorizontal(scaleOptions.position, axis) !== positionIsHorizontal(item.dposition)) {
5803                scaleOptions.position = item.dposition;
5804            }
5805            updated[id] = true;
5806            let scale = null;
5807            if (id in scales && scales[id].type === scaleType) {
5808                scale = scales[id];
5809            } else {
5810                const scaleClass = registry.getScale(scaleType);
5811                scale = new scaleClass({
5812                    id,
5813                    type: scaleType,
5814                    ctx: this.ctx,
5815                    chart: this
5816                });
5817                scales[scale.id] = scale;
5818            }
5819            scale.init(scaleOptions, options);
5820        });
5821        each(updated, (hasUpdated, id)=>{
5822            if (!hasUpdated) {
5823                delete scales[id];
5824            }
5825        });
5826        each(scales, (scale)=>{
5827            layouts.configure(this, scale, scale.options);
5828            layouts.addBox(this, scale);
5829        });
5830    }
5831 _updateMetasets() {
5832        const metasets = this._metasets;
5833        const numData = this.data.datasets.length;
5834        const numMeta = metasets.length;
5835        metasets.sort((a, b)=>a.index - b.index);
5836        if (numMeta > numData) {
5837            for(let i = numData; i < numMeta; ++i){
5838                this._destroyDatasetMeta(i);
5839            }
5840            metasets.splice(numData, numMeta - numData);
5841        }
5842        this._sortedMetasets = metasets.slice(0).sort(compare2Level('order', 'index'));
5843    }
5844 _removeUnreferencedMetasets() {
5845        const { _metasets: metasets , data: { datasets  }  } = this;
5846        if (metasets.length > datasets.length) {
5847            delete this._stacks;
5848        }
5849        metasets.forEach((meta, index)=>{
5850            if (datasets.filter((x)=>x === meta._dataset).length === 0) {
5851                this._destroyDatasetMeta(index);
5852            }
5853        });
5854    }
5855    buildOrUpdateControllers() {
5856        const newControllers = [];
5857        const datasets = this.data.datasets;
5858        let i, ilen;
5859        this._removeUnreferencedMetasets();
5860        for(i = 0, ilen = datasets.length; i < ilen; i++){
5861            const dataset = datasets[i];
5862            let meta = this.getDatasetMeta(i);
5863            const type = dataset.type || this.config.type;
5864            if (meta.type && meta.type !== type) {
5865                this._destroyDatasetMeta(i);
5866                meta = this.getDatasetMeta(i);
5867            }
5868            meta.type = type;
5869            meta.indexAxis = dataset.indexAxis || getIndexAxis(type, this.options);
5870            meta.order = dataset.order || 0;
5871            meta.index = i;
5872            meta.label = '' + dataset.label;
5873            meta.visible = this.isDatasetVisible(i);
5874            if (meta.controller) {
5875                meta.controller.updateIndex(i);
5876                meta.controller.linkScales();
5877            } else {
5878                const ControllerClass = registry.getController(type);
5879                const { datasetElementType , dataElementType  } = defaults.datasets[type];
5880                Object.assign(ControllerClass, {
5881                    dataElementType: registry.getElement(dataElementType),
5882                    datasetElementType: datasetElementType && registry.getElement(datasetElementType)
5883                });
5884                meta.controller = new ControllerClass(this, i);
5885                newControllers.push(meta.controller);
5886            }
5887        }
5888        this._updateMetasets();
5889        return newControllers;
5890    }
5891 _resetElements() {
5892        each(this.data.datasets, (dataset, datasetIndex)=>{
5893            this.getDatasetMeta(datasetIndex).controller.reset();
5894        }, this);
5895    }
5896 reset() {
5897        this._resetElements();
5898        this.notifyPlugins('reset');
5899    }
5900    update(mode) {
5901        const config = this.config;
5902        config.update();
5903        const options = this._options = config.createResolver(config.chartOptionScopes(), this.getContext());
5904        const animsDisabled = this._animationsDisabled = !options.animation;
5905        this._updateScales();
5906        this._checkEventBindings();
5907        this._updateHiddenIndices();
5908        this._plugins.invalidate();
5909        if (this.notifyPlugins('beforeUpdate', {
5910            mode,
5911            cancelable: true
5912        }) === false) {
5913            return;
5914        }
5915        const newControllers = this.buildOrUpdateControllers();
5916        this.notifyPlugins('beforeElementsUpdate');
5917        let minPadding = 0;
5918        for(let i = 0, ilen = this.data.datasets.length; i < ilen; i++){
5919            const { controller  } = this.getDatasetMeta(i);
5920            const reset = !animsDisabled && newControllers.indexOf(controller) === -1;
5921            controller.buildOrUpdateElements(reset);
5922            minPadding = Math.max(+controller.getMaxOverflow(), minPadding);
5923        }
5924        minPadding = this._minPadding = options.layout.autoPadding ? minPadding : 0;
5925        this._updateLayout(minPadding);
5926        if (!animsDisabled) {
5927            each(newControllers, (controller)=>{
5928                controller.reset();
5929            });
5930        }
5931        this._updateDatasets(mode);
5932        this.notifyPlugins('afterUpdate', {
5933            mode
5934        });
5935        this._layers.sort(compare2Level('z', '_idx'));
5936        const { _active , _lastEvent  } = this;
5937        if (_lastEvent) {
5938            this._eventHandler(_lastEvent, true);
5939        } else if (_active.length) {
5940            this._updateHoverStyles(_active, _active, true);
5941        }
5942        this.render();
5943    }
5944 _updateScales() {
5945        each(this.scales, (scale)=>{
5946            layouts.removeBox(this, scale);
5947        });
5948        this.ensureScalesHaveIDs();
5949        this.buildOrUpdateScales();
5950    }
5951 _checkEventBindings() {
5952        const options = this.options;
5953        const existingEvents = new Set(Object.keys(this._listeners));
5954        const newEvents = new Set(options.events);
5955        if (!setsEqual(existingEvents, newEvents) || !!this._responsiveListeners !== options.responsive) {
5956            this.unbindEvents();
5957            this.bindEvents();
5958        }
5959    }
5960 _updateHiddenIndices() {
5961        const { _hiddenIndices  } = this;
5962        const changes = this._getUniformDataChanges() || [];
5963        for (const { method , start , count  } of changes){
5964            const move = method === '_removeElements' ? -count : count;
5965            moveNumericKeys(_hiddenIndices, start, move);
5966        }
5967    }
5968 _getUniformDataChanges() {
5969        const _dataChanges = this._dataChanges;
5970        if (!_dataChanges || !_dataChanges.length) {
5971            return;
5972        }
5973        this._dataChanges = [];
5974        const datasetCount = this.data.datasets.length;
5975        const makeSet = (idx)=>new Set(_dataChanges.filter((c)=>c[0] === idx).map((c, i)=>i + ',' + c.splice(1).join(',')));
5976        const changeSet = makeSet(0);
5977        for(let i = 1; i < datasetCount; i++){
5978            if (!setsEqual(changeSet, makeSet(i))) {
5979                return;
5980            }
5981        }
5982        return Array.from(changeSet).map((c)=>c.split(',')).map((a)=>({
5983                method: a[1],
5984                start: +a[2],
5985                count: +a[3]
5986            }));
5987    }
5988 _updateLayout(minPadding) {
5989        if (this.notifyPlugins('beforeLayout', {
5990            cancelable: true
5991        }) === false) {
5992            return;
5993        }
5994        layouts.update(this, this.width, this.height, minPadding);
5995        const area = this.chartArea;
5996        const noArea = area.width <= 0 || area.height <= 0;
5997        this._layers = [];
5998        each(this.boxes, (box)=>{
5999            if (noArea && box.position === 'chartArea') {
6000                return;
6001            }
6002            if (box.configure) {
6003                box.configure();
6004            }
6005            this._layers.push(...box._layers());
6006        }, this);
6007        this._layers.forEach((item, index)=>{
6008            item._idx = index;
6009        });
6010        this.notifyPlugins('afterLayout');
6011    }
6012 _updateDatasets(mode) {
6013        if (this.notifyPlugins('beforeDatasetsUpdate', {
6014            mode,
6015            cancelable: true
6016        }) === false) {
6017            return;
6018        }
6019        for(let i = 0, ilen = this.data.datasets.length; i < ilen; ++i){
6020            this.getDatasetMeta(i).controller.configure();
6021        }
6022        for(let i = 0, ilen = this.data.datasets.length; i < ilen; ++i){
6023            this._updateDataset(i, isFunction(mode) ? mode({
6024                datasetIndex: i
6025            }) : mode);
6026        }
6027        this.notifyPlugins('afterDatasetsUpdate', {
6028            mode
6029        });
6030    }
6031 _updateDataset(index, mode) {
6032        const meta = this.getDatasetMeta(index);
6033        const args = {
6034            meta,
6035            index,
6036            mode,
6037            cancelable: true
6038        };
6039        if (this.notifyPlugins('beforeDatasetUpdate', args) === false) {
6040            return;
6041        }
6042        meta.controller._update(mode);
6043        args.cancelable = false;
6044        this.notifyPlugins('afterDatasetUpdate', args);
6045    }
6046    render() {
6047        if (this.notifyPlugins('beforeRender', {
6048            cancelable: true
6049        }) === false) {
6050            return;
6051        }
6052        if (animator.has(this)) {
6053            if (this.attached && !animator.running(this)) {
6054                animator.start(this);
6055            }
6056        } else {
6057            this.draw();
6058            onAnimationsComplete({
6059                chart: this
6060            });
6061        }
6062    }
6063    draw() {
6064        let i;
6065        if (this._resizeBeforeDraw) {
6066            const { width , height  } = this._resizeBeforeDraw;
6067            this._resize
vendor: 1,654 bytes, lines 6067-6120
6067BeforeDraw = null;
6068            this._resize(width, height);
6069        }
6070        this.clear();
6071        if (this.width <= 0 || this.height <= 0) {
6072            return;
6073        }
6074        if (this.notifyPlugins('beforeDraw', {
6075            cancelable: true
6076        }) === false) {
6077            return;
6078        }
6079        const layers = this._layers;
6080        for(i = 0; i < layers.length && layers[i].z <= 0; ++i){
6081            layers[i].draw(this.chartArea);
6082        }
6083        this._drawDatasets();
6084        for(; i < layers.length; ++i){
6085            layers[i].draw(this.chartArea);
6086        }
6087        this.notifyPlugins('afterDraw');
6088    }
6089 _getSortedDatasetMetas(filterVisible) {
6090        const metasets = this._sortedMetasets;
6091        const result = [];
6092        let i, ilen;
6093        for(i = 0, ilen = metasets.length; i < ilen; ++i){
6094            const meta = metasets[i];
6095            if (!filterVisible || meta.visible) {
6096                result.push(meta);
6097            }
6098        }
6099        return result;
6100    }
6101 getSortedVisibleDatasetMetas() {
6102        return this._getSortedDatasetMetas(true);
6103    }
6104 _drawDatasets() {
6105        if (this.notifyPlugins('beforeDatasetsDraw', {
6106            cancelable: true
6107        }) === false) {
6108            return;
6109        }
6110        const metasets = this.getSortedVisibleDatasetMetas();
6111        for(let i = metasets.length - 1; i >= 0; --i){
6112            this._drawDataset(metasets[i]);
6113        }
6114        this.notifyPlugins('afterDatasetsDraw');
6115    }
6116 _drawDataset(meta) {
6117        const ctx = this.ctx;
6118        const clip = meta._clip;
6119        const useClip = !clip.disabled;
6120        const area = getDatasetArea(meta, this.chartArea)
vendor: 22,238 bytes, lines 6120-6707
6120;
6121        const args = {
6122            meta,
6123            index: meta.index,
6124            cancelable: true
6125        };
6126        if (this.notifyPlugins('beforeDatasetDraw', args) === false) {
6127            return;
6128        }
6129        if (useClip) {
6130            clipArea(ctx, {
6131                left: clip.left === false ? 0 : area.left - clip.left,
6132                right: clip.right === false ? this.width : area.right + clip.right,
6133                top: clip.top === false ? 0 : area.top - clip.top,
6134                bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom
6135            });
6136        }
6137        meta.controller.draw();
6138        if (useClip) {
6139            unclipArea(ctx);
6140        }
6141        args.cancelable = false;
6142        this.notifyPlugins('afterDatasetDraw', args);
6143    }
6144 isPointInArea(point) {
6145        return _isPointInArea(point, this.chartArea, this._minPadding);
6146    }
6147    getElementsAtEventForMode(e, mode, options, useFinalPosition) {
6148        const method = Interaction.modes[mode];
6149        if (typeof method === 'function') {
6150            return method(this, e, options, useFinalPosition);
6151        }
6152        return [];
6153    }
6154    getDatasetMeta(datasetIndex) {
6155        const dataset = this.data.datasets[datasetIndex];
6156        const metasets = this._metasets;
6157        let meta = metasets.filter((x)=>x && x._dataset === dataset).pop();
6158        if (!meta) {
6159            meta = {
6160                type: null,
6161                data: [],
6162                dataset: null,
6163                controller: null,
6164                hidden: null,
6165                xAxisID: null,
6166                yAxisID: null,
6167                order: dataset && dataset.order || 0,
6168                index: datasetIndex,
6169                _dataset: dataset,
6170                _parsed: [],
6171                _sorted: false
6172            };
6173            metasets.push(meta);
6174        }
6175        return meta;
6176    }
6177    getContext() {
6178        return this.$context || (this.$context = createContext(null, {
6179            chart: this,
6180            type: 'chart'
6181        }));
6182    }
6183    getVisibleDatasetCount() {
6184        return this.getSortedVisibleDatasetMetas().length;
6185    }
6186    isDatasetVisible(datasetIndex) {
6187        const dataset = this.data.datasets[datasetIndex];
6188        if (!dataset) {
6189            return false;
6190        }
6191        const meta = this.getDatasetMeta(datasetIndex);
6192        return typeof meta.hidden === 'boolean' ? !meta.hidden : !dataset.hidden;
6193    }
6194    setDatasetVisibility(datasetIndex, visible) {
6195        const meta = this.getDatasetMeta(datasetIndex);
6196        meta.hidden = !visible;
6197    }
6198    toggleDataVisibility(index) {
6199        this._hiddenIndices[index] = !this._hiddenIndices[index];
6200    }
6201    getDataVisibility(index) {
6202        return !this._hiddenIndices[index];
6203    }
6204 _updateVisibility(datasetIndex, dataIndex, visible) {
6205        const mode = visible ? 'show' : 'hide';
6206        const meta = this.getDatasetMeta(datasetIndex);
6207        const anims = meta.controller._resolveAnimations(undefined, mode);
6208        if (defined(dataIndex)) {
6209            meta.data[dataIndex].hidden = !visible;
6210            this.update();
6211        } else {
6212            this.setDatasetVisibility(datasetIndex, visible);
6213            anims.update(meta, {
6214                visible
6215            });
6216            this.update((ctx)=>ctx.datasetIndex === datasetIndex ? mode : undefined);
6217        }
6218    }
6219    hide(datasetIndex, dataIndex) {
6220        this._updateVisibility(datasetIndex, dataIndex, false);
6221    }
6222    show(datasetIndex, dataIndex) {
6223        this._updateVisibility(datasetIndex, dataIndex, true);
6224    }
6225 _destroyDatasetMeta(datasetIndex) {
6226        const meta = this._metasets[datasetIndex];
6227        if (meta && meta.controller) {
6228            meta.controller._destroy();
6229        }
6230        delete this._metasets[datasetIndex];
6231    }
6232    _stop() {
6233        let i, ilen;
6234        this.stop();
6235        animator.remove(this);
6236        for(i = 0, ilen = this.data.datasets.length; i < ilen; ++i){
6237            this._destroyDatasetMeta(i);
6238        }
6239    }
6240    destroy() {
6241        this.notifyPlugins('beforeDestroy');
6242        const { canvas , ctx  } = this;
6243        this._stop();
6244        this.config.clearCache();
6245        if (canvas) {
6246            this.unbindEvents();
6247            clearCanvas(canvas, ctx);
6248            this.platform.releaseContext(ctx);
6249            this.canvas = null;
6250            this.ctx = null;
6251        }
6252        delete instances[this.id];
6253        this.notifyPlugins('afterDestroy');
6254    }
6255    toBase64Image(...args) {
6256        return this.canvas.toDataURL(...args);
6257    }
6258 bindEvents() {
6259        this.bindUserEvents();
6260        if (this.options.responsive) {
6261            this.bindResponsiveEvents();
6262        } else {
6263            this.attached = true;
6264        }
6265    }
6266 bindUserEvents() {
6267        const listeners = this._listeners;
6268        const platform = this.platform;
6269        const _add = (type, listener)=>{
6270            platform.addEventListener(this, type, listener);
6271            listeners[type] = listener;
6272        };
6273        const listener = (e, x, y)=>{
6274            e.offsetX = x;
6275            e.offsetY = y;
6276            this._eventHandler(e);
6277        };
6278        each(this.options.events, (type)=>_add(type, listener));
6279    }
6280 bindResponsiveEvents() {
6281        if (!this._responsiveListeners) {
6282            this._responsiveListeners = {};
6283        }
6284        const listeners = this._responsiveListeners;
6285        const platform = this.platform;
6286        const _add = (type, listener)=>{
6287            platform.addEventListener(this, type, listener);
6288            listeners[type] = listener;
6289        };
6290        const _remove = (type, listener)=>{
6291            if (listeners[type]) {
6292                platform.removeEventListener(this, type, listener);
6293                delete listeners[type];
6294            }
6295        };
6296        const listener = (width, height)=>{
6297            if (this.canvas) {
6298                this.resize(width, height);
6299            }
6300        };
6301        let detached;
6302        const attached = ()=>{
6303            _remove('attach', attached);
6304            this.attached = true;
6305            this.resize();
6306            _add('resize', listener);
6307            _add('detach', detached);
6308        };
6309        detached = ()=>{
6310            this.attached = false;
6311            _remove('resize', listener);
6312            this._stop();
6313            this._resize(0, 0);
6314            _add('attach', attached);
6315        };
6316        if (platform.isAttached(this.canvas)) {
6317            attached();
6318        } else {
6319            detached();
6320        }
6321    }
6322 unbindEvents() {
6323        each(this._listeners, (listener, type)=>{
6324            this.platform.removeEventListener(this, type, listener);
6325        });
6326        this._listeners = {};
6327        each(this._responsiveListeners, (listener, type)=>{
6328            this.platform.removeEventListener(this, type, listener);
6329        });
6330        this._responsiveListeners = undefined;
6331    }
6332    updateHoverStyle(items, mode, enabled) {
6333        const prefix = enabled ? 'set' : 'remove';
6334        let meta, item, i, ilen;
6335        if (mode === 'dataset') {
6336            meta = this.getDatasetMeta(items[0].datasetIndex);
6337            meta.controller['_' + prefix + 'DatasetHoverStyle']();
6338        }
6339        for(i = 0, ilen = items.length; i < ilen; ++i){
6340            item = items[i];
6341            const controller = item && this.getDatasetMeta(item.datasetIndex).controller;
6342            if (controller) {
6343                controller[prefix + 'HoverStyle'](item.element, item.datasetIndex, item.index);
6344            }
6345        }
6346    }
6347 getActiveElements() {
6348        return this._active || [];
6349    }
6350 setActiveElements(activeElements) {
6351        const lastActive = this._active || [];
6352        const active = activeElements.map(({ datasetIndex , index  })=>{
6353            const meta = this.getDatasetMeta(datasetIndex);
6354            if (!meta) {
6355                throw new Error('No dataset found at index ' + datasetIndex);
6356            }
6357            return {
6358                datasetIndex,
6359                element: meta.data[index],
6360                index
6361            };
6362        });
6363        const changed = !_elementsEqual(active, lastActive);
6364        if (changed) {
6365            this._active = active;
6366            this._lastEvent = null;
6367            this._updateHoverStyles(active, lastActive);
6368        }
6369    }
6370 notifyPlugins(hook, args, filter) {
6371        return this._plugins.notify(this, hook, args, filter);
6372    }
6373 isPluginEnabled(pluginId) {
6374        return this._plugins._cache.filter((p)=>p.plugin.id === pluginId).length === 1;
6375    }
6376 _updateHoverStyles(active, lastActive, replay) {
6377        const hoverOptions = this.options.hover;
6378        const diff = (a, b)=>a.filter((x)=>!b.some((y)=>x.datasetIndex === y.datasetIndex && x.index === y.index));
6379        const deactivated = diff(lastActive, active);
6380        const activated = replay ? active : diff(active, lastActive);
6381        if (deactivated.length) {
6382            this.updateHoverStyle(deactivated, hoverOptions.mode, false);
6383        }
6384        if (activated.length && hoverOptions.mode) {
6385            this.updateHoverStyle(activated, hoverOptions.mode, true);
6386        }
6387    }
6388 _eventHandler(e, replay) {
6389        const args = {
6390            event: e,
6391            replay,
6392            cancelable: true,
6393            inChartArea: this.isPointInArea(e)
6394        };
6395        const eventFilter = (plugin)=>(plugin.options.events || this.options.events).includes(e.native.type);
6396        if (this.notifyPlugins('beforeEvent', args, eventFilter) === false) {
6397            return;
6398        }
6399        const changed = this._handleEvent(e, replay, args.inChartArea);
6400        args.cancelable = false;
6401        this.notifyPlugins('afterEvent', args, eventFilter);
6402        if (changed || args.changed) {
6403            this.render();
6404        }
6405        return this;
6406    }
6407 _handleEvent(e, replay, inChartArea) {
6408        const { _active: lastActive = [] , options  } = this;
6409        const useFinalPosition = replay;
6410        const active = this._getActiveElements(e, lastActive, inChartArea, useFinalPosition);
6411        const isClick = _isClickEvent(e);
6412        const lastEvent = determineLastEvent(e, this._lastEvent, inChartArea, isClick);
6413        if (inChartArea) {
6414            this._lastEvent = null;
6415            callback(options.onHover, [
6416                e,
6417                active,
6418                this
6419            ], this);
6420            if (isClick) {
6421                callback(options.onClick, [
6422                    e,
6423                    active,
6424                    this
6425                ], this);
6426            }
6427        }
6428        const changed = !_elementsEqual(active, lastActive);
6429        if (changed || replay) {
6430            this._active = active;
6431            this._updateHoverStyles(active, lastActive, replay);
6432        }
6433        this._lastEvent = lastEvent;
6434        return changed;
6435    }
6436 _getActiveElements(e, lastActive, inChartArea, useFinalPosition) {
6437        if (e.type === 'mouseout') {
6438            return [];
6439        }
6440        if (!inChartArea) {
6441            return lastActive;
6442        }
6443        const hoverOptions = this.options.hover;
6444        return this.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition);
6445    }
6446}
6447function invalidatePlugins() {
6448    return each(Chart.instances, (chart)=>chart._plugins.invalidate());
6449}
6450
6451function clipArc(ctx, element, endAngle) {
6452    const { startAngle , pixelMargin , x , y , outerRadius , innerRadius  } = element;
6453    let angleMargin = pixelMargin / outerRadius;
6454    // Draw an inner border by clipping the arc and drawing a double-width border
6455    // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders
6456    ctx.beginPath();
6457    ctx.arc(x, y, outerRadius, startAngle - angleMargin, endAngle + angleMargin);
6458    if (innerRadius > pixelMargin) {
6459        angleMargin = pixelMargin / innerRadius;
6460        ctx.arc(x, y, innerRadius, endAngle + angleMargin, startAngle - angleMargin, true);
6461    } else {
6462        ctx.arc(x, y, pixelMargin, endAngle + HALF_PI, startAngle - HALF_PI);
6463    }
6464    ctx.closePath();
6465    ctx.clip();
6466}
6467function toRadiusCorners(value) {
6468    return _readValueToProps(value, [
6469        'outerStart',
6470        'outerEnd',
6471        'innerStart',
6472        'innerEnd'
6473    ]);
6474}
6475/**
6476 * Parse border radius from the provided options
6477 */ function parseBorderRadius$1(arc, innerRadius, outerRadius, angleDelta) {
6478    const o = toRadiusCorners(arc.options.borderRadius);
6479    const halfThickness = (outerRadius - innerRadius) / 2;
6480    const innerLimit = Math.min(halfThickness, angleDelta * innerRadius / 2);
6481    // Outer limits are complicated. We want to compute the available angular distance at
6482    // a radius of outerRadius - borderRadius because for small angular distances, this term limits.
6483    // We compute at r = outerRadius - borderRadius because this circle defines the center of the border corners.
6484    //
6485    // If the borderRadius is large, that value can become negative.
6486    // This causes the outer borders to lose their radius entirely, which is rather unexpected. To solve that, if borderRadius > outerRadius
6487    // we know that the thickness term will dominate and compute the limits at that point
6488    const computeOuterLimit = (val)=>{
6489        const outerArcLimit = (outerRadius - Math.min(halfThickness, val)) * angleDelta / 2;
6490        return _limitValue(val, 0, Math.min(halfThickness, outerArcLimit));
6491    };
6492    return {
6493        outerStart: computeOuterLimit(o.outerStart),
6494        outerEnd: computeOuterLimit(o.outerEnd),
6495        innerStart: _limitValue(o.innerStart, 0, innerLimit),
6496        innerEnd: _limitValue(o.innerEnd, 0, innerLimit)
6497    };
6498}
6499/**
6500 * Convert (r, 𝜃) to (x, y)
6501 */ function rThetaToXY(r, theta, x, y) {
6502    return {
6503        x: x + r * Math.cos(theta),
6504        y: y + r * Math.sin(theta)
6505    };
6506}
6507/**
6508 * Path the arc, respecting border radius by separating into left and right halves.
6509 *
6510 *   Start      End
6511 *
6512 *    1--->a--->2    Outer
6513 *   /           \
6514 *   8           3
6515 *   |           |
6516 *   |           |
6517 *   7           4
6518 *   \           /
6519 *    6<---b<---5    Inner
6520 */ function pathArc(ctx, element, offset, spacing, end, circular) {
6521    const { x , y , startAngle: start , pixelMargin , innerRadius: innerR  } = element;
6522    const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0);
6523    const innerRadius = innerR > 0 ? innerR + spacing + offset + pixelMargin : 0;
6524    let spacingOffset = 0;
6525    const alpha = end - start;
6526    if (spacing) {
6527        // When spacing is present, it is the same for all items
6528        // So we adjust the start and end angle of the arc such that
6529        // the distance is the same as it would be without the spacing
6530        const noSpacingInnerRadius = innerR > 0 ? innerR - spacing : 0;
6531        const noSpacingOuterRadius = outerRadius > 0 ? outerRadius - spacing : 0;
6532        const avNogSpacingRadius = (noSpacingInnerRadius + noSpacingOuterRadius) / 2;
6533        const adjustedAngle = avNogSpacingRadius !== 0 ? alpha * avNogSpacingRadius / (avNogSpacingRadius + spacing) : alpha;
6534        spacingOffset = (alpha - adjustedAngle) / 2;
6535    }
6536    const beta = Math.max(0.001, alpha * outerRadius - offset / PI) / outerRadius;
6537    const angleOffset = (alpha - beta) / 2;
6538    const startAngle = start + angleOffset + spacingOffset;
6539    const endAngle = end - angleOffset - spacingOffset;
6540    const { outerStart , outerEnd , innerStart , innerEnd  } = parseBorderRadius$1(element, innerRadius, outerRadius, endAngle - startAngle);
6541    const outerStartAdjustedRadius = outerRadius - outerStart;
6542    const outerEndAdjustedRadius = outerRadius - outerEnd;
6543    const outerStartAdjustedAngle = startAngle + outerStart / outerStartAdjustedRadius;
6544    const outerEndAdjustedAngle = endAngle - outerEnd / outerEndAdjustedRadius;
6545    const innerStartAdjustedRadius = innerRadius + innerStart;
6546    const innerEndAdjustedRadius = innerRadius + innerEnd;
6547    const innerStartAdjustedAngle = startAngle + innerStart / innerStartAdjustedRadius;
6548    const innerEndAdjustedAngle = endAngle - innerEnd / innerEndAdjustedRadius;
6549    ctx.beginPath();
6550    if (circular) {
6551        // The first arc segments from point 1 to point a to point 2
6552        const outerMidAdjustedAngle = (outerStartAdjustedAngle + outerEndAdjustedAngle) / 2;
6553        ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerMidAdjustedAngle);
6554        ctx.arc(x, y, outerRadius, outerMidAdjustedAngle, outerEndAdjustedAngle);
6555        // The corner segment from point 2 to point 3
6556        if (outerEnd > 0) {
6557            const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y);
6558            ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI);
6559        }
6560        // The line from point 3 to point 4
6561        const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y);
6562        ctx.lineTo(p4.x, p4.y);
6563        // The corner segment from point 4 to point 5
6564        if (innerEnd > 0) {
6565            const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y);
6566            ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI);
6567        }
6568        // The inner arc from point 5 to point b to point 6
6569        const innerMidAdjustedAngle = (endAngle - innerEnd / innerRadius + (startAngle + innerStart / innerRadius)) / 2;
6570        ctx.arc(x, y, innerRadius, endAngle - innerEnd / innerRadius, innerMidAdjustedAngle, true);
6571        ctx.arc(x, y, innerRadius, innerMidAdjustedAngle, startAngle + innerStart / innerRadius, true);
6572        // The corner segment from point 6 to point 7
6573        if (innerStart > 0) {
6574            const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y);
6575            ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI);
6576        }
6577        // The line from point 7 to point 8
6578        const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y);
6579        ctx.lineTo(p8.x, p8.y);
6580        // The corner segment from point 8 to point 1
6581        if (outerStart > 0) {
6582            const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y);
6583            ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle);
6584        }
6585    } else {
6586        ctx.moveTo(x, y);
6587        const outerStartX = Math.cos(outerStartAdjustedAngle) * outerRadius + x;
6588        const outerStartY = Math.sin(outerStartAdjustedAngle) * outerRadius + y;
6589        ctx.lineTo(outerStartX, outerStartY);
6590        const outerEndX = Math.cos(outerEndAdjustedAngle) * outerRadius + x;
6591        const outerEndY = Math.sin(outerEndAdjustedAngle) * outerRadius + y;
6592        ctx.lineTo(outerEndX, outerEndY);
6593    }
6594    ctx.closePath();
6595}
6596function drawArc(ctx, element, offset, spacing, circular) {
6597    const { fullCircles , startAngle , circumference  } = element;
6598    let endAngle = element.endAngle;
6599    if (fullCircles) {
6600        pathArc(ctx, element, offset, spacing, endAngle, circular);
6601        for(let i = 0; i < fullCircles; ++i){
6602            ctx.fill();
6603        }
6604        if (!isNaN(circumference)) {
6605            endAngle = startAngle + (circumference % TAU || TAU);
6606        }
6607    }
6608    pathArc(ctx, element, offset, spacing, endAngle, circular);
6609    ctx.fill();
6610    return endAngle;
6611}
6612function drawBorder(ctx, element, offset, spacing, circular) {
6613    const { fullCircles , startAngle , circumference , options  } = element;
6614    const { borderWidth , borderJoinStyle , borderDash , borderDashOffset  } = options;
6615    const inner = options.borderAlign === 'inner';
6616    if (!borderWidth) {
6617        return;
6618    }
6619    ctx.setLineDash(borderDash || []);
6620    ctx.lineDashOffset = borderDashOffset;
6621    if (inner) {
6622        ctx.lineWidth = borderWidth * 2;
6623        ctx.lineJoin = borderJoinStyle || 'round';
6624    } else {
6625        ctx.lineWidth = borderWidth;
6626        ctx.lineJoin = borderJoinStyle || 'bevel';
6627    }
6628    let endAngle = element.endAngle;
6629    if (fullCircles) {
6630        pathArc(ctx, element, offset, spacing, endAngle, circular);
6631        for(let i = 0; i < fullCircles; ++i){
6632            ctx.stroke();
6633        }
6634        if (!isNaN(circumference)) {
6635            endAngle = startAngle + (circumference % TAU || TAU);
6636        }
6637    }
6638    if (inner) {
6639        clipArc(ctx, element, endAngle);
6640    }
6641    if (!fullCircles) {
6642        pathArc(ctx, element, offset, spacing, endAngle, circular);
6643        ctx.stroke();
6644    }
6645}
6646class ArcElement extends Element {
6647    static id = 'arc';
6648    static defaults = {
6649        borderAlign: 'center',
6650        borderColor: '#fff',
6651        borderDash: [],
6652        borderDashOffset: 0,
6653        borderJoinStyle: undefined,
6654        borderRadius: 0,
6655        borderWidth: 2,
6656        offset: 0,
6657        spacing: 0,
6658        angle: undefined,
6659        circular: true
6660    };
6661    static defaultRoutes = {
6662        backgroundColor: 'backgroundColor'
6663    };
6664    static descriptors = {
6665        _scriptable: true,
6666        _indexable: (name)=>name !== 'borderDash'
6667    };
6668    circumference;
6669    endAngle;
6670    fullCircles;
6671    innerRadius;
6672    outerRadius;
6673    pixelMargin;
6674    startAngle;
6675    constructor(cfg){
6676        super();
6677        this.options = undefined;
6678        this.circumference = undefined;
6679        this.startAngle = undefined;
6680        this.endAngle = undefined;
6681        this.innerRadius = undefined;
6682        this.outerRadius = undefined;
6683        this.pixelMargin = 0;
6684        this.fullCircles = 0;
6685        if (cfg) {
6686            Object.assign(this, cfg);
6687        }
6688    }
6689    inRange(chartX, chartY, useFinalPosition) {
6690        const point = this.getProps([
6691            'x',
6692            'y'
6693        ], useFinalPosition);
6694        const { angle , distance  } = getAngleFromPoint(point, {
6695            x: chartX,
6696            y: chartY
6697        });
6698        const { startAngle , endAngle , innerRadius , outerRadius , circumference  } = this.getProps([
6699            'startAngle',
6700            'endAngle',
6701            'innerRadius',
6702            'outerRadius',
6703            'circumference'
6704        ], useFinalPosition);
6705        const rAdjust = (this.options.spacing + this.options.borderWidth) / 2;
6706        const _circumference = valueOrDefault(circumference, endAngle - startAngle);
6707        const 
6707nonZeroBetween = _angleBetween(angle, startAngle, endAngle) && startAngle !== endAngle;
6708        const betweenAngles = _circumference >= TAU || nonZeroBetween
vendor: 20,737 bytes, lines 6708-7332
6708;
6709        const withinRadius = _isBetween(distance, innerRadius + rAdjust, outerRadius + rAdjust);
6710        return betweenAngles && withinRadius;
6711    }
6712    getCenterPoint(useFinalPosition) {
6713        const { x , y , startAngle , endAngle , innerRadius , outerRadius  } = this.getProps([
6714            'x',
6715            'y',
6716            'startAngle',
6717            'endAngle',
6718            'innerRadius',
6719            'outerRadius'
6720        ], useFinalPosition);
6721        const { offset , spacing  } = this.options;
6722        const halfAngle = (startAngle + endAngle) / 2;
6723        const halfRadius = (innerRadius + outerRadius + spacing + offset) / 2;
6724        return {
6725            x: x + Math.cos(halfAngle) * halfRadius,
6726            y: y + Math.sin(halfAngle) * halfRadius
6727        };
6728    }
6729    tooltipPosition(useFinalPosition) {
6730        return this.getCenterPoint(useFinalPosition);
6731    }
6732    draw(ctx) {
6733        const { options , circumference  } = this;
6734        const offset = (options.offset || 0) / 4;
6735        const spacing = (options.spacing || 0) / 2;
6736        const circular = options.circular;
6737        this.pixelMargin = options.borderAlign === 'inner' ? 0.33 : 0;
6738        this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0;
6739        if (circumference === 0 || this.innerRadius < 0 || this.outerRadius < 0) {
6740            return;
6741        }
6742        ctx.save();
6743        const halfAngle = (this.startAngle + this.endAngle) / 2;
6744        ctx.translate(Math.cos(halfAngle) * offset, Math.sin(halfAngle) * offset);
6745        const fix = 1 - Math.sin(Math.min(PI, circumference || 0));
6746        const radiusOffset = offset * fix;
6747        ctx.fillStyle = options.backgroundColor;
6748        ctx.strokeStyle = options.borderColor;
6749        drawArc(ctx, this, radiusOffset, spacing, circular);
6750        drawBorder(ctx, this, radiusOffset, spacing, circular);
6751        ctx.restore();
6752    }
6753}
6754
6755function setStyle(ctx, options, style = options) {
6756    ctx.lineCap = valueOrDefault(style.borderCapStyle, options.borderCapStyle);
6757    ctx.setLineDash(valueOrDefault(style.borderDash, options.borderDash));
6758    ctx.lineDashOffset = valueOrDefault(style.borderDashOffset, options.borderDashOffset);
6759    ctx.lineJoin = valueOrDefault(style.borderJoinStyle, options.borderJoinStyle);
6760    ctx.lineWidth = valueOrDefault(style.borderWidth, options.borderWidth);
6761    ctx.strokeStyle = valueOrDefault(style.borderColor, options.borderColor);
6762}
6763function lineTo(ctx, previous, target) {
6764    ctx.lineTo(target.x, target.y);
6765}
6766 function getLineMethod(options) {
6767    if (options.stepped) {
6768        return _steppedLineTo;
6769    }
6770    if (options.tension || options.cubicInterpolationMode === 'monotone') {
6771        return _bezierCurveTo;
6772    }
6773    return lineTo;
6774}
6775function pathVars(points, segment, params = {}) {
6776    const count = points.length;
6777    const { start: paramsStart = 0 , end: paramsEnd = count - 1  } = params;
6778    const { start: segmentStart , end: segmentEnd  } = segment;
6779    const start = Math.max(paramsStart, segmentStart);
6780    const end = Math.min(paramsEnd, segmentEnd);
6781    const outside = paramsStart < segmentStart && paramsEnd < segmentStart || paramsStart > segmentEnd && paramsEnd > segmentEnd;
6782    return {
6783        count,
6784        start,
6785        loop: segment.loop,
6786        ilen: end < start && !outside ? count + end - start : end - start
6787    };
6788}
6789 function pathSegment(ctx, line, segment, params) {
6790    const { points , options  } = line;
6791    const { count , start , loop , ilen  } = pathVars(points, segment, params);
6792    const lineMethod = getLineMethod(options);
6793    let { move =true , reverse  } = params || {};
6794    let i, point, prev;
6795    for(i = 0; i <= ilen; ++i){
6796        point = points[(start + (reverse ? ilen - i : i)) % count];
6797        if (point.skip) {
6798            continue;
6799        } else if (move) {
6800            ctx.moveTo(point.x, point.y);
6801            move = false;
6802        } else {
6803            lineMethod(ctx, prev, point, reverse, options.stepped);
6804        }
6805        prev = point;
6806    }
6807    if (loop) {
6808        point = points[(start + (reverse ? ilen : 0)) % count];
6809        lineMethod(ctx, prev, point, reverse, options.stepped);
6810    }
6811    return !!loop;
6812}
6813 function fastPathSegment(ctx, line, segment, params) {
6814    const points = line.points;
6815    const { count , start , ilen  } = pathVars(points, segment, params);
6816    const { move =true , reverse  } = params || {};
6817    let avgX = 0;
6818    let countX = 0;
6819    let i, point, prevX, minY, maxY, lastY;
6820    const pointIndex = (index)=>(start + (reverse ? ilen - index : index)) % count;
6821    const drawX = ()=>{
6822        if (minY !== maxY) {
6823            ctx.lineTo(avgX, maxY);
6824            ctx.lineTo(avgX, minY);
6825            ctx.lineTo(avgX, lastY);
6826        }
6827    };
6828    if (move) {
6829        point = points[pointIndex(0)];
6830        ctx.moveTo(point.x, point.y);
6831    }
6832    for(i = 0; i <= ilen; ++i){
6833        point = points[pointIndex(i)];
6834        if (point.skip) {
6835            continue;
6836        }
6837        const x = point.x;
6838        const y = point.y;
6839        const truncX = x | 0;
6840        if (truncX === prevX) {
6841            if (y < minY) {
6842                minY = y;
6843            } else if (y > maxY) {
6844                maxY = y;
6845            }
6846            avgX = (countX * avgX + x) / ++countX;
6847        } else {
6848            drawX();
6849            ctx.lineTo(x, y);
6850            prevX = truncX;
6851            countX = 0;
6852            minY = maxY = y;
6853        }
6854        lastY = y;
6855    }
6856    drawX();
6857}
6858 function _getSegmentMethod(line) {
6859    const opts = line.options;
6860    const borderDash = opts.borderDash && opts.borderDash.length;
6861    const useFastPath = !line._decimated && !line._loop && !opts.tension && opts.cubicInterpolationMode !== 'monotone' && !opts.stepped && !borderDash;
6862    return useFastPath ? fastPathSegment : pathSegment;
6863}
6864 function _getInterpolationMethod(options) {
6865    if (options.stepped) {
6866        return _steppedInterpolation;
6867    }
6868    if (options.tension || options.cubicInterpolationMode === 'monotone') {
6869        return _bezierInterpolation;
6870    }
6871    return _pointInLine;
6872}
6873function strokePathWithCache(ctx, line, start, count) {
6874    let path = line._path;
6875    if (!path) {
6876        path = line._path = new Path2D();
6877        if (line.path(path, start, count)) {
6878            path.closePath();
6879        }
6880    }
6881    setStyle(ctx, line.options);
6882    ctx.stroke(path);
6883}
6884function strokePathDirect(ctx, line, start, count) {
6885    const { segments , options  } = line;
6886    const segmentMethod = _getSegmentMethod(line);
6887    for (const segment of segments){
6888        setStyle(ctx, options, segment.style);
6889        ctx.beginPath();
6890        if (segmentMethod(ctx, line, segment, {
6891            start,
6892            end: start + count - 1
6893        })) {
6894            ctx.closePath();
6895        }
6896        ctx.stroke();
6897    }
6898}
6899const usePath2D = typeof Path2D === 'function';
6900function draw(ctx, line, start, count) {
6901    if (usePath2D && !line.options.segment) {
6902        strokePathWithCache(ctx, line, start, count);
6903    } else {
6904        strokePathDirect(ctx, line, start, count);
6905    }
6906}
6907class LineElement extends Element {
6908    static id = 'line';
6909 static defaults = {
6910        borderCapStyle: 'butt',
6911        borderDash: [],
6912        borderDashOffset: 0,
6913        borderJoinStyle: 'miter',
6914        borderWidth: 3,
6915        capBezierPoints: true,
6916        cubicInterpolationMode: 'default',
6917        fill: false,
6918        spanGaps: false,
6919        stepped: false,
6920        tension: 0
6921    };
6922 static defaultRoutes = {
6923        backgroundColor: 'backgroundColor',
6924        borderColor: 'borderColor'
6925    };
6926    static descriptors = {
6927        _scriptable: true,
6928        _indexable: (name)=>name !== 'borderDash' && name !== 'fill'
6929    };
6930    constructor(cfg){
6931        super();
6932        this.animated = true;
6933        this.options = undefined;
6934        this._chart = undefined;
6935        this._loop = undefined;
6936        this._fullLoop = undefined;
6937        this._path = undefined;
6938        this._points = undefined;
6939        this._segments = undefined;
6940        this._decimated = false;
6941        this._pointsUpdated = false;
6942        this._datasetIndex = undefined;
6943        if (cfg) {
6944            Object.assign(this, cfg);
6945        }
6946    }
6947    updateControlPoints(chartArea, indexAxis) {
6948        const options = this.options;
6949        if ((options.tension || options.cubicInterpolationMode === 'monotone') && !options.stepped && !this._pointsUpdated) {
6950            const loop = options.spanGaps ? this._loop : this._fullLoop;
6951            _updateBezierControlPoints(this._points, options, chartArea, loop, indexAxis);
6952            this._pointsUpdated = true;
6953        }
6954    }
6955    set points(points) {
6956        this._points = points;
6957        delete this._segments;
6958        delete this._path;
6959        this._pointsUpdated = false;
6960    }
6961    get points() {
6962        return this._points;
6963    }
6964    get segments() {
6965        return this._segments || (this._segments = _computeSegments(this, this.options.segment));
6966    }
6967 first() {
6968        const segments = this.segments;
6969        const points = this.points;
6970        return segments.length && points[segments[0].start];
6971    }
6972 last() {
6973        const segments = this.segments;
6974        const points = this.points;
6975        const count = segments.length;
6976        return count && points[segments[count - 1].end];
6977    }
6978 interpolate(point, property) {
6979        const options = this.options;
6980        const value = point[property];
6981        const points = this.points;
6982        const segments = _boundSegments(this, {
6983            property,
6984            start: value,
6985            end: value
6986        });
6987        if (!segments.length) {
6988            return;
6989        }
6990        const result = [];
6991        const _interpolate = _getInterpolationMethod(options);
6992        let i, ilen;
6993        for(i = 0, ilen = segments.length; i < ilen; ++i){
6994            const { start , end  } = segments[i];
6995            const p1 = points[start];
6996            const p2 = points[end];
6997            if (p1 === p2) {
6998                result.push(p1);
6999                continue;
7000            }
7001            const t = Math.abs((value - p1[property]) / (p2[property] - p1[property]));
7002            const interpolated = _interpolate(p1, p2, t, options.stepped);
7003            interpolated[property] = point[property];
7004            result.push(interpolated);
7005        }
7006        return result.length === 1 ? result[0] : result;
7007    }
7008 pathSegment(ctx, segment, params) {
7009        const segmentMethod = _getSegmentMethod(this);
7010        return segmentMethod(ctx, this, segment, params);
7011    }
7012 path(ctx, start, count) {
7013        const segments = this.segments;
7014        const segmentMethod = _getSegmentMethod(this);
7015        let loop = this._loop;
7016        start = start || 0;
7017        count = count || this.points.length - start;
7018        for (const segment of segments){
7019            loop &= segmentMethod(ctx, this, segment, {
7020                start,
7021                end: start + count - 1
7022            });
7023        }
7024        return !!loop;
7025    }
7026 draw(ctx, chartArea, start, count) {
7027        const options = this.options || {};
7028        const points = this.points || [];
7029        if (points.length && options.borderWidth) {
7030            ctx.save();
7031            draw(ctx, this, start, count);
7032            ctx.restore();
7033        }
7034        if (this.animated) {
7035            this._pointsUpdated = false;
7036            this._path = undefined;
7037        }
7038    }
7039}
7040
7041function inRange$1(el, pos, axis, useFinalPosition) {
7042    const options = el.options;
7043    const { [axis]: value  } = el.getProps([
7044        axis
7045    ], useFinalPosition);
7046    return Math.abs(pos - value) < options.radius + options.hitRadius;
7047}
7048class PointElement extends Element {
7049    static id = 'point';
7050    parsed;
7051    skip;
7052    stop;
7053    /**
7054   * @type {any}
7055   */ static defaults = {
7056        borderWidth: 1,
7057        hitRadius: 1,
7058        hoverBorderWidth: 1,
7059        hoverRadius: 4,
7060        pointStyle: 'circle',
7061        radius: 3,
7062        rotation: 0
7063    };
7064    /**
7065   * @type {any}
7066   */ static defaultRoutes = {
7067        backgroundColor: 'backgroundColor',
7068        borderColor: 'borderColor'
7069    };
7070    constructor(cfg){
7071        super();
7072        this.options = undefined;
7073        this.parsed = undefined;
7074        this.skip = undefined;
7075        this.stop = undefined;
7076        if (cfg) {
7077            Object.assign(this, cfg);
7078        }
7079    }
7080    inRange(mouseX, mouseY, useFinalPosition) {
7081        const options = this.options;
7082        const { x , y  } = this.getProps([
7083            'x',
7084            'y'
7085        ], useFinalPosition);
7086        return Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2) < Math.pow(options.hitRadius + options.radius, 2);
7087    }
7088    inXRange(mouseX, useFinalPosition) {
7089        return inRange$1(this, mouseX, 'x', useFinalPosition);
7090    }
7091    inYRange(mouseY, useFinalPosition) {
7092        return inRange$1(this, mouseY, 'y', useFinalPosition);
7093    }
7094    getCenterPoint(useFinalPosition) {
7095        const { x , y  } = this.getProps([
7096            'x',
7097            'y'
7098        ], useFinalPosition);
7099        return {
7100            x,
7101            y
7102        };
7103    }
7104    size(options) {
7105        options = options || this.options || {};
7106        let radius = options.radius || 0;
7107        radius = Math.max(radius, radius && options.hoverRadius || 0);
7108        const borderWidth = radius && options.borderWidth || 0;
7109        return (radius + borderWidth) * 2;
7110    }
7111    draw(ctx, area) {
7112        const options = this.options;
7113        if (this.skip || options.radius < 0.1 || !_isPointInArea(this, area, this.size(options) / 2)) {
7114            return;
7115        }
7116        ctx.strokeStyle = options.borderColor;
7117        ctx.lineWidth = options.borderWidth;
7118        ctx.fillStyle = options.backgroundColor;
7119        drawPoint(ctx, options, this.x, this.y);
7120    }
7121    getRange() {
7122        const options = this.options || {};
7123        // @ts-expect-error Fallbacks should never be hit in practice
7124        return options.radius + options.hitRadius;
7125    }
7126}
7127
7128function getBarBounds(bar, useFinalPosition) {
7129    const { x , y , base , width , height  } =  bar.getProps([
7130        'x',
7131        'y',
7132        'base',
7133        'width',
7134        'height'
7135    ], useFinalPosition);
7136    let left, right, top, bottom, half;
7137    if (bar.horizontal) {
7138        half = height / 2;
7139        left = Math.min(x, base);
7140        right = Math.max(x, base);
7141        top = y - half;
7142        bottom = y + half;
7143    } else {
7144        half = width / 2;
7145        left = x - half;
7146        right = x + half;
7147        top = Math.min(y, base);
7148        bottom = Math.max(y, base);
7149    }
7150    return {
7151        left,
7152        top,
7153        right,
7154        bottom
7155    };
7156}
7157function skipOrLimit(skip, value, min, max) {
7158    return skip ? 0 : _limitValue(value, min, max);
7159}
7160function parseBorderWidth(bar, maxW, maxH) {
7161    const value = bar.options.borderWidth;
7162    const skip = bar.borderSkipped;
7163    const o = toTRBL(value);
7164    return {
7165        t: skipOrLimit(skip.top, o.top, 0, maxH),
7166        r: skipOrLimit(skip.right, o.right, 0, maxW),
7167        b: skipOrLimit(skip.bottom, o.bottom, 0, maxH),
7168        l: skipOrLimit(skip.left, o.left, 0, maxW)
7169    };
7170}
7171function parseBorderRadius(bar, maxW, maxH) {
7172    const { enableBorderRadius  } = bar.getProps([
7173        'enableBorderRadius'
7174    ]);
7175    const value = bar.options.borderRadius;
7176    const o = toTRBLCorners(value);
7177    const maxR = Math.min(maxW, maxH);
7178    const skip = bar.borderSkipped;
7179    const enableBorder = enableBorderRadius || isObject(value);
7180    return {
7181        topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR),
7182        topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR),
7183        bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR),
7184        bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR)
7185    };
7186}
7187function boundingRects(bar) {
7188    const bounds = getBarBounds(bar);
7189    const width = bounds.right - bounds.left;
7190    const height = bounds.bottom - bounds.top;
7191    const border = parseBorderWidth(bar, width / 2, height / 2);
7192    const radius = parseBorderRadius(bar, width / 2, height / 2);
7193    return {
7194        outer: {
7195            x: bounds.left,
7196            y: bounds.top,
7197            w: width,
7198            h: height,
7199            radius
7200        },
7201        inner: {
7202            x: bounds.left + border.l,
7203            y: bounds.top + border.t,
7204            w: width - border.l - border.r,
7205            h: height - border.t - border.b,
7206            radius: {
7207                topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)),
7208                topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)),
7209                bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)),
7210                bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r))
7211            }
7212        }
7213    };
7214}
7215function inRange(bar, x, y, useFinalPosition) {
7216    const skipX = x === null;
7217    const skipY = y === null;
7218    const skipBoth = skipX && skipY;
7219    const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition);
7220    return bounds && (skipX || _isBetween(x, bounds.left, bounds.right)) && (skipY || _isBetween(y, bounds.top, bounds.bottom));
7221}
7222function hasRadius(radius) {
7223    return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;
7224}
7225 function addNormalRectPath(ctx, rect) {
7226    ctx.rect(rect.x, rect.y, rect.w, rect.h);
7227}
7228function inflateRect(rect, amount, refRect = {}) {
7229    const x = rect.x !== refRect.x ? -amount : 0;
7230    const y = rect.y !== refRect.y ? -amount : 0;
7231    const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x;
7232    const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y;
7233    return {
7234        x: rect.x + x,
7235        y: rect.y + y,
7236        w: rect.w + w,
7237        h: rect.h + h,
7238        radius: rect.radius
7239    };
7240}
7241class BarElement extends Element {
7242    static id = 'bar';
7243 static defaults = {
7244        borderSkipped: 'start',
7245        borderWidth: 0,
7246        borderRadius: 0,
7247        inflateAmount: 'auto',
7248        pointStyle: undefined
7249    };
7250 static defaultRoutes = {
7251        backgroundColor: 'backgroundColor',
7252        borderColor: 'borderColor'
7253    };
7254    constructor(cfg){
7255        super();
7256        this.options = undefined;
7257        this.horizontal = undefined;
7258        this.base = undefined;
7259        this.width = undefined;
7260        this.height = undefined;
7261        this.inflateAmount = undefined;
7262        if (cfg) {
7263            Object.assign(this, cfg);
7264        }
7265    }
7266    draw(ctx) {
7267        const { inflateAmount , options: { borderColor , backgroundColor  }  } = this;
7268        const { inner , outer  } = boundingRects(this);
7269        const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath;
7270        ctx.save();
7271        if (outer.w !== inner.w || outer.h !== inner.h) {
7272            ctx.beginPath();
7273            addRectPath(ctx, inflateRect(outer, inflateAmount, inner));
7274            ctx.clip();
7275            addRectPath(ctx, inflateRect(inner, -inflateAmount, outer));
7276            ctx.fillStyle = borderColor;
7277            ctx.fill('evenodd');
7278        }
7279        ctx.beginPath();
7280        addRectPath(ctx, inflateRect(inner, inflateAmount));
7281        ctx.fillStyle = backgroundColor;
7282        ctx.fill();
7283        ctx.restore();
7284    }
7285    inRange(mouseX, mouseY, useFinalPosition) {
7286        return inRange(this, mouseX, mouseY, useFinalPosition);
7287    }
7288    inXRange(mouseX, useFinalPosition) {
7289        return inRange(this, mouseX, null, useFinalPosition);
7290    }
7291    inYRange(mouseY, useFinalPosition) {
7292        return inRange(this, null, mouseY, useFinalPosition);
7293    }
7294    getCenterPoint(useFinalPosition) {
7295        const { x , y , base , horizontal  } =  this.getProps([
7296            'x',
7297            'y',
7298            'base',
7299            'horizontal'
7300        ], useFinalPosition);
7301        return {
7302            x: horizontal ? (x + base) / 2 : x,
7303            y: horizontal ? y : (y + base) / 2
7304        };
7305    }
7306    getRange(axis) {
7307        return axis === 'x' ? this.width / 2 : this.height / 2;
7308    }
7309}
7310
7311var elements = /*#__PURE__*/Object.freeze({
7312__proto__: null,
7313ArcElement: ArcElement,
7314BarElement: BarElement,
7315LineElement: LineElement,
7316PointElement: PointElement
7317});
7318
7319const BORDER_COLORS = [
7320    'rgb(54, 162, 235)',
7321    'rgb(255, 99, 132)',
7322    'rgb(255, 159, 64)',
7323    'rgb(255, 205, 86)',
7324    'rgb(75, 192, 192)',
7325    'rgb(153, 102, 255)',
7326    'rgb(201, 203, 207)' // grey
7327];
7328// Border colors with 50% transparency
7329const BACKGROUND_COLORS = /* #__PURE__ */ BORDER_COLORS.map((color)=>color.replace('rgb(', 'rgba(').replace(')', ', 0.5)'));
7330function getBorderColor(i) {
7331    return BORDER_COLORS[i % BORDER_COLORS.length];
7332}
vendor: 6,257 bytes, lines 7333-7507
7333function getBackgroundColor(i) {
7334    return BACKGROUND_COLORS[i % BACKGROUND_COLORS.length];
7335}
7336function colorizeDefaultDataset(dataset, i) {
7337    dataset.borderColor = getBorderColor(i);
7338    dataset.backgroundColor = getBackgroundColor(i);
7339    return ++i;
7340}
7341function colorizeDoughnutDataset(dataset, i) {
7342    dataset.backgroundColor = dataset.data.map(()=>getBorderColor(i++));
7343    return i;
7344}
7345function colorizePolarAreaDataset(dataset, i) {
7346    dataset.backgroundColor = dataset.data.map(()=>getBackgroundColor(i++));
7347    return i;
7348}
7349function getColorizer(chart) {
7350    let i = 0;
7351    return (dataset, datasetIndex)=>{
7352        const controller = chart.getDatasetMeta(datasetIndex).controller;
7353        if (controller instanceof DoughnutController) {
7354            i = colorizeDoughnutDataset(dataset, i);
7355        } else if (controller instanceof PolarAreaController) {
7356            i = colorizePolarAreaDataset(dataset, i);
7357        } else if (controller) {
7358            i = colorizeDefaultDataset(dataset, i);
7359        }
7360    };
7361}
7362function containsColorsDefinitions(descriptors) {
7363    let k;
7364    for(k in descriptors){
7365        if (descriptors[k].borderColor || descriptors[k].backgroundColor) {
7366            return true;
7367        }
7368    }
7369    return false;
7370}
7371function containsColorsDefinition(descriptor) {
7372    return descriptor && (descriptor.borderColor || descriptor.backgroundColor);
7373}
7374function containsDefaultColorsDefenitions() {
7375    return defaults.borderColor !== 'rgba(0,0,0,0.1)' || defaults.backgroundColor !== 'rgba(0,0,0,0.1)';
7376}
7377var plugin_colors = {
7378    id: 'colors',
7379    defaults: {
7380        enabled: true,
7381        forceOverride: false
7382    },
7383    beforeLayout (chart, _args, options) {
7384        if (!options.enabled) {
7385            return;
7386        }
7387        const { data: { datasets  } , options: chartOptions  } = chart.config;
7388        const { elements  } = chartOptions;
7389        const containsColorDefenition = containsColorsDefinitions(datasets) || containsColorsDefinition(chartOptions) || elements && containsColorsDefinitions(elements) || containsDefaultColorsDefenitions();
7390        if (!options.forceOverride && containsColorDefenition) {
7391            return;
7392        }
7393        const colorizer = getColorizer(chart);
7394        datasets.forEach(colorizer);
7395    }
7396};
7397
7398function lttbDecimation(data, start, count, availableWidth, options) {
7399 const samples = options.samples || availableWidth;
7400    if (samples >= count) {
7401        return data.slice(start, start + count);
7402    }
7403    const decimated = [];
7404    const bucketWidth = (count - 2) / (samples - 2);
7405    let sampledIndex = 0;
7406    const endIndex = start + count - 1;
7407    let a = start;
7408    let i, maxAreaPoint, maxArea, area, nextA;
7409    decimated[sampledIndex++] = data[a];
7410    for(i = 0; i < samples - 2; i++){
7411        let avgX = 0;
7412        let avgY = 0;
7413        let j;
7414        const avgRangeStart = Math.floor((i + 1) * bucketWidth) + 1 + start;
7415        const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketWidth) + 1, count) + start;
7416        const avgRangeLength = avgRangeEnd - avgRangeStart;
7417        for(j = avgRangeStart; j < avgRangeEnd; j++){
7418            avgX += data[j].x;
7419            avgY += data[j].y;
7420        }
7421        avgX /= avgRangeLength;
7422        avgY /= avgRangeLength;
7423        const rangeOffs = Math.floor(i * bucketWidth) + 1 + start;
7424        const rangeTo = Math.min(Math.floor((i + 1) * bucketWidth) + 1, count) + start;
7425        const { x: pointAx , y: pointAy  } = data[a];
7426        maxArea = area = -1;
7427        for(j = rangeOffs; j < rangeTo; j++){
7428            area = 0.5 * Math.abs((pointAx - avgX) * (data[j].y - pointAy) - (pointAx - data[j].x) * (avgY - pointAy));
7429            if (area > maxArea) {
7430                maxArea = area;
7431                maxAreaPoint = data[j];
7432                nextA = j;
7433            }
7434        }
7435        decimated[sampledIndex++] = maxAreaPoint;
7436        a = nextA;
7437    }
7438    decimated[sampledIndex++] = data[endIndex];
7439    return decimated;
7440}
7441function minMaxDecimation(data, start, count, availableWidth) {
7442    let avgX = 0;
7443    let countX = 0;
7444    let i, point, x, y, prevX, minIndex, maxIndex, startIndex, minY, maxY;
7445    const decimated = [];
7446    const endIndex = start + count - 1;
7447    const xMin = data[start].x;
7448    const xMax = data[endIndex].x;
7449    const dx = xMax - xMin;
7450    for(i = start; i < start + count; ++i){
7451        point = data[i];
7452        x = (point.x - xMin) / dx * availableWidth;
7453        y = point.y;
7454        const truncX = x | 0;
7455        if (truncX === prevX) {
7456            if (y < minY) {
7457                minY = y;
7458                minIndex = i;
7459            } else if (y > maxY) {
7460                maxY = y;
7461                maxIndex = i;
7462            }
7463            avgX = (countX * avgX + point.x) / ++countX;
7464        } else {
7465            const lastIndex = i - 1;
7466            if (!isNullOrUndef(minIndex) && !isNullOrUndef(maxIndex)) {
7467                const intermediateIndex1 = Math.min(minIndex, maxIndex);
7468                const intermediateIndex2 = Math.max(minIndex, maxIndex);
7469                if (intermediateIndex1 !== startIndex && intermediateIndex1 !== lastIndex) {
7470                    decimated.push({
7471                        ...data[intermediateIndex1],
7472                        x: avgX
7473                    });
7474                }
7475                if (intermediateIndex2 !== startIndex && intermediateIndex2 !== lastIndex) {
7476                    decimated.push({
7477                        ...data[intermediateIndex2],
7478                        x: avgX
7479                    });
7480                }
7481            }
7482            if (i > 0 && lastIndex !== startIndex) {
7483                decimated.push(data[lastIndex]);
7484            }
7485            decimated.push(point);
7486            prevX = truncX;
7487            countX = 0;
7488            minY = maxY = y;
7489            minIndex = maxIndex = startIndex = i;
7490        }
7491    }
7492    return decimated;
7493}
7494function cleanDecimatedDataset(dataset) {
7495    if (dataset._decimated) {
7496        const data = dataset._data;
7497        delete dataset._decimated;
7498        delete dataset._data;
7499        Object.defineProperty(dataset, 'data', {
7500            configurable: true,
7501            enumerable: true,
7502            writable: true,
7503            value: data
7504        });
7505    }
7506}
7507function cleanDecimatedData(chart) {
vendor: 31,943 bytes, lines 7508-8479
7508    chart.data.datasets.forEach((dataset)=>{
7509        cleanDecimatedDataset(dataset);
7510    });
7511}
7512function getStartAndCountOfVisiblePointsSimplified(meta, points) {
7513    const pointCount = points.length;
7514    let start = 0;
7515    let count;
7516    const { iScale  } = meta;
7517    const { min , max , minDefined , maxDefined  } = iScale.getUserBounds();
7518    if (minDefined) {
7519        start = _limitValue(_lookupByKey(points, iScale.axis, min).lo, 0, pointCount - 1);
7520    }
7521    if (maxDefined) {
7522        count = _limitValue(_lookupByKey(points, iScale.axis, max).hi + 1, start, pointCount) - start;
7523    } else {
7524        count = pointCount - start;
7525    }
7526    return {
7527        start,
7528        count
7529    };
7530}
7531var plugin_decimation = {
7532    id: 'decimation',
7533    defaults: {
7534        algorithm: 'min-max',
7535        enabled: false
7536    },
7537    beforeElementsUpdate: (chart, args, options)=>{
7538        if (!options.enabled) {
7539            cleanDecimatedData(chart);
7540            return;
7541        }
7542        const availableWidth = chart.width;
7543        chart.data.datasets.forEach((dataset, datasetIndex)=>{
7544            const { _data , indexAxis  } = dataset;
7545            const meta = chart.getDatasetMeta(datasetIndex);
7546            const data = _data || dataset.data;
7547            if (resolve([
7548                indexAxis,
7549                chart.options.indexAxis
7550            ]) === 'y') {
7551                return;
7552            }
7553            if (!meta.controller.supportsDecimation) {
7554                return;
7555            }
7556            const xAxis = chart.scales[meta.xAxisID];
7557            if (xAxis.type !== 'linear' && xAxis.type !== 'time') {
7558                return;
7559            }
7560            if (chart.options.parsing) {
7561                return;
7562            }
7563            let { start , count  } = getStartAndCountOfVisiblePointsSimplified(meta, data);
7564            const threshold = options.threshold || 4 * availableWidth;
7565            if (count <= threshold) {
7566                cleanDecimatedDataset(dataset);
7567                return;
7568            }
7569            if (isNullOrUndef(_data)) {
7570                dataset._data = data;
7571                delete dataset.data;
7572                Object.defineProperty(dataset, 'data', {
7573                    configurable: true,
7574                    enumerable: true,
7575                    get: function() {
7576                        return this._decimated;
7577                    },
7578                    set: function(d) {
7579                        this._data = d;
7580                    }
7581                });
7582            }
7583            let decimated;
7584            switch(options.algorithm){
7585                case 'lttb':
7586                    decimated = lttbDecimation(data, start, count, availableWidth, options);
7587                    break;
7588                case 'min-max':
7589                    decimated = minMaxDecimation(data, start, count, availableWidth);
7590                    break;
7591                default:
7592                    throw new Error(`Unsupported decimation algorithm '${options.algorithm}'`);
7593            }
7594            dataset._decimated = decimated;
7595        });
7596    },
7597    destroy (chart) {
7598        cleanDecimatedData(chart);
7599    }
7600};
7601
7602function _segments(line, target, property) {
7603    const segments = line.segments;
7604    const points = line.points;
7605    const tpoints = target.points;
7606    const parts = [];
7607    for (const segment of segments){
7608        let { start , end  } = segment;
7609        end = _findSegmentEnd(start, end, points);
7610        const bounds = _getBounds(property, points[start], points[end], segment.loop);
7611        if (!target.segments) {
7612            parts.push({
7613                source: segment,
7614                target: bounds,
7615                start: points[start],
7616                end: points[end]
7617            });
7618            continue;
7619        }
7620        const targetSegments = _boundSegments(target, bounds);
7621        for (const tgt of targetSegments){
7622            const subBounds = _getBounds(property, tpoints[tgt.start], tpoints[tgt.end], tgt.loop);
7623            const fillSources = _boundSegment(segment, points, subBounds);
7624            for (const fillSource of fillSources){
7625                parts.push({
7626                    source: fillSource,
7627                    target: tgt,
7628                    start: {
7629                        [property]: _getEdge(bounds, subBounds, 'start', Math.max)
7630                    },
7631                    end: {
7632                        [property]: _getEdge(bounds, subBounds, 'end', Math.min)
7633                    }
7634                });
7635            }
7636        }
7637    }
7638    return parts;
7639}
7640function _getBounds(property, first, last, loop) {
7641    if (loop) {
7642        return;
7643    }
7644    let start = first[property];
7645    let end = last[property];
7646    if (property === 'angle') {
7647        start = _normalizeAngle(start);
7648        end = _normalizeAngle(end);
7649    }
7650    return {
7651        property,
7652        start,
7653        end
7654    };
7655}
7656function _pointsFromSegments(boundary, line) {
7657    const { x =null , y =null  } = boundary || {};
7658    const linePoints = line.points;
7659    const points = [];
7660    line.segments.forEach(({ start , end  })=>{
7661        end = _findSegmentEnd(start, end, linePoints);
7662        const first = linePoints[start];
7663        const last = linePoints[end];
7664        if (y !== null) {
7665            points.push({
7666                x: first.x,
7667                y
7668            });
7669            points.push({
7670                x: last.x,
7671                y
7672            });
7673        } else if (x !== null) {
7674            points.push({
7675                x,
7676                y: first.y
7677            });
7678            points.push({
7679                x,
7680                y: last.y
7681            });
7682        }
7683    });
7684    return points;
7685}
7686function _findSegmentEnd(start, end, points) {
7687    for(; end > start; end--){
7688        const point = points[end];
7689        if (!isNaN(point.x) && !isNaN(point.y)) {
7690            break;
7691        }
7692    }
7693    return end;
7694}
7695function _getEdge(a, b, prop, fn) {
7696    if (a && b) {
7697        return fn(a[prop], b[prop]);
7698    }
7699    return a ? a[prop] : b ? b[prop] : 0;
7700}
7701
7702function _createBoundaryLine(boundary, line) {
7703    let points = [];
7704    let _loop = false;
7705    if (isArray(boundary)) {
7706        _loop = true;
7707        points = boundary;
7708    } else {
7709        points = _pointsFromSegments(boundary, line);
7710    }
7711    return points.length ? new LineElement({
7712        points,
7713        options: {
7714            tension: 0
7715        },
7716        _loop,
7717        _fullLoop: _loop
7718    }) : null;
7719}
7720function _shouldApplyFill(source) {
7721    return source && source.fill !== false;
7722}
7723
7724function _resolveTarget(sources, index, propagate) {
7725    const source = sources[index];
7726    let fill = source.fill;
7727    const visited = [
7728        index
7729    ];
7730    let target;
7731    if (!propagate) {
7732        return fill;
7733    }
7734    while(fill !== false && visited.indexOf(fill) === -1){
7735        if (!isNumberFinite(fill)) {
7736            return fill;
7737        }
7738        target = sources[fill];
7739        if (!target) {
7740            return false;
7741        }
7742        if (target.visible) {
7743            return fill;
7744        }
7745        visited.push(fill);
7746        fill = target.fill;
7747    }
7748    return false;
7749}
7750 function _decodeFill(line, index, count) {
7751     const fill = parseFillOption(line);
7752    if (isObject(fill)) {
7753        return isNaN(fill.value) ? false : fill;
7754    }
7755    let target = parseFloat(fill);
7756    if (isNumberFinite(target) && Math.floor(target) === target) {
7757        return decodeTargetIndex(fill[0], index, target, count);
7758    }
7759    return [
7760        'origin',
7761        'start',
7762        'end',
7763        'stack',
7764        'shape'
7765    ].indexOf(fill) >= 0 && fill;
7766}
7767function decodeTargetIndex(firstCh, index, target, count) {
7768    if (firstCh === '-' || firstCh === '+') {
7769        target = index + target;
7770    }
7771    if (target === index || target < 0 || target >= count) {
7772        return false;
7773    }
7774    return target;
7775}
7776 function _getTargetPixel(fill, scale) {
7777    let pixel = null;
7778    if (fill === 'start') {
7779        pixel = scale.bottom;
7780    } else if (fill === 'end') {
7781        pixel = scale.top;
7782    } else if (isObject(fill)) {
7783        pixel = scale.getPixelForValue(fill.value);
7784    } else if (scale.getBasePixel) {
7785        pixel = scale.getBasePixel();
7786    }
7787    return pixel;
7788}
7789 function _getTargetValue(fill, scale, startValue) {
7790    let value;
7791    if (fill === 'start') {
7792        value = startValue;
7793    } else if (fill === 'end') {
7794        value = scale.options.reverse ? scale.min : scale.max;
7795    } else if (isObject(fill)) {
7796        value = fill.value;
7797    } else {
7798        value = scale.getBaseValue();
7799    }
7800    return value;
7801}
7802 function parseFillOption(line) {
7803    const options = line.options;
7804    const fillOption = options.fill;
7805    let fill = valueOrDefault(fillOption && fillOption.target, fillOption);
7806    if (fill === undefined) {
7807        fill = !!options.backgroundColor;
7808    }
7809    if (fill === false || fill === null) {
7810        return false;
7811    }
7812    if (fill === true) {
7813        return 'origin';
7814    }
7815    return fill;
7816}
7817
7818function _buildStackLine(source) {
7819    const { scale , index , line  } = source;
7820    const points = [];
7821    const segments = line.segments;
7822    const sourcePoints = line.points;
7823    const linesBelow = getLinesBelow(scale, index);
7824    linesBelow.push(_createBoundaryLine({
7825        x: null,
7826        y: scale.bottom
7827    }, line));
7828    for(let i = 0; i < segments.length; i++){
7829        const segment = segments[i];
7830        for(let j = segment.start; j <= segment.end; j++){
7831            addPointsBelow(points, sourcePoints[j], linesBelow);
7832        }
7833    }
7834    return new LineElement({
7835        points,
7836        options: {}
7837    });
7838}
7839 function getLinesBelow(scale, index) {
7840    const below = [];
7841    const metas = scale.getMatchingVisibleMetas('line');
7842    for(let i = 0; i < metas.length; i++){
7843        const meta = metas[i];
7844        if (meta.index === index) {
7845            break;
7846        }
7847        if (!meta.hidden) {
7848            below.unshift(meta.dataset);
7849        }
7850    }
7851    return below;
7852}
7853 function addPointsBelow(points, sourcePoint, linesBelow) {
7854    const postponed = [];
7855    for(let j = 0; j < linesBelow.length; j++){
7856        const line = linesBelow[j];
7857        const { first , last , point  } = findPoint(line, sourcePoint, 'x');
7858        if (!point || first && last) {
7859            continue;
7860        }
7861        if (first) {
7862            postponed.unshift(point);
7863        } else {
7864            points.push(point);
7865            if (!last) {
7866                break;
7867            }
7868        }
7869    }
7870    points.push(...postponed);
7871}
7872 function findPoint(line, sourcePoint, property) {
7873    const point = line.interpolate(sourcePoint, property);
7874    if (!point) {
7875        return {};
7876    }
7877    const pointValue = point[property];
7878    const segments = line.segments;
7879    const linePoints = line.points;
7880    let first = false;
7881    let last = false;
7882    for(let i = 0; i < segments.length; i++){
7883        const segment = segments[i];
7884        const firstValue = linePoints[segment.start][property];
7885        const lastValue = linePoints[segment.end][property];
7886        if (_isBetween(pointValue, firstValue, lastValue)) {
7887            first = pointValue === firstValue;
7888            last = pointValue === lastValue;
7889            break;
7890        }
7891    }
7892    return {
7893        first,
7894        last,
7895        point
7896    };
7897}
7898
7899class simpleArc {
7900    constructor(opts){
7901        this.x = opts.x;
7902        this.y = opts.y;
7903        this.radius = opts.radius;
7904    }
7905    pathSegment(ctx, bounds, opts) {
7906        const { x , y , radius  } = this;
7907        bounds = bounds || {
7908            start: 0,
7909            end: TAU
7910        };
7911        ctx.arc(x, y, radius, bounds.end, bounds.start, true);
7912        return !opts.bounds;
7913    }
7914    interpolate(point) {
7915        const { x , y , radius  } = this;
7916        const angle = point.angle;
7917        return {
7918            x: x + Math.cos(angle) * radius,
7919            y: y + Math.sin(angle) * radius,
7920            angle
7921        };
7922    }
7923}
7924
7925function _getTarget(source) {
7926    const { chart , fill , line  } = source;
7927    if (isNumberFinite(fill)) {
7928        return getLineByIndex(chart, fill);
7929    }
7930    if (fill === 'stack') {
7931        return _buildStackLine(source);
7932    }
7933    if (fill === 'shape') {
7934        return true;
7935    }
7936    const boundary = computeBoundary(source);
7937    if (boundary instanceof simpleArc) {
7938        return boundary;
7939    }
7940    return _createBoundaryLine(boundary, line);
7941}
7942 function getLineByIndex(chart, index) {
7943    const meta = chart.getDatasetMeta(index);
7944    const visible = meta && chart.isDatasetVisible(index);
7945    return visible ? meta.dataset : null;
7946}
7947function computeBoundary(source) {
7948    const scale = source.scale || {};
7949    if (scale.getPointPositionForValue) {
7950        return computeCircularBoundary(source);
7951    }
7952    return computeLinearBoundary(source);
7953}
7954function computeLinearBoundary(source) {
7955    const { scale ={} , fill  } = source;
7956    const pixel = _getTargetPixel(fill, scale);
7957    if (isNumberFinite(pixel)) {
7958        const horizontal = scale.isHorizontal();
7959        return {
7960            x: horizontal ? pixel : null,
7961            y: horizontal ? null : pixel
7962        };
7963    }
7964    return null;
7965}
7966function computeCircularBoundary(source) {
7967    const { scale , fill  } = source;
7968    const options = scale.options;
7969    const length = scale.getLabels().length;
7970    const start = options.reverse ? scale.max : scale.min;
7971    const value = _getTargetValue(fill, scale, start);
7972    const target = [];
7973    if (options.grid.circular) {
7974        const center = scale.getPointPositionForValue(0, start);
7975        return new simpleArc({
7976            x: center.x,
7977            y: center.y,
7978            radius: scale.getDistanceFromCenterForValue(value)
7979        });
7980    }
7981    for(let i = 0; i < length; ++i){
7982        target.push(scale.getPointPositionForValue(i, value));
7983    }
7984    return target;
7985}
7986
7987function _drawfill(ctx, source, area) {
7988    const target = _getTarget(source);
7989    const { line , scale , axis  } = source;
7990    const lineOpts = line.options;
7991    const fillOption = lineOpts.fill;
7992    const color = lineOpts.backgroundColor;
7993    const { above =color , below =color  } = fillOption || {};
7994    if (target && line.points.length) {
7995        clipArea(ctx, area);
7996        doFill(ctx, {
7997            line,
7998            target,
7999            above,
8000            below,
8001            area,
8002            scale,
8003            axis
8004        });
8005        unclipArea(ctx);
8006    }
8007}
8008function doFill(ctx, cfg) {
8009    const { line , target , above , below , area , scale  } = cfg;
8010    const property = line._loop ? 'angle' : cfg.axis;
8011    ctx.save();
8012    if (property === 'x' && below !== above) {
8013        clipVertical(ctx, target, area.top);
8014        fill(ctx, {
8015            line,
8016            target,
8017            color: above,
8018            scale,
8019            property
8020        });
8021        ctx.restore();
8022        ctx.save();
8023        clipVertical(ctx, target, area.bottom);
8024    }
8025    fill(ctx, {
8026        line,
8027        target,
8028        color: below,
8029        scale,
8030        property
8031    });
8032    ctx.restore();
8033}
8034function clipVertical(ctx, target, clipY) {
8035    const { segments , points  } = target;
8036    let first = true;
8037    let lineLoop = false;
8038    ctx.beginPath();
8039    for (const segment of segments){
8040        const { start , end  } = segment;
8041        const firstPoint = points[start];
8042        const lastPoint = points[_findSegmentEnd(start, end, points)];
8043        if (first) {
8044            ctx.moveTo(firstPoint.x, firstPoint.y);
8045            first = false;
8046        } else {
8047            ctx.lineTo(firstPoint.x, clipY);
8048            ctx.lineTo(firstPoint.x, firstPoint.y);
8049        }
8050        lineLoop = !!target.pathSegment(ctx, segment, {
8051            move: lineLoop
8052        });
8053        if (lineLoop) {
8054            ctx.closePath();
8055        } else {
8056            ctx.lineTo(lastPoint.x, clipY);
8057        }
8058    }
8059    ctx.lineTo(target.first().x, clipY);
8060    ctx.closePath();
8061    ctx.clip();
8062}
8063function fill(ctx, cfg) {
8064    const { line , target , property , color , scale  } = cfg;
8065    const segments = _segments(line, target, property);
8066    for (const { source: src , target: tgt , start , end  } of segments){
8067        const { style: { backgroundColor =color  } = {}  } = src;
8068        const notShape = target !== true;
8069        ctx.save();
8070        ctx.fillStyle = backgroundColor;
8071        clipBounds(ctx, scale, notShape && _getBounds(property, start, end));
8072        ctx.beginPath();
8073        const lineLoop = !!line.pathSegment(ctx, src);
8074        let loop;
8075        if (notShape) {
8076            if (lineLoop) {
8077                ctx.closePath();
8078            } else {
8079                interpolatedLineTo(ctx, target, end, property);
8080            }
8081            const targetLoop = !!target.pathSegment(ctx, tgt, {
8082                move: lineLoop,
8083                reverse: true
8084            });
8085            loop = lineLoop && targetLoop;
8086            if (!loop) {
8087                interpolatedLineTo(ctx, target, start, property);
8088            }
8089        }
8090        ctx.closePath();
8091        ctx.fill(loop ? 'evenodd' : 'nonzero');
8092        ctx.restore();
8093    }
8094}
8095function clipBounds(ctx, scale, bounds) {
8096    const { top , bottom  } = scale.chart.chartArea;
8097    const { property , start , end  } = bounds || {};
8098    if (property === 'x') {
8099        ctx.beginPath();
8100        ctx.rect(start, top, end - start, bottom - top);
8101        ctx.clip();
8102    }
8103}
8104function interpolatedLineTo(ctx, target, point, property) {
8105    const interpolatedPoint = target.interpolate(point, property);
8106    if (interpolatedPoint) {
8107        ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y);
8108    }
8109}
8110
8111var index = {
8112    id: 'filler',
8113    afterDatasetsUpdate (chart, _args, options) {
8114        const count = (chart.data.datasets || []).length;
8115        const sources = [];
8116        let meta, i, line, source;
8117        for(i = 0; i < count; ++i){
8118            meta = chart.getDatasetMeta(i);
8119            line = meta.dataset;
8120            source = null;
8121            if (line && line.options && line instanceof LineElement) {
8122                source = {
8123                    visible: chart.isDatasetVisible(i),
8124                    index: i,
8125                    fill: _decodeFill(line, i, count),
8126                    chart,
8127                    axis: meta.controller.options.indexAxis,
8128                    scale: meta.vScale,
8129                    line
8130                };
8131            }
8132            meta.$filler = source;
8133            sources.push(source);
8134        }
8135        for(i = 0; i < count; ++i){
8136            source = sources[i];
8137            if (!source || source.fill === false) {
8138                continue;
8139            }
8140            source.fill = _resolveTarget(sources, i, options.propagate);
8141        }
8142    },
8143    beforeDraw (chart, _args, options) {
8144        const draw = options.drawTime === 'beforeDraw';
8145        const metasets = chart.getSortedVisibleDatasetMetas();
8146        const area = chart.chartArea;
8147        for(let i = metasets.length - 1; i >= 0; --i){
8148            const source = metasets[i].$filler;
8149            if (!source) {
8150                continue;
8151            }
8152            source.line.updateControlPoints(area, source.axis);
8153            if (draw && source.fill) {
8154                _drawfill(chart.ctx, source, area);
8155            }
8156        }
8157    },
8158    beforeDatasetsDraw (chart, _args, options) {
8159        if (options.drawTime !== 'beforeDatasetsDraw') {
8160            return;
8161        }
8162        const metasets = chart.getSortedVisibleDatasetMetas();
8163        for(let i = metasets.length - 1; i >= 0; --i){
8164            const source = metasets[i].$filler;
8165            if (_shouldApplyFill(source)) {
8166                _drawfill(chart.ctx, source, chart.chartArea);
8167            }
8168        }
8169    },
8170    beforeDatasetDraw (chart, args, options) {
8171        const source = args.meta.$filler;
8172        if (!_shouldApplyFill(source) || options.drawTime !== 'beforeDatasetDraw') {
8173            return;
8174        }
8175        _drawfill(chart.ctx, source, chart.chartArea);
8176    },
8177    defaults: {
8178        propagate: true,
8179        drawTime: 'beforeDatasetDraw'
8180    }
8181};
8182
8183const getBoxSize = (labelOpts, fontSize)=>{
8184    let { boxHeight =fontSize , boxWidth =fontSize  } = labelOpts;
8185    if (labelOpts.usePointStyle) {
8186        boxHeight = Math.min(boxHeight, fontSize);
8187        boxWidth = labelOpts.pointStyleWidth || Math.min(boxWidth, fontSize);
8188    }
8189    return {
8190        boxWidth,
8191        boxHeight,
8192        itemHeight: Math.max(fontSize, boxHeight)
8193    };
8194};
8195const itemsEqual = (a, b)=>a !== null && b !== null && a.datasetIndex === b.datasetIndex && a.index === b.index;
8196class Legend extends Element {
8197 constructor(config){
8198        super();
8199        this._added = false;
8200        this.legendHitBoxes = [];
8201 this._hoveredItem = null;
8202        this.doughnutMode = false;
8203        this.chart = config.chart;
8204        this.options = config.options;
8205        this.ctx = config.ctx;
8206        this.legendItems = undefined;
8207        this.columnSizes = undefined;
8208        this.lineWidths = undefined;
8209        this.maxHeight = undefined;
8210        this.maxWidth = undefined;
8211        this.top = undefined;
8212        this.bottom = undefined;
8213        this.left = undefined;
8214        this.right = undefined;
8215        this.height = undefined;
8216        this.width = undefined;
8217        this._margins = undefined;
8218        this.position = undefined;
8219        this.weight = undefined;
8220        this.fullSize = undefined;
8221    }
8222    update(maxWidth, maxHeight, margins) {
8223        this.maxWidth = maxWidth;
8224        this.maxHeight = maxHeight;
8225        this._margins = margins;
8226        this.setDimensions();
8227        this.buildLabels();
8228        this.fit();
8229    }
8230    setDimensions() {
8231        if (this.isHorizontal()) {
8232            this.width = this.maxWidth;
8233            this.left = this._margins.left;
8234            this.right = this.width;
8235        } else {
8236            this.height = this.maxHeight;
8237            this.top = this._margins.top;
8238            this.bottom = this.height;
8239        }
8240    }
8241    buildLabels() {
8242        const labelOpts = this.options.labels || {};
8243        let legendItems = callback(labelOpts.generateLabels, [
8244            this.chart
8245        ], this) || [];
8246        if (labelOpts.filter) {
8247            legendItems = legendItems.filter((item)=>labelOpts.filter(item, this.chart.data));
8248        }
8249        if (labelOpts.sort) {
8250            legendItems = legendItems.sort((a, b)=>labelOpts.sort(a, b, this.chart.data));
8251        }
8252        if (this.options.reverse) {
8253            legendItems.reverse();
8254        }
8255        this.legendItems = legendItems;
8256    }
8257    fit() {
8258        const { options , ctx  } = this;
8259        if (!options.display) {
8260            this.width = this.height = 0;
8261            return;
8262        }
8263        const labelOpts = options.labels;
8264        const labelFont = toFont(labelOpts.font);
8265        const fontSize = labelFont.size;
8266        const titleHeight = this._computeTitleHeight();
8267        const { boxWidth , itemHeight  } = getBoxSize(labelOpts, fontSize);
8268        let width, height;
8269        ctx.font = labelFont.string;
8270        if (this.isHorizontal()) {
8271            width = this.maxWidth;
8272            height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight) + 10;
8273        } else {
8274            height = this.maxHeight;
8275            width = this._fitCols(titleHeight, labelFont, boxWidth, itemHeight) + 10;
8276        }
8277        this.width = Math.min(width, options.maxWidth || this.maxWidth);
8278        this.height = Math.min(height, options.maxHeight || this.maxHeight);
8279    }
8280 _fitRows(titleHeight, fontSize, boxWidth, itemHeight) {
8281        const { ctx , maxWidth , options: { labels: { padding  }  }  } = this;
8282        const hitboxes = this.legendHitBoxes = [];
8283        const lineWidths = this.lineWidths = [
8284            0
8285        ];
8286        const lineHeight = itemHeight + padding;
8287        let totalHeight = titleHeight;
8288        ctx.textAlign = 'left';
8289        ctx.textBaseline = 'middle';
8290        let row = -1;
8291        let top = -lineHeight;
8292        this.legendItems.forEach((legendItem, i)=>{
8293            const itemWidth = boxWidth + fontSize / 2 + ctx.measureText(legendItem.text).width;
8294            if (i === 0 || lineWidths[lineWidths.length - 1] + itemWidth + 2 * padding > maxWidth) {
8295                totalHeight += lineHeight;
8296                lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0;
8297                top += lineHeight;
8298                row++;
8299            }
8300            hitboxes[i] = {
8301                left: 0,
8302                top,
8303                row,
8304                width: itemWidth,
8305                height: itemHeight
8306            };
8307            lineWidths[lineWidths.length - 1] += itemWidth + padding;
8308        });
8309        return totalHeight;
8310    }
8311    _fitCols(titleHeight, labelFont, boxWidth, _itemHeight) {
8312        const { ctx , maxHeight , options: { labels: { padding  }  }  } = this;
8313        const hitboxes = this.legendHitBoxes = [];
8314        const columnSizes = this.columnSizes = [];
8315        const heightLimit = maxHeight - titleHeight;
8316        let totalWidth = padding;
8317        let currentColWidth = 0;
8318        let currentColHeight = 0;
8319        let left = 0;
8320        let col = 0;
8321        this.legendItems.forEach((legendItem, i)=>{
8322            const { itemWidth , itemHeight  } = calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight);
8323            if (i > 0 && currentColHeight + itemHeight + 2 * padding > heightLimit) {
8324                totalWidth += currentColWidth + padding;
8325                columnSizes.push({
8326                    width: currentColWidth,
8327                    height: currentColHeight
8328                });
8329                left += currentColWidth + padding;
8330                col++;
8331                currentColWidth = currentColHeight = 0;
8332            }
8333            hitboxes[i] = {
8334                left,
8335                top: currentColHeight,
8336                col,
8337                width: itemWidth,
8338                height: itemHeight
8339            };
8340            currentColWidth = Math.max(currentColWidth, itemWidth);
8341            currentColHeight += itemHeight + padding;
8342        });
8343        totalWidth += currentColWidth;
8344        columnSizes.push({
8345            width: currentColWidth,
8346            height: currentColHeight
8347        });
8348        return totalWidth;
8349    }
8350    adjustHitBoxes() {
8351        if (!this.options.display) {
8352            return;
8353        }
8354        const titleHeight = this._computeTitleHeight();
8355        const { legendHitBoxes: hitboxes , options: { align , labels: { padding  } , rtl  }  } = this;
8356        const rtlHelper = getRtlAdapter(rtl, this.left, this.width);
8357        if (this.isHorizontal()) {
8358            let row = 0;
8359            let left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);
8360            for (const hitbox of hitboxes){
8361                if (row !== hitbox.row) {
8362                    row = hitbox.row;
8363                    left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);
8364                }
8365                hitbox.top += this.top + titleHeight + padding;
8366                hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(left), hitbox.width);
8367                left += hitbox.width + padding;
8368            }
8369        } else {
8370            let col = 0;
8371            let top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);
8372            for (const hitbox of hitboxes){
8373                if (hitbox.col !== col) {
8374                    col = hitbox.col;
8375                    top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);
8376                }
8377                hitbox.top = top;
8378                hitbox.left += this.left + padding;
8379                hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(hitbox.left), hitbox.width);
8380                top += hitbox.height + padding;
8381            }
8382        }
8383    }
8384    isHorizontal() {
8385        return this.options.position === 'top' || this.options.position === 'bottom';
8386    }
8387    draw() {
8388        if (this.options.display) {
8389            const ctx = this.ctx;
8390            clipArea(ctx, this);
8391            this._draw();
8392            unclipArea(ctx);
8393        }
8394    }
8395 _draw() {
8396        const { options: opts , columnSizes , lineWidths , ctx  } = this;
8397        const { align , labels: labelOpts  } = opts;
8398        const defaultColor = defaults.color;
8399        const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);
8400        const labelFont = toFont(labelOpts.font);
8401        const { padding  } = labelOpts;
8402        const fontSize = labelFont.size;
8403        const halfFontSize = fontSize / 2;
8404        let cursor;
8405        this.drawTitle();
8406        ctx.textAlign = rtlHelper.textAlign('left');
8407        ctx.textBaseline = 'middle';
8408        ctx.lineWidth = 0.5;
8409        ctx.font = labelFont.string;
8410        const { boxWidth , boxHeight , itemHeight  } = getBoxSize(labelOpts, fontSize);
8411        const drawLegendBox = function(x, y, legendItem) {
8412            if (isNaN(boxWidth) || boxWidth <= 0 || isNaN(boxHeight) || boxHeight < 0) {
8413                return;
8414            }
8415            ctx.save();
8416            const lineWidth = valueOrDefault(legendItem.lineWidth, 1);
8417            ctx.fillStyle = valueOrDefault(legendItem.fillStyle, defaultColor);
8418            ctx.lineCap = valueOrDefault(legendItem.lineCap, 'butt');
8419            ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, 0);
8420            ctx.lineJoin = valueOrDefault(legendItem.lineJoin, 'miter');
8421            ctx.lineWidth = lineWidth;
8422            ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, defaultColor);
8423            ctx.setLineDash(valueOrDefault(legendItem.lineDash, []));
8424            if (labelOpts.usePointStyle) {
8425                const drawOptions = {
8426                    radius: boxHeight * Math.SQRT2 / 2,
8427                    pointStyle: legendItem.pointStyle,
8428                    rotation: legendItem.rotation,
8429                    borderWidth: lineWidth
8430                };
8431                const centerX = rtlHelper.xPlus(x, boxWidth / 2);
8432                const centerY = y + halfFontSize;
8433                drawPointLegend(ctx, drawOptions, centerX, centerY, labelOpts.pointStyleWidth && boxWidth);
8434            } else {
8435                const yBoxTop = y + Math.max((fontSize - boxHeight) / 2, 0);
8436                const xBoxLeft = rtlHelper.leftForLtr(x, boxWidth);
8437                const borderRadius = toTRBLCorners(legendItem.borderRadius);
8438                ctx.beginPath();
8439                if (Object.values(borderRadius).some((v)=>v !== 0)) {
8440                    addRoundedRectPath(ctx, {
8441                        x: xBoxLeft,
8442                        y: yBoxTop,
8443                        w: boxWidth,
8444                        h: boxHeight,
8445                        radius: borderRadius
8446                    });
8447                } else {
8448                    ctx.rect(xBoxLeft, yBoxTop, boxWidth, boxHeight);
8449                }
8450                ctx.fill();
8451                if (lineWidth !== 0) {
8452                    ctx.stroke();
8453                }
8454            }
8455            ctx.restore();
8456        };
8457        const fillText = function(x, y, legendItem) {
8458            renderText(ctx, legendItem.text, x, y + itemHeight / 2, labelFont, {
8459                strikethrough: legendItem.hidden,
8460                textAlign: rtlHelper.textAlign(legendItem.textAlign)
8461            });
8462        };
8463        const isHorizontal = this.isHorizontal();
8464        const titleHeight = this._computeTitleHeight();
8465        if (isHorizontal) {
8466            cursor = {
8467                x: _alignStartEnd(align, this.left + padding, this.right - lineWidths[0]),
8468                y: this.top + padding + titleHeight,
8469                line: 0
8470            };
8471        } else {
8472            cursor = {
8473                x: this.left + padding,
8474                y: _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[0].height),
8475                line: 0
8476            };
8477        }
8478        overrideTextDirection(this.ctx, opts.textDirection);
8479        const lineHeight = itemHeight + padding;
vendor: 6,468 bytes, lines 8480-8632
8480        this.legendItems.forEach((legendItem, i)=>{
8481            ctx.strokeStyle = legendItem.fontColor;
8482            ctx.fillStyle = legendItem.fontColor;
8483            const textWidth = ctx.measureText(legendItem.text).width;
8484            const textAlign = rtlHelper.textAlign(legendItem.textAlign || (legendItem.textAlign = labelOpts.textAlign));
8485            const width = boxWidth + halfFontSize + textWidth;
8486            let x = cursor.x;
8487            let y = cursor.y;
8488            rtlHelper.setWidth(this.width);
8489            if (isHorizontal) {
8490                if (i > 0 && x + width + padding > this.right) {
8491                    y = cursor.y += lineHeight;
8492                    cursor.line++;
8493                    x = cursor.x = _alignStartEnd(align, this.left + padding, this.right - lineWidths[cursor.line]);
8494                }
8495            } else if (i > 0 && y + lineHeight > this.bottom) {
8496                x = cursor.x = x + columnSizes[cursor.line].width + padding;
8497                cursor.line++;
8498                y = cursor.y = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[cursor.line].height);
8499            }
8500            const realX = rtlHelper.x(x);
8501            drawLegendBox(realX, y, legendItem);
8502            x = _textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : this.right, opts.rtl);
8503            fillText(rtlHelper.x(x), y, legendItem);
8504            if (isHorizontal) {
8505                cursor.x += width + padding;
8506            } else if (typeof legendItem.text !== 'string') {
8507                const fontLineHeight = labelFont.lineHeight;
8508                cursor.y += calculateLegendItemHeight(legendItem, fontLineHeight) + padding;
8509            } else {
8510                cursor.y += lineHeight;
8511            }
8512        });
8513        restoreTextDirection(this.ctx, opts.textDirection);
8514    }
8515 drawTitle() {
8516        const opts = this.options;
8517        const titleOpts = opts.title;
8518        const titleFont = toFont(titleOpts.font);
8519        const titlePadding = toPadding(titleOpts.padding);
8520        if (!titleOpts.display) {
8521            return;
8522        }
8523        const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);
8524        const ctx = this.ctx;
8525        const position = titleOpts.position;
8526        const halfFontSize = titleFont.size / 2;
8527        const topPaddingPlusHalfFontSize = titlePadding.top + halfFontSize;
8528        let y;
8529        let left = this.left;
8530        let maxWidth = this.width;
8531        if (this.isHorizontal()) {
8532            maxWidth = Math.max(...this.lineWidths);
8533            y = this.top + topPaddingPlusHalfFontSize;
8534            left = _alignStartEnd(opts.align, left, this.right - maxWidth);
8535        } else {
8536            const maxHeight = this.columnSizes.reduce((acc, size)=>Math.max(acc, size.height), 0);
8537            y = topPaddingPlusHalfFontSize + _alignStartEnd(opts.align, this.top, this.bottom - maxHeight - opts.labels.padding - this._computeTitleHeight());
8538        }
8539        const x = _alignStartEnd(position, left, left + maxWidth);
8540        ctx.textAlign = rtlHelper.textAlign(_toLeftRightCenter(position));
8541        ctx.textBaseline = 'middle';
8542        ctx.strokeStyle = titleOpts.color;
8543        ctx.fillStyle = titleOpts.color;
8544        ctx.font = titleFont.string;
8545        renderText(ctx, titleOpts.text, x, y, titleFont);
8546    }
8547 _computeTitleHeight() {
8548        const titleOpts = this.options.title;
8549        const titleFont = toFont(titleOpts.font);
8550        const titlePadding = toPadding(titleOpts.padding);
8551        return titleOpts.display ? titleFont.lineHeight + titlePadding.height : 0;
8552    }
8553 _getLegendItemAt(x, y) {
8554        let i, hitBox, lh;
8555        if (_isBetween(x, this.left, this.right) && _isBetween(y, this.top, this.bottom)) {
8556            lh = this.legendHitBoxes;
8557            for(i = 0; i < lh.length; ++i){
8558                hitBox = lh[i];
8559                if (_isBetween(x, hitBox.left, hitBox.left + hitBox.width) && _isBetween(y, hitBox.top, hitBox.top + hitBox.height)) {
8560                    return this.legendItems[i];
8561                }
8562            }
8563        }
8564        return null;
8565    }
8566 handleEvent(e) {
8567        const opts = this.options;
8568        if (!isListened(e.type, opts)) {
8569            return;
8570        }
8571        const hoveredItem = this._getLegendItemAt(e.x, e.y);
8572        if (e.type === 'mousemove' || e.type === 'mouseout') {
8573            const previous = this._hoveredItem;
8574            const sameItem = itemsEqual(previous, hoveredItem);
8575            if (previous && !sameItem) {
8576                callback(opts.onLeave, [
8577                    e,
8578                    previous,
8579                    this
8580                ], this);
8581            }
8582            this._hoveredItem = hoveredItem;
8583            if (hoveredItem && !sameItem) {
8584                callback(opts.onHover, [
8585                    e,
8586                    hoveredItem,
8587                    this
8588                ], this);
8589            }
8590        } else if (hoveredItem) {
8591            callback(opts.onClick, [
8592                e,
8593                hoveredItem,
8594                this
8595            ], this);
8596        }
8597    }
8598}
8599function calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight) {
8600    const itemWidth = calculateItemWidth(legendItem, boxWidth, labelFont, ctx);
8601    const itemHeight = calculateItemHeight(_itemHeight, legendItem, labelFont.lineHeight);
8602    return {
8603        itemWidth,
8604        itemHeight
8605    };
8606}
8607function calculateItemWidth(legendItem, boxWidth, labelFont, ctx) {
8608    let legendItemText = legendItem.text;
8609    if (legendItemText && typeof legendItemText !== 'string') {
8610        legendItemText = legendItemText.reduce((a, b)=>a.length > b.length ? a : b);
8611    }
8612    return boxWidth + labelFont.size / 2 + ctx.measureText(legendItemText).width;
8613}
8614function calculateItemHeight(_itemHeight, legendItem, fontLineHeight) {
8615    let itemHeight = _itemHeight;
8616    if (typeof legendItem.text !== 'string') {
8617        itemHeight = calculateLegendItemHeight(legendItem, fontLineHeight);
8618    }
8619    return itemHeight;
8620}
8621function calculateLegendItemHeight(legendItem, fontLineHeight) {
8622    const labelHeight = legendItem.text ? legendItem.text.length : 0;
8623    return fontLineHeight * labelHeight;
8624}
8625function isListened(type, opts) {
8626    if ((type === 'mousemove' || type === 'mouseout') && (opts.onHover || opts.onLeave)) {
8627        return true;
8628    }
8629    if (opts.onClick && (type === 'click' || type === 'mouseup')) {
8630        return true;
8631    }
8632    return false;
vendor: 8,531 bytes, lines 8633-8918
8633}
8634var plugin_legend = {
8635    id: 'legend',
8636 _element: Legend,
8637    start (chart, _args, options) {
8638        const legend = chart.legend = new Legend({
8639            ctx: chart.ctx,
8640            options,
8641            chart
8642        });
8643        layouts.configure(chart, legend, options);
8644        layouts.addBox(chart, legend);
8645    },
8646    stop (chart) {
8647        layouts.removeBox(chart, chart.legend);
8648        delete chart.legend;
8649    },
8650    beforeUpdate (chart, _args, options) {
8651        const legend = chart.legend;
8652        layouts.configure(chart, legend, options);
8653        legend.options = options;
8654    },
8655    afterUpdate (chart) {
8656        const legend = chart.legend;
8657        legend.buildLabels();
8658        legend.adjustHitBoxes();
8659    },
8660    afterEvent (chart, args) {
8661        if (!args.replay) {
8662            chart.legend.handleEvent(args.event);
8663        }
8664    },
8665    defaults: {
8666        display: true,
8667        position: 'top',
8668        align: 'center',
8669        fullSize: true,
8670        reverse: false,
8671        weight: 1000,
8672        onClick (e, legendItem, legend) {
8673            const index = legendItem.datasetIndex;
8674            const ci = legend.chart;
8675            if (ci.isDatasetVisible(index)) {
8676                ci.hide(index);
8677                legendItem.hidden = true;
8678            } else {
8679                ci.show(index);
8680                legendItem.hidden = false;
8681            }
8682        },
8683        onHover: null,
8684        onLeave: null,
8685        labels: {
8686            color: (ctx)=>ctx.chart.options.color,
8687            boxWidth: 40,
8688            padding: 10,
8689            generateLabels (chart) {
8690                const datasets = chart.data.datasets;
8691                const { labels: { usePointStyle , pointStyle , textAlign , color , useBorderRadius , borderRadius  }  } = chart.legend.options;
8692                return chart._getSortedDatasetMetas().map((meta)=>{
8693                    const style = meta.controller.getStyle(usePointStyle ? 0 : undefined);
8694                    const borderWidth = toPadding(style.borderWidth);
8695                    return {
8696                        text: datasets[meta.index].label,
8697                        fillStyle: style.backgroundColor,
8698                        fontColor: color,
8699                        hidden: !meta.visible,
8700                        lineCap: style.borderCapStyle,
8701                        lineDash: style.borderDash,
8702                        lineDashOffset: style.borderDashOffset,
8703                        lineJoin: style.borderJoinStyle,
8704                        lineWidth: (borderWidth.width + borderWidth.height) / 4,
8705                        strokeStyle: style.borderColor,
8706                        pointStyle: pointStyle || style.pointStyle,
8707                        rotation: style.rotation,
8708                        textAlign: textAlign || style.textAlign,
8709                        borderRadius: useBorderRadius && (borderRadius || style.borderRadius),
8710                        datasetIndex: meta.index
8711                    };
8712                }, this);
8713            }
8714        },
8715        title: {
8716            color: (ctx)=>ctx.chart.options.color,
8717            display: false,
8718            position: 'center',
8719            text: ''
8720        }
8721    },
8722    descriptors: {
8723        _scriptable: (name)=>!name.startsWith('on'),
8724        labels: {
8725            _scriptable: (name)=>![
8726                    'generateLabels',
8727                    'filter',
8728                    'sort'
8729                ].includes(name)
8730        }
8731    }
8732};
8733
8734class Title extends Element {
8735 constructor(config){
8736        super();
8737        this.chart = config.chart;
8738        this.options = config.options;
8739        this.ctx = config.ctx;
8740        this._padding = undefined;
8741        this.top = undefined;
8742        this.bottom = undefined;
8743        this.left = undefined;
8744        this.right = undefined;
8745        this.width = undefined;
8746        this.height = undefined;
8747        this.position = undefined;
8748        this.weight = undefined;
8749        this.fullSize = undefined;
8750    }
8751    update(maxWidth, maxHeight) {
8752        const opts = this.options;
8753        this.left = 0;
8754        this.top = 0;
8755        if (!opts.display) {
8756            this.width = this.height = this.right = this.bottom = 0;
8757            return;
8758        }
8759        this.width = this.right = maxWidth;
8760        this.height = this.bottom = maxHeight;
8761        const lineCount = isArray(opts.text) ? opts.text.length : 1;
8762        this._padding = toPadding(opts.padding);
8763        const textSize = lineCount * toFont(opts.font).lineHeight + this._padding.height;
8764        if (this.isHorizontal()) {
8765            this.height = textSize;
8766        } else {
8767            this.width = textSize;
8768        }
8769    }
8770    isHorizontal() {
8771        const pos = this.options.position;
8772        return pos === 'top' || pos === 'bottom';
8773    }
8774    _drawArgs(offset) {
8775        const { top , left , bottom , right , options  } = this;
8776        const align = options.align;
8777        let rotation = 0;
8778        let maxWidth, titleX, titleY;
8779        if (this.isHorizontal()) {
8780            titleX = _alignStartEnd(align, left, right);
8781            titleY = top + offset;
8782            maxWidth = right - left;
8783        } else {
8784            if (options.position === 'left') {
8785                titleX = left + offset;
8786                titleY = _alignStartEnd(align, bottom, top);
8787                rotation = PI * -0.5;
8788            } else {
8789                titleX = right - offset;
8790                titleY = _alignStartEnd(align, top, bottom);
8791                rotation = PI * 0.5;
8792            }
8793            maxWidth = bottom - top;
8794        }
8795        return {
8796            titleX,
8797            titleY,
8798            maxWidth,
8799            rotation
8800        };
8801    }
8802    draw() {
8803        const ctx = this.ctx;
8804        const opts = this.options;
8805        if (!opts.display) {
8806            return;
8807        }
8808        const fontOpts = toFont(opts.font);
8809        const lineHeight = fontOpts.lineHeight;
8810        const offset = lineHeight / 2 + this._padding.top;
8811        const { titleX , titleY , maxWidth , rotation  } = this._drawArgs(offset);
8812        renderText(ctx, opts.text, 0, 0, fontOpts, {
8813            color: opts.color,
8814            maxWidth,
8815            rotation,
8816            textAlign: _toLeftRightCenter(opts.align),
8817            textBaseline: 'middle',
8818            translation: [
8819                titleX,
8820                titleY
8821            ]
8822        });
8823    }
8824}
8825function createTitle(chart, titleOpts) {
8826    const title = new Title({
8827        ctx: chart.ctx,
8828        options: titleOpts,
8829        chart
8830    });
8831    layouts.configure(chart, title, titleOpts);
8832    layouts.addBox(chart, title);
8833    chart.titleBlock = title;
8834}
8835var plugin_title = {
8836    id: 'title',
8837 _element: Title,
8838    start (chart, _args, options) {
8839        createTitle(chart, options);
8840    },
8841    stop (chart) {
8842        const titleBlock = chart.titleBlock;
8843        layouts.removeBox(chart, titleBlock);
8844        delete chart.titleBlock;
8845    },
8846    beforeUpdate (chart, _args, options) {
8847        const title = chart.titleBlock;
8848        layouts.configure(chart, title, options);
8849        title.options = options;
8850    },
8851    defaults: {
8852        align: 'center',
8853        display: false,
8854        font: {
8855            weight: 'bold'
8856        },
8857        fullSize: true,
8858        padding: 10,
8859        position: 'top',
8860        text: '',
8861        weight: 2000
8862    },
8863    defaultRoutes: {
8864        color: 'color'
8865    },
8866    descriptors: {
8867        _scriptable: true,
8868        _indexable: false
8869    }
8870};
8871
8872const map = new WeakMap();
8873var plugin_subtitle = {
8874    id: 'subtitle',
8875    start (chart, _args, options) {
8876        const title = new Title({
8877            ctx: chart.ctx,
8878            options,
8879            chart
8880        });
8881        layouts.configure(chart, title, options);
8882        layouts.addBox(chart, title);
8883        map.set(chart, title);
8884    },
8885    stop (chart) {
8886        layouts.removeBox(chart, map.get(chart));
8887        map.delete(chart);
8888    },
8889    beforeUpdate (chart, _args, options) {
8890        const title = map.get(chart);
8891        layouts.configure(chart, title, options);
8892        title.options = options;
8893    },
8894    defaults: {
8895        align: 'center',
8896        display: false,
8897        font: {
8898            weight: 'normal'
8899        },
8900        fullSize: true,
8901        padding: 0,
8902        position: 'top',
8903        text: '',
8904        weight: 1500
8905    },
8906    defaultRoutes: {
8907        color: 'color'
8908    },
8909    descriptors: {
8910        _scriptable: true,
8911        _indexable: false
8912    }
8913};
8914
8915const positioners = {
8916 average (items) {
8917        if (!items.length) {
8918            return false;
vendor: 4,574 bytes, lines 8919-9048
8919        }
8920        let i, len;
8921        let xSet = new Set();
8922        let y = 0;
8923        let count = 0;
8924        for(i = 0, len = items.length; i < len; ++i){
8925            const el = items[i].element;
8926            if (el && el.hasValue()) {
8927                const pos = el.tooltipPosition();
8928                xSet.add(pos.x);
8929                y += pos.y;
8930                ++count;
8931            }
8932        }
8933        if (count === 0 || xSet.size === 0) {
8934            return false;
8935        }
8936        const xAverage = [
8937            ...xSet
8938        ].reduce((a, b)=>a + b) / xSet.size;
8939        return {
8940            x: xAverage,
8941            y: y / count
8942        };
8943    },
8944 nearest (items, eventPosition) {
8945        if (!items.length) {
8946            return false;
8947        }
8948        let x = eventPosition.x;
8949        let y = eventPosition.y;
8950        let minDistance = Number.POSITIVE_INFINITY;
8951        let i, len, nearestElement;
8952        for(i = 0, len = items.length; i < len; ++i){
8953            const el = items[i].element;
8954            if (el && el.hasValue()) {
8955                const center = el.getCenterPoint();
8956                const d = distanceBetweenPoints(eventPosition, center);
8957                if (d < minDistance) {
8958                    minDistance = d;
8959                    nearestElement = el;
8960                }
8961            }
8962        }
8963        if (nearestElement) {
8964            const tp = nearestElement.tooltipPosition();
8965            x = tp.x;
8966            y = tp.y;
8967        }
8968        return {
8969            x,
8970            y
8971        };
8972    }
8973};
8974function pushOrConcat(base, toPush) {
8975    if (toPush) {
8976        if (isArray(toPush)) {
8977            Array.prototype.push.apply(base, toPush);
8978        } else {
8979            base.push(toPush);
8980        }
8981    }
8982    return base;
8983}
8984 function splitNewlines(str) {
8985    if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) {
8986        return str.split('\n');
8987    }
8988    return str;
8989}
8990 function createTooltipItem(chart, item) {
8991    const { element , datasetIndex , index  } = item;
8992    const controller = chart.getDatasetMeta(datasetIndex).controller;
8993    const { label , value  } = controller.getLabelAndValue(index);
8994    return {
8995        chart,
8996        label,
8997        parsed: controller.getParsed(index),
8998        raw: chart.data.datasets[datasetIndex].data[index],
8999        formattedValue: value,
9000        dataset: controller.getDataset(),
9001        dataIndex: index,
9002        datasetIndex,
9003        element
9004    };
9005}
9006 function getTooltipSize(tooltip, options) {
9007    const ctx = tooltip.chart.ctx;
9008    const { body , footer , title  } = tooltip;
9009    const { boxWidth , boxHeight  } = options;
9010    const bodyFont = toFont(options.bodyFont);
9011    const titleFont = toFont(options.titleFont);
9012    const footerFont = toFont(options.footerFont);
9013    const titleLineCount = title.length;
9014    const footerLineCount = footer.length;
9015    const bodyLineItemCount = body.length;
9016    const padding = toPadding(options.padding);
9017    let height = padding.height;
9018    let width = 0;
9019    let combinedBodyLength = body.reduce((count, bodyItem)=>count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length, 0);
9020    combinedBodyLength += tooltip.beforeBody.length + tooltip.afterBody.length;
9021    if (titleLineCount) {
9022        height += titleLineCount * titleFont.lineHeight + (titleLineCount - 1) * options.titleSpacing + options.titleMarginBottom;
9023    }
9024    if (combinedBodyLength) {
9025        const bodyLineHeight = options.displayColors ? Math.max(boxHeight, bodyFont.lineHeight) : bodyFont.lineHeight;
9026        height += bodyLineItemCount * bodyLineHeight + (combinedBodyLength - bodyLineItemCount) * bodyFont.lineHeight + (combinedBodyLength - 1) * options.bodySpacing;
9027    }
9028    if (footerLineCount) {
9029        height += options.footerMarginTop + footerLineCount * footerFont.lineHeight + (footerLineCount - 1) * options.footerSpacing;
9030    }
9031    let widthPadding = 0;
9032    const maxLineWidth = function(line) {
9033        width = Math.max(width, ctx.measureText(line).width + widthPadding);
9034    };
9035    ctx.save();
9036    ctx.font = titleFont.string;
9037    each(tooltip.title, maxLineWidth);
9038    ctx.font = bodyFont.string;
9039    each(tooltip.beforeBody.concat(tooltip.afterBody), maxLineWidth);
9040    widthPadding = options.displayColors ? boxWidth + 2 + options.boxPadding : 0;
9041    each(body, (bodyItem)=>{
9042        each(bodyItem.before, maxLineWidth);
9043        each(bodyItem.lines, maxLineWidth);
9044        each(bodyItem.after, maxLineWidth);
9045    });
9046    widthPadding = 0;
9047    ctx.font = footerFont.string;
9048    each(tooltip.footer, maxLineWidth);
vendor: 27,218 bytes, lines 9049-9730
9049    ctx.restore();
9050    width += padding.width;
9051    return {
9052        width,
9053        height
9054    };
9055}
9056function determineYAlign(chart, size) {
9057    const { y , height  } = size;
9058    if (y < height / 2) {
9059        return 'top';
9060    } else if (y > chart.height - height / 2) {
9061        return 'bottom';
9062    }
9063    return 'center';
9064}
9065function doesNotFitWithAlign(xAlign, chart, options, size) {
9066    const { x , width  } = size;
9067    const caret = options.caretSize + options.caretPadding;
9068    if (xAlign === 'left' && x + width + caret > chart.width) {
9069        return true;
9070    }
9071    if (xAlign === 'right' && x - width - caret < 0) {
9072        return true;
9073    }
9074}
9075function determineXAlign(chart, options, size, yAlign) {
9076    const { x , width  } = size;
9077    const { width: chartWidth , chartArea: { left , right  }  } = chart;
9078    let xAlign = 'center';
9079    if (yAlign === 'center') {
9080        xAlign = x <= (left + right) / 2 ? 'left' : 'right';
9081    } else if (x <= width / 2) {
9082        xAlign = 'left';
9083    } else if (x >= chartWidth - width / 2) {
9084        xAlign = 'right';
9085    }
9086    if (doesNotFitWithAlign(xAlign, chart, options, size)) {
9087        xAlign = 'center';
9088    }
9089    return xAlign;
9090}
9091 function determineAlignment(chart, options, size) {
9092    const yAlign = size.yAlign || options.yAlign || determineYAlign(chart, size);
9093    return {
9094        xAlign: size.xAlign || options.xAlign || determineXAlign(chart, options, size, yAlign),
9095        yAlign
9096    };
9097}
9098function alignX(size, xAlign) {
9099    let { x , width  } = size;
9100    if (xAlign === 'right') {
9101        x -= width;
9102    } else if (xAlign === 'center') {
9103        x -= width / 2;
9104    }
9105    return x;
9106}
9107function alignY(size, yAlign, paddingAndSize) {
9108    let { y , height  } = size;
9109    if (yAlign === 'top') {
9110        y += paddingAndSize;
9111    } else if (yAlign === 'bottom') {
9112        y -= height + paddingAndSize;
9113    } else {
9114        y -= height / 2;
9115    }
9116    return y;
9117}
9118 function getBackgroundPoint(options, size, alignment, chart) {
9119    const { caretSize , caretPadding , cornerRadius  } = options;
9120    const { xAlign , yAlign  } = alignment;
9121    const paddingAndSize = caretSize + caretPadding;
9122    const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(cornerRadius);
9123    let x = alignX(size, xAlign);
9124    const y = alignY(size, yAlign, paddingAndSize);
9125    if (yAlign === 'center') {
9126        if (xAlign === 'left') {
9127            x += paddingAndSize;
9128        } else if (xAlign === 'right') {
9129            x -= paddingAndSize;
9130        }
9131    } else if (xAlign === 'left') {
9132        x -= Math.max(topLeft, bottomLeft) + caretSize;
9133    } else if (xAlign === 'right') {
9134        x += Math.max(topRight, bottomRight) + caretSize;
9135    }
9136    return {
9137        x: _limitValue(x, 0, chart.width - size.width),
9138        y: _limitValue(y, 0, chart.height - size.height)
9139    };
9140}
9141function getAlignedX(tooltip, align, options) {
9142    const padding = toPadding(options.padding);
9143    return align === 'center' ? tooltip.x + tooltip.width / 2 : align === 'right' ? tooltip.x + tooltip.width - padding.right : tooltip.x + padding.left;
9144}
9145 function getBeforeAfterBodyLines(callback) {
9146    return pushOrConcat([], splitNewlines(callback));
9147}
9148function createTooltipContext(parent, tooltip, tooltipItems) {
9149    return createContext(parent, {
9150        tooltip,
9151        tooltipItems,
9152        type: 'tooltip'
9153    });
9154}
9155function overrideCallbacks(callbacks, context) {
9156    const override = context && context.dataset && context.dataset.tooltip && context.dataset.tooltip.callbacks;
9157    return override ? callbacks.override(override) : callbacks;
9158}
9159const defaultCallbacks = {
9160    beforeTitle: noop,
9161    title (tooltipItems) {
9162        if (tooltipItems.length > 0) {
9163            const item = tooltipItems[0];
9164            const labels = item.chart.data.labels;
9165            const labelCount = labels ? labels.length : 0;
9166            if (this && this.options && this.options.mode === 'dataset') {
9167                return item.dataset.label || '';
9168            } else if (item.label) {
9169                return item.label;
9170            } else if (labelCount > 0 && item.dataIndex < labelCount) {
9171                return labels[item.dataIndex];
9172            }
9173        }
9174        return '';
9175    },
9176    afterTitle: noop,
9177    beforeBody: noop,
9178    beforeLabel: noop,
9179    label (tooltipItem) {
9180        if (this && this.options && this.options.mode === 'dataset') {
9181            return tooltipItem.label + ': ' + tooltipItem.formattedValue || tooltipItem.formattedValue;
9182        }
9183        let label = tooltipItem.dataset.label || '';
9184        if (label) {
9185            label += ': ';
9186        }
9187        const value = tooltipItem.formattedValue;
9188        if (!isNullOrUndef(value)) {
9189            label += value;
9190        }
9191        return label;
9192    },
9193    labelColor (tooltipItem) {
9194        const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);
9195        const options = meta.controller.getStyle(tooltipItem.dataIndex);
9196        return {
9197            borderColor: options.borderColor,
9198            backgroundColor: options.backgroundColor,
9199            borderWidth: options.borderWidth,
9200            borderDash: options.borderDash,
9201            borderDashOffset: options.borderDashOffset,
9202            borderRadius: 0
9203        };
9204    },
9205    labelTextColor () {
9206        return this.options.bodyColor;
9207    },
9208    labelPointStyle (tooltipItem) {
9209        const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);
9210        const options = meta.controller.getStyle(tooltipItem.dataIndex);
9211        return {
9212            pointStyle: options.pointStyle,
9213            rotation: options.rotation
9214        };
9215    },
9216    afterLabel: noop,
9217    afterBody: noop,
9218    beforeFooter: noop,
9219    footer: noop,
9220    afterFooter: noop
9221};
9222 function invokeCallbackWithFallback(callbacks, name, ctx, arg) {
9223    const result = callbacks[name].call(ctx, arg);
9224    if (typeof result === 'undefined') {
9225        return defaultCallbacks[name].call(ctx, arg);
9226    }
9227    return result;
9228}
9229class Tooltip extends Element {
9230 static positioners = positioners;
9231    constructor(config){
9232        super();
9233        this.opacity = 0;
9234        this._active = [];
9235        this._eventPosition = undefined;
9236        this._size = undefined;
9237        this._cachedAnimations = undefined;
9238        this._tooltipItems = [];
9239        this.$animations = undefined;
9240        this.$context = undefined;
9241        this.chart = config.chart;
9242        this.options = config.options;
9243        this.dataPoints = undefined;
9244        this.title = undefined;
9245        this.beforeBody = undefined;
9246        this.body = undefined;
9247        this.afterBody = undefined;
9248        this.footer = undefined;
9249        this.xAlign = undefined;
9250        this.yAlign = undefined;
9251        this.x = undefined;
9252        this.y = undefined;
9253        this.height = undefined;
9254        this.width = undefined;
9255        this.caretX = undefined;
9256        this.caretY = undefined;
9257        this.labelColors = undefined;
9258        this.labelPointStyles = undefined;
9259        this.labelTextColors = undefined;
9260    }
9261    initialize(options) {
9262        this.options = options;
9263        this._cachedAnimations = undefined;
9264        this.$context = undefined;
9265    }
9266 _resolveAnimations() {
9267        const cached = this._cachedAnimations;
9268        if (cached) {
9269            return cached;
9270        }
9271        const chart = this.chart;
9272        const options = this.options.setContext(this.getContext());
9273        const opts = options.enabled && chart.options.animation && options.animations;
9274        const animations = new Animations(this.chart, opts);
9275        if (opts._cacheable) {
9276            this._cachedAnimations = Object.freeze(animations);
9277        }
9278        return animations;
9279    }
9280 getContext() {
9281        return this.$context || (this.$context = createTooltipContext(this.chart.getContext(), this, this._tooltipItems));
9282    }
9283    getTitle(context, options) {
9284        const { callbacks  } = options;
9285        const beforeTitle = invokeCallbackWithFallback(callbacks, 'beforeTitle', this, context);
9286        const title = invokeCallbackWithFallback(callbacks, 'title', this, context);
9287        const afterTitle = invokeCallbackWithFallback(callbacks, 'afterTitle', this, context);
9288        let lines = [];
9289        lines = pushOrConcat(lines, splitNewlines(beforeTitle));
9290        lines = pushOrConcat(lines, splitNewlines(title));
9291        lines = pushOrConcat(lines, splitNewlines(afterTitle));
9292        return lines;
9293    }
9294    getBeforeBody(tooltipItems, options) {
9295        return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'beforeBody', this, tooltipItems));
9296    }
9297    getBody(tooltipItems, options) {
9298        const { callbacks  } = options;
9299        const bodyItems = [];
9300        each(tooltipItems, (context)=>{
9301            const bodyItem = {
9302                before: [],
9303                lines: [],
9304                after: []
9305            };
9306            const scoped = overrideCallbacks(callbacks, context);
9307            pushOrConcat(bodyItem.before, splitNewlines(invokeCallbackWithFallback(scoped, 'beforeLabel', this, context)));
9308            pushOrConcat(bodyItem.lines, invokeCallbackWithFallback(scoped, 'label', this, context));
9309            pushOrConcat(bodyItem.after, splitNewlines(invokeCallbackWithFallback(scoped, 'afterLabel', this, context)));
9310            bodyItems.push(bodyItem);
9311        });
9312        return bodyItems;
9313    }
9314    getAfterBody(tooltipItems, options) {
9315        return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'afterBody', this, tooltipItems));
9316    }
9317    getFooter(tooltipItems, options) {
9318        const { callbacks  } = options;
9319        const beforeFooter = invokeCallbackWithFallback(callbacks, 'beforeFooter', this, tooltipItems);
9320        const footer = invokeCallbackWithFallback(callbacks, 'footer', this, tooltipItems);
9321        const afterFooter = invokeCallbackWithFallback(callbacks, 'afterFooter', this, tooltipItems);
9322        let lines = [];
9323        lines = pushOrConcat(lines, splitNewlines(beforeFooter));
9324        lines = pushOrConcat(lines, splitNewlines(footer));
9325        lines = pushOrConcat(lines, splitNewlines(afterFooter));
9326        return lines;
9327    }
9328 _createItems(options) {
9329        const active = this._active;
9330        const data = this.chart.data;
9331        const labelColors = [];
9332        const labelPointStyles = [];
9333        const labelTextColors = [];
9334        let tooltipItems = [];
9335        let i, len;
9336        for(i = 0, len = active.length; i < len; ++i){
9337            tooltipItems.push(createTooltipItem(this.chart, active[i]));
9338        }
9339        if (options.filter) {
9340            tooltipItems = tooltipItems.filter((element, index, array)=>options.filter(element, index, array, data));
9341        }
9342        if (options.itemSort) {
9343            tooltipItems = tooltipItems.sort((a, b)=>options.itemSort(a, b, data));
9344        }
9345        each(tooltipItems, (context)=>{
9346            const scoped = overrideCallbacks(options.callbacks, context);
9347            labelColors.push(invokeCallbackWithFallback(scoped, 'labelColor', this, context));
9348            labelPointStyles.push(invokeCallbackWithFallback(scoped, 'labelPointStyle', this, context));
9349            labelTextColors.push(invokeCallbackWithFallback(scoped, 'labelTextColor', this, context));
9350        });
9351        this.labelColors = labelColors;
9352        this.labelPointStyles = labelPointStyles;
9353        this.labelTextColors = labelTextColors;
9354        this.dataPoints = tooltipItems;
9355        return tooltipItems;
9356    }
9357    update(changed, replay) {
9358        const options = this.options.setContext(this.getContext());
9359        const active = this._active;
9360        let properties;
9361        let tooltipItems = [];
9362        if (!active.length) {
9363            if (this.opacity !== 0) {
9364                properties = {
9365                    opacity: 0
9366                };
9367            }
9368        } else {
9369            const position = positioners[options.position].call(this, active, this._eventPosition);
9370            tooltipItems = this._createItems(options);
9371            this.title = this.getTitle(tooltipItems, options);
9372            this.beforeBody = this.getBeforeBody(tooltipItems, options);
9373            this.body = this.getBody(tooltipItems, options);
9374            this.afterBody = this.getAfterBody(tooltipItems, options);
9375            this.footer = this.getFooter(tooltipItems, options);
9376            const size = this._size = getTooltipSize(this, options);
9377            const positionAndSize = Object.assign({}, position, size);
9378            const alignment = determineAlignment(this.chart, options, positionAndSize);
9379            const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, this.chart);
9380            this.xAlign = alignment.xAlign;
9381            this.yAlign = alignment.yAlign;
9382            properties = {
9383                opacity: 1,
9384                x: backgroundPoint.x,
9385                y: backgroundPoint.y,
9386                width: size.width,
9387                height: size.height,
9388                caretX: position.x,
9389                caretY: position.y
9390            };
9391        }
9392        this._tooltipItems = tooltipItems;
9393        this.$context = undefined;
9394        if (properties) {
9395            this._resolveAnimations().update(this, properties);
9396        }
9397        if (changed && options.external) {
9398            options.external.call(this, {
9399                chart: this.chart,
9400                tooltip: this,
9401                replay
9402            });
9403        }
9404    }
9405    drawCaret(tooltipPoint, ctx, size, options) {
9406        const caretPosition = this.getCaretPosition(tooltipPoint, size, options);
9407        ctx.lineTo(caretPosition.x1, caretPosition.y1);
9408        ctx.lineTo(caretPosition.x2, caretPosition.y2);
9409        ctx.lineTo(caretPosition.x3, caretPosition.y3);
9410    }
9411    getCaretPosition(tooltipPoint, size, options) {
9412        const { xAlign , yAlign  } = this;
9413        const { caretSize , cornerRadius  } = options;
9414        const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(cornerRadius);
9415        const { x: ptX , y: ptY  } = tooltipPoint;
9416        const { width , height  } = size;
9417        let x1, x2, x3, y1, y2, y3;
9418        if (yAlign === 'center') {
9419            y2 = ptY + height / 2;
9420            if (xAlign === 'left') {
9421                x1 = ptX;
9422                x2 = x1 - caretSize;
9423                y1 = y2 + caretSize;
9424                y3 = y2 - caretSize;
9425            } else {
9426                x1 = ptX + width;
9427                x2 = x1 + caretSize;
9428                y1 = y2 - caretSize;
9429                y3 = y2 + caretSize;
9430            }
9431            x3 = x1;
9432        } else {
9433            if (xAlign === 'left') {
9434                x2 = ptX + Math.max(topLeft, bottomLeft) + caretSize;
9435            } else if (xAlign === 'right') {
9436                x2 = ptX + width - Math.max(topRight, bottomRight) - caretSize;
9437            } else {
9438                x2 = this.caretX;
9439            }
9440            if (yAlign === 'top') {
9441                y1 = ptY;
9442                y2 = y1 - caretSize;
9443                x1 = x2 - caretSize;
9444                x3 = x2 + caretSize;
9445            } else {
9446                y1 = ptY + height;
9447                y2 = y1 + caretSize;
9448                x1 = x2 + caretSize;
9449                x3 = x2 - caretSize;
9450            }
9451            y3 = y1;
9452        }
9453        return {
9454            x1,
9455            x2,
9456            x3,
9457            y1,
9458            y2,
9459            y3
9460        };
9461    }
9462    drawTitle(pt, ctx, options) {
9463        const title = this.title;
9464        const length = title.length;
9465        let titleFont, titleSpacing, i;
9466        if (length) {
9467            const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
9468            pt.x = getAlignedX(this, options.titleAlign, options);
9469            ctx.textAlign = rtlHelper.textAlign(options.titleAlign);
9470            ctx.textBaseline = 'middle';
9471            titleFont = toFont(options.titleFont);
9472            titleSpacing = options.titleSpacing;
9473            ctx.fillStyle = options.titleColor;
9474            ctx.font = titleFont.string;
9475            for(i = 0; i < length; ++i){
9476                ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFont.lineHeight / 2);
9477                pt.y += titleFont.lineHeight + titleSpacing;
9478                if (i + 1 === length) {
9479                    pt.y += options.titleMarginBottom - titleSpacing;
9480                }
9481            }
9482        }
9483    }
9484 _drawColorBox(ctx, pt, i, rtlHelper, options) {
9485        const labelColor = this.labelColors[i];
9486        const labelPointStyle = this.labelPointStyles[i];
9487        const { boxHeight , boxWidth  } = options;
9488        const bodyFont = toFont(options.bodyFont);
9489        const colorX = getAlignedX(this, 'left', options);
9490        const rtlColorX = rtlHelper.x(colorX);
9491        const yOffSet = boxHeight < bodyFont.lineHeight ? (bodyFont.lineHeight - boxHeight) / 2 : 0;
9492        const colorY = pt.y + yOffSet;
9493        if (options.usePointStyle) {
9494            const drawOptions = {
9495                radius: Math.min(boxWidth, boxHeight) / 2,
9496                pointStyle: labelPointStyle.pointStyle,
9497                rotation: labelPointStyle.rotation,
9498                borderWidth: 1
9499            };
9500            const centerX = rtlHelper.leftForLtr(rtlColorX, boxWidth) + boxWidth / 2;
9501            const centerY = colorY + boxHeight / 2;
9502            ctx.strokeStyle = options.multiKeyBackground;
9503            ctx.fillStyle = options.multiKeyBackground;
9504            drawPoint(ctx, drawOptions, centerX, centerY);
9505            ctx.strokeStyle = labelColor.borderColor;
9506            ctx.fillStyle = labelColor.backgroundColor;
9507            drawPoint(ctx, drawOptions, centerX, centerY);
9508        } else {
9509            ctx.lineWidth = isObject(labelColor.borderWidth) ? Math.max(...Object.values(labelColor.borderWidth)) : labelColor.borderWidth || 1;
9510            ctx.strokeStyle = labelColor.borderColor;
9511            ctx.setLineDash(labelColor.borderDash || []);
9512            ctx.lineDashOffset = labelColor.borderDashOffset || 0;
9513            const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth);
9514            const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - 2);
9515            const borderRadius = toTRBLCorners(labelColor.borderRadius);
9516            if (Object.values(borderRadius).some((v)=>v !== 0)) {
9517                ctx.beginPath();
9518                ctx.fillStyle = options.multiKeyBackground;
9519                addRoundedRectPath(ctx, {
9520                    x: outerX,
9521                    y: colorY,
9522                    w: boxWidth,
9523                    h: boxHeight,
9524                    radius: borderRadius
9525                });
9526                ctx.fill();
9527                ctx.stroke();
9528                ctx.fillStyle = labelColor.backgroundColor;
9529                ctx.beginPath();
9530                addRoundedRectPath(ctx, {
9531                    x: innerX,
9532                    y: colorY + 1,
9533                    w: boxWidth - 2,
9534                    h: boxHeight - 2,
9535                    radius: borderRadius
9536                });
9537                ctx.fill();
9538            } else {
9539                ctx.fillStyle = options.multiKeyBackground;
9540                ctx.fillRect(outerX, colorY, boxWidth, boxHeight);
9541                ctx.strokeRect(outerX, colorY, boxWidth, boxHeight);
9542                ctx.fillStyle = labelColor.backgroundColor;
9543                ctx.fillRect(innerX, colorY + 1, boxWidth - 2, boxHeight - 2);
9544            }
9545        }
9546        ctx.fillStyle = this.labelTextColors[i];
9547    }
9548    drawBody(pt, ctx, options) {
9549        const { body  } = this;
9550        const { bodySpacing , bodyAlign , displayColors , boxHeight , boxWidth , boxPadding  } = options;
9551        const bodyFont = toFont(options.bodyFont);
9552        let bodyLineHeight = bodyFont.lineHeight;
9553        let xLinePadding = 0;
9554        const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
9555        const fillLineOfText = function(line) {
9556            ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyLineHeight / 2);
9557            pt.y += bodyLineHeight + bodySpacing;
9558        };
9559        const bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign);
9560        let bodyItem, textColor, lines, i, j, ilen, jlen;
9561        ctx.textAlign = bodyAlign;
9562        ctx.textBaseline = 'middle';
9563        ctx.font = bodyFont.string;
9564        pt.x = getAlignedX(this, bodyAlignForCalculation, options);
9565        ctx.fillStyle = options.bodyColor;
9566        each(this.beforeBody, fillLineOfText);
9567        xLinePadding = displayColors && bodyAlignForCalculation !== 'right' ? bodyAlign === 'center' ? boxWidth / 2 + boxPadding : boxWidth + 2 + boxPadding : 0;
9568        for(i = 0, ilen = body.length; i < ilen; ++i){
9569            bodyItem = body[i];
9570            textColor = this.labelTextColors[i];
9571            ctx.fillStyle = textColor;
9572            each(bodyItem.before, fillLineOfText);
9573            lines = bodyItem.lines;
9574            if (displayColors && lines.length) {
9575                this._drawColorBox(ctx, pt, i, rtlHelper, options);
9576                bodyLineHeight = Math.max(bodyFont.lineHeight, boxHeight);
9577            }
9578            for(j = 0, jlen = lines.length; j < jlen; ++j){
9579                fillLineOfText(lines[j]);
9580                bodyLineHeight = bodyFont.lineHeight;
9581            }
9582            each(bodyItem.after, fillLineOfText);
9583        }
9584        xLinePadding = 0;
9585        bodyLineHeight = bodyFont.lineHeight;
9586        each(this.afterBody, fillLineOfText);
9587        pt.y -= bodySpacing;
9588    }
9589    drawFooter(pt, ctx, options) {
9590        const footer = this.footer;
9591        const length = footer.length;
9592        let footerFont, i;
9593        if (length) {
9594            const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
9595            pt.x = getAlignedX(this, options.footerAlign, options);
9596            pt.y += options.footerMarginTop;
9597            ctx.textAlign = rtlHelper.textAlign(options.footerAlign);
9598            ctx.textBaseline = 'middle';
9599            footerFont = toFont(options.footerFont);
9600            ctx.fillStyle = options.footerColor;
9601            ctx.font = footerFont.string;
9602            for(i = 0; i < length; ++i){
9603                ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFont.lineHeight / 2);
9604                pt.y += footerFont.lineHeight + options.footerSpacing;
9605            }
9606        }
9607    }
9608    drawBackground(pt, ctx, tooltipSize, options) {
9609        const { xAlign , yAlign  } = this;
9610        const { x , y  } = pt;
9611        const { width , height  } = tooltipSize;
9612        const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(options.cornerRadius);
9613        ctx.fillStyle = options.backgroundColor;
9614        ctx.strokeStyle = options.borderColor;
9615        ctx.lineWidth = options.borderWidth;
9616        ctx.beginPath();
9617        ctx.moveTo(x + topLeft, y);
9618        if (yAlign === 'top') {
9619            this.drawCaret(pt, ctx, tooltipSize, options);
9620        }
9621        ctx.lineTo(x + width - topRight, y);
9622        ctx.quadraticCurveTo(x + width, y, x + width, y + topRight);
9623        if (yAlign === 'center' && xAlign === 'right') {
9624            this.drawCaret(pt, ctx, tooltipSize, options);
9625        }
9626        ctx.lineTo(x + width, y + height - bottomRight);
9627        ctx.quadraticCurveTo(x + width, y + height, x + width - bottomRight, y + height);
9628        if (yAlign === 'bottom') {
9629            this.drawCaret(pt, ctx, tooltipSize, options);
9630        }
9631        ctx.lineTo(x + bottomLeft, y + height);
9632        ctx.quadraticCurveTo(x, y + height, x, y + height - bottomLeft);
9633        if (yAlign === 'center' && xAlign === 'left') {
9634            this.drawCaret(pt, ctx, tooltipSize, options);
9635        }
9636        ctx.lineTo(x, y + topLeft);
9637        ctx.quadraticCurveTo(x, y, x + topLeft, y);
9638        ctx.closePath();
9639        ctx.fill();
9640        if (options.borderWidth > 0) {
9641            ctx.stroke();
9642        }
9643    }
9644 _updateAnimationTarget(options) {
9645        const chart = this.chart;
9646        const anims = this.$animations;
9647        const animX = anims && anims.x;
9648        const animY = anims && anims.y;
9649        if (animX || animY) {
9650            const position = positioners[options.position].call(this, this._active, this._eventPosition);
9651            if (!position) {
9652                return;
9653            }
9654            const size = this._size = getTooltipSize(this, options);
9655            const positionAndSize = Object.assign({}, position, this._size);
9656            const alignment = determineAlignment(chart, options, positionAndSize);
9657            const point = getBackgroundPoint(options, positionAndSize, alignment, chart);
9658            if (animX._to !== point.x || animY._to !== point.y) {
9659                this.xAlign = alignment.xAlign;
9660                this.yAlign = alignment.yAlign;
9661                this.width = size.width;
9662                this.height = size.height;
9663                this.caretX = position.x;
9664                this.caretY = position.y;
9665                this._resolveAnimations().update(this, point);
9666            }
9667        }
9668    }
9669 _willRender() {
9670        return !!this.opacity;
9671    }
9672    draw(ctx) {
9673        const options = this.options.setContext(this.getContext());
9674        let opacity = this.opacity;
9675        if (!opacity) {
9676            return;
9677        }
9678        this._updateAnimationTarget(options);
9679        const tooltipSize = {
9680            width: this.width,
9681            height: this.height
9682        };
9683        const pt = {
9684            x: this.x,
9685            y: this.y
9686        };
9687        opacity = Math.abs(opacity) < 1e-3 ? 0 : opacity;
9688        const padding = toPadding(options.padding);
9689        const hasTooltipContent = this.title.length || this.beforeBody.length || this.body.length || this.afterBody.length || this.footer.length;
9690        if (options.enabled && hasTooltipContent) {
9691            ctx.save();
9692            ctx.globalAlpha = opacity;
9693            this.drawBackground(pt, ctx, tooltipSize, options);
9694            overrideTextDirection(ctx, options.textDirection);
9695            pt.y += padding.top;
9696            this.drawTitle(pt, ctx, options);
9697            this.drawBody(pt, ctx, options);
9698            this.drawFooter(pt, ctx, options);
9699            restoreTextDirection(ctx, options.textDirection);
9700            ctx.restore();
9701        }
9702    }
9703 getActiveElements() {
9704        return this._active || [];
9705    }
9706 setActiveElements(activeElements, eventPosition) {
9707        const lastActive = this._active;
9708        const active = activeElements.map(({ datasetIndex , index  })=>{
9709            const meta = this.chart.getDatasetMeta(datasetIndex);
9710            if (!meta) {
9711                throw new Error('Cannot find a dataset at index ' + datasetIndex);
9712            }
9713            return {
9714                datasetIndex,
9715                element: meta.data[index],
9716                index
9717            };
9718        });
9719        const changed = !_elementsEqual(lastActive, active);
9720        const positionChanged = this._positionChanged(active, eventPosition);
9721        if (changed || positionChanged) {
9722            this._active = active;
9723            this._eventPosition = eventPosition;
9724            this._ignoreReplayEvents = true;
9725            this.update(true);
9726        }
9727    }
9728 handleEvent(e, replay, inChartArea = true) {
9729        if (replay && this._ignoreReplayEvents) {
9730            return false;
vendor: 9,295 bytes, lines 9731-10039
9731        }
9732        this._ignoreReplayEvents = false;
9733        const options = this.options;
9734        const lastActive = this._active || [];
9735        const active = this._getActiveElements(e, lastActive, replay, inChartArea);
9736        const positionChanged = this._positionChanged(active, e);
9737        const changed = replay || !_elementsEqual(active, lastActive) || positionChanged;
9738        if (changed) {
9739            this._active = active;
9740            if (options.enabled || options.external) {
9741                this._eventPosition = {
9742                    x: e.x,
9743                    y: e.y
9744                };
9745                this.update(true, replay);
9746            }
9747        }
9748        return changed;
9749    }
9750 _getActiveElements(e, lastActive, replay, inChartArea) {
9751        const options = this.options;
9752        if (e.type === 'mouseout') {
9753            return [];
9754        }
9755        if (!inChartArea) {
9756            return lastActive.filter((i)=>this.chart.data.datasets[i.datasetIndex] && this.chart.getDatasetMeta(i.datasetIndex).controller.getParsed(i.index) !== undefined);
9757        }
9758        const active = this.chart.getElementsAtEventForMode(e, options.mode, options, replay);
9759        if (options.reverse) {
9760            active.reverse();
9761        }
9762        return active;
9763    }
9764 _positionChanged(active, e) {
9765        const { caretX , caretY , options  } = this;
9766        const position = positioners[options.position].call(this, active, e);
9767        return position !== false && (caretX !== position.x || caretY !== position.y);
9768    }
9769}
9770var plugin_tooltip = {
9771    id: 'tooltip',
9772    _element: Tooltip,
9773    positioners,
9774    afterInit (chart, _args, options) {
9775        if (options) {
9776            chart.tooltip = new Tooltip({
9777                chart,
9778                options
9779            });
9780        }
9781    },
9782    beforeUpdate (chart, _args, options) {
9783        if (chart.tooltip) {
9784            chart.tooltip.initialize(options);
9785        }
9786    },
9787    reset (chart, _args, options) {
9788        if (chart.tooltip) {
9789            chart.tooltip.initialize(options);
9790        }
9791    },
9792    afterDraw (chart) {
9793        const tooltip = chart.tooltip;
9794        if (tooltip && tooltip._willRender()) {
9795            const args = {
9796                tooltip
9797            };
9798            if (chart.notifyPlugins('beforeTooltipDraw', {
9799                ...args,
9800                cancelable: true
9801            }) === false) {
9802                return;
9803            }
9804            tooltip.draw(chart.ctx);
9805            chart.notifyPlugins('afterTooltipDraw', args);
9806        }
9807    },
9808    afterEvent (chart, args) {
9809        if (chart.tooltip) {
9810            const useFinalPosition = args.replay;
9811            if (chart.tooltip.handleEvent(args.event, useFinalPosition, args.inChartArea)) {
9812                args.changed = true;
9813            }
9814        }
9815    },
9816    defaults: {
9817        enabled: true,
9818        external: null,
9819        position: 'average',
9820        backgroundColor: 'rgba(0,0,0,0.8)',
9821        titleColor: '#fff',
9822        titleFont: {
9823            weight: 'bold'
9824        },
9825        titleSpacing: 2,
9826        titleMarginBottom: 6,
9827        titleAlign: 'left',
9828        bodyColor: '#fff',
9829        bodySpacing: 2,
9830        bodyFont: {},
9831        bodyAlign: 'left',
9832        footerColor: '#fff',
9833        footerSpacing: 2,
9834        footerMarginTop: 6,
9835        footerFont: {
9836            weight: 'bold'
9837        },
9838        footerAlign: 'left',
9839        padding: 6,
9840        caretPadding: 2,
9841        caretSize: 5,
9842        cornerRadius: 6,
9843        boxHeight: (ctx, opts)=>opts.bodyFont.size,
9844        boxWidth: (ctx, opts)=>opts.bodyFont.size,
9845        multiKeyBackground: '#fff',
9846        displayColors: true,
9847        boxPadding: 0,
9848        borderColor: 'rgba(0,0,0,0)',
9849        borderWidth: 0,
9850        animation: {
9851            duration: 400,
9852            easing: 'easeOutQuart'
9853        },
9854        animations: {
9855            numbers: {
9856                type: 'number',
9857                properties: [
9858                    'x',
9859                    'y',
9860                    'width',
9861                    'height',
9862                    'caretX',
9863                    'caretY'
9864                ]
9865            },
9866            opacity: {
9867                easing: 'linear',
9868                duration: 200
9869            }
9870        },
9871        callbacks: defaultCallbacks
9872    },
9873    defaultRoutes: {
9874        bodyFont: 'font',
9875        footerFont: 'font',
9876        titleFont: 'font'
9877    },
9878    descriptors: {
9879        _scriptable: (name)=>name !== 'filter' && name !== 'itemSort' && name !== 'external',
9880        _indexable: false,
9881        callbacks: {
9882            _scriptable: false,
9883            _indexable: false
9884        },
9885        animation: {
9886            _fallback: false
9887        },
9888        animations: {
9889            _fallback: 'animation'
9890        }
9891    },
9892    additionalOptionScopes: [
9893        'interaction'
9894    ]
9895};
9896
9897var plugins = /*#__PURE__*/Object.freeze({
9898__proto__: null,
9899Colors: plugin_colors,
9900Decimation: plugin_decimation,
9901Filler: index,
9902Legend: plugin_legend,
9903SubTitle: plugin_subtitle,
9904Title: plugin_title,
9905Tooltip: plugin_tooltip
9906});
9907
9908const addIfString = (labels, raw, index, addedLabels)=>{
9909    if (typeof raw === 'string') {
9910        index = labels.push(raw) - 1;
9911        addedLabels.unshift({
9912            index,
9913            label: raw
9914        });
9915    } else if (isNaN(raw)) {
9916        index = null;
9917    }
9918    return index;
9919};
9920function findOrAddLabel(labels, raw, index, addedLabels) {
9921    const first = labels.indexOf(raw);
9922    if (first === -1) {
9923        return addIfString(labels, raw, index, addedLabels);
9924    }
9925    const last = labels.lastIndexOf(raw);
9926    return first !== last ? index : first;
9927}
9928const validIndex = (index, max)=>index === null ? null : _limitValue(Math.round(index), 0, max);
9929function _getLabelForValue(value) {
9930    const labels = this.getLabels();
9931    if (value >= 0 && value < labels.length) {
9932        return labels[value];
9933    }
9934    return value;
9935}
9936class CategoryScale extends Scale {
9937    static id = 'category';
9938 static defaults = {
9939        ticks: {
9940            callback: _getLabelForValue
9941        }
9942    };
9943    constructor(cfg){
9944        super(cfg);
9945         this._startValue = undefined;
9946        this._valueRange = 0;
9947        this._addedLabels = [];
9948    }
9949    init(scaleOptions) {
9950        const added = this._addedLabels;
9951        if (added.length) {
9952            const labels = this.getLabels();
9953            for (const { index , label  } of added){
9954                if (labels[index] === label) {
9955                    labels.splice(index, 1);
9956                }
9957            }
9958            this._addedLabels = [];
9959        }
9960        super.init(scaleOptions);
9961    }
9962    parse(raw, index) {
9963        if (isNullOrUndef(raw)) {
9964            return null;
9965        }
9966        const labels = this.getLabels();
9967        index = isFinite(index) && labels[index] === raw ? index : findOrAddLabel(labels, raw, valueOrDefault(index, raw), this._addedLabels);
9968        return validIndex(index, labels.length - 1);
9969    }
9970    determineDataLimits() {
9971        const { minDefined , maxDefined  } = this.getUserBounds();
9972        let { min , max  } = this.getMinMax(true);
9973        if (this.options.bounds === 'ticks') {
9974            if (!minDefined) {
9975                min = 0;
9976            }
9977            if (!maxDefined) {
9978                max = this.getLabels().length - 1;
9979            }
9980        }
9981        this.min = min;
9982        this.max = max;
9983    }
9984    buildTicks() {
9985        const min = this.min;
9986        const max = this.max;
9987        const offset = this.options.offset;
9988        const ticks = [];
9989        let labels = this.getLabels();
9990        labels = min === 0 && max === labels.length - 1 ? labels : labels.slice(min, max + 1);
9991        this._valueRange = Math.max(labels.length - (offset ? 0 : 1), 1);
9992        this._startValue = this.min - (offset ? 0.5 : 0);
9993        for(let value = min; value <= max; value++){
9994            ticks.push({
9995                value
9996            });
9997        }
9998        return ticks;
9999    }
10000    getLabelForValue(value) {
10001        return _getLabelForValue.call(this, value);
10002    }
10003 configure() {
10004        super.configure();
10005        if (!this.isHorizontal()) {
10006            this._reversePixels = !this._reversePixels;
10007        }
10008    }
10009    getPixelForValue(value) {
10010        if (typeof value !== 'number') {
10011            value = this.parse(value);
10012        }
10013        return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);
10014    }
10015    getPixelForTick(index) {
10016        const ticks = this.ticks;
10017        if (index < 0 || index > ticks.length - 1) {
10018            return null;
10019        }
10020        return this.getPixelForValue(ticks[index].value);
10021    }
10022    getValueForPixel(pixel) {
10023        return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange);
10024    }
10025    getBasePixel() {
10026        return this.bottom;
10027    }
10028}
10029
10030function generateTicks$1(generationOptions, dataRange) {
10031    const ticks = [];
10032    const MIN_SPACING = 1e-14;
10033    const { bounds , step , min , max , precision , count , maxTicks , maxDigits , includeBounds  } = generationOptions;
10034    const unit = step || 1;
10035    const maxSpaces = maxTicks - 1;
10036    const { min: rmin , max: rmax  } = dataRange;
10037    const minDefined = !isNullOrUndef(min);
10038    const maxDefined = !isNullOrUndef(max);
10039    const countDefined = !isNullOrUndef(count);
vendor: 21,137 bytes, lines 10040-10657
10040    const minSpacing = (rmax - rmin) / (maxDigits + 1);
10041    let spacing = niceNum((rmax - rmin) / maxSpaces / unit) * unit;
10042    let factor, niceMin, niceMax, numSpaces;
10043    if (spacing < MIN_SPACING && !minDefined && !maxDefined) {
10044        return [
10045            {
10046                value: rmin
10047            },
10048            {
10049                value: rmax
10050            }
10051        ];
10052    }
10053    numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);
10054    if (numSpaces > maxSpaces) {
10055        spacing = niceNum(numSpaces * spacing / maxSpaces / unit) * unit;
10056    }
10057    if (!isNullOrUndef(precision)) {
10058        factor = Math.pow(10, precision);
10059        spacing = Math.ceil(spacing * factor) / factor;
10060    }
10061    if (bounds === 'ticks') {
10062        niceMin = Math.floor(rmin / spacing) * spacing;
10063        niceMax = Math.ceil(rmax / spacing) * spacing;
10064    } else {
10065        niceMin = rmin;
10066        niceMax = rmax;
10067    }
10068    if (minDefined && maxDefined && step && almostWhole((max - min) / step, spacing / 1000)) {
10069        numSpaces = Math.round(Math.min((max - min) / spacing, maxTicks));
10070        spacing = (max - min) / numSpaces;
10071        niceMin = min;
10072        niceMax = max;
10073    } else if (countDefined) {
10074        niceMin = minDefined ? min : niceMin;
10075        niceMax = maxDefined ? max : niceMax;
10076        numSpaces = count - 1;
10077        spacing = (niceMax - niceMin) / numSpaces;
10078    } else {
10079        numSpaces = (niceMax - niceMin) / spacing;
10080        if (almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
10081            numSpaces = Math.round(numSpaces);
10082        } else {
10083            numSpaces = Math.ceil(numSpaces);
10084        }
10085    }
10086    const decimalPlaces = Math.max(_decimalPlaces(spacing), _decimalPlaces(niceMin));
10087    factor = Math.pow(10, isNullOrUndef(precision) ? decimalPlaces : precision);
10088    niceMin = Math.round(niceMin * factor) / factor;
10089    niceMax = Math.round(niceMax * factor) / factor;
10090    let j = 0;
10091    if (minDefined) {
10092        if (includeBounds && niceMin !== min) {
10093            ticks.push({
10094                value: min
10095            });
10096            if (niceMin < min) {
10097                j++;
10098            }
10099            if (almostEquals(Math.round((niceMin + j * spacing) * factor) / factor, min, relativeLabelSize(min, minSpacing, generationOptions))) {
10100                j++;
10101            }
10102        } else if (niceMin < min) {
10103            j++;
10104        }
10105    }
10106    for(; j < numSpaces; ++j){
10107        const tickValue = Math.round((niceMin + j * spacing) * factor) / factor;
10108        if (maxDefined && tickValue > max) {
10109            break;
10110        }
10111        ticks.push({
10112            value: tickValue
10113        });
10114    }
10115    if (maxDefined && includeBounds && niceMax !== max) {
10116        if (ticks.length && almostEquals(ticks[ticks.length - 1].value, max, relativeLabelSize(max, minSpacing, generationOptions))) {
10117            ticks[ticks.length - 1].value = max;
10118        } else {
10119            ticks.push({
10120                value: max
10121            });
10122        }
10123    } else if (!maxDefined || niceMax === max) {
10124        ticks.push({
10125            value: niceMax
10126        });
10127    }
10128    return ticks;
10129}
10130function relativeLabelSize(value, minSpacing, { horizontal , minRotation  }) {
10131    const rad = toRadians(minRotation);
10132    const ratio = (horizontal ? Math.sin(rad) : Math.cos(rad)) || 0.001;
10133    const length = 0.75 * minSpacing * ('' + value).length;
10134    return Math.min(minSpacing / ratio, length);
10135}
10136class LinearScaleBase extends Scale {
10137    constructor(cfg){
10138        super(cfg);
10139         this.start = undefined;
10140         this.end = undefined;
10141         this._startValue = undefined;
10142         this._endValue = undefined;
10143        this._valueRange = 0;
10144    }
10145    parse(raw, index) {
10146        if (isNullOrUndef(raw)) {
10147            return null;
10148        }
10149        if ((typeof raw === 'number' || raw instanceof Number) && !isFinite(+raw)) {
10150            return null;
10151        }
10152        return +raw;
10153    }
10154    handleTickRangeOptions() {
10155        const { beginAtZero  } = this.options;
10156        const { minDefined , maxDefined  } = this.getUserBounds();
10157        let { min , max  } = this;
10158        const setMin = (v)=>min = minDefined ? min : v;
10159        const setMax = (v)=>max = maxDefined ? max : v;
10160        if (beginAtZero) {
10161            const minSign = sign(min);
10162            const maxSign = sign(max);
10163            if (minSign < 0 && maxSign < 0) {
10164                setMax(0);
10165            } else if (minSign > 0 && maxSign > 0) {
10166                setMin(0);
10167            }
10168        }
10169        if (min === max) {
10170            let offset = max === 0 ? 1 : Math.abs(max * 0.05);
10171            setMax(max + offset);
10172            if (!beginAtZero) {
10173                setMin(min - offset);
10174            }
10175        }
10176        this.min = min;
10177        this.max = max;
10178    }
10179    getTickLimit() {
10180        const tickOpts = this.options.ticks;
10181        let { maxTicksLimit , stepSize  } = tickOpts;
10182        let maxTicks;
10183        if (stepSize) {
10184            maxTicks = Math.ceil(this.max / stepSize) - Math.floor(this.min / stepSize) + 1;
10185            if (maxTicks > 1000) {
10186                console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`);
10187                maxTicks = 1000;
10188            }
10189        } else {
10190            maxTicks = this.computeTickLimit();
10191            maxTicksLimit = maxTicksLimit || 11;
10192        }
10193        if (maxTicksLimit) {
10194            maxTicks = Math.min(maxTicksLimit, maxTicks);
10195        }
10196        return maxTicks;
10197    }
10198 computeTickLimit() {
10199        return Number.POSITIVE_INFINITY;
10200    }
10201    buildTicks() {
10202        const opts = this.options;
10203        const tickOpts = opts.ticks;
10204        let maxTicks = this.getTickLimit();
10205        maxTicks = Math.max(2, maxTicks);
10206        const numericGeneratorOptions = {
10207            maxTicks,
10208            bounds: opts.bounds,
10209            min: opts.min,
10210            max: opts.max,
10211            precision: tickOpts.precision,
10212            step: tickOpts.stepSize,
10213            count: tickOpts.count,
10214            maxDigits: this._maxDigits(),
10215            horizontal: this.isHorizontal(),
10216            minRotation: tickOpts.minRotation || 0,
10217            includeBounds: tickOpts.includeBounds !== false
10218        };
10219        const dataRange = this._range || this;
10220        const ticks = generateTicks$1(numericGeneratorOptions, dataRange);
10221        if (opts.bounds === 'ticks') {
10222            _setMinAndMaxByKey(ticks, this, 'value');
10223        }
10224        if (opts.reverse) {
10225            ticks.reverse();
10226            this.start = this.max;
10227            this.end = this.min;
10228        } else {
10229            this.start = this.min;
10230            this.end = this.max;
10231        }
10232        return ticks;
10233    }
10234 configure() {
10235        const ticks = this.ticks;
10236        let start = this.min;
10237        let end = this.max;
10238        super.configure();
10239        if (this.options.offset && ticks.length) {
10240            const offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;
10241            start -= offset;
10242            end += offset;
10243        }
10244        this._startValue = start;
10245        this._endValue = end;
10246        this._valueRange = end - start;
10247    }
10248    getLabelForValue(value) {
10249        return formatNumber(value, this.chart.options.locale, this.options.ticks.format);
10250    }
10251}
10252
10253class LinearScale extends LinearScaleBase {
10254    static id = 'linear';
10255 static defaults = {
10256        ticks: {
10257            callback: Ticks.formatters.numeric
10258        }
10259    };
10260    determineDataLimits() {
10261        const { min , max  } = this.getMinMax(true);
10262        this.min = isNumberFinite(min) ? min : 0;
10263        this.max = isNumberFinite(max) ? max : 1;
10264        this.handleTickRangeOptions();
10265    }
10266 computeTickLimit() {
10267        const horizontal = this.isHorizontal();
10268        const length = horizontal ? this.width : this.height;
10269        const minRotation = toRadians(this.options.ticks.minRotation);
10270        const ratio = (horizontal ? Math.sin(minRotation) : Math.cos(minRotation)) || 0.001;
10271        const tickFont = this._resolveTickFontOptions(0);
10272        return Math.ceil(length / Math.min(40, tickFont.lineHeight / ratio));
10273    }
10274    getPixelForValue(value) {
10275        return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);
10276    }
10277    getValueForPixel(pixel) {
10278        return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange;
10279    }
10280}
10281
10282const log10Floor = (v)=>Math.floor(log10(v));
10283const changeExponent = (v, m)=>Math.pow(10, log10Floor(v) + m);
10284function isMajor(tickVal) {
10285    const remain = tickVal / Math.pow(10, log10Floor(tickVal));
10286    return remain === 1;
10287}
10288function steps(min, max, rangeExp) {
10289    const rangeStep = Math.pow(10, rangeExp);
10290    const start = Math.floor(min / rangeStep);
10291    const end = Math.ceil(max / rangeStep);
10292    return end - start;
10293}
10294function startExp(min, max) {
10295    const range = max - min;
10296    let rangeExp = log10Floor(range);
10297    while(steps(min, max, rangeExp) > 10){
10298        rangeExp++;
10299    }
10300    while(steps(min, max, rangeExp) < 10){
10301        rangeExp--;
10302    }
10303    return Math.min(rangeExp, log10Floor(min));
10304}
10305 function generateTicks(generationOptions, { min , max  }) {
10306    min = finiteOrDefault(generationOptions.min, min);
10307    const ticks = [];
10308    const minExp = log10Floor(min);
10309    let exp = startExp(min, max);
10310    let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;
10311    const stepSize = Math.pow(10, exp);
10312    const base = minExp > exp ? Math.pow(10, minExp) : 0;
10313    const start = Math.round((min - base) * precision) / precision;
10314    const offset = Math.floor((min - base) / stepSize / 10) * stepSize * 10;
10315    let significand = Math.floor((start - offset) / Math.pow(10, exp));
10316    let value = finiteOrDefault(generationOptions.min, Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision);
10317    while(value < max){
10318        ticks.push({
10319            value,
10320            major: isMajor(value),
10321            significand
10322        });
10323        if (significand >= 10) {
10324            significand = significand < 15 ? 15 : 20;
10325        } else {
10326            significand++;
10327        }
10328        if (significand >= 20) {
10329            exp++;
10330            significand = 2;
10331            precision = exp >= 0 ? 1 : precision;
10332        }
10333        value = Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision;
10334    }
10335    const lastTick = finiteOrDefault(generationOptions.max, value);
10336    ticks.push({
10337        value: lastTick,
10338        major: isMajor(lastTick),
10339        significand
10340    });
10341    return ticks;
10342}
10343class LogarithmicScale extends Scale {
10344    static id = 'logarithmic';
10345 static defaults = {
10346        ticks: {
10347            callback: Ticks.formatters.logarithmic,
10348            major: {
10349                enabled: true
10350            }
10351        }
10352    };
10353    constructor(cfg){
10354        super(cfg);
10355         this.start = undefined;
10356         this.end = undefined;
10357         this._startValue = undefined;
10358        this._valueRange = 0;
10359    }
10360    parse(raw, index) {
10361        const value = LinearScaleBase.prototype.parse.apply(this, [
10362            raw,
10363            index
10364        ]);
10365        if (value === 0) {
10366            this._zero = true;
10367            return undefined;
10368        }
10369        return isNumberFinite(value) && value > 0 ? value : null;
10370    }
10371    determineDataLimits() {
10372        const { min , max  } = this.getMinMax(true);
10373        this.min = isNumberFinite(min) ? Math.max(0, min) : null;
10374        this.max = isNumberFinite(max) ? Math.max(0, max) : null;
10375        if (this.options.beginAtZero) {
10376            this._zero = true;
10377        }
10378        if (this._zero && this.min !== this._suggestedMin && !isNumberFinite(this._userMin)) {
10379            this.min = min === changeExponent(this.min, 0) ? changeExponent(this.min, -1) : changeExponent(this.min, 0);
10380        }
10381        this.handleTickRangeOptions();
10382    }
10383    handleTickRangeOptions() {
10384        const { minDefined , maxDefined  } = this.getUserBounds();
10385        let min = this.min;
10386        let max = this.max;
10387        const setMin = (v)=>min = minDefined ? min : v;
10388        const setMax = (v)=>max = maxDefined ? max : v;
10389        if (min === max) {
10390            if (min <= 0) {
10391                setMin(1);
10392                setMax(10);
10393            } else {
10394                setMin(changeExponent(min, -1));
10395                setMax(changeExponent(max, +1));
10396            }
10397        }
10398        if (min <= 0) {
10399            setMin(changeExponent(max, -1));
10400        }
10401        if (max <= 0) {
10402            setMax(changeExponent(min, +1));
10403        }
10404        this.min = min;
10405        this.max = max;
10406    }
10407    buildTicks() {
10408        const opts = this.options;
10409        const generationOptions = {
10410            min: this._userMin,
10411            max: this._userMax
10412        };
10413        const ticks = generateTicks(generationOptions, this);
10414        if (opts.bounds === 'ticks') {
10415            _setMinAndMaxByKey(ticks, this, 'value');
10416        }
10417        if (opts.reverse) {
10418            ticks.reverse();
10419            this.start = this.max;
10420            this.end = this.min;
10421        } else {
10422            this.start = this.min;
10423            this.end = this.max;
10424        }
10425        return ticks;
10426    }
10427 getLabelForValue(value) {
10428        return value === undefined ? '0' : formatNumber(value, this.chart.options.locale, this.options.ticks.format);
10429    }
10430 configure() {
10431        const start = this.min;
10432        super.configure();
10433        this._startValue = log10(start);
10434        this._valueRange = log10(this.max) - log10(start);
10435    }
10436    getPixelForValue(value) {
10437        if (value === undefined || value === 0) {
10438            value = this.min;
10439        }
10440        if (value === null || isNaN(value)) {
10441            return NaN;
10442        }
10443        return this.getPixelForDecimal(value === this.min ? 0 : (log10(value) - this._startValue) / this._valueRange);
10444    }
10445    getValueForPixel(pixel) {
10446        const decimal = this.getDecimalForPixel(pixel);
10447        return Math.pow(10, this._startValue + decimal * this._valueRange);
10448    }
10449}
10450
10451function getTickBackdropHeight(opts) {
10452    const tickOpts = opts.ticks;
10453    if (tickOpts.display && opts.display) {
10454        const padding = toPadding(tickOpts.backdropPadding);
10455        return valueOrDefault(tickOpts.font && tickOpts.font.size, defaults.font.size) + padding.height;
10456    }
10457    return 0;
10458}
10459function measureLabelSize(ctx, font, label) {
10460    label = isArray(label) ? label : [
10461        label
10462    ];
10463    return {
10464        w: _longestText(ctx, font.string, label),
10465        h: label.length * font.lineHeight
10466    };
10467}
10468function determineLimits(angle, pos, size, min, max) {
10469    if (angle === min || angle === max) {
10470        return {
10471            start: pos - size / 2,
10472            end: pos + size / 2
10473        };
10474    } else if (angle < min || angle > max) {
10475        return {
10476            start: pos - size,
10477            end: pos
10478        };
10479    }
10480    return {
10481        start: pos,
10482        end: pos + size
10483    };
10484}
10485 function fitWithPointLabels(scale) {
10486    const orig = {
10487        l: scale.left + scale._padding.left,
10488        r: scale.right - scale._padding.right,
10489        t: scale.top + scale._padding.top,
10490        b: scale.bottom - scale._padding.bottom
10491    };
10492    const limits = Object.assign({}, orig);
10493    const labelSizes = [];
10494    const padding = [];
10495    const valueCount = scale._pointLabels.length;
10496    const pointLabelOpts = scale.options.pointLabels;
10497    const additionalAngle = pointLabelOpts.centerPointLabels ? PI / valueCount : 0;
10498    for(let i = 0; i < valueCount; i++){
10499        const opts = pointLabelOpts.setContext(scale.getPointLabelContext(i));
10500        padding[i] = opts.padding;
10501        const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i], additionalAngle);
10502        const plFont = toFont(opts.font);
10503        const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]);
10504        labelSizes[i] = textSize;
10505        const angleRadians = _normalizeAngle(scale.getIndexAngle(i) + additionalAngle);
10506        const angle = Math.round(toDegrees(angleRadians));
10507        const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
10508        const vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
10509        updateLimits(limits, orig, angleRadians, hLimits, vLimits);
10510    }
10511    scale.setCenterPoint(orig.l - limits.l, limits.r - orig.r, orig.t - limits.t, limits.b - orig.b);
10512    scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding);
10513}
10514function updateLimits(limits, orig, angle, hLimits, vLimits) {
10515    const sin = Math.abs(Math.sin(angle));
10516    const cos = Math.abs(Math.cos(angle));
10517    let x = 0;
10518    let y = 0;
10519    if (hLimits.start < orig.l) {
10520        x = (orig.l - hLimits.start) / sin;
10521        limits.l = Math.min(limits.l, orig.l - x);
10522    } else if (hLimits.end > orig.r) {
10523        x = (hLimits.end - orig.r) / sin;
10524        limits.r = Math.max(limits.r, orig.r + x);
10525    }
10526    if (vLimits.start < orig.t) {
10527        y = (orig.t - vLimits.start) / cos;
10528        limits.t = Math.min(limits.t, orig.t - y);
10529    } else if (vLimits.end > orig.b) {
10530        y = (vLimits.end - orig.b) / cos;
10531        limits.b = Math.max(limits.b, orig.b + y);
10532    }
10533}
10534function createPointLabelItem(scale, index, itemOpts) {
10535    const outerDistance = scale.drawingArea;
10536    const { extra , additionalAngle , padding , size  } = itemOpts;
10537    const pointLabelPosition = scale.getPointPosition(index, outerDistance + extra + padding, additionalAngle);
10538    const angle = Math.round(toDegrees(_normalizeAngle(pointLabelPosition.angle + HALF_PI)));
10539    const y = yForAngle(pointLabelPosition.y, size.h, angle);
10540    const textAlign = getTextAlignForAngle(angle);
10541    const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign);
10542    return {
10543        visible: true,
10544        x: pointLabelPosition.x,
10545        y,
10546        textAlign,
10547        left,
10548        top: y,
10549        right: left + size.w,
10550        bottom: y + size.h
10551    };
10552}
10553function isNotOverlapped(item, area) {
10554    if (!area) {
10555        return true;
10556    }
10557    const { left , top , right , bottom  } = item;
10558    const apexesInArea = _isPointInArea({
10559        x: left,
10560        y: top
10561    }, area) || _isPointInArea({
10562        x: left,
10563        y: bottom
10564    }, area) || _isPointInArea({
10565        x: right,
10566        y: top
10567    }, area) || _isPointInArea({
10568        x: right,
10569        y: bottom
10570    }, area);
10571    return !apexesInArea;
10572}
10573function buildPointLabelItems(scale, labelSizes, padding) {
10574    const items = [];
10575    const valueCount = scale._pointLabels.length;
10576    const opts = scale.options;
10577    const { centerPointLabels , display  } = opts.pointLabels;
10578    const itemOpts = {
10579        extra: getTickBackdropHeight(opts) / 2,
10580        additionalAngle: centerPointLabels ? PI / valueCount : 0
10581    };
10582    let area;
10583    for(let i = 0; i < valueCount; i++){
10584        itemOpts.padding = padding[i];
10585        itemOpts.size = labelSizes[i];
10586        const item = createPointLabelItem(scale, i, itemOpts);
10587        items.push(item);
10588        if (display === 'auto') {
10589            item.visible = isNotOverlapped(item, area);
10590            if (item.visible) {
10591                area = item;
10592            }
10593        }
10594    }
10595    return items;
10596}
10597function getTextAlignForAngle(angle) {
10598    if (angle === 0 || angle === 180) {
10599        return 'center';
10600    } else if (angle < 180) {
10601        return 'left';
10602    }
10603    return 'right';
10604}
10605function leftForTextAlign(x, w, align) {
10606    if (align === 'right') {
10607        x -= w;
10608    } else if (align === 'center') {
10609        x -= w / 2;
10610    }
10611    return x;
10612}
10613function yForAngle(y, h, angle) {
10614    if (angle === 90 || angle === 270) {
10615        y -= h / 2;
10616    } else if (angle > 270 || angle < 90) {
10617        y -= h;
10618    }
10619    return y;
10620}
10621function drawPointLabelBox(ctx, opts, item) {
10622    const { left , top , right , bottom  } = item;
10623    const { backdropColor  } = opts;
10624    if (!isNullOrUndef(backdropColor)) {
10625        const borderRadius = toTRBLCorners(opts.borderRadius);
10626        const padding = toPadding(opts.backdropPadding);
10627        ctx.fillStyle = backdropColor;
10628        const backdropLeft = left - padding.left;
10629        const backdropTop = top - padding.top;
10630        const backdropWidth = right - left + padding.width;
10631        const backdropHeight = bottom - top + padding.height;
10632        if (Object.values(borderRadius).some((v)=>v !== 0)) {
10633            ctx.beginPath();
10634            addRoundedRectPath(ctx, {
10635                x: backdropLeft,
10636                y: backdropTop,
10637                w: backdropWidth,
10638                h: backdropHeight,
10639                radius: borderRadius
10640            });
10641            ctx.fill();
10642        } else {
10643            ctx.fillRect(backdropLeft, backdropTop, backdropWidth, backdropHeight);
10644        }
10645    }
10646}
10647function drawPointLabels(scale, labelCount) {
10648    const { ctx , options: { pointLabels  }  } = scale;
10649    for(let i = labelCount - 1; i >= 0; i--){
10650        const item = scale._pointLabelItems[i];
10651        if (!item.visible) {
10652            continue;
10653        }
10654        const optsAtIndex = pointLabels.setContext(scale.getPointLabelContext(i));
10655        drawPointLabelBox(ctx, optsAtIndex, item);
10656        const plFont = toFont(optsAtIndex.font);
10657        const { x , y , textAlign  }
vendor: 14,912 bytes, lines 10657-11108
10657 = item;
10658        renderText(ctx, scale._pointLabels[i], x, y + plFont.lineHeight / 2, plFont, {
10659            color: optsAtIndex.color,
10660            textAlign: textAlign,
10661            textBaseline: 'middle'
10662        });
10663    }
10664}
10665function pathRadiusLine(scale, radius, circular, labelCount) {
10666    const { ctx  } = scale;
10667    if (circular) {
10668        ctx.arc(scale.xCenter, scale.yCenter, radius, 0, TAU);
10669    } else {
10670        let pointPosition = scale.getPointPosition(0, radius);
10671        ctx.moveTo(pointPosition.x, pointPosition.y);
10672        for(let i = 1; i < labelCount; i++){
10673            pointPosition = scale.getPointPosition(i, radius);
10674            ctx.lineTo(pointPosition.x, pointPosition.y);
10675        }
10676    }
10677}
10678function drawRadiusLine(scale, gridLineOpts, radius, labelCount, borderOpts) {
10679    const ctx = scale.ctx;
10680    const circular = gridLineOpts.circular;
10681    const { color , lineWidth  } = gridLineOpts;
10682    if (!circular && !labelCount || !color || !lineWidth || radius < 0) {
10683        return;
10684    }
10685    ctx.save();
10686    ctx.strokeStyle = color;
10687    ctx.lineWidth = lineWidth;
10688    ctx.setLineDash(borderOpts.dash || []);
10689    ctx.lineDashOffset = borderOpts.dashOffset;
10690    ctx.beginPath();
10691    pathRadiusLine(scale, radius, circular, labelCount);
10692    ctx.closePath();
10693    ctx.stroke();
10694    ctx.restore();
10695}
10696function createPointLabelContext(parent, index, label) {
10697    return createContext(parent, {
10698        label,
10699        index,
10700        type: 'pointLabel'
10701    });
10702}
10703class RadialLinearScale extends LinearScaleBase {
10704    static id = 'radialLinear';
10705 static defaults = {
10706        display: true,
10707        animate: true,
10708        position: 'chartArea',
10709        angleLines: {
10710            display: true,
10711            lineWidth: 1,
10712            borderDash: [],
10713            borderDashOffset: 0.0
10714        },
10715        grid: {
10716            circular: false
10717        },
10718        startAngle: 0,
10719        ticks: {
10720            showLabelBackdrop: true,
10721            callback: Ticks.formatters.numeric
10722        },
10723        pointLabels: {
10724            backdropColor: undefined,
10725            backdropPadding: 2,
10726            display: true,
10727            font: {
10728                size: 10
10729            },
10730            callback (label) {
10731                return label;
10732            },
10733            padding: 5,
10734            centerPointLabels: false
10735        }
10736    };
10737    static defaultRoutes = {
10738        'angleLines.color': 'borderColor',
10739        'pointLabels.color': 'color',
10740        'ticks.color': 'color'
10741    };
10742    static descriptors = {
10743        angleLines: {
10744            _fallback: 'grid'
10745        }
10746    };
10747    constructor(cfg){
10748        super(cfg);
10749         this.xCenter = undefined;
10750         this.yCenter = undefined;
10751         this.drawingArea = undefined;
10752         this._pointLabels = [];
10753        this._pointLabelItems = [];
10754    }
10755    setDimensions() {
10756        const padding = this._padding = toPadding(getTickBackdropHeight(this.options) / 2);
10757        const w = this.width = this.maxWidth - padding.width;
10758        const h = this.height = this.maxHeight - padding.height;
10759        this.xCenter = Math.floor(this.left + w / 2 + padding.left);
10760        this.yCenter = Math.floor(this.top + h / 2 + padding.top);
10761        this.drawingArea = Math.floor(Math.min(w, h) / 2);
10762    }
10763    determineDataLimits() {
10764        const { min , max  } = this.getMinMax(false);
10765        this.min = isNumberFinite(min) && !isNaN(min) ? min : 0;
10766        this.max = isNumberFinite(max) && !isNaN(max) ? max : 0;
10767        this.handleTickRangeOptions();
10768    }
10769 computeTickLimit() {
10770        return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));
10771    }
10772    generateTickLabels(ticks) {
10773        LinearScaleBase.prototype.generateTickLabels.call(this, ticks);
10774        this._pointLabels = this.getLabels().map((value, index)=>{
10775            const label = callback(this.options.pointLabels.callback, [
10776                value,
10777                index
10778            ], this);
10779            return label || label === 0 ? label : '';
10780        }).filter((v, i)=>this.chart.getDataVisibility(i));
10781    }
10782    fit() {
10783        const opts = this.options;
10784        if (opts.display && opts.pointLabels.display) {
10785            fitWithPointLabels(this);
10786        } else {
10787            this.setCenterPoint(0, 0, 0, 0);
10788        }
10789    }
10790    setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) {
10791        this.xCenter += Math.floor((leftMovement - rightMovement) / 2);
10792        this.yCenter += Math.floor((topMovement - bottomMovement) / 2);
10793        this.drawingArea -= Math.min(this.drawingArea / 2, Math.max(leftMovement, rightMovement, topMovement, bottomMovement));
10794    }
10795    getIndexAngle(index) {
10796        const angleMultiplier = TAU / (this._pointLabels.length || 1);
10797        const startAngle = this.options.startAngle || 0;
10798        return _normalizeAngle(index * angleMultiplier + toRadians(startAngle));
10799    }
10800    getDistanceFromCenterForValue(value) {
10801        if (isNullOrUndef(value)) {
10802            return NaN;
10803        }
10804        const scalingFactor = this.drawingArea / (this.max - this.min);
10805        if (this.options.reverse) {
10806            return (this.max - value) * scalingFactor;
10807        }
10808        return (value - this.min) * scalingFactor;
10809    }
10810    getValueForDistanceFromCenter(distance) {
10811        if (isNullOrUndef(distance)) {
10812            return NaN;
10813        }
10814        const scaledDistance = distance / (this.drawingArea / (this.max - this.min));
10815        return this.options.reverse ? this.max - scaledDistance : this.min + scaledDistance;
10816    }
10817    getPointLabelContext(index) {
10818        const pointLabels = this._pointLabels || [];
10819        if (index >= 0 && index < pointLabels.length) {
10820            const pointLabel = pointLabels[index];
10821            return createPointLabelContext(this.getContext(), index, pointLabel);
10822        }
10823    }
10824    getPointPosition(index, distanceFromCenter, additionalAngle = 0) {
10825        const angle = this.getIndexAngle(index) - HALF_PI + additionalAngle;
10826        return {
10827            x: Math.cos(angle) * distanceFromCenter + this.xCenter,
10828            y: Math.sin(angle) * distanceFromCenter + this.yCenter,
10829            angle
10830        };
10831    }
10832    getPointPositionForValue(index, value) {
10833        return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
10834    }
10835    getBasePosition(index) {
10836        return this.getPointPositionForValue(index || 0, this.getBaseValue());
10837    }
10838    getPointLabelPosition(index) {
10839        const { left , top , right , bottom  } = this._pointLabelItems[index];
10840        return {
10841            left,
10842            top,
10843            right,
10844            bottom
10845        };
10846    }
10847 drawBackground() {
10848        const { backgroundColor , grid: { circular  }  } = this.options;
10849        if (backgroundColor) {
10850            const ctx = this.ctx;
10851            ctx.save();
10852            ctx.beginPath();
10853            pathRadiusLine(this, this.getDistanceFromCenterForValue(this._endValue), circular, this._pointLabels.length);
10854            ctx.closePath();
10855            ctx.fillStyle = backgroundColor;
10856            ctx.fill();
10857            ctx.restore();
10858        }
10859    }
10860 drawGrid() {
10861        const ctx = this.ctx;
10862        const opts = this.options;
10863        const { angleLines , grid , border  } = opts;
10864        const labelCount = this._pointLabels.length;
10865        let i, offset, position;
10866        if (opts.pointLabels.display) {
10867            drawPointLabels(this, labelCount);
10868        }
10869        if (grid.display) {
10870            this.ticks.forEach((tick, index)=>{
10871                if (index !== 0 || index === 0 && this.min < 0) {
10872                    offset = this.getDistanceFromCenterForValue(tick.value);
10873                    const context = this.getContext(index);
10874                    const optsAtIndex = grid.setContext(context);
10875                    const optsAtIndexBorder = border.setContext(context);
10876                    drawRadiusLine(this, optsAtIndex, offset, labelCount, optsAtIndexBorder);
10877                }
10878            });
10879        }
10880        if (angleLines.display) {
10881            ctx.save();
10882            for(i = labelCount - 1; i >= 0; i--){
10883                const optsAtIndex = angleLines.setContext(this.getPointLabelContext(i));
10884                const { color , lineWidth  } = optsAtIndex;
10885                if (!lineWidth || !color) {
10886                    continue;
10887                }
10888                ctx.lineWidth = lineWidth;
10889                ctx.strokeStyle = color;
10890                ctx.setLineDash(optsAtIndex.borderDash);
10891                ctx.lineDashOffset = optsAtIndex.borderDashOffset;
10892                offset = this.getDistanceFromCenterForValue(opts.reverse ? this.min : this.max);
10893                position = this.getPointPosition(i, offset);
10894                ctx.beginPath();
10895                ctx.moveTo(this.xCenter, this.yCenter);
10896                ctx.lineTo(position.x, position.y);
10897                ctx.stroke();
10898            }
10899            ctx.restore();
10900        }
10901    }
10902 drawBorder() {}
10903 drawLabels() {
10904        const ctx = this.ctx;
10905        const opts = this.options;
10906        const tickOpts = opts.ticks;
10907        if (!tickOpts.display) {
10908            return;
10909        }
10910        const startAngle = this.getIndexAngle(0);
10911        let offset, width;
10912        ctx.save();
10913        ctx.translate(this.xCenter, this.yCenter);
10914        ctx.rotate(startAngle);
10915        ctx.textAlign = 'center';
10916        ctx.textBaseline = 'middle';
10917        this.ticks.forEach((tick, index)=>{
10918            if (index === 0 && this.min >= 0 && !opts.reverse) {
10919                return;
10920            }
10921            const optsAtIndex = tickOpts.setContext(this.getContext(index));
10922            const tickFont = toFont(optsAtIndex.font);
10923            offset = this.getDistanceFromCenterForValue(this.ticks[index].value);
10924            if (optsAtIndex.showLabelBackdrop) {
10925                ctx.font = tickFont.string;
10926                width = ctx.measureText(tick.label).width;
10927                ctx.fillStyle = optsAtIndex.backdropColor;
10928                const padding = toPadding(optsAtIndex.backdropPadding);
10929                ctx.fillRect(-width / 2 - padding.left, -offset - tickFont.size / 2 - padding.top, width + padding.width, tickFont.size + padding.height);
10930            }
10931            renderText(ctx, tick.label, 0, -offset, tickFont, {
10932                color: optsAtIndex.color,
10933                strokeColor: optsAtIndex.textStrokeColor,
10934                strokeWidth: optsAtIndex.textStrokeWidth
10935            });
10936        });
10937        ctx.restore();
10938    }
10939 drawTitle() {}
10940}
10941
10942const INTERVALS = {
10943    millisecond: {
10944        common: true,
10945        size: 1,
10946        steps: 1000
10947    },
10948    second: {
10949        common: true,
10950        size: 1000,
10951        steps: 60
10952    },
10953    minute: {
10954        common: true,
10955        size: 60000,
10956        steps: 60
10957    },
10958    hour: {
10959        common: true,
10960        size: 3600000,
10961        steps: 24
10962    },
10963    day: {
10964        common: true,
10965        size: 86400000,
10966        steps: 30
10967    },
10968    week: {
10969        common: false,
10970        size: 604800000,
10971        steps: 4
10972    },
10973    month: {
10974        common: true,
10975        size: 2.628e9,
10976        steps: 12
10977    },
10978    quarter: {
10979        common: false,
10980        size: 7.884e9,
10981        steps: 4
10982    },
10983    year: {
10984        common: true,
10985        size: 3.154e10
10986    }
10987};
10988 const UNITS =  /* #__PURE__ */ Object.keys(INTERVALS);
10989 function sorter(a, b) {
10990    return a - b;
10991}
10992 function parse(scale, input) {
10993    if (isNullOrUndef(input)) {
10994        return null;
10995    }
10996    const adapter = scale._adapter;
10997    const { parser , round , isoWeekday  } = scale._parseOpts;
10998    let value = input;
10999    if (typeof parser === 'function') {
11000        value = parser(value);
11001    }
11002    if (!isNumberFinite(value)) {
11003        value = typeof parser === 'string' ? adapter.parse(value, parser) : adapter.parse(value);
11004    }
11005    if (value === null) {
11006        return null;
11007    }
11008    if (round) {
11009        value = round === 'week' && (isNumber(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, 'isoWeek', isoWeekday) : adapter.startOf(value, round);
11010    }
11011    return +value;
11012}
11013 function determineUnitForAutoTicks(minUnit, min, max, capacity) {
11014    const ilen = UNITS.length;
11015    for(let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i){
11016        const interval = INTERVALS[UNITS[i]];
11017        const factor = interval.steps ? interval.steps : Number.MAX_SAFE_INTEGER;
11018        if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
11019            return UNITS[i];
11020        }
11021    }
11022    return UNITS[ilen - 1];
11023}
11024 function determineUnitForFormatting(scale, numTicks, minUnit, min, max) {
11025    for(let i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--){
11026        const unit = UNITS[i];
11027        if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) {
11028            return unit;
11029        }
11030    }
11031    return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];
11032}
11033 function determineMajorUnit(unit) {
11034    for(let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i){
11035        if (INTERVALS[UNITS[i]].common) {
11036            return UNITS[i];
11037        }
11038    }
11039}
11040 function addTick(ticks, time, timestamps) {
11041    if (!timestamps) {
11042        ticks[time] = true;
11043    } else if (timestamps.length) {
11044        const { lo , hi  } = _lookup(timestamps, time);
11045        const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi];
11046        ticks[timestamp] = true;
11047    }
11048}
11049 function setMajorTicks(scale, ticks, map, majorUnit) {
11050    const adapter = scale._adapter;
11051    const first = +adapter.startOf(ticks[0].value, majorUnit);
11052    const last = ticks[ticks.length - 1].value;
11053    let major, index;
11054    for(major = first; major <= last; major = +adapter.add(major, 1, majorUnit)){
11055        index = map[major];
11056        if (index >= 0) {
11057            ticks[index].major = true;
11058        }
11059    }
11060    return ticks;
11061}
11062 function ticksFromTimestamps(scale, values, majorUnit) {
11063    const ticks = [];
11064     const map = {};
11065    const ilen = values.length;
11066    let i, value;
11067    for(i = 0; i < ilen; ++i){
11068        value = values[i];
11069        map[value] = i;
11070        ticks.push({
11071            value,
11072            major: false
11073        });
11074    }
11075    return ilen === 0 || !majorUnit ? ticks : setMajorTicks(scale, ticks, map, majorUnit);
11076}
11077class TimeScale extends Scale {
11078    static id = 'time';
11079 static defaults = {
11080 bounds: 'data',
11081        adapters: {},
11082        time: {
11083            parser: false,
11084            unit: false,
11085            round: false,
11086            isoWeekday: false,
11087            minUnit: 'millisecond',
11088            displayFormats: {}
11089        },
11090        ticks: {
11091 source: 'auto',
11092            callback: false,
11093            major: {
11094                enabled: false
11095            }
11096        }
11097    };
11098 constructor(props){
11099        super(props);
11100         this._cache = {
11101            data: [],
11102            labels: [],
11103            all: []
11104        };
11105         this._unit = 'day';
11106         this._majorUnit = undefined;
11107        this._offsets = {};
11108        this._normalized = false;
vendor: 4,855 bytes, lines 11109-11235
11109        this._parseOpts = undefined;
11110    }
11111    init(scaleOpts, opts = {}) {
11112        const time = scaleOpts.time || (scaleOpts.time = {});
11113         const adapter = this._adapter = new adapters._date(scaleOpts.adapters.date);
11114        adapter.init(opts);
11115        mergeIf(time.displayFormats, adapter.formats());
11116        this._parseOpts = {
11117            parser: time.parser,
11118            round: time.round,
11119            isoWeekday: time.isoWeekday
11120        };
11121        super.init(scaleOpts);
11122        this._normalized = opts.normalized;
11123    }
11124 parse(raw, index) {
11125        if (raw === undefined) {
11126            return null;
11127        }
11128        return parse(this, raw);
11129    }
11130    beforeLayout() {
11131        super.beforeLayout();
11132        this._cache = {
11133            data: [],
11134            labels: [],
11135            all: []
11136        };
11137    }
11138    determineDataLimits() {
11139        const options = this.options;
11140        const adapter = this._adapter;
11141        const unit = options.time.unit || 'day';
11142        let { min , max , minDefined , maxDefined  } = this.getUserBounds();
11143 function _applyBounds(bounds) {
11144            if (!minDefined && !isNaN(bounds.min)) {
11145                min = Math.min(min, bounds.min);
11146            }
11147            if (!maxDefined && !isNaN(bounds.max)) {
11148                max = Math.max(max, bounds.max);
11149            }
11150        }
11151        if (!minDefined || !maxDefined) {
11152            _applyBounds(this._getLabelBounds());
11153            if (options.bounds !== 'ticks' || options.ticks.source !== 'labels') {
11154                _applyBounds(this.getMinMax(false));
11155            }
11156        }
11157        min = isNumberFinite(min) && !isNaN(min) ? min : +adapter.startOf(Date.now(), unit);
11158        max = isNumberFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit) + 1;
11159        this.min = Math.min(min, max - 1);
11160        this.max = Math.max(min + 1, max);
11161    }
11162 _getLabelBounds() {
11163        const arr = this.getLabelTimestamps();
11164        let min = Number.POSITIVE_INFINITY;
11165        let max = Number.NEGATIVE_INFINITY;
11166        if (arr.length) {
11167            min = arr[0];
11168            max = arr[arr.length - 1];
11169        }
11170        return {
11171            min,
11172            max
11173        };
11174    }
11175 buildTicks() {
11176        const options = this.options;
11177        const timeOpts = options.time;
11178        const tickOpts = options.ticks;
11179        const timestamps = tickOpts.source === 'labels' ? this.getLabelTimestamps() : this._generate();
11180        if (options.bounds === 'ticks' && timestamps.length) {
11181            this.min = this._userMin || timestamps[0];
11182            this.max = this._userMax || timestamps[timestamps.length - 1];
11183        }
11184        const min = this.min;
11185        const max = this.max;
11186        const ticks = _filterBetween(timestamps, min, max);
11187        this._unit = timeOpts.unit || (tickOpts.autoSkip ? determineUnitForAutoTicks(timeOpts.minUnit, this.min, this.max, this._getLabelCapacity(min)) : determineUnitForFormatting(this, ticks.length, timeOpts.minUnit, this.min, this.max));
11188        this._majorUnit = !tickOpts.major.enabled || this._unit === 'year' ? undefined : determineMajorUnit(this._unit);
11189        this.initOffsets(timestamps);
11190        if (options.reverse) {
11191            ticks.reverse();
11192        }
11193        return ticksFromTimestamps(this, ticks, this._majorUnit);
11194    }
11195    afterAutoSkip() {
11196        if (this.options.offsetAfterAutoskip) {
11197            this.initOffsets(this.ticks.map((tick)=>+tick.value));
11198        }
11199    }
11200 initOffsets(timestamps = []) {
11201        let start = 0;
11202        let end = 0;
11203        let first, last;
11204        if (this.options.offset && timestamps.length) {
11205            first = this.getDecimalForValue(timestamps[0]);
11206            if (timestamps.length === 1) {
11207                start = 1 - first;
11208            } else {
11209                start = (this.getDecimalForValue(timestamps[1]) - first) / 2;
11210            }
11211            last = this.getDecimalForValue(timestamps[timestamps.length - 1]);
11212            if (timestamps.length === 1) {
11213                end = last;
11214            } else {
11215                end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2;
11216            }
11217        }
11218        const limit = timestamps.length < 3 ? 0.5 : 0.25;
11219        start = _limitValue(start, 0, limit);
11220        end = _limitValue(end, 0, limit);
11221        this._offsets = {
11222            start,
11223            end,
11224            factor: 1 / (start + 1 + end)
11225        };
11226    }
11227 _generate() {
11228        const adapter = this._adapter;
11229        const min = this.min;
11230        const max = this.max;
11231        const options = this.options;
11232        const timeOpts = options.time;
11233        const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min));
11234        const stepSize = valueOrDefault(options.ticks.stepSize, 1);
11235        const weekday = minor === 'week' ? timeOpts.isoWeekday : false;
vendor: 4,589 bytes, lines 11236-11343
11236        const hasWeekday = isNumber(weekday) || weekday === true;
11237        const ticks = {};
11238        let first = min;
11239        let time, count;
11240        if (hasWeekday) {
11241            first = +adapter.startOf(first, 'isoWeek', weekday);
11242        }
11243        first = +adapter.startOf(first, hasWeekday ? 'day' : minor);
11244        if (adapter.diff(max, min, minor) > 100000 * stepSize) {
11245            throw new Error(min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor);
11246        }
11247        const timestamps = options.ticks.source === 'data' && this.getDataTimestamps();
11248        for(time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++){
11249            addTick(ticks, time, timestamps);
11250        }
11251        if (time === max || options.bounds === 'ticks' || count === 1) {
11252            addTick(ticks, time, timestamps);
11253        }
11254        return Object.keys(ticks).sort(sorter).map((x)=>+x);
11255    }
11256 getLabelForValue(value) {
11257        const adapter = this._adapter;
11258        const timeOpts = this.options.time;
11259        if (timeOpts.tooltipFormat) {
11260            return adapter.format(value, timeOpts.tooltipFormat);
11261        }
11262        return adapter.format(value, timeOpts.displayFormats.datetime);
11263    }
11264 format(value, format) {
11265        const options = this.options;
11266        const formats = options.time.displayFormats;
11267        const unit = this._unit;
11268        const fmt = format || formats[unit];
11269        return this._adapter.format(value, fmt);
11270    }
11271 _tickFormatFunction(time, index, ticks, format) {
11272        const options = this.options;
11273        const formatter = options.ticks.callback;
11274        if (formatter) {
11275            return callback(formatter, [
11276                time,
11277                index,
11278                ticks
11279            ], this);
11280        }
11281        const formats = options.time.displayFormats;
11282        const unit = this._unit;
11283        const majorUnit = this._majorUnit;
11284        const minorFormat = unit && formats[unit];
11285        const majorFormat = majorUnit && formats[majorUnit];
11286        const tick = ticks[index];
11287        const major = majorUnit && majorFormat && tick && tick.major;
11288        return this._adapter.format(time, format || (major ? majorFormat : minorFormat));
11289    }
11290 generateTickLabels(ticks) {
11291        let i, ilen, tick;
11292        for(i = 0, ilen = ticks.length; i < ilen; ++i){
11293            tick = ticks[i];
11294            tick.label = this._tickFormatFunction(tick.value, i, ticks);
11295        }
11296    }
11297 getDecimalForValue(value) {
11298        return value === null ? NaN : (value - this.min) / (this.max - this.min);
11299    }
11300 getPixelForValue(value) {
11301        const offsets = this._offsets;
11302        const pos = this.getDecimalForValue(value);
11303        return this.getPixelForDecimal((offsets.start + pos) * offsets.factor);
11304    }
11305 getValueForPixel(pixel) {
11306        const offsets = this._offsets;
11307        const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
11308        return this.min + pos * (this.max - this.min);
11309    }
11310 _getLabelSize(label) {
11311        const ticksOpts = this.options.ticks;
11312        const tickLabelWidth = this.ctx.measureText(label).width;
11313        const angle = toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);
11314        const cosRotation = Math.cos(angle);
11315        const sinRotation = Math.sin(angle);
11316        const tickFontSize = this._resolveTickFontOptions(0).size;
11317        return {
11318            w: tickLabelWidth * cosRotation + tickFontSize * sinRotation,
11319            h: tickLabelWidth * sinRotation + tickFontSize * cosRotation
11320        };
11321    }
11322 _getLabelCapacity(exampleTime) {
11323        const timeOpts = this.options.time;
11324        const displayFormats = timeOpts.displayFormats;
11325        const format = displayFormats[timeOpts.unit] || displayFormats.millisecond;
11326        const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [
11327            exampleTime
11328        ], this._majorUnit), format);
11329        const size = this._getLabelSize(exampleLabel);
11330        const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1;
11331        return capacity > 0 ? capacity : 1;
11332    }
11333 getDataTimestamps() {
11334        let timestamps = this._cache.data || [];
11335        let i, ilen;
11336        if (timestamps.length) {
11337            return timestamps;
11338        }
11339        const metas = this.getMatchingVisibleMetas();
11340        if (this._normalized && metas.length) {
11341            return this._cache.data = metas[0].controller.getAllParsedValues(this);
11342        }
11343        for(i = 0, ilen = metas.length; i < ilen; ++i){
vendor: 5,509 bytes, lines 11344-11492
11344            timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this));
11345        }
11346        return this._cache.data = this.normalize(timestamps);
11347    }
11348 getLabelTimestamps() {
11349        const timestamps = this._cache.labels || [];
11350        let i, ilen;
11351        if (timestamps.length) {
11352            return timestamps;
11353        }
11354        const labels = this.getLabels();
11355        for(i = 0, ilen = labels.length; i < ilen; ++i){
11356            timestamps.push(parse(this, labels[i]));
11357        }
11358        return this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps);
11359    }
11360 normalize(values) {
11361        return _arrayUnique(values.sort(sorter));
11362    }
11363}
11364
11365function interpolate(table, val, reverse) {
11366    let lo = 0;
11367    let hi = table.length - 1;
11368    let prevSource, nextSource, prevTarget, nextTarget;
11369    if (reverse) {
11370        if (val >= table[lo].pos && val <= table[hi].pos) {
11371            ({ lo , hi  } = _lookupByKey(table, 'pos', val));
11372        }
11373        ({ pos: prevSource , time: prevTarget  } = table[lo]);
11374        ({ pos: nextSource , time: nextTarget  } = table[hi]);
11375    } else {
11376        if (val >= table[lo].time && val <= table[hi].time) {
11377            ({ lo , hi  } = _lookupByKey(table, 'time', val));
11378        }
11379        ({ time: prevSource , pos: prevTarget  } = table[lo]);
11380        ({ time: nextSource , pos: nextTarget  } = table[hi]);
11381    }
11382    const span = nextSource - prevSource;
11383    return span ? prevTarget + (nextTarget - prevTarget) * (val - prevSource) / span : prevTarget;
11384}
11385class TimeSeriesScale extends TimeScale {
11386    static id = 'timeseries';
11387 static defaults = TimeScale.defaults;
11388 constructor(props){
11389        super(props);
11390         this._table = [];
11391         this._minPos = undefined;
11392         this._tableRange = undefined;
11393    }
11394 initOffsets() {
11395        const timestamps = this._getTimestampsForTable();
11396        const table = this._table = this.buildLookupTable(timestamps);
11397        this._minPos = interpolate(table, this.min);
11398        this._tableRange = interpolate(table, this.max) - this._minPos;
11399        super.initOffsets(timestamps);
11400    }
11401 buildLookupTable(timestamps) {
11402        const { min , max  } = this;
11403        const items = [];
11404        const table = [];
11405        let i, ilen, prev, curr, next;
11406        for(i = 0, ilen = timestamps.length; i < ilen; ++i){
11407            curr = timestamps[i];
11408            if (curr >= min && curr <= max) {
11409                items.push(curr);
11410            }
11411        }
11412        if (items.length < 2) {
11413            return [
11414                {
11415                    time: min,
11416                    pos: 0
11417                },
11418                {
11419                    time: max,
11420                    pos: 1
11421                }
11422            ];
11423        }
11424        for(i = 0, ilen = items.length; i < ilen; ++i){
11425            next = items[i + 1];
11426            prev = items[i - 1];
11427            curr = items[i];
11428            if (Math.round((next + prev) / 2) !== curr) {
11429                table.push({
11430                    time: curr,
11431                    pos: i / (ilen - 1)
11432                });
11433            }
11434        }
11435        return table;
11436    }
11437 _generate() {
11438        const min = this.min;
11439        const max = this.max;
11440        let timestamps = super.getDataTimestamps();
11441        if (!timestamps.includes(min) || !timestamps.length) {
11442            timestamps.splice(0, 0, min);
11443        }
11444        if (!timestamps.includes(max) || timestamps.length === 1) {
11445            timestamps.push(max);
11446        }
11447        return timestamps.sort((a, b)=>a - b);
11448    }
11449 _getTimestampsForTable() {
11450        let timestamps = this._cache.all || [];
11451        if (timestamps.length) {
11452            return timestamps;
11453        }
11454        const data = this.getDataTimestamps();
11455        const label = this.getLabelTimestamps();
11456        if (data.length && label.length) {
11457            timestamps = this.normalize(data.concat(label));
11458        } else {
11459            timestamps = data.length ? data : label;
11460        }
11461        timestamps = this._cache.all = timestamps;
11462        return timestamps;
11463    }
11464 getDecimalForValue(value) {
11465        return (interpolate(this._table, value) - this._minPos) / this._tableRange;
11466    }
11467 getValueForPixel(pixel) {
11468        const offsets = this._offsets;
11469        const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
11470        return interpolate(this._table, decimal * this._tableRange + this._minPos, true);
11471    }
11472}
11473
11474var scales = /*#__PURE__*/Object.freeze({
11475__proto__: null,
11476CategoryScale: CategoryScale,
11477LinearScale: LinearScale,
11478LogarithmicScale: LogarithmicScale,
11479RadialLinearScale: RadialLinearScale,
11480TimeScale: TimeScale,
11481TimeSeriesScale: TimeSeriesScale
11482});
11483
11484const registerables = [
11485    controllers,
11486    elements,
11487    plugins,
11488    scales
11489];
11490
11491export { Animation, Animations, ArcElement, BarController, BarElement, BasePlatform, BasicPlatform, BubbleController, CategoryScale, Chart, plugin_colors as Colors, DatasetController, plugin_decimation as Decimation, DomPlatform, DoughnutController, Element, index as Filler, Interaction, plugin_legend as Legend, LineController, LineElement, LinearScale, LogarithmicScale, PieController, PointElement, PolarAreaController, RadarController, RadialLinearScale, Scale, ScatterController, plugin_subtitle as SubTitle, Ticks, TimeScale, TimeSeriesScale, plugin_title as Title, plugin_tooltip as Tooltip, adapters as _adapters, _detectPlatform, animator, controllers, defaults, elements, layouts, plugins, registerables, registry, scales };
11492//# sourceMappingURL=chart.js.map

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.