Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/*!
2
 * Chart.js v4.4.2
3
 * https://www.chartjs.org
4
 * (c) 2024 Chart.js Contributors
5
 * Released under the MIT License
6
 */
7
 
8
/**
9
 * Description of import into Moodle:
10
 *
11
 * - Download Chartjs source code (zip) file from https://github.com/chartjs/Chart.js/releases/latest.
12
 * - You must build Chart.js to generate the dist files (https://www.chartjs.org/docs/latest/developers/contributing.html#building-and-testing).
13
 *   Chart.js will generate a new file dist/chart.umd.js with minified format, in order to avoid the minification
14
 *   we need to modify rollup.config.js, find below code in the file:
15
 *   ```
16
 *   1. // UMD build
17
 *   2. // dist/chart.umd.js
18
 *   3. {
19
 *   4.     input: 'src/index.umd.ts',
20
 *   5.     plugins: plugins(true),
21
 *   6.     output: {
22
 *   7.       ...
23
 *   8.     },
24
 *   9. },
25
 *   ```
26
 *
27
 *   Change line 5 into:
28
 *   ```
29
 *   plugins: plugins(),
30
 *   ```
31
 *
32
 *   Save the file and run build script again.
33
 *
34
 * - Copy /dist/chart.umd.js content to lib/amd/src/chartjs-lazy.js.
35
 * - Remove below line in the lib/amd/src/chartjs-lazy.js:
36
 *   ```
37
 *   //# sourceMappingURL=chart.umd.js.map
38
 *   ```
39
 * - Convert line endings to LF-Unix format.
40
 * - Change the version number and the copyright year at the file header block.
41
 * - Keep these instructions in the file.
42
 * - Visit lib/tests/other/chartjstestpage.php to see if the library still works after the update.
43
 *
44
 */
45
 
46
(function (global, factory) {
47
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
48
typeof define === 'function' && define.amd ? define(factory) :
49
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Chart = factory());
50
})(this, (function () { 'use strict';
51
 
52
var plugins = /*#__PURE__*/Object.freeze({
53
__proto__: null,
54
get Colors () { return plugin_colors; },
55
get Decimation () { return plugin_decimation; },
56
get Filler () { return index; },
57
get Legend () { return plugin_legend; },
58
get SubTitle () { return plugin_subtitle; },
59
get Title () { return plugin_title; },
60
get Tooltip () { return plugin_tooltip; }
61
});
62
 
63
/**
64
 * @namespace Chart.helpers
65
 */ /**
66
 * An empty function that can be used, for example, for optional callback.
67
 */ function noop() {
68
/* noop */ }
69
/**
70
 * Returns a unique id, sequentially generated from a global variable.
71
 */ const uid = (()=>{
72
    let id = 0;
73
    return ()=>id++;
74
})();
75
/**
76
 * Returns true if `value` is neither null nor undefined, else returns false.
77
 * @param value - The value to test.
78
 * @since 2.7.0
79
 */ function isNullOrUndef(value) {
80
    return value === null || typeof value === 'undefined';
81
}
82
/**
83
 * Returns true if `value` is an array (including typed arrays), else returns false.
84
 * @param value - The value to test.
85
 * @function
86
 */ function isArray(value) {
87
    if (Array.isArray && Array.isArray(value)) {
88
        return true;
89
    }
90
    const type = Object.prototype.toString.call(value);
91
    if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') {
92
        return true;
93
    }
94
    return false;
95
}
96
/**
97
 * Returns true if `value` is an object (excluding null), else returns false.
98
 * @param value - The value to test.
99
 * @since 2.7.0
100
 */ function isObject(value) {
101
    return value !== null && Object.prototype.toString.call(value) === '[object Object]';
102
}
103
/**
104
 * Returns true if `value` is a finite number, else returns false
105
 * @param value  - The value to test.
106
 */ function isNumberFinite(value) {
107
    return (typeof value === 'number' || value instanceof Number) && isFinite(+value);
108
}
109
/**
110
 * Returns `value` if finite, else returns `defaultValue`.
111
 * @param value - The value to return if defined.
112
 * @param defaultValue - The value to return if `value` is not finite.
113
 */ function finiteOrDefault(value, defaultValue) {
114
    return isNumberFinite(value) ? value : defaultValue;
115
}
116
/**
117
 * Returns `value` if defined, else returns `defaultValue`.
118
 * @param value - The value to return if defined.
119
 * @param defaultValue - The value to return if `value` is undefined.
120
 */ function valueOrDefault(value, defaultValue) {
121
    return typeof value === 'undefined' ? defaultValue : value;
122
}
123
const toPercentage = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 : +value / dimension;
124
const toDimension = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 * dimension : +value;
125
/**
126
 * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
127
 * value returned by `fn`. If `fn` is not a function, this method returns undefined.
128
 * @param fn - The function to call.
129
 * @param args - The arguments with which `fn` should be called.
130
 * @param [thisArg] - The value of `this` provided for the call to `fn`.
131
 */ function callback(fn, args, thisArg) {
132
    if (fn && typeof fn.call === 'function') {
133
        return fn.apply(thisArg, args);
134
    }
135
}
136
function each(loopable, fn, thisArg, reverse) {
137
    let i, len, keys;
138
    if (isArray(loopable)) {
139
        len = loopable.length;
140
        if (reverse) {
141
            for(i = len - 1; i >= 0; i--){
142
                fn.call(thisArg, loopable[i], i);
143
            }
144
        } else {
145
            for(i = 0; i < len; i++){
146
                fn.call(thisArg, loopable[i], i);
147
            }
148
        }
149
    } else if (isObject(loopable)) {
150
        keys = Object.keys(loopable);
151
        len = keys.length;
152
        for(i = 0; i < len; i++){
153
            fn.call(thisArg, loopable[keys[i]], keys[i]);
154
        }
155
    }
156
}
157
/**
158
 * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
159
 * @param a0 - The array to compare
160
 * @param a1 - The array to compare
161
 * @private
162
 */ function _elementsEqual(a0, a1) {
163
    let i, ilen, v0, v1;
164
    if (!a0 || !a1 || a0.length !== a1.length) {
165
        return false;
166
    }
167
    for(i = 0, ilen = a0.length; i < ilen; ++i){
168
        v0 = a0[i];
169
        v1 = a1[i];
170
        if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {
171
            return false;
172
        }
173
    }
174
    return true;
175
}
176
/**
177
 * Returns a deep copy of `source` without keeping references on objects and arrays.
178
 * @param source - The value to clone.
179
 */ function clone$1(source) {
180
    if (isArray(source)) {
181
        return source.map(clone$1);
182
    }
183
    if (isObject(source)) {
184
        const target = Object.create(null);
185
        const keys = Object.keys(source);
186
        const klen = keys.length;
187
        let k = 0;
188
        for(; k < klen; ++k){
189
            target[keys[k]] = clone$1(source[keys[k]]);
190
        }
191
        return target;
192
    }
193
    return source;
194
}
195
function isValidKey(key) {
196
    return [
197
        '__proto__',
198
        'prototype',
199
        'constructor'
200
    ].indexOf(key) === -1;
201
}
202
/**
203
 * The default merger when Chart.helpers.merge is called without merger option.
204
 * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.
205
 * @private
206
 */ function _merger(key, target, source, options) {
207
    if (!isValidKey(key)) {
208
        return;
209
    }
210
    const tval = target[key];
211
    const sval = source[key];
212
    if (isObject(tval) && isObject(sval)) {
213
        // eslint-disable-next-line @typescript-eslint/no-use-before-define
214
        merge(tval, sval, options);
215
    } else {
216
        target[key] = clone$1(sval);
217
    }
218
}
219
function merge(target, source, options) {
220
    const sources = isArray(source) ? source : [
221
        source
222
    ];
223
    const ilen = sources.length;
224
    if (!isObject(target)) {
225
        return target;
226
    }
227
    options = options || {};
228
    const merger = options.merger || _merger;
229
    let current;
230
    for(let i = 0; i < ilen; ++i){
231
        current = sources[i];
232
        if (!isObject(current)) {
233
            continue;
234
        }
235
        const keys = Object.keys(current);
236
        for(let k = 0, klen = keys.length; k < klen; ++k){
237
            merger(keys[k], target, current, options);
238
        }
239
    }
240
    return target;
241
}
242
function mergeIf(target, source) {
243
    // eslint-disable-next-line @typescript-eslint/no-use-before-define
244
    return merge(target, source, {
245
        merger: _mergerIf
246
    });
247
}
248
/**
249
 * Merges source[key] in target[key] only if target[key] is undefined.
250
 * @private
251
 */ function _mergerIf(key, target, source) {
252
    if (!isValidKey(key)) {
253
        return;
254
    }
255
    const tval = target[key];
256
    const sval = source[key];
257
    if (isObject(tval) && isObject(sval)) {
258
        mergeIf(tval, sval);
259
    } else if (!Object.prototype.hasOwnProperty.call(target, key)) {
260
        target[key] = clone$1(sval);
261
    }
262
}
263
/**
264
 * @private
265
 */ function _deprecated(scope, value, previous, current) {
266
    if (value !== undefined) {
267
        console.warn(scope + ': "' + previous + '" is deprecated. Please use "' + current + '" instead');
268
    }
269
}
270
// resolveObjectKey resolver cache
271
const keyResolvers = {
272
    // Chart.helpers.core resolveObjectKey should resolve empty key to root object
273
    '': (v)=>v,
274
    // default resolvers
275
    x: (o)=>o.x,
276
    y: (o)=>o.y
277
};
278
/**
279
 * @private
280
 */ function _splitKey(key) {
281
    const parts = key.split('.');
282
    const keys = [];
283
    let tmp = '';
284
    for (const part of parts){
285
        tmp += part;
286
        if (tmp.endsWith('\\')) {
287
            tmp = tmp.slice(0, -1) + '.';
288
        } else {
289
            keys.push(tmp);
290
            tmp = '';
291
        }
292
    }
293
    return keys;
294
}
295
function _getKeyResolver(key) {
296
    const keys = _splitKey(key);
297
    return (obj)=>{
298
        for (const k of keys){
299
            if (k === '') {
300
                break;
301
            }
302
            obj = obj && obj[k];
303
        }
304
        return obj;
305
    };
306
}
307
function resolveObjectKey(obj, key) {
308
    const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));
309
    return resolver(obj);
310
}
311
/**
312
 * @private
313
 */ function _capitalize(str) {
314
    return str.charAt(0).toUpperCase() + str.slice(1);
315
}
316
const defined = (value)=>typeof value !== 'undefined';
317
const isFunction = (value)=>typeof value === 'function';
318
// Adapted from https://stackoverflow.com/questions/31128855/comparing-ecma6-sets-for-equality#31129384
319
const setsEqual = (a, b)=>{
320
    if (a.size !== b.size) {
321
        return false;
322
    }
323
    for (const item of a){
324
        if (!b.has(item)) {
325
            return false;
326
        }
327
    }
328
    return true;
329
};
330
/**
331
 * @param e - The event
332
 * @private
333
 */ function _isClickEvent(e) {
334
    return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu';
335
}
336
 
337
/**
338
 * @alias Chart.helpers.math
339
 * @namespace
340
 */ const PI = Math.PI;
341
const TAU = 2 * PI;
342
const PITAU = TAU + PI;
343
const INFINITY = Number.POSITIVE_INFINITY;
344
const RAD_PER_DEG = PI / 180;
345
const HALF_PI = PI / 2;
346
const QUARTER_PI = PI / 4;
347
const TWO_THIRDS_PI = PI * 2 / 3;
348
const log10 = Math.log10;
349
const sign = Math.sign;
350
function almostEquals(x, y, epsilon) {
351
    return Math.abs(x - y) < epsilon;
352
}
353
/**
354
 * Implementation of the nice number algorithm used in determining where axis labels will go
355
 */ function niceNum(range) {
356
    const roundedRange = Math.round(range);
357
    range = almostEquals(range, roundedRange, range / 1000) ? roundedRange : range;
358
    const niceRange = Math.pow(10, Math.floor(log10(range)));
359
    const fraction = range / niceRange;
360
    const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;
361
    return niceFraction * niceRange;
362
}
363
/**
364
 * Returns an array of factors sorted from 1 to sqrt(value)
365
 * @private
366
 */ function _factorize(value) {
367
    const result = [];
368
    const sqrt = Math.sqrt(value);
369
    let i;
370
    for(i = 1; i < sqrt; i++){
371
        if (value % i === 0) {
372
            result.push(i);
373
            result.push(value / i);
374
        }
375
    }
376
    if (sqrt === (sqrt | 0)) {
377
        result.push(sqrt);
378
    }
379
    result.sort((a, b)=>a - b).pop();
380
    return result;
381
}
382
function isNumber(n) {
383
    return !isNaN(parseFloat(n)) && isFinite(n);
384
}
385
function almostWhole(x, epsilon) {
386
    const rounded = Math.round(x);
387
    return rounded - epsilon <= x && rounded + epsilon >= x;
388
}
389
/**
390
 * @private
391
 */ function _setMinAndMaxByKey(array, target, property) {
392
    let i, ilen, value;
393
    for(i = 0, ilen = array.length; i < ilen; i++){
394
        value = array[i][property];
395
        if (!isNaN(value)) {
396
            target.min = Math.min(target.min, value);
397
            target.max = Math.max(target.max, value);
398
        }
399
    }
400
}
401
function toRadians(degrees) {
402
    return degrees * (PI / 180);
403
}
404
function toDegrees(radians) {
405
    return radians * (180 / PI);
406
}
407
/**
408
 * Returns the number of decimal places
409
 * i.e. the number of digits after the decimal point, of the value of this Number.
410
 * @param x - A number.
411
 * @returns The number of decimal places.
412
 * @private
413
 */ function _decimalPlaces(x) {
414
    if (!isNumberFinite(x)) {
415
        return;
416
    }
417
    let e = 1;
418
    let p = 0;
419
    while(Math.round(x * e) / e !== x){
420
        e *= 10;
421
        p++;
422
    }
423
    return p;
424
}
425
// Gets the angle from vertical upright to the point about a centre.
426
function getAngleFromPoint(centrePoint, anglePoint) {
427
    const distanceFromXCenter = anglePoint.x - centrePoint.x;
428
    const distanceFromYCenter = anglePoint.y - centrePoint.y;
429
    const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
430
    let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
431
    if (angle < -0.5 * PI) {
432
        angle += TAU; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
433
    }
434
    return {
435
        angle,
436
        distance: radialDistanceFromCenter
437
    };
438
}
439
function distanceBetweenPoints(pt1, pt2) {
440
    return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
441
}
442
/**
443
 * Shortest distance between angles, in either direction.
444
 * @private
445
 */ function _angleDiff(a, b) {
446
    return (a - b + PITAU) % TAU - PI;
447
}
448
/**
449
 * Normalize angle to be between 0 and 2*PI
450
 * @private
451
 */ function _normalizeAngle(a) {
452
    return (a % TAU + TAU) % TAU;
453
}
454
/**
455
 * @private
456
 */ function _angleBetween(angle, start, end, sameAngleIsFullCircle) {
457
    const a = _normalizeAngle(angle);
458
    const s = _normalizeAngle(start);
459
    const e = _normalizeAngle(end);
460
    const angleToStart = _normalizeAngle(s - a);
461
    const angleToEnd = _normalizeAngle(e - a);
462
    const startToAngle = _normalizeAngle(a - s);
463
    const endToAngle = _normalizeAngle(a - e);
464
    return a === s || a === e || sameAngleIsFullCircle && s === e || angleToStart > angleToEnd && startToAngle < endToAngle;
465
}
466
/**
467
 * Limit `value` between `min` and `max`
468
 * @param value
469
 * @param min
470
 * @param max
471
 * @private
472
 */ function _limitValue(value, min, max) {
473
    return Math.max(min, Math.min(max, value));
474
}
475
/**
476
 * @param {number} value
477
 * @private
478
 */ function _int16Range(value) {
479
    return _limitValue(value, -32768, 32767);
480
}
481
/**
482
 * @param value
483
 * @param start
484
 * @param end
485
 * @param [epsilon]
486
 * @private
487
 */ function _isBetween(value, start, end, epsilon = 1e-6) {
488
    return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;
489
}
490
 
491
function _lookup(table, value, cmp) {
492
    cmp = cmp || ((index)=>table[index] < value);
493
    let hi = table.length - 1;
494
    let lo = 0;
495
    let mid;
496
    while(hi - lo > 1){
497
        mid = lo + hi >> 1;
498
        if (cmp(mid)) {
499
            lo = mid;
500
        } else {
501
            hi = mid;
502
        }
503
    }
504
    return {
505
        lo,
506
        hi
507
    };
508
}
509
/**
510
 * Binary search
511
 * @param table - the table search. must be sorted!
512
 * @param key - property name for the value in each entry
513
 * @param value - value to find
514
 * @param last - lookup last index
515
 * @private
516
 */ const _lookupByKey = (table, key, value, last)=>_lookup(table, value, last ? (index)=>{
517
        const ti = table[index][key];
518
        return ti < value || ti === value && table[index + 1][key] === value;
519
    } : (index)=>table[index][key] < value);
520
/**
521
 * Reverse binary search
522
 * @param table - the table search. must be sorted!
523
 * @param key - property name for the value in each entry
524
 * @param value - value to find
525
 * @private
526
 */ const _rlookupByKey = (table, key, value)=>_lookup(table, value, (index)=>table[index][key] >= value);
527
/**
528
 * Return subset of `values` between `min` and `max` inclusive.
529
 * Values are assumed to be in sorted order.
530
 * @param values - sorted array of values
531
 * @param min - min value
532
 * @param max - max value
533
 */ function _filterBetween(values, min, max) {
534
    let start = 0;
535
    let end = values.length;
536
    while(start < end && values[start] < min){
537
        start++;
538
    }
539
    while(end > start && values[end - 1] > max){
540
        end--;
541
    }
542
    return start > 0 || end < values.length ? values.slice(start, end) : values;
543
}
544
const arrayEvents = [
545
    'push',
546
    'pop',
547
    'shift',
548
    'splice',
549
    'unshift'
550
];
551
function listenArrayEvents(array, listener) {
552
    if (array._chartjs) {
553
        array._chartjs.listeners.push(listener);
554
        return;
555
    }
556
    Object.defineProperty(array, '_chartjs', {
557
        configurable: true,
558
        enumerable: false,
559
        value: {
560
            listeners: [
561
                listener
562
            ]
563
        }
564
    });
565
    arrayEvents.forEach((key)=>{
566
        const method = '_onData' + _capitalize(key);
567
        const base = array[key];
568
        Object.defineProperty(array, key, {
569
            configurable: true,
570
            enumerable: false,
571
            value (...args) {
572
                const res = base.apply(this, args);
573
                array._chartjs.listeners.forEach((object)=>{
574
                    if (typeof object[method] === 'function') {
575
                        object[method](...args);
576
                    }
577
                });
578
                return res;
579
            }
580
        });
581
    });
582
}
583
function unlistenArrayEvents(array, listener) {
584
    const stub = array._chartjs;
585
    if (!stub) {
586
        return;
587
    }
588
    const listeners = stub.listeners;
589
    const index = listeners.indexOf(listener);
590
    if (index !== -1) {
591
        listeners.splice(index, 1);
592
    }
593
    if (listeners.length > 0) {
594
        return;
595
    }
596
    arrayEvents.forEach((key)=>{
597
        delete array[key];
598
    });
599
    delete array._chartjs;
600
}
601
/**
602
 * @param items
603
 */ function _arrayUnique(items) {
604
    const set = new Set(items);
605
    if (set.size === items.length) {
606
        return items;
607
    }
608
    return Array.from(set);
609
}
610
 
611
function fontString(pixelSize, fontStyle, fontFamily) {
612
    return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
613
}
614
/**
615
* Request animation polyfill
616
*/ const requestAnimFrame = function() {
617
    if (typeof window === 'undefined') {
618
        return function(callback) {
619
            return callback();
620
        };
621
    }
622
    return window.requestAnimationFrame;
623
}();
624
/**
625
 * Throttles calling `fn` once per animation frame
626
 * Latest arguments are used on the actual call
627
 */ function throttled(fn, thisArg) {
628
    let argsToUse = [];
629
    let ticking = false;
630
    return function(...args) {
631
        // Save the args for use later
632
        argsToUse = args;
633
        if (!ticking) {
634
            ticking = true;
635
            requestAnimFrame.call(window, ()=>{
636
                ticking = false;
637
                fn.apply(thisArg, argsToUse);
638
            });
639
        }
640
    };
641
}
642
/**
643
 * Debounces calling `fn` for `delay` ms
644
 */ function debounce(fn, delay) {
645
    let timeout;
646
    return function(...args) {
647
        if (delay) {
648
            clearTimeout(timeout);
649
            timeout = setTimeout(fn, delay, args);
650
        } else {
651
            fn.apply(this, args);
652
        }
653
        return delay;
654
    };
655
}
656
/**
657
 * Converts 'start' to 'left', 'end' to 'right' and others to 'center'
658
 * @private
659
 */ const _toLeftRightCenter = (align)=>align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';
660
/**
661
 * Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`
662
 * @private
663
 */ const _alignStartEnd = (align, start, end)=>align === 'start' ? start : align === 'end' ? end : (start + end) / 2;
664
/**
665
 * Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`
666
 * @private
667
 */ const _textX = (align, left, right, rtl)=>{
668
    const check = rtl ? 'left' : 'right';
669
    return align === check ? right : align === 'center' ? (left + right) / 2 : left;
670
};
671
/**
672
 * Return start and count of visible points.
673
 * @private
674
 */ function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {
675
    const pointCount = points.length;
676
    let start = 0;
677
    let count = pointCount;
678
    if (meta._sorted) {
679
        const { iScale , _parsed  } = meta;
680
        const axis = iScale.axis;
681
        const { min , max , minDefined , maxDefined  } = iScale.getUserBounds();
682
        if (minDefined) {
683
            start = _limitValue(Math.min(// @ts-expect-error Need to type _parsed
684
            _lookupByKey(_parsed, axis, min).lo, // @ts-expect-error Need to fix types on _lookupByKey
685
            animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo), 0, pointCount - 1);
686
        }
687
        if (maxDefined) {
688
            count = _limitValue(Math.max(// @ts-expect-error Need to type _parsed
689
            _lookupByKey(_parsed, iScale.axis, max, true).hi + 1, // @ts-expect-error Need to fix types on _lookupByKey
690
            animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1), start, pointCount) - start;
691
        } else {
692
            count = pointCount - start;
693
        }
694
    }
695
    return {
696
        start,
697
        count
698
    };
699
}
700
/**
701
 * Checks if the scale ranges have changed.
702
 * @param {object} meta - dataset meta.
703
 * @returns {boolean}
704
 * @private
705
 */ function _scaleRangesChanged(meta) {
706
    const { xScale , yScale , _scaleRanges  } = meta;
707
    const newRanges = {
708
        xmin: xScale.min,
709
        xmax: xScale.max,
710
        ymin: yScale.min,
711
        ymax: yScale.max
712
    };
713
    if (!_scaleRanges) {
714
        meta._scaleRanges = newRanges;
715
        return true;
716
    }
717
    const changed = _scaleRanges.xmin !== xScale.min || _scaleRanges.xmax !== xScale.max || _scaleRanges.ymin !== yScale.min || _scaleRanges.ymax !== yScale.max;
718
    Object.assign(_scaleRanges, newRanges);
719
    return changed;
720
}
721
 
722
class Animator {
723
    constructor(){
724
        this._request = null;
725
        this._charts = new Map();
726
        this._running = false;
727
        this._lastDate = undefined;
728
    }
729
 _notify(chart, anims, date, type) {
730
        const callbacks = anims.listeners[type];
731
        const numSteps = anims.duration;
732
        callbacks.forEach((fn)=>fn({
733
                chart,
734
                initial: anims.initial,
735
                numSteps,
736
                currentStep: Math.min(date - anims.start, numSteps)
737
            }));
738
    }
739
 _refresh() {
740
        if (this._request) {
741
            return;
742
        }
743
        this._running = true;
744
        this._request = requestAnimFrame.call(window, ()=>{
745
            this._update();
746
            this._request = null;
747
            if (this._running) {
748
                this._refresh();
749
            }
750
        });
751
    }
752
 _update(date = Date.now()) {
753
        let remaining = 0;
754
        this._charts.forEach((anims, chart)=>{
755
            if (!anims.running || !anims.items.length) {
756
                return;
757
            }
758
            const items = anims.items;
759
            let i = items.length - 1;
760
            let draw = false;
761
            let item;
762
            for(; i >= 0; --i){
763
                item = items[i];
764
                if (item._active) {
765
                    if (item._total > anims.duration) {
766
                        anims.duration = item._total;
767
                    }
768
                    item.tick(date);
769
                    draw = true;
770
                } else {
771
                    items[i] = items[items.length - 1];
772
                    items.pop();
773
                }
774
            }
775
            if (draw) {
776
                chart.draw();
777
                this._notify(chart, anims, date, 'progress');
778
            }
779
            if (!items.length) {
780
                anims.running = false;
781
                this._notify(chart, anims, date, 'complete');
782
                anims.initial = false;
783
            }
784
            remaining += items.length;
785
        });
786
        this._lastDate = date;
787
        if (remaining === 0) {
788
            this._running = false;
789
        }
790
    }
791
 _getAnims(chart) {
792
        const charts = this._charts;
793
        let anims = charts.get(chart);
794
        if (!anims) {
795
            anims = {
796
                running: false,
797
                initial: true,
798
                items: [],
799
                listeners: {
800
                    complete: [],
801
                    progress: []
802
                }
803
            };
804
            charts.set(chart, anims);
805
        }
806
        return anims;
807
    }
808
 listen(chart, event, cb) {
809
        this._getAnims(chart).listeners[event].push(cb);
810
    }
811
 add(chart, items) {
812
        if (!items || !items.length) {
813
            return;
814
        }
815
        this._getAnims(chart).items.push(...items);
816
    }
817
 has(chart) {
818
        return this._getAnims(chart).items.length > 0;
819
    }
820
 start(chart) {
821
        const anims = this._charts.get(chart);
822
        if (!anims) {
823
            return;
824
        }
825
        anims.running = true;
826
        anims.start = Date.now();
827
        anims.duration = anims.items.reduce((acc, cur)=>Math.max(acc, cur._duration), 0);
828
        this._refresh();
829
    }
830
    running(chart) {
831
        if (!this._running) {
832
            return false;
833
        }
834
        const anims = this._charts.get(chart);
835
        if (!anims || !anims.running || !anims.items.length) {
836
            return false;
837
        }
838
        return true;
839
    }
840
 stop(chart) {
841
        const anims = this._charts.get(chart);
842
        if (!anims || !anims.items.length) {
843
            return;
844
        }
845
        const items = anims.items;
846
        let i = items.length - 1;
847
        for(; i >= 0; --i){
848
            items[i].cancel();
849
        }
850
        anims.items = [];
851
        this._notify(chart, anims, Date.now(), 'complete');
852
    }
853
 remove(chart) {
854
        return this._charts.delete(chart);
855
    }
856
}
857
var animator = /* #__PURE__ */ new Animator();
858
 
859
/*!
860
 * @kurkle/color v0.3.2
861
 * https://github.com/kurkle/color#readme
862
 * (c) 2023 Jukka Kurkela
863
 * Released under the MIT License
864
 */
865
function round(v) {
866
  return v + 0.5 | 0;
867
}
868
const lim = (v, l, h) => Math.max(Math.min(v, h), l);
869
function p2b(v) {
870
  return lim(round(v * 2.55), 0, 255);
871
}
872
function n2b(v) {
873
  return lim(round(v * 255), 0, 255);
874
}
875
function b2n(v) {
876
  return lim(round(v / 2.55) / 100, 0, 1);
877
}
878
function n2p(v) {
879
  return lim(round(v * 100), 0, 100);
880
}
881
const map$1 = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, A: 10, B: 11, C: 12, D: 13, E: 14, F: 15, a: 10, b: 11, c: 12, d: 13, e: 14, f: 15};
882
const hex = [...'0123456789ABCDEF'];
883
const h1 = b => hex[b & 0xF];
884
const h2 = b => hex[(b & 0xF0) >> 4] + hex[b & 0xF];
885
const eq = b => ((b & 0xF0) >> 4) === (b & 0xF);
886
const isShort = v => eq(v.r) && eq(v.g) && eq(v.b) && eq(v.a);
887
function hexParse(str) {
888
  var len = str.length;
889
  var ret;
890
  if (str[0] === '#') {
891
    if (len === 4 || len === 5) {
892
      ret = {
893
        r: 255 & map$1[str[1]] * 17,
894
        g: 255 & map$1[str[2]] * 17,
895
        b: 255 & map$1[str[3]] * 17,
896
        a: len === 5 ? map$1[str[4]] * 17 : 255
897
      };
898
    } else if (len === 7 || len === 9) {
899
      ret = {
900
        r: map$1[str[1]] << 4 | map$1[str[2]],
901
        g: map$1[str[3]] << 4 | map$1[str[4]],
902
        b: map$1[str[5]] << 4 | map$1[str[6]],
903
        a: len === 9 ? (map$1[str[7]] << 4 | map$1[str[8]]) : 255
904
      };
905
    }
906
  }
907
  return ret;
908
}
909
const alpha = (a, f) => a < 255 ? f(a) : '';
910
function hexString(v) {
911
  var f = isShort(v) ? h1 : h2;
912
  return v
913
    ? '#' + f(v.r) + f(v.g) + f(v.b) + alpha(v.a, f)
914
    : undefined;
915
}
916
const HUE_RE = /^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;
917
function hsl2rgbn(h, s, l) {
918
  const a = s * Math.min(l, 1 - l);
919
  const f = (n, k = (n + h / 30) % 12) => l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
920
  return [f(0), f(8), f(4)];
921
}
922
function hsv2rgbn(h, s, v) {
923
  const f = (n, k = (n + h / 60) % 6) => v - v * s * Math.max(Math.min(k, 4 - k, 1), 0);
924
  return [f(5), f(3), f(1)];
925
}
926
function hwb2rgbn(h, w, b) {
927
  const rgb = hsl2rgbn(h, 1, 0.5);
928
  let i;
929
  if (w + b > 1) {
930
    i = 1 / (w + b);
931
    w *= i;
932
    b *= i;
933
  }
934
  for (i = 0; i < 3; i++) {
935
    rgb[i] *= 1 - w - b;
936
    rgb[i] += w;
937
  }
938
  return rgb;
939
}
940
function hueValue(r, g, b, d, max) {
941
  if (r === max) {
942
    return ((g - b) / d) + (g < b ? 6 : 0);
943
  }
944
  if (g === max) {
945
    return (b - r) / d + 2;
946
  }
947
  return (r - g) / d + 4;
948
}
949
function rgb2hsl(v) {
950
  const range = 255;
951
  const r = v.r / range;
952
  const g = v.g / range;
953
  const b = v.b / range;
954
  const max = Math.max(r, g, b);
955
  const min = Math.min(r, g, b);
956
  const l = (max + min) / 2;
957
  let h, s, d;
958
  if (max !== min) {
959
    d = max - min;
960
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
961
    h = hueValue(r, g, b, d, max);
962
    h = h * 60 + 0.5;
963
  }
964
  return [h | 0, s || 0, l];
965
}
966
function calln(f, a, b, c) {
967
  return (
968
    Array.isArray(a)
969
      ? f(a[0], a[1], a[2])
970
      : f(a, b, c)
971
  ).map(n2b);
972
}
973
function hsl2rgb(h, s, l) {
974
  return calln(hsl2rgbn, h, s, l);
975
}
976
function hwb2rgb(h, w, b) {
977
  return calln(hwb2rgbn, h, w, b);
978
}
979
function hsv2rgb(h, s, v) {
980
  return calln(hsv2rgbn, h, s, v);
981
}
982
function hue(h) {
983
  return (h % 360 + 360) % 360;
984
}
985
function hueParse(str) {
986
  const m = HUE_RE.exec(str);
987
  let a = 255;
988
  let v;
989
  if (!m) {
990
    return;
991
  }
992
  if (m[5] !== v) {
993
    a = m[6] ? p2b(+m[5]) : n2b(+m[5]);
994
  }
995
  const h = hue(+m[2]);
996
  const p1 = +m[3] / 100;
997
  const p2 = +m[4] / 100;
998
  if (m[1] === 'hwb') {
999
    v = hwb2rgb(h, p1, p2);
1000
  } else if (m[1] === 'hsv') {
1001
    v = hsv2rgb(h, p1, p2);
1002
  } else {
1003
    v = hsl2rgb(h, p1, p2);
1004
  }
1005
  return {
1006
    r: v[0],
1007
    g: v[1],
1008
    b: v[2],
1009
    a: a
1010
  };
1011
}
1012
function rotate(v, deg) {
1013
  var h = rgb2hsl(v);
1014
  h[0] = hue(h[0] + deg);
1015
  h = hsl2rgb(h);
1016
  v.r = h[0];
1017
  v.g = h[1];
1018
  v.b = h[2];
1019
}
1020
function hslString(v) {
1021
  if (!v) {
1022
    return;
1023
  }
1024
  const a = rgb2hsl(v);
1025
  const h = a[0];
1026
  const s = n2p(a[1]);
1027
  const l = n2p(a[2]);
1028
  return v.a < 255
1029
    ? `hsla(${h}, ${s}%, ${l}%, ${b2n(v.a)})`
1030
    : `hsl(${h}, ${s}%, ${l}%)`;
1031
}
1032
const map$2 = {
1033
  x: 'dark',
1034
  Z: 'light',
1035
  Y: 're',
1036
  X: 'blu',
1037
  W: 'gr',
1038
  V: 'medium',
1039
  U: 'slate',
1040
  A: 'ee',
1041
  T: 'ol',
1042
  S: 'or',
1043
  B: 'ra',
1044
  C: 'lateg',
1045
  D: 'ights',
1046
  R: 'in',
1047
  Q: 'turquois',
1048
  E: 'hi',
1049
  P: 'ro',
1050
  O: 'al',
1051
  N: 'le',
1052
  M: 'de',
1053
  L: 'yello',
1054
  F: 'en',
1055
  K: 'ch',
1056
  G: 'arks',
1057
  H: 'ea',
1058
  I: 'ightg',
1059
  J: 'wh'
1060
};
1061
const names$1 = {
1062
  OiceXe: 'f0f8ff',
1063
  antiquewEte: 'faebd7',
1064
  aqua: 'ffff',
1065
  aquamarRe: '7fffd4',
1066
  azuY: 'f0ffff',
1067
  beige: 'f5f5dc',
1068
  bisque: 'ffe4c4',
1069
  black: '0',
1070
  blanKedOmond: 'ffebcd',
1071
  Xe: 'ff',
1072
  XeviTet: '8a2be2',
1073
  bPwn: 'a52a2a',
1074
  burlywood: 'deb887',
1075
  caMtXe: '5f9ea0',
1076
  KartYuse: '7fff00',
1077
  KocTate: 'd2691e',
1078
  cSO: 'ff7f50',
1079
  cSnflowerXe: '6495ed',
1080
  cSnsilk: 'fff8dc',
1081
  crimson: 'dc143c',
1082
  cyan: 'ffff',
1083
  xXe: '8b',
1084
  xcyan: '8b8b',
1085
  xgTMnPd: 'b8860b',
1086
  xWay: 'a9a9a9',
1087
  xgYF: '6400',
1088
  xgYy: 'a9a9a9',
1089
  xkhaki: 'bdb76b',
1090
  xmagFta: '8b008b',
1091
  xTivegYF: '556b2f',
1092
  xSange: 'ff8c00',
1093
  xScEd: '9932cc',
1094
  xYd: '8b0000',
1095
  xsOmon: 'e9967a',
1096
  xsHgYF: '8fbc8f',
1097
  xUXe: '483d8b',
1098
  xUWay: '2f4f4f',
1099
  xUgYy: '2f4f4f',
1100
  xQe: 'ced1',
1101
  xviTet: '9400d3',
1102
  dAppRk: 'ff1493',
1103
  dApskyXe: 'bfff',
1104
  dimWay: '696969',
1105
  dimgYy: '696969',
1106
  dodgerXe: '1e90ff',
1107
  fiYbrick: 'b22222',
1108
  flSOwEte: 'fffaf0',
1109
  foYstWAn: '228b22',
1110
  fuKsia: 'ff00ff',
1111
  gaRsbSo: 'dcdcdc',
1112
  ghostwEte: 'f8f8ff',
1113
  gTd: 'ffd700',
1114
  gTMnPd: 'daa520',
1115
  Way: '808080',
1116
  gYF: '8000',
1117
  gYFLw: 'adff2f',
1118
  gYy: '808080',
1119
  honeyMw: 'f0fff0',
1120
  hotpRk: 'ff69b4',
1121
  RdianYd: 'cd5c5c',
1122
  Rdigo: '4b0082',
1123
  ivSy: 'fffff0',
1124
  khaki: 'f0e68c',
1125
  lavFMr: 'e6e6fa',
1126
  lavFMrXsh: 'fff0f5',
1127
  lawngYF: '7cfc00',
1128
  NmoncEffon: 'fffacd',
1129
  ZXe: 'add8e6',
1130
  ZcSO: 'f08080',
1131
  Zcyan: 'e0ffff',
1132
  ZgTMnPdLw: 'fafad2',
1133
  ZWay: 'd3d3d3',
1134
  ZgYF: '90ee90',
1135
  ZgYy: 'd3d3d3',
1136
  ZpRk: 'ffb6c1',
1137
  ZsOmon: 'ffa07a',
1138
  ZsHgYF: '20b2aa',
1139
  ZskyXe: '87cefa',
1140
  ZUWay: '778899',
1141
  ZUgYy: '778899',
1142
  ZstAlXe: 'b0c4de',
1143
  ZLw: 'ffffe0',
1144
  lime: 'ff00',
1145
  limegYF: '32cd32',
1146
  lRF: 'faf0e6',
1147
  magFta: 'ff00ff',
1148
  maPon: '800000',
1149
  VaquamarRe: '66cdaa',
1150
  VXe: 'cd',
1151
  VScEd: 'ba55d3',
1152
  VpurpN: '9370db',
1153
  VsHgYF: '3cb371',
1154
  VUXe: '7b68ee',
1155
  VsprRggYF: 'fa9a',
1156
  VQe: '48d1cc',
1157
  VviTetYd: 'c71585',
1158
  midnightXe: '191970',
1159
  mRtcYam: 'f5fffa',
1160
  mistyPse: 'ffe4e1',
1161
  moccasR: 'ffe4b5',
1162
  navajowEte: 'ffdead',
1163
  navy: '80',
1164
  Tdlace: 'fdf5e6',
1165
  Tive: '808000',
1166
  TivedBb: '6b8e23',
1167
  Sange: 'ffa500',
1168
  SangeYd: 'ff4500',
1169
  ScEd: 'da70d6',
1170
  pOegTMnPd: 'eee8aa',
1171
  pOegYF: '98fb98',
1172
  pOeQe: 'afeeee',
1173
  pOeviTetYd: 'db7093',
1174
  papayawEp: 'ffefd5',
1175
  pHKpuff: 'ffdab9',
1176
  peru: 'cd853f',
1177
  pRk: 'ffc0cb',
1178
  plum: 'dda0dd',
1179
  powMrXe: 'b0e0e6',
1180
  purpN: '800080',
1181
  YbeccapurpN: '663399',
1182
  Yd: 'ff0000',
1183
  Psybrown: 'bc8f8f',
1184
  PyOXe: '4169e1',
1185
  saddNbPwn: '8b4513',
1186
  sOmon: 'fa8072',
1187
  sandybPwn: 'f4a460',
1188
  sHgYF: '2e8b57',
1189
  sHshell: 'fff5ee',
1190
  siFna: 'a0522d',
1191
  silver: 'c0c0c0',
1192
  skyXe: '87ceeb',
1193
  UXe: '6a5acd',
1194
  UWay: '708090',
1195
  UgYy: '708090',
1196
  snow: 'fffafa',
1197
  sprRggYF: 'ff7f',
1198
  stAlXe: '4682b4',
1199
  tan: 'd2b48c',
1200
  teO: '8080',
1201
  tEstN: 'd8bfd8',
1202
  tomato: 'ff6347',
1203
  Qe: '40e0d0',
1204
  viTet: 'ee82ee',
1205
  JHt: 'f5deb3',
1206
  wEte: 'ffffff',
1207
  wEtesmoke: 'f5f5f5',
1208
  Lw: 'ffff00',
1209
  LwgYF: '9acd32'
1210
};
1211
function unpack() {
1212
  const unpacked = {};
1213
  const keys = Object.keys(names$1);
1214
  const tkeys = Object.keys(map$2);
1215
  let i, j, k, ok, nk;
1216
  for (i = 0; i < keys.length; i++) {
1217
    ok = nk = keys[i];
1218
    for (j = 0; j < tkeys.length; j++) {
1219
      k = tkeys[j];
1220
      nk = nk.replace(k, map$2[k]);
1221
    }
1222
    k = parseInt(names$1[ok], 16);
1223
    unpacked[nk] = [k >> 16 & 0xFF, k >> 8 & 0xFF, k & 0xFF];
1224
  }
1225
  return unpacked;
1226
}
1227
let names;
1228
function nameParse(str) {
1229
  if (!names) {
1230
    names = unpack();
1231
    names.transparent = [0, 0, 0, 0];
1232
  }
1233
  const a = names[str.toLowerCase()];
1234
  return a && {
1235
    r: a[0],
1236
    g: a[1],
1237
    b: a[2],
1238
    a: a.length === 4 ? a[3] : 255
1239
  };
1240
}
1241
const RGB_RE = /^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;
1242
function rgbParse(str) {
1243
  const m = RGB_RE.exec(str);
1244
  let a = 255;
1245
  let r, g, b;
1246
  if (!m) {
1247
    return;
1248
  }
1249
  if (m[7] !== r) {
1250
    const v = +m[7];
1251
    a = m[8] ? p2b(v) : lim(v * 255, 0, 255);
1252
  }
1253
  r = +m[1];
1254
  g = +m[3];
1255
  b = +m[5];
1256
  r = 255 & (m[2] ? p2b(r) : lim(r, 0, 255));
1257
  g = 255 & (m[4] ? p2b(g) : lim(g, 0, 255));
1258
  b = 255 & (m[6] ? p2b(b) : lim(b, 0, 255));
1259
  return {
1260
    r: r,
1261
    g: g,
1262
    b: b,
1263
    a: a
1264
  };
1265
}
1266
function rgbString(v) {
1267
  return v && (
1268
    v.a < 255
1269
      ? `rgba(${v.r}, ${v.g}, ${v.b}, ${b2n(v.a)})`
1270
      : `rgb(${v.r}, ${v.g}, ${v.b})`
1271
  );
1272
}
1273
const to = v => v <= 0.0031308 ? v * 12.92 : Math.pow(v, 1.0 / 2.4) * 1.055 - 0.055;
1274
const from = v => v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
1275
function interpolate$1(rgb1, rgb2, t) {
1276
  const r = from(b2n(rgb1.r));
1277
  const g = from(b2n(rgb1.g));
1278
  const b = from(b2n(rgb1.b));
1279
  return {
1280
    r: n2b(to(r + t * (from(b2n(rgb2.r)) - r))),
1281
    g: n2b(to(g + t * (from(b2n(rgb2.g)) - g))),
1282
    b: n2b(to(b + t * (from(b2n(rgb2.b)) - b))),
1283
    a: rgb1.a + t * (rgb2.a - rgb1.a)
1284
  };
1285
}
1286
function modHSL(v, i, ratio) {
1287
  if (v) {
1288
    let tmp = rgb2hsl(v);
1289
    tmp[i] = Math.max(0, Math.min(tmp[i] + tmp[i] * ratio, i === 0 ? 360 : 1));
1290
    tmp = hsl2rgb(tmp);
1291
    v.r = tmp[0];
1292
    v.g = tmp[1];
1293
    v.b = tmp[2];
1294
  }
1295
}
1296
function clone(v, proto) {
1297
  return v ? Object.assign(proto || {}, v) : v;
1298
}
1299
function fromObject(input) {
1300
  var v = {r: 0, g: 0, b: 0, a: 255};
1301
  if (Array.isArray(input)) {
1302
    if (input.length >= 3) {
1303
      v = {r: input[0], g: input[1], b: input[2], a: 255};
1304
      if (input.length > 3) {
1305
        v.a = n2b(input[3]);
1306
      }
1307
    }
1308
  } else {
1309
    v = clone(input, {r: 0, g: 0, b: 0, a: 1});
1310
    v.a = n2b(v.a);
1311
  }
1312
  return v;
1313
}
1314
function functionParse(str) {
1315
  if (str.charAt(0) === 'r') {
1316
    return rgbParse(str);
1317
  }
1318
  return hueParse(str);
1319
}
1320
class Color {
1321
  constructor(input) {
1322
    if (input instanceof Color) {
1323
      return input;
1324
    }
1325
    const type = typeof input;
1326
    let v;
1327
    if (type === 'object') {
1328
      v = fromObject(input);
1329
    } else if (type === 'string') {
1330
      v = hexParse(input) || nameParse(input) || functionParse(input);
1331
    }
1332
    this._rgb = v;
1333
    this._valid = !!v;
1334
  }
1335
  get valid() {
1336
    return this._valid;
1337
  }
1338
  get rgb() {
1339
    var v = clone(this._rgb);
1340
    if (v) {
1341
      v.a = b2n(v.a);
1342
    }
1343
    return v;
1344
  }
1345
  set rgb(obj) {
1346
    this._rgb = fromObject(obj);
1347
  }
1348
  rgbString() {
1349
    return this._valid ? rgbString(this._rgb) : undefined;
1350
  }
1351
  hexString() {
1352
    return this._valid ? hexString(this._rgb) : undefined;
1353
  }
1354
  hslString() {
1355
    return this._valid ? hslString(this._rgb) : undefined;
1356
  }
1357
  mix(color, weight) {
1358
    if (color) {
1359
      const c1 = this.rgb;
1360
      const c2 = color.rgb;
1361
      let w2;
1362
      const p = weight === w2 ? 0.5 : weight;
1363
      const w = 2 * p - 1;
1364
      const a = c1.a - c2.a;
1365
      const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
1366
      w2 = 1 - w1;
1367
      c1.r = 0xFF & w1 * c1.r + w2 * c2.r + 0.5;
1368
      c1.g = 0xFF & w1 * c1.g + w2 * c2.g + 0.5;
1369
      c1.b = 0xFF & w1 * c1.b + w2 * c2.b + 0.5;
1370
      c1.a = p * c1.a + (1 - p) * c2.a;
1371
      this.rgb = c1;
1372
    }
1373
    return this;
1374
  }
1375
  interpolate(color, t) {
1376
    if (color) {
1377
      this._rgb = interpolate$1(this._rgb, color._rgb, t);
1378
    }
1379
    return this;
1380
  }
1381
  clone() {
1382
    return new Color(this.rgb);
1383
  }
1384
  alpha(a) {
1385
    this._rgb.a = n2b(a);
1386
    return this;
1387
  }
1388
  clearer(ratio) {
1389
    const rgb = this._rgb;
1390
    rgb.a *= 1 - ratio;
1391
    return this;
1392
  }
1393
  greyscale() {
1394
    const rgb = this._rgb;
1395
    const val = round(rgb.r * 0.3 + rgb.g * 0.59 + rgb.b * 0.11);
1396
    rgb.r = rgb.g = rgb.b = val;
1397
    return this;
1398
  }
1399
  opaquer(ratio) {
1400
    const rgb = this._rgb;
1401
    rgb.a *= 1 + ratio;
1402
    return this;
1403
  }
1404
  negate() {
1405
    const v = this._rgb;
1406
    v.r = 255 - v.r;
1407
    v.g = 255 - v.g;
1408
    v.b = 255 - v.b;
1409
    return this;
1410
  }
1411
  lighten(ratio) {
1412
    modHSL(this._rgb, 2, ratio);
1413
    return this;
1414
  }
1415
  darken(ratio) {
1416
    modHSL(this._rgb, 2, -ratio);
1417
    return this;
1418
  }
1419
  saturate(ratio) {
1420
    modHSL(this._rgb, 1, ratio);
1421
    return this;
1422
  }
1423
  desaturate(ratio) {
1424
    modHSL(this._rgb, 1, -ratio);
1425
    return this;
1426
  }
1427
  rotate(deg) {
1428
    rotate(this._rgb, deg);
1429
    return this;
1430
  }
1431
}
1432
 
1433
function isPatternOrGradient(value) {
1434
    if (value && typeof value === 'object') {
1435
        const type = value.toString();
1436
        return type === '[object CanvasPattern]' || type === '[object CanvasGradient]';
1437
    }
1438
    return false;
1439
}
1440
function color(value) {
1441
    return isPatternOrGradient(value) ? value : new Color(value);
1442
}
1443
function getHoverColor(value) {
1444
    return isPatternOrGradient(value) ? value : new Color(value).saturate(0.5).darken(0.1).hexString();
1445
}
1446
 
1447
const numbers = [
1448
    'x',
1449
    'y',
1450
    'borderWidth',
1451
    'radius',
1452
    'tension'
1453
];
1454
const colors = [
1455
    'color',
1456
    'borderColor',
1457
    'backgroundColor'
1458
];
1459
function applyAnimationsDefaults(defaults) {
1460
    defaults.set('animation', {
1461
        delay: undefined,
1462
        duration: 1000,
1463
        easing: 'easeOutQuart',
1464
        fn: undefined,
1465
        from: undefined,
1466
        loop: undefined,
1467
        to: undefined,
1468
        type: undefined
1469
    });
1470
    defaults.describe('animation', {
1471
        _fallback: false,
1472
        _indexable: false,
1473
        _scriptable: (name)=>name !== 'onProgress' && name !== 'onComplete' && name !== 'fn'
1474
    });
1475
    defaults.set('animations', {
1476
        colors: {
1477
            type: 'color',
1478
            properties: colors
1479
        },
1480
        numbers: {
1481
            type: 'number',
1482
            properties: numbers
1483
        }
1484
    });
1485
    defaults.describe('animations', {
1486
        _fallback: 'animation'
1487
    });
1488
    defaults.set('transitions', {
1489
        active: {
1490
            animation: {
1491
                duration: 400
1492
            }
1493
        },
1494
        resize: {
1495
            animation: {
1496
                duration: 0
1497
            }
1498
        },
1499
        show: {
1500
            animations: {
1501
                colors: {
1502
                    from: 'transparent'
1503
                },
1504
                visible: {
1505
                    type: 'boolean',
1506
                    duration: 0
1507
                }
1508
            }
1509
        },
1510
        hide: {
1511
            animations: {
1512
                colors: {
1513
                    to: 'transparent'
1514
                },
1515
                visible: {
1516
                    type: 'boolean',
1517
                    easing: 'linear',
1518
                    fn: (v)=>v | 0
1519
                }
1520
            }
1521
        }
1522
    });
1523
}
1524
 
1525
function applyLayoutsDefaults(defaults) {
1526
    defaults.set('layout', {
1527
        autoPadding: true,
1528
        padding: {
1529
            top: 0,
1530
            right: 0,
1531
            bottom: 0,
1532
            left: 0
1533
        }
1534
    });
1535
}
1536
 
1537
const intlCache = new Map();
1538
function getNumberFormat(locale, options) {
1539
    options = options || {};
1540
    const cacheKey = locale + JSON.stringify(options);
1541
    let formatter = intlCache.get(cacheKey);
1542
    if (!formatter) {
1543
        formatter = new Intl.NumberFormat(locale, options);
1544
        intlCache.set(cacheKey, formatter);
1545
    }
1546
    return formatter;
1547
}
1548
function formatNumber(num, locale, options) {
1549
    return getNumberFormat(locale, options).format(num);
1550
}
1551
 
1552
const formatters = {
1553
 values (value) {
1554
        return isArray(value) ?  value : '' + value;
1555
    },
1556
 numeric (tickValue, index, ticks) {
1557
        if (tickValue === 0) {
1558
            return '0';
1559
        }
1560
        const locale = this.chart.options.locale;
1561
        let notation;
1562
        let delta = tickValue;
1563
        if (ticks.length > 1) {
1564
            const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));
1565
            if (maxTick < 1e-4 || maxTick > 1e+15) {
1566
                notation = 'scientific';
1567
            }
1568
            delta = calculateDelta(tickValue, ticks);
1569
        }
1570
        const logDelta = log10(Math.abs(delta));
1571
        const numDecimal = isNaN(logDelta) ? 1 : Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0);
1572
        const options = {
1573
            notation,
1574
            minimumFractionDigits: numDecimal,
1575
            maximumFractionDigits: numDecimal
1576
        };
1577
        Object.assign(options, this.options.ticks.format);
1578
        return formatNumber(tickValue, locale, options);
1579
    },
1580
 logarithmic (tickValue, index, ticks) {
1581
        if (tickValue === 0) {
1582
            return '0';
1583
        }
1584
        const remain = ticks[index].significand || tickValue / Math.pow(10, Math.floor(log10(tickValue)));
1585
        if ([
1586
            1,
1587
            2,
1588
            3,
1589
            5,
1590
            10,
1591
            15
1592
        ].includes(remain) || index > 0.8 * ticks.length) {
1593
            return formatters.numeric.call(this, tickValue, index, ticks);
1594
        }
1595
        return '';
1596
    }
1597
};
1598
function calculateDelta(tickValue, ticks) {
1599
    let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;
1600
    if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) {
1601
        delta = tickValue - Math.floor(tickValue);
1602
    }
1603
    return delta;
1604
}
1605
 var Ticks = {
1606
    formatters
1607
};
1608
 
1609
function applyScaleDefaults(defaults) {
1610
    defaults.set('scale', {
1611
        display: true,
1612
        offset: false,
1613
        reverse: false,
1614
        beginAtZero: false,
1615
 bounds: 'ticks',
1616
        clip: true,
1617
 grace: 0,
1618
        grid: {
1619
            display: true,
1620
            lineWidth: 1,
1621
            drawOnChartArea: true,
1622
            drawTicks: true,
1623
            tickLength: 8,
1624
            tickWidth: (_ctx, options)=>options.lineWidth,
1625
            tickColor: (_ctx, options)=>options.color,
1626
            offset: false
1627
        },
1628
        border: {
1629
            display: true,
1630
            dash: [],
1631
            dashOffset: 0.0,
1632
            width: 1
1633
        },
1634
        title: {
1635
            display: false,
1636
            text: '',
1637
            padding: {
1638
                top: 4,
1639
                bottom: 4
1640
            }
1641
        },
1642
        ticks: {
1643
            minRotation: 0,
1644
            maxRotation: 50,
1645
            mirror: false,
1646
            textStrokeWidth: 0,
1647
            textStrokeColor: '',
1648
            padding: 3,
1649
            display: true,
1650
            autoSkip: true,
1651
            autoSkipPadding: 3,
1652
            labelOffset: 0,
1653
            callback: Ticks.formatters.values,
1654
            minor: {},
1655
            major: {},
1656
            align: 'center',
1657
            crossAlign: 'near',
1658
            showLabelBackdrop: false,
1659
            backdropColor: 'rgba(255, 255, 255, 0.75)',
1660
            backdropPadding: 2
1661
        }
1662
    });
1663
    defaults.route('scale.ticks', 'color', '', 'color');
1664
    defaults.route('scale.grid', 'color', '', 'borderColor');
1665
    defaults.route('scale.border', 'color', '', 'borderColor');
1666
    defaults.route('scale.title', 'color', '', 'color');
1667
    defaults.describe('scale', {
1668
        _fallback: false,
1669
        _scriptable: (name)=>!name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser',
1670
        _indexable: (name)=>name !== 'borderDash' && name !== 'tickBorderDash' && name !== 'dash'
1671
    });
1672
    defaults.describe('scales', {
1673
        _fallback: 'scale'
1674
    });
1675
    defaults.describe('scale.ticks', {
1676
        _scriptable: (name)=>name !== 'backdropPadding' && name !== 'callback',
1677
        _indexable: (name)=>name !== 'backdropPadding'
1678
    });
1679
}
1680
 
1681
const overrides = Object.create(null);
1682
const descriptors = Object.create(null);
1683
 function getScope$1(node, key) {
1684
    if (!key) {
1685
        return node;
1686
    }
1687
    const keys = key.split('.');
1688
    for(let i = 0, n = keys.length; i < n; ++i){
1689
        const k = keys[i];
1690
        node = node[k] || (node[k] = Object.create(null));
1691
    }
1692
    return node;
1693
}
1694
function set(root, scope, values) {
1695
    if (typeof scope === 'string') {
1696
        return merge(getScope$1(root, scope), values);
1697
    }
1698
    return merge(getScope$1(root, ''), scope);
1699
}
1700
 class Defaults {
1701
    constructor(_descriptors, _appliers){
1702
        this.animation = undefined;
1703
        this.backgroundColor = 'rgba(0,0,0,0.1)';
1704
        this.borderColor = 'rgba(0,0,0,0.1)';
1705
        this.color = '#666';
1706
        this.datasets = {};
1707
        this.devicePixelRatio = (context)=>context.chart.platform.getDevicePixelRatio();
1708
        this.elements = {};
1709
        this.events = [
1710
            'mousemove',
1711
            'mouseout',
1712
            'click',
1713
            'touchstart',
1714
            'touchmove'
1715
        ];
1716
        this.font = {
1717
            family: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
1718
            size: 12,
1719
            style: 'normal',
1720
            lineHeight: 1.2,
1721
            weight: null
1722
        };
1723
        this.hover = {};
1724
        this.hoverBackgroundColor = (ctx, options)=>getHoverColor(options.backgroundColor);
1725
        this.hoverBorderColor = (ctx, options)=>getHoverColor(options.borderColor);
1726
        this.hoverColor = (ctx, options)=>getHoverColor(options.color);
1727
        this.indexAxis = 'x';
1728
        this.interaction = {
1729
            mode: 'nearest',
1730
            intersect: true,
1731
            includeInvisible: false
1732
        };
1733
        this.maintainAspectRatio = true;
1734
        this.onHover = null;
1735
        this.onClick = null;
1736
        this.parsing = true;
1737
        this.plugins = {};
1738
        this.responsive = true;
1739
        this.scale = undefined;
1740
        this.scales = {};
1741
        this.showLine = true;
1742
        this.drawActiveElementsOnTop = true;
1743
        this.describe(_descriptors);
1744
        this.apply(_appliers);
1745
    }
1746
 set(scope, values) {
1747
        return set(this, scope, values);
1748
    }
1749
 get(scope) {
1750
        return getScope$1(this, scope);
1751
    }
1752
 describe(scope, values) {
1753
        return set(descriptors, scope, values);
1754
    }
1755
    override(scope, values) {
1756
        return set(overrides, scope, values);
1757
    }
1758
 route(scope, name, targetScope, targetName) {
1759
        const scopeObject = getScope$1(this, scope);
1760
        const targetScopeObject = getScope$1(this, targetScope);
1761
        const privateName = '_' + name;
1762
        Object.defineProperties(scopeObject, {
1763
            [privateName]: {
1764
                value: scopeObject[name],
1765
                writable: true
1766
            },
1767
            [name]: {
1768
                enumerable: true,
1769
                get () {
1770
                    const local = this[privateName];
1771
                    const target = targetScopeObject[targetName];
1772
                    if (isObject(local)) {
1773
                        return Object.assign({}, target, local);
1774
                    }
1775
                    return valueOrDefault(local, target);
1776
                },
1777
                set (value) {
1778
                    this[privateName] = value;
1779
                }
1780
            }
1781
        });
1782
    }
1783
    apply(appliers) {
1784
        appliers.forEach((apply)=>apply(this));
1785
    }
1786
}
1787
var defaults = /* #__PURE__ */ new Defaults({
1788
    _scriptable: (name)=>!name.startsWith('on'),
1789
    _indexable: (name)=>name !== 'events',
1790
    hover: {
1791
        _fallback: 'interaction'
1792
    },
1793
    interaction: {
1794
        _scriptable: false,
1795
        _indexable: false
1796
    }
1797
}, [
1798
    applyAnimationsDefaults,
1799
    applyLayoutsDefaults,
1800
    applyScaleDefaults
1801
]);
1802
 
1803
/**
1804
 * Note: typedefs are auto-exported, so use a made-up `dom` namespace where
1805
 * necessary to avoid duplicates with `export * from './helpers`; see
1806
 * https://github.com/microsoft/TypeScript/issues/46011
1807
 * @typedef { import('../core/core.controller.js').default } dom.Chart
1808
 * @typedef { import('../../types').ChartEvent } ChartEvent
1809
 */ /**
1810
 * @private
1811
 */ function _isDomSupported() {
1812
    return typeof window !== 'undefined' && typeof document !== 'undefined';
1813
}
1814
/**
1815
 * @private
1816
 */ function _getParentNode(domNode) {
1817
    let parent = domNode.parentNode;
1818
    if (parent && parent.toString() === '[object ShadowRoot]') {
1819
        parent = parent.host;
1820
    }
1821
    return parent;
1822
}
1823
/**
1824
 * convert max-width/max-height values that may be percentages into a number
1825
 * @private
1826
 */ function parseMaxStyle(styleValue, node, parentProperty) {
1827
    let valueInPixels;
1828
    if (typeof styleValue === 'string') {
1829
        valueInPixels = parseInt(styleValue, 10);
1830
        if (styleValue.indexOf('%') !== -1) {
1831
            // percentage * size in dimension
1832
            valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
1833
        }
1834
    } else {
1835
        valueInPixels = styleValue;
1836
    }
1837
    return valueInPixels;
1838
}
1839
const getComputedStyle = (element)=>element.ownerDocument.defaultView.getComputedStyle(element, null);
1840
function getStyle(el, property) {
1841
    return getComputedStyle(el).getPropertyValue(property);
1842
}
1843
const positions = [
1844
    'top',
1845
    'right',
1846
    'bottom',
1847
    'left'
1848
];
1849
function getPositionedStyle(styles, style, suffix) {
1850
    const result = {};
1851
    suffix = suffix ? '-' + suffix : '';
1852
    for(let i = 0; i < 4; i++){
1853
        const pos = positions[i];
1854
        result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0;
1855
    }
1856
    result.width = result.left + result.right;
1857
    result.height = result.top + result.bottom;
1858
    return result;
1859
}
1860
const useOffsetPos = (x, y, target)=>(x > 0 || y > 0) && (!target || !target.shadowRoot);
1861
/**
1862
 * @param e
1863
 * @param canvas
1864
 * @returns Canvas position
1865
 */ function getCanvasPosition(e, canvas) {
1866
    const touches = e.touches;
1867
    const source = touches && touches.length ? touches[0] : e;
1868
    const { offsetX , offsetY  } = source;
1869
    let box = false;
1870
    let x, y;
1871
    if (useOffsetPos(offsetX, offsetY, e.target)) {
1872
        x = offsetX;
1873
        y = offsetY;
1874
    } else {
1875
        const rect = canvas.getBoundingClientRect();
1876
        x = source.clientX - rect.left;
1877
        y = source.clientY - rect.top;
1878
        box = true;
1879
    }
1880
    return {
1881
        x,
1882
        y,
1883
        box
1884
    };
1885
}
1886
/**
1887
 * Gets an event's x, y coordinates, relative to the chart area
1888
 * @param event
1889
 * @param chart
1890
 * @returns x and y coordinates of the event
1891
 */ function getRelativePosition(event, chart) {
1892
    if ('native' in event) {
1893
        return event;
1894
    }
1895
    const { canvas , currentDevicePixelRatio  } = chart;
1896
    const style = getComputedStyle(canvas);
1897
    const borderBox = style.boxSizing === 'border-box';
1898
    const paddings = getPositionedStyle(style, 'padding');
1899
    const borders = getPositionedStyle(style, 'border', 'width');
1900
    const { x , y , box  } = getCanvasPosition(event, canvas);
1901
    const xOffset = paddings.left + (box && borders.left);
1902
    const yOffset = paddings.top + (box && borders.top);
1903
    let { width , height  } = chart;
1904
    if (borderBox) {
1905
        width -= paddings.width + borders.width;
1906
        height -= paddings.height + borders.height;
1907
    }
1908
    return {
1909
        x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),
1910
        y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)
1911
    };
1912
}
1913
function getContainerSize(canvas, width, height) {
1914
    let maxWidth, maxHeight;
1915
    if (width === undefined || height === undefined) {
1916
        const container = _getParentNode(canvas);
1917
        if (!container) {
1918
            width = canvas.clientWidth;
1919
            height = canvas.clientHeight;
1920
        } else {
1921
            const rect = container.getBoundingClientRect(); // this is the border box of the container
1922
            const containerStyle = getComputedStyle(container);
1923
            const containerBorder = getPositionedStyle(containerStyle, 'border', 'width');
1924
            const containerPadding = getPositionedStyle(containerStyle, 'padding');
1925
            width = rect.width - containerPadding.width - containerBorder.width;
1926
            height = rect.height - containerPadding.height - containerBorder.height;
1927
            maxWidth = parseMaxStyle(containerStyle.maxWidth, container, 'clientWidth');
1928
            maxHeight = parseMaxStyle(containerStyle.maxHeight, container, 'clientHeight');
1929
        }
1930
    }
1931
    return {
1932
        width,
1933
        height,
1934
        maxWidth: maxWidth || INFINITY,
1935
        maxHeight: maxHeight || INFINITY
1936
    };
1937
}
1938
const round1 = (v)=>Math.round(v * 10) / 10;
1939
// eslint-disable-next-line complexity
1940
function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) {
1941
    const style = getComputedStyle(canvas);
1942
    const margins = getPositionedStyle(style, 'margin');
1943
    const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY;
1944
    const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY;
1945
    const containerSize = getContainerSize(canvas, bbWidth, bbHeight);
1946
    let { width , height  } = containerSize;
1947
    if (style.boxSizing === 'content-box') {
1948
        const borders = getPositionedStyle(style, 'border', 'width');
1949
        const paddings = getPositionedStyle(style, 'padding');
1950
        width -= paddings.width + borders.width;
1951
        height -= paddings.height + borders.height;
1952
    }
1953
    width = Math.max(0, width - margins.width);
1954
    height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height);
1955
    width = round1(Math.min(width, maxWidth, containerSize.maxWidth));
1956
    height = round1(Math.min(height, maxHeight, containerSize.maxHeight));
1957
    if (width && !height) {
1958
        // https://github.com/chartjs/Chart.js/issues/4659
1959
        // If the canvas has width, but no height, default to aspectRatio of 2 (canvas default)
1960
        height = round1(width / 2);
1961
    }
1962
    const maintainHeight = bbWidth !== undefined || bbHeight !== undefined;
1963
    if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) {
1964
        height = containerSize.height;
1965
        width = round1(Math.floor(height * aspectRatio));
1966
    }
1967
    return {
1968
        width,
1969
        height
1970
    };
1971
}
1972
/**
1973
 * @param chart
1974
 * @param forceRatio
1975
 * @param forceStyle
1976
 * @returns True if the canvas context size or transformation has changed.
1977
 */ function retinaScale(chart, forceRatio, forceStyle) {
1978
    const pixelRatio = forceRatio || 1;
1979
    const deviceHeight = Math.floor(chart.height * pixelRatio);
1980
    const deviceWidth = Math.floor(chart.width * pixelRatio);
1981
    chart.height = Math.floor(chart.height);
1982
    chart.width = Math.floor(chart.width);
1983
    const canvas = chart.canvas;
1984
    // If no style has been set on the canvas, the render size is used as display size,
1985
    // making the chart visually bigger, so let's enforce it to the "correct" values.
1986
    // See https://github.com/chartjs/Chart.js/issues/3575
1987
    if (canvas.style && (forceStyle || !canvas.style.height && !canvas.style.width)) {
1988
        canvas.style.height = `${chart.height}px`;
1989
        canvas.style.width = `${chart.width}px`;
1990
    }
1991
    if (chart.currentDevicePixelRatio !== pixelRatio || canvas.height !== deviceHeight || canvas.width !== deviceWidth) {
1992
        chart.currentDevicePixelRatio = pixelRatio;
1993
        canvas.height = deviceHeight;
1994
        canvas.width = deviceWidth;
1995
        chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
1996
        return true;
1997
    }
1998
    return false;
1999
}
2000
/**
2001
 * Detects support for options object argument in addEventListener.
2002
 * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
2003
 * @private
2004
 */ const supportsEventListenerOptions = function() {
2005
    let passiveSupported = false;
2006
    try {
2007
        const options = {
2008
            get passive () {
2009
                passiveSupported = true;
2010
                return false;
2011
            }
2012
        };
2013
        if (_isDomSupported()) {
2014
            window.addEventListener('test', null, options);
2015
            window.removeEventListener('test', null, options);
2016
        }
2017
    } catch (e) {
2018
    // continue regardless of error
2019
    }
2020
    return passiveSupported;
2021
}();
2022
/**
2023
 * The "used" size is the final value of a dimension property after all calculations have
2024
 * been performed. This method uses the computed style of `element` but returns undefined
2025
 * if the computed style is not expressed in pixels. That can happen in some cases where
2026
 * `element` has a size relative to its parent and this last one is not yet displayed,
2027
 * for example because of `display: none` on a parent node.
2028
 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
2029
 * @returns Size in pixels or undefined if unknown.
2030
 */ function readUsedSize(element, property) {
2031
    const value = getStyle(element, property);
2032
    const matches = value && value.match(/^(\d+)(\.\d+)?px$/);
2033
    return matches ? +matches[1] : undefined;
2034
}
2035
 
2036
/**
2037
 * Converts the given font object into a CSS font string.
2038
 * @param font - A font object.
2039
 * @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
2040
 * @private
2041
 */ function toFontString(font) {
2042
    if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {
2043
        return null;
2044
    }
2045
    return (font.style ? font.style + ' ' : '') + (font.weight ? font.weight + ' ' : '') + font.size + 'px ' + font.family;
2046
}
2047
/**
2048
 * @private
2049
 */ function _measureText(ctx, data, gc, longest, string) {
2050
    let textWidth = data[string];
2051
    if (!textWidth) {
2052
        textWidth = data[string] = ctx.measureText(string).width;
2053
        gc.push(string);
2054
    }
2055
    if (textWidth > longest) {
2056
        longest = textWidth;
2057
    }
2058
    return longest;
2059
}
2060
/**
2061
 * @private
2062
 */ // eslint-disable-next-line complexity
2063
function _longestText(ctx, font, arrayOfThings, cache) {
2064
    cache = cache || {};
2065
    let data = cache.data = cache.data || {};
2066
    let gc = cache.garbageCollect = cache.garbageCollect || [];
2067
    if (cache.font !== font) {
2068
        data = cache.data = {};
2069
        gc = cache.garbageCollect = [];
2070
        cache.font = font;
2071
    }
2072
    ctx.save();
2073
    ctx.font = font;
2074
    let longest = 0;
2075
    const ilen = arrayOfThings.length;
2076
    let i, j, jlen, thing, nestedThing;
2077
    for(i = 0; i < ilen; i++){
2078
        thing = arrayOfThings[i];
2079
        // Undefined strings and arrays should not be measured
2080
        if (thing !== undefined && thing !== null && !isArray(thing)) {
2081
            longest = _measureText(ctx, data, gc, longest, thing);
2082
        } else if (isArray(thing)) {
2083
            // if it is an array lets measure each element
2084
            // to do maybe simplify this function a bit so we can do this more recursively?
2085
            for(j = 0, jlen = thing.length; j < jlen; j++){
2086
                nestedThing = thing[j];
2087
                // Undefined strings and arrays should not be measured
2088
                if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) {
2089
                    longest = _measureText(ctx, data, gc, longest, nestedThing);
2090
                }
2091
            }
2092
        }
2093
    }
2094
    ctx.restore();
2095
    const gcLen = gc.length / 2;
2096
    if (gcLen > arrayOfThings.length) {
2097
        for(i = 0; i < gcLen; i++){
2098
            delete data[gc[i]];
2099
        }
2100
        gc.splice(0, gcLen);
2101
    }
2102
    return longest;
2103
}
2104
/**
2105
 * Returns the aligned pixel value to avoid anti-aliasing blur
2106
 * @param chart - The chart instance.
2107
 * @param pixel - A pixel value.
2108
 * @param width - The width of the element.
2109
 * @returns The aligned pixel value.
2110
 * @private
2111
 */ function _alignPixel(chart, pixel, width) {
2112
    const devicePixelRatio = chart.currentDevicePixelRatio;
2113
    const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;
2114
    return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
2115
}
2116
/**
2117
 * Clears the entire canvas.
2118
 */ function clearCanvas(canvas, ctx) {
2119
    ctx = ctx || canvas.getContext('2d');
2120
    ctx.save();
2121
    // canvas.width and canvas.height do not consider the canvas transform,
2122
    // while clearRect does
2123
    ctx.resetTransform();
2124
    ctx.clearRect(0, 0, canvas.width, canvas.height);
2125
    ctx.restore();
2126
}
2127
function drawPoint(ctx, options, x, y) {
2128
    // eslint-disable-next-line @typescript-eslint/no-use-before-define
2129
    drawPointLegend(ctx, options, x, y, null);
2130
}
2131
// eslint-disable-next-line complexity
2132
function drawPointLegend(ctx, options, x, y, w) {
2133
    let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW;
2134
    const style = options.pointStyle;
2135
    const rotation = options.rotation;
2136
    const radius = options.radius;
2137
    let rad = (rotation || 0) * RAD_PER_DEG;
2138
    if (style && typeof style === 'object') {
2139
        type = style.toString();
2140
        if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
2141
            ctx.save();
2142
            ctx.translate(x, y);
2143
            ctx.rotate(rad);
2144
            ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);
2145
            ctx.restore();
2146
            return;
2147
        }
2148
    }
2149
    if (isNaN(radius) || radius <= 0) {
2150
        return;
2151
    }
2152
    ctx.beginPath();
2153
    switch(style){
2154
        // Default includes circle
2155
        default:
2156
            if (w) {
2157
                ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);
2158
            } else {
2159
                ctx.arc(x, y, radius, 0, TAU);
2160
            }
2161
            ctx.closePath();
2162
            break;
2163
        case 'triangle':
2164
            width = w ? w / 2 : radius;
2165
            ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
2166
            rad += TWO_THIRDS_PI;
2167
            ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
2168
            rad += TWO_THIRDS_PI;
2169
            ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
2170
            ctx.closePath();
2171
            break;
2172
        case 'rectRounded':
2173
            // NOTE: the rounded rect implementation changed to use `arc` instead of
2174
            // `quadraticCurveTo` since it generates better results when rect is
2175
            // almost a circle. 0.516 (instead of 0.5) produces results with visually
2176
            // closer proportion to the previous impl and it is inscribed in the
2177
            // circle with `radius`. For more details, see the following PRs:
2178
            // https://github.com/chartjs/Chart.js/issues/5597
2179
            // https://github.com/chartjs/Chart.js/issues/5858
2180
            cornerRadius = radius * 0.516;
2181
            size = radius - cornerRadius;
2182
            xOffset = Math.cos(rad + QUARTER_PI) * size;
2183
            xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
2184
            yOffset = Math.sin(rad + QUARTER_PI) * size;
2185
            yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
2186
            ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
2187
            ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);
2188
            ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);
2189
            ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
2190
            ctx.closePath();
2191
            break;
2192
        case 'rect':
2193
            if (!rotation) {
2194
                size = Math.SQRT1_2 * radius;
2195
                width = w ? w / 2 : size;
2196
                ctx.rect(x - width, y - size, 2 * width, 2 * size);
2197
                break;
2198
            }
2199
            rad += QUARTER_PI;
2200
        /* falls through */ case 'rectRot':
2201
            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
2202
            xOffset = Math.cos(rad) * radius;
2203
            yOffset = Math.sin(rad) * radius;
2204
            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
2205
            ctx.moveTo(x - xOffsetW, y - yOffset);
2206
            ctx.lineTo(x + yOffsetW, y - xOffset);
2207
            ctx.lineTo(x + xOffsetW, y + yOffset);
2208
            ctx.lineTo(x - yOffsetW, y + xOffset);
2209
            ctx.closePath();
2210
            break;
2211
        case 'crossRot':
2212
            rad += QUARTER_PI;
2213
        /* falls through */ case 'cross':
2214
            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
2215
            xOffset = Math.cos(rad) * radius;
2216
            yOffset = Math.sin(rad) * radius;
2217
            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
2218
            ctx.moveTo(x - xOffsetW, y - yOffset);
2219
            ctx.lineTo(x + xOffsetW, y + yOffset);
2220
            ctx.moveTo(x + yOffsetW, y - xOffset);
2221
            ctx.lineTo(x - yOffsetW, y + xOffset);
2222
            break;
2223
        case 'star':
2224
            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
2225
            xOffset = Math.cos(rad) * radius;
2226
            yOffset = Math.sin(rad) * radius;
2227
            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
2228
            ctx.moveTo(x - xOffsetW, y - yOffset);
2229
            ctx.lineTo(x + xOffsetW, y + yOffset);
2230
            ctx.moveTo(x + yOffsetW, y - xOffset);
2231
            ctx.lineTo(x - yOffsetW, y + xOffset);
2232
            rad += QUARTER_PI;
2233
            xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
2234
            xOffset = Math.cos(rad) * radius;
2235
            yOffset = Math.sin(rad) * radius;
2236
            yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
2237
            ctx.moveTo(x - xOffsetW, y - yOffset);
2238
            ctx.lineTo(x + xOffsetW, y + yOffset);
2239
            ctx.moveTo(x + yOffsetW, y - xOffset);
2240
            ctx.lineTo(x - yOffsetW, y + xOffset);
2241
            break;
2242
        case 'line':
2243
            xOffset = w ? w / 2 : Math.cos(rad) * radius;
2244
            yOffset = Math.sin(rad) * radius;
2245
            ctx.moveTo(x - xOffset, y - yOffset);
2246
            ctx.lineTo(x + xOffset, y + yOffset);
2247
            break;
2248
        case 'dash':
2249
            ctx.moveTo(x, y);
2250
            ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);
2251
            break;
2252
        case false:
2253
            ctx.closePath();
2254
            break;
2255
    }
2256
    ctx.fill();
2257
    if (options.borderWidth > 0) {
2258
        ctx.stroke();
2259
    }
2260
}
2261
/**
2262
 * Returns true if the point is inside the rectangle
2263
 * @param point - The point to test
2264
 * @param area - The rectangle
2265
 * @param margin - allowed margin
2266
 * @private
2267
 */ function _isPointInArea(point, area, margin) {
2268
    margin = margin || 0.5; // margin - default is to match rounded decimals
2269
    return !area || point && point.x > area.left - margin && point.x < area.right + margin && point.y > area.top - margin && point.y < area.bottom + margin;
2270
}
2271
function clipArea(ctx, area) {
2272
    ctx.save();
2273
    ctx.beginPath();
2274
    ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
2275
    ctx.clip();
2276
}
2277
function unclipArea(ctx) {
2278
    ctx.restore();
2279
}
2280
/**
2281
 * @private
2282
 */ function _steppedLineTo(ctx, previous, target, flip, mode) {
2283
    if (!previous) {
2284
        return ctx.lineTo(target.x, target.y);
2285
    }
2286
    if (mode === 'middle') {
2287
        const midpoint = (previous.x + target.x) / 2.0;
2288
        ctx.lineTo(midpoint, previous.y);
2289
        ctx.lineTo(midpoint, target.y);
2290
    } else if (mode === 'after' !== !!flip) {
2291
        ctx.lineTo(previous.x, target.y);
2292
    } else {
2293
        ctx.lineTo(target.x, previous.y);
2294
    }
2295
    ctx.lineTo(target.x, target.y);
2296
}
2297
/**
2298
 * @private
2299
 */ function _bezierCurveTo(ctx, previous, target, flip) {
2300
    if (!previous) {
2301
        return ctx.lineTo(target.x, target.y);
2302
    }
2303
    ctx.bezierCurveTo(flip ? previous.cp1x : previous.cp2x, flip ? previous.cp1y : previous.cp2y, flip ? target.cp2x : target.cp1x, flip ? target.cp2y : target.cp1y, target.x, target.y);
2304
}
2305
function setRenderOpts(ctx, opts) {
2306
    if (opts.translation) {
2307
        ctx.translate(opts.translation[0], opts.translation[1]);
2308
    }
2309
    if (!isNullOrUndef(opts.rotation)) {
2310
        ctx.rotate(opts.rotation);
2311
    }
2312
    if (opts.color) {
2313
        ctx.fillStyle = opts.color;
2314
    }
2315
    if (opts.textAlign) {
2316
        ctx.textAlign = opts.textAlign;
2317
    }
2318
    if (opts.textBaseline) {
2319
        ctx.textBaseline = opts.textBaseline;
2320
    }
2321
}
2322
function decorateText(ctx, x, y, line, opts) {
2323
    if (opts.strikethrough || opts.underline) {
2324
        /**
2325
     * Now that IE11 support has been dropped, we can use more
2326
     * of the TextMetrics object. The actual bounding boxes
2327
     * are unflagged in Chrome, Firefox, Edge, and Safari so they
2328
     * can be safely used.
2329
     * See https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics#Browser_compatibility
2330
     */ const metrics = ctx.measureText(line);
2331
        const left = x - metrics.actualBoundingBoxLeft;
2332
        const right = x + metrics.actualBoundingBoxRight;
2333
        const top = y - metrics.actualBoundingBoxAscent;
2334
        const bottom = y + metrics.actualBoundingBoxDescent;
2335
        const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;
2336
        ctx.strokeStyle = ctx.fillStyle;
2337
        ctx.beginPath();
2338
        ctx.lineWidth = opts.decorationWidth || 2;
2339
        ctx.moveTo(left, yDecoration);
2340
        ctx.lineTo(right, yDecoration);
2341
        ctx.stroke();
2342
    }
2343
}
2344
function drawBackdrop(ctx, opts) {
2345
    const oldColor = ctx.fillStyle;
2346
    ctx.fillStyle = opts.color;
2347
    ctx.fillRect(opts.left, opts.top, opts.width, opts.height);
2348
    ctx.fillStyle = oldColor;
2349
}
2350
/**
2351
 * Render text onto the canvas
2352
 */ function renderText(ctx, text, x, y, font, opts = {}) {
2353
    const lines = isArray(text) ? text : [
2354
        text
2355
    ];
2356
    const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';
2357
    let i, line;
2358
    ctx.save();
2359
    ctx.font = font.string;
2360
    setRenderOpts(ctx, opts);
2361
    for(i = 0; i < lines.length; ++i){
2362
        line = lines[i];
2363
        if (opts.backdrop) {
2364
            drawBackdrop(ctx, opts.backdrop);
2365
        }
2366
        if (stroke) {
2367
            if (opts.strokeColor) {
2368
                ctx.strokeStyle = opts.strokeColor;
2369
            }
2370
            if (!isNullOrUndef(opts.strokeWidth)) {
2371
                ctx.lineWidth = opts.strokeWidth;
2372
            }
2373
            ctx.strokeText(line, x, y, opts.maxWidth);
2374
        }
2375
        ctx.fillText(line, x, y, opts.maxWidth);
2376
        decorateText(ctx, x, y, line, opts);
2377
        y += Number(font.lineHeight);
2378
    }
2379
    ctx.restore();
2380
}
2381
/**
2382
 * Add a path of a rectangle with rounded corners to the current sub-path
2383
 * @param ctx - Context
2384
 * @param rect - Bounding rect
2385
 */ function addRoundedRectPath(ctx, rect) {
2386
    const { x , y , w , h , radius  } = rect;
2387
    // top left arc
2388
    ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, 1.5 * PI, PI, true);
2389
    // line from top left to bottom left
2390
    ctx.lineTo(x, y + h - radius.bottomLeft);
2391
    // bottom left arc
2392
    ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true);
2393
    // line from bottom left to bottom right
2394
    ctx.lineTo(x + w - radius.bottomRight, y + h);
2395
    // bottom right arc
2396
    ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true);
2397
    // line from bottom right to top right
2398
    ctx.lineTo(x + w, y + radius.topRight);
2399
    // top right arc
2400
    ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true);
2401
    // line from top right to top left
2402
    ctx.lineTo(x + radius.topLeft, y);
2403
}
2404
 
2405
/**
2406
 * Creates a Proxy for resolving raw values for options.
2407
 * @param scopes - The option scopes to look for values, in resolution order
2408
 * @param prefixes - The prefixes for values, in resolution order.
2409
 * @param rootScopes - The root option scopes
2410
 * @param fallback - Parent scopes fallback
2411
 * @param getTarget - callback for getting the target for changed values
2412
 * @returns Proxy
2413
 * @private
2414
 */ function _createResolver(scopes, prefixes = [
2415
    ''
2416
], rootScopes, fallback, getTarget = ()=>scopes[0]) {
2417
    const finalRootScopes = rootScopes || scopes;
2418
    if (typeof fallback === 'undefined') {
2419
        fallback = _resolve('_fallback', scopes);
2420
    }
2421
    const cache = {
2422
        [Symbol.toStringTag]: 'Object',
2423
        _cacheable: true,
2424
        _scopes: scopes,
2425
        _rootScopes: finalRootScopes,
2426
        _fallback: fallback,
2427
        _getTarget: getTarget,
2428
        override: (scope)=>_createResolver([
2429
                scope,
2430
                ...scopes
2431
            ], prefixes, finalRootScopes, fallback)
2432
    };
2433
    return new Proxy(cache, {
2434
        /**
2435
     * A trap for the delete operator.
2436
     */ deleteProperty (target, prop) {
2437
            delete target[prop]; // remove from cache
2438
            delete target._keys; // remove cached keys
2439
            delete scopes[0][prop]; // remove from top level scope
2440
            return true;
2441
        },
2442
        /**
2443
     * A trap for getting property values.
2444
     */ get (target, prop) {
2445
            return _cached(target, prop, ()=>_resolveWithPrefixes(prop, prefixes, scopes, target));
2446
        },
2447
        /**
2448
     * A trap for Object.getOwnPropertyDescriptor.
2449
     * Also used by Object.hasOwnProperty.
2450
     */ getOwnPropertyDescriptor (target, prop) {
2451
            return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop);
2452
        },
2453
        /**
2454
     * A trap for Object.getPrototypeOf.
2455
     */ getPrototypeOf () {
2456
            return Reflect.getPrototypeOf(scopes[0]);
2457
        },
2458
        /**
2459
     * A trap for the in operator.
2460
     */ has (target, prop) {
2461
            return getKeysFromAllScopes(target).includes(prop);
2462
        },
2463
        /**
2464
     * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.
2465
     */ ownKeys (target) {
2466
            return getKeysFromAllScopes(target);
2467
        },
2468
        /**
2469
     * A trap for setting property values.
2470
     */ set (target, prop, value) {
2471
            const storage = target._storage || (target._storage = getTarget());
2472
            target[prop] = storage[prop] = value; // set to top level scope + cache
2473
            delete target._keys; // remove cached keys
2474
            return true;
2475
        }
2476
    });
2477
}
2478
/**
2479
 * Returns an Proxy for resolving option values with context.
2480
 * @param proxy - The Proxy returned by `_createResolver`
2481
 * @param context - Context object for scriptable/indexable options
2482
 * @param subProxy - The proxy provided for scriptable options
2483
 * @param descriptorDefaults - Defaults for descriptors
2484
 * @private
2485
 */ function _attachContext(proxy, context, subProxy, descriptorDefaults) {
2486
    const cache = {
2487
        _cacheable: false,
2488
        _proxy: proxy,
2489
        _context: context,
2490
        _subProxy: subProxy,
2491
        _stack: new Set(),
2492
        _descriptors: _descriptors(proxy, descriptorDefaults),
2493
        setContext: (ctx)=>_attachContext(proxy, ctx, subProxy, descriptorDefaults),
2494
        override: (scope)=>_attachContext(proxy.override(scope), context, subProxy, descriptorDefaults)
2495
    };
2496
    return new Proxy(cache, {
2497
        /**
2498
     * A trap for the delete operator.
2499
     */ deleteProperty (target, prop) {
2500
            delete target[prop]; // remove from cache
2501
            delete proxy[prop]; // remove from proxy
2502
            return true;
2503
        },
2504
        /**
2505
     * A trap for getting property values.
2506
     */ get (target, prop, receiver) {
2507
            return _cached(target, prop, ()=>_resolveWithContext(target, prop, receiver));
2508
        },
2509
        /**
2510
     * A trap for Object.getOwnPropertyDescriptor.
2511
     * Also used by Object.hasOwnProperty.
2512
     */ getOwnPropertyDescriptor (target, prop) {
2513
            return target._descriptors.allKeys ? Reflect.has(proxy, prop) ? {
2514
                enumerable: true,
2515
                configurable: true
2516
            } : undefined : Reflect.getOwnPropertyDescriptor(proxy, prop);
2517
        },
2518
        /**
2519
     * A trap for Object.getPrototypeOf.
2520
     */ getPrototypeOf () {
2521
            return Reflect.getPrototypeOf(proxy);
2522
        },
2523
        /**
2524
     * A trap for the in operator.
2525
     */ has (target, prop) {
2526
            return Reflect.has(proxy, prop);
2527
        },
2528
        /**
2529
     * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.
2530
     */ ownKeys () {
2531
            return Reflect.ownKeys(proxy);
2532
        },
2533
        /**
2534
     * A trap for setting property values.
2535
     */ set (target, prop, value) {
2536
            proxy[prop] = value; // set to proxy
2537
            delete target[prop]; // remove from cache
2538
            return true;
2539
        }
2540
    });
2541
}
2542
/**
2543
 * @private
2544
 */ function _descriptors(proxy, defaults = {
2545
    scriptable: true,
2546
    indexable: true
2547
}) {
2548
    const { _scriptable =defaults.scriptable , _indexable =defaults.indexable , _allKeys =defaults.allKeys  } = proxy;
2549
    return {
2550
        allKeys: _allKeys,
2551
        scriptable: _scriptable,
2552
        indexable: _indexable,
2553
        isScriptable: isFunction(_scriptable) ? _scriptable : ()=>_scriptable,
2554
        isIndexable: isFunction(_indexable) ? _indexable : ()=>_indexable
2555
    };
2556
}
2557
const readKey = (prefix, name)=>prefix ? prefix + _capitalize(name) : name;
2558
const needsSubResolver = (prop, value)=>isObject(value) && prop !== 'adapters' && (Object.getPrototypeOf(value) === null || value.constructor === Object);
2559
function _cached(target, prop, resolve) {
2560
    if (Object.prototype.hasOwnProperty.call(target, prop)) {
2561
        return target[prop];
2562
    }
2563
    const value = resolve();
2564
    // cache the resolved value
2565
    target[prop] = value;
2566
    return value;
2567
}
2568
function _resolveWithContext(target, prop, receiver) {
2569
    const { _proxy , _context , _subProxy , _descriptors: descriptors  } = target;
2570
    let value = _proxy[prop]; // resolve from proxy
2571
    // resolve with context
2572
    if (isFunction(value) && descriptors.isScriptable(prop)) {
2573
        value = _resolveScriptable(prop, value, target, receiver);
2574
    }
2575
    if (isArray(value) && value.length) {
2576
        value = _resolveArray(prop, value, target, descriptors.isIndexable);
2577
    }
2578
    if (needsSubResolver(prop, value)) {
2579
        // if the resolved value is an object, create a sub resolver for it
2580
        value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors);
2581
    }
2582
    return value;
2583
}
2584
function _resolveScriptable(prop, getValue, target, receiver) {
2585
    const { _proxy , _context , _subProxy , _stack  } = target;
2586
    if (_stack.has(prop)) {
2587
        throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop);
2588
    }
2589
    _stack.add(prop);
2590
    let value = getValue(_context, _subProxy || receiver);
2591
    _stack.delete(prop);
2592
    if (needsSubResolver(prop, value)) {
2593
        // When scriptable option returns an object, create a resolver on that.
2594
        value = createSubResolver(_proxy._scopes, _proxy, prop, value);
2595
    }
2596
    return value;
2597
}
2598
function _resolveArray(prop, value, target, isIndexable) {
2599
    const { _proxy , _context , _subProxy , _descriptors: descriptors  } = target;
2600
    if (typeof _context.index !== 'undefined' && isIndexable(prop)) {
2601
        return value[_context.index % value.length];
2602
    } else if (isObject(value[0])) {
2603
        // Array of objects, return array or resolvers
2604
        const arr = value;
2605
        const scopes = _proxy._scopes.filter((s)=>s !== arr);
2606
        value = [];
2607
        for (const item of arr){
2608
            const resolver = createSubResolver(scopes, _proxy, prop, item);
2609
            value.push(_attachContext(resolver, _context, _subProxy && _subProxy[prop], descriptors));
2610
        }
2611
    }
2612
    return value;
2613
}
2614
function resolveFallback(fallback, prop, value) {
2615
    return isFunction(fallback) ? fallback(prop, value) : fallback;
2616
}
2617
const getScope = (key, parent)=>key === true ? parent : typeof key === 'string' ? resolveObjectKey(parent, key) : undefined;
2618
function addScopes(set, parentScopes, key, parentFallback, value) {
2619
    for (const parent of parentScopes){
2620
        const scope = getScope(key, parent);
2621
        if (scope) {
2622
            set.add(scope);
2623
            const fallback = resolveFallback(scope._fallback, key, value);
2624
            if (typeof fallback !== 'undefined' && fallback !== key && fallback !== parentFallback) {
2625
                // When we reach the descriptor that defines a new _fallback, return that.
2626
                // The fallback will resume to that new scope.
2627
                return fallback;
2628
            }
2629
        } else if (scope === false && typeof parentFallback !== 'undefined' && key !== parentFallback) {
2630
            // Fallback to `false` results to `false`, when falling back to different key.
2631
            // For example `interaction` from `hover` or `plugins.tooltip` and `animation` from `animations`
2632
            return null;
2633
        }
2634
    }
2635
    return false;
2636
}
2637
function createSubResolver(parentScopes, resolver, prop, value) {
2638
    const rootScopes = resolver._rootScopes;
2639
    const fallback = resolveFallback(resolver._fallback, prop, value);
2640
    const allScopes = [
2641
        ...parentScopes,
2642
        ...rootScopes
2643
    ];
2644
    const set = new Set();
2645
    set.add(value);
2646
    let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value);
2647
    if (key === null) {
2648
        return false;
2649
    }
2650
    if (typeof fallback !== 'undefined' && fallback !== prop) {
2651
        key = addScopesFromKey(set, allScopes, fallback, key, value);
2652
        if (key === null) {
2653
            return false;
2654
        }
2655
    }
2656
    return _createResolver(Array.from(set), [
2657
        ''
2658
    ], rootScopes, fallback, ()=>subGetTarget(resolver, prop, value));
2659
}
2660
function addScopesFromKey(set, allScopes, key, fallback, item) {
2661
    while(key){
2662
        key = addScopes(set, allScopes, key, fallback, item);
2663
    }
2664
    return key;
2665
}
2666
function subGetTarget(resolver, prop, value) {
2667
    const parent = resolver._getTarget();
2668
    if (!(prop in parent)) {
2669
        parent[prop] = {};
2670
    }
2671
    const target = parent[prop];
2672
    if (isArray(target) && isObject(value)) {
2673
        // For array of objects, the object is used to store updated values
2674
        return value;
2675
    }
2676
    return target || {};
2677
}
2678
function _resolveWithPrefixes(prop, prefixes, scopes, proxy) {
2679
    let value;
2680
    for (const prefix of prefixes){
2681
        value = _resolve(readKey(prefix, prop), scopes);
2682
        if (typeof value !== 'undefined') {
2683
            return needsSubResolver(prop, value) ? createSubResolver(scopes, proxy, prop, value) : value;
2684
        }
2685
    }
2686
}
2687
function _resolve(key, scopes) {
2688
    for (const scope of scopes){
2689
        if (!scope) {
2690
            continue;
2691
        }
2692
        const value = scope[key];
2693
        if (typeof value !== 'undefined') {
2694
            return value;
2695
        }
2696
    }
2697
}
2698
function getKeysFromAllScopes(target) {
2699
    let keys = target._keys;
2700
    if (!keys) {
2701
        keys = target._keys = resolveKeysFromAllScopes(target._scopes);
2702
    }
2703
    return keys;
2704
}
2705
function resolveKeysFromAllScopes(scopes) {
2706
    const set = new Set();
2707
    for (const scope of scopes){
2708
        for (const key of Object.keys(scope).filter((k)=>!k.startsWith('_'))){
2709
            set.add(key);
2710
        }
2711
    }
2712
    return Array.from(set);
2713
}
2714
function _parseObjectDataRadialScale(meta, data, start, count) {
2715
    const { iScale  } = meta;
2716
    const { key ='r'  } = this._parsing;
2717
    const parsed = new Array(count);
2718
    let i, ilen, index, item;
2719
    for(i = 0, ilen = count; i < ilen; ++i){
2720
        index = i + start;
2721
        item = data[index];
2722
        parsed[i] = {
2723
            r: iScale.parse(resolveObjectKey(item, key), index)
2724
        };
2725
    }
2726
    return parsed;
2727
}
2728
 
2729
const EPSILON = Number.EPSILON || 1e-14;
2730
const getPoint = (points, i)=>i < points.length && !points[i].skip && points[i];
2731
const getValueAxis = (indexAxis)=>indexAxis === 'x' ? 'y' : 'x';
2732
function splineCurve(firstPoint, middlePoint, afterPoint, t) {
2733
    // Props to Rob Spencer at scaled innovation for his post on splining between points
2734
    // http://scaledinnovation.com/analytics/splines/aboutSplines.html
2735
    // This function must also respect "skipped" points
2736
    const previous = firstPoint.skip ? middlePoint : firstPoint;
2737
    const current = middlePoint;
2738
    const next = afterPoint.skip ? middlePoint : afterPoint;
2739
    const d01 = distanceBetweenPoints(current, previous);
2740
    const d12 = distanceBetweenPoints(next, current);
2741
    let s01 = d01 / (d01 + d12);
2742
    let s12 = d12 / (d01 + d12);
2743
    // If all points are the same, s01 & s02 will be inf
2744
    s01 = isNaN(s01) ? 0 : s01;
2745
    s12 = isNaN(s12) ? 0 : s12;
2746
    const fa = t * s01; // scaling factor for triangle Ta
2747
    const fb = t * s12;
2748
    return {
2749
        previous: {
2750
            x: current.x - fa * (next.x - previous.x),
2751
            y: current.y - fa * (next.y - previous.y)
2752
        },
2753
        next: {
2754
            x: current.x + fb * (next.x - previous.x),
2755
            y: current.y + fb * (next.y - previous.y)
2756
        }
2757
    };
2758
}
2759
/**
2760
 * Adjust tangents to ensure monotonic properties
2761
 */ function monotoneAdjust(points, deltaK, mK) {
2762
    const pointsLen = points.length;
2763
    let alphaK, betaK, tauK, squaredMagnitude, pointCurrent;
2764
    let pointAfter = getPoint(points, 0);
2765
    for(let i = 0; i < pointsLen - 1; ++i){
2766
        pointCurrent = pointAfter;
2767
        pointAfter = getPoint(points, i + 1);
2768
        if (!pointCurrent || !pointAfter) {
2769
            continue;
2770
        }
2771
        if (almostEquals(deltaK[i], 0, EPSILON)) {
2772
            mK[i] = mK[i + 1] = 0;
2773
            continue;
2774
        }
2775
        alphaK = mK[i] / deltaK[i];
2776
        betaK = mK[i + 1] / deltaK[i];
2777
        squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
2778
        if (squaredMagnitude <= 9) {
2779
            continue;
2780
        }
2781
        tauK = 3 / Math.sqrt(squaredMagnitude);
2782
        mK[i] = alphaK * tauK * deltaK[i];
2783
        mK[i + 1] = betaK * tauK * deltaK[i];
2784
    }
2785
}
2786
function monotoneCompute(points, mK, indexAxis = 'x') {
2787
    const valueAxis = getValueAxis(indexAxis);
2788
    const pointsLen = points.length;
2789
    let delta, pointBefore, pointCurrent;
2790
    let pointAfter = getPoint(points, 0);
2791
    for(let i = 0; i < pointsLen; ++i){
2792
        pointBefore = pointCurrent;
2793
        pointCurrent = pointAfter;
2794
        pointAfter = getPoint(points, i + 1);
2795
        if (!pointCurrent) {
2796
            continue;
2797
        }
2798
        const iPixel = pointCurrent[indexAxis];
2799
        const vPixel = pointCurrent[valueAxis];
2800
        if (pointBefore) {
2801
            delta = (iPixel - pointBefore[indexAxis]) / 3;
2802
            pointCurrent[`cp1${indexAxis}`] = iPixel - delta;
2803
            pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i];
2804
        }
2805
        if (pointAfter) {
2806
            delta = (pointAfter[indexAxis] - iPixel) / 3;
2807
            pointCurrent[`cp2${indexAxis}`] = iPixel + delta;
2808
            pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i];
2809
        }
2810
    }
2811
}
2812
/**
2813
 * This function calculates Bézier control points in a similar way than |splineCurve|,
2814
 * but preserves monotonicity of the provided data and ensures no local extremums are added
2815
 * between the dataset discrete points due to the interpolation.
2816
 * See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
2817
 */ function splineCurveMonotone(points, indexAxis = 'x') {
2818
    const valueAxis = getValueAxis(indexAxis);
2819
    const pointsLen = points.length;
2820
    const deltaK = Array(pointsLen).fill(0);
2821
    const mK = Array(pointsLen);
2822
    // Calculate slopes (deltaK) and initialize tangents (mK)
2823
    let i, pointBefore, pointCurrent;
2824
    let pointAfter = getPoint(points, 0);
2825
    for(i = 0; i < pointsLen; ++i){
2826
        pointBefore = pointCurrent;
2827
        pointCurrent = pointAfter;
2828
        pointAfter = getPoint(points, i + 1);
2829
        if (!pointCurrent) {
2830
            continue;
2831
        }
2832
        if (pointAfter) {
2833
            const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis];
2834
            // In the case of two points that appear at the same x pixel, slopeDeltaX is 0
2835
            deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0;
2836
        }
2837
        mK[i] = !pointBefore ? deltaK[i] : !pointAfter ? deltaK[i - 1] : sign(deltaK[i - 1]) !== sign(deltaK[i]) ? 0 : (deltaK[i - 1] + deltaK[i]) / 2;
2838
    }
2839
    monotoneAdjust(points, deltaK, mK);
2840
    monotoneCompute(points, mK, indexAxis);
2841
}
2842
function capControlPoint(pt, min, max) {
2843
    return Math.max(Math.min(pt, max), min);
2844
}
2845
function capBezierPoints(points, area) {
2846
    let i, ilen, point, inArea, inAreaPrev;
2847
    let inAreaNext = _isPointInArea(points[0], area);
2848
    for(i = 0, ilen = points.length; i < ilen; ++i){
2849
        inAreaPrev = inArea;
2850
        inArea = inAreaNext;
2851
        inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area);
2852
        if (!inArea) {
2853
            continue;
2854
        }
2855
        point = points[i];
2856
        if (inAreaPrev) {
2857
            point.cp1x = capControlPoint(point.cp1x, area.left, area.right);
2858
            point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom);
2859
        }
2860
        if (inAreaNext) {
2861
            point.cp2x = capControlPoint(point.cp2x, area.left, area.right);
2862
            point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom);
2863
        }
2864
    }
2865
}
2866
/**
2867
 * @private
2868
 */ function _updateBezierControlPoints(points, options, area, loop, indexAxis) {
2869
    let i, ilen, point, controlPoints;
2870
    // Only consider points that are drawn in case the spanGaps option is used
2871
    if (options.spanGaps) {
2872
        points = points.filter((pt)=>!pt.skip);
2873
    }
2874
    if (options.cubicInterpolationMode === 'monotone') {
2875
        splineCurveMonotone(points, indexAxis);
2876
    } else {
2877
        let prev = loop ? points[points.length - 1] : points[0];
2878
        for(i = 0, ilen = points.length; i < ilen; ++i){
2879
            point = points[i];
2880
            controlPoints = splineCurve(prev, point, points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen], options.tension);
2881
            point.cp1x = controlPoints.previous.x;
2882
            point.cp1y = controlPoints.previous.y;
2883
            point.cp2x = controlPoints.next.x;
2884
            point.cp2y = controlPoints.next.y;
2885
            prev = point;
2886
        }
2887
    }
2888
    if (options.capBezierPoints) {
2889
        capBezierPoints(points, area);
2890
    }
2891
}
2892
 
2893
const atEdge = (t)=>t === 0 || t === 1;
2894
const elasticIn = (t, s, p)=>-(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));
2895
const elasticOut = (t, s, p)=>Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;
2896
/**
2897
 * Easing functions adapted from Robert Penner's easing equations.
2898
 * @namespace Chart.helpers.easing.effects
2899
 * @see http://www.robertpenner.com/easing/
2900
 */ const effects = {
2901
    linear: (t)=>t,
2902
    easeInQuad: (t)=>t * t,
2903
    easeOutQuad: (t)=>-t * (t - 2),
2904
    easeInOutQuad: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1),
2905
    easeInCubic: (t)=>t * t * t,
2906
    easeOutCubic: (t)=>(t -= 1) * t * t + 1,
2907
    easeInOutCubic: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2),
2908
    easeInQuart: (t)=>t * t * t * t,
2909
    easeOutQuart: (t)=>-((t -= 1) * t * t * t - 1),
2910
    easeInOutQuart: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t : -0.5 * ((t -= 2) * t * t * t - 2),
2911
    easeInQuint: (t)=>t * t * t * t * t,
2912
    easeOutQuint: (t)=>(t -= 1) * t * t * t * t + 1,
2913
    easeInOutQuint: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2),
2914
    easeInSine: (t)=>-Math.cos(t * HALF_PI) + 1,
2915
    easeOutSine: (t)=>Math.sin(t * HALF_PI),
2916
    easeInOutSine: (t)=>-0.5 * (Math.cos(PI * t) - 1),
2917
    easeInExpo: (t)=>t === 0 ? 0 : Math.pow(2, 10 * (t - 1)),
2918
    easeOutExpo: (t)=>t === 1 ? 1 : -Math.pow(2, -10 * t) + 1,
2919
    easeInOutExpo: (t)=>atEdge(t) ? t : t < 0.5 ? 0.5 * Math.pow(2, 10 * (t * 2 - 1)) : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2),
2920
    easeInCirc: (t)=>t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1),
2921
    easeOutCirc: (t)=>Math.sqrt(1 - (t -= 1) * t),
2922
    easeInOutCirc: (t)=>(t /= 0.5) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1),
2923
    easeInElastic: (t)=>atEdge(t) ? t : elasticIn(t, 0.075, 0.3),
2924
    easeOutElastic: (t)=>atEdge(t) ? t : elasticOut(t, 0.075, 0.3),
2925
    easeInOutElastic (t) {
2926
        const s = 0.1125;
2927
        const p = 0.45;
2928
        return atEdge(t) ? t : t < 0.5 ? 0.5 * elasticIn(t * 2, s, p) : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p);
2929
    },
2930
    easeInBack (t) {
2931
        const s = 1.70158;
2932
        return t * t * ((s + 1) * t - s);
2933
    },
2934
    easeOutBack (t) {
2935
        const s = 1.70158;
2936
        return (t -= 1) * t * ((s + 1) * t + s) + 1;
2937
    },
2938
    easeInOutBack (t) {
2939
        let s = 1.70158;
2940
        if ((t /= 0.5) < 1) {
2941
            return 0.5 * (t * t * (((s *= 1.525) + 1) * t - s));
2942
        }
2943
        return 0.5 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2);
2944
    },
2945
    easeInBounce: (t)=>1 - effects.easeOutBounce(1 - t),
2946
    easeOutBounce (t) {
2947
        const m = 7.5625;
2948
        const d = 2.75;
2949
        if (t < 1 / d) {
2950
            return m * t * t;
2951
        }
2952
        if (t < 2 / d) {
2953
            return m * (t -= 1.5 / d) * t + 0.75;
2954
        }
2955
        if (t < 2.5 / d) {
2956
            return m * (t -= 2.25 / d) * t + 0.9375;
2957
        }
2958
        return m * (t -= 2.625 / d) * t + 0.984375;
2959
    },
2960
    easeInOutBounce: (t)=>t < 0.5 ? effects.easeInBounce(t * 2) * 0.5 : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5
2961
};
2962
 
2963
/**
2964
 * @private
2965
 */ function _pointInLine(p1, p2, t, mode) {
2966
    return {
2967
        x: p1.x + t * (p2.x - p1.x),
2968
        y: p1.y + t * (p2.y - p1.y)
2969
    };
2970
}
2971
/**
2972
 * @private
2973
 */ function _steppedInterpolation(p1, p2, t, mode) {
2974
    return {
2975
        x: p1.x + t * (p2.x - p1.x),
2976
        y: mode === 'middle' ? t < 0.5 ? p1.y : p2.y : mode === 'after' ? t < 1 ? p1.y : p2.y : t > 0 ? p2.y : p1.y
2977
    };
2978
}
2979
/**
2980
 * @private
2981
 */ function _bezierInterpolation(p1, p2, t, mode) {
2982
    const cp1 = {
2983
        x: p1.cp2x,
2984
        y: p1.cp2y
2985
    };
2986
    const cp2 = {
2987
        x: p2.cp1x,
2988
        y: p2.cp1y
2989
    };
2990
    const a = _pointInLine(p1, cp1, t);
2991
    const b = _pointInLine(cp1, cp2, t);
2992
    const c = _pointInLine(cp2, p2, t);
2993
    const d = _pointInLine(a, b, t);
2994
    const e = _pointInLine(b, c, t);
2995
    return _pointInLine(d, e, t);
2996
}
2997
 
2998
const LINE_HEIGHT = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/;
2999
const FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;
3000
/**
3001
 * @alias Chart.helpers.options
3002
 * @namespace
3003
 */ /**
3004
 * Converts the given line height `value` in pixels for a specific font `size`.
3005
 * @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
3006
 * @param size - The font size (in pixels) used to resolve relative `value`.
3007
 * @returns The effective line height in pixels (size * 1.2 if value is invalid).
3008
 * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
3009
 * @since 2.7.0
3010
 */ function toLineHeight(value, size) {
3011
    const matches = ('' + value).match(LINE_HEIGHT);
3012
    if (!matches || matches[1] === 'normal') {
3013
        return size * 1.2;
3014
    }
3015
    value = +matches[2];
3016
    switch(matches[3]){
3017
        case 'px':
3018
            return value;
3019
        case '%':
3020
            value /= 100;
3021
            break;
3022
    }
3023
    return size * value;
3024
}
3025
const numberOrZero = (v)=>+v || 0;
3026
function _readValueToProps(value, props) {
3027
    const ret = {};
3028
    const objProps = isObject(props);
3029
    const keys = objProps ? Object.keys(props) : props;
3030
    const read = isObject(value) ? objProps ? (prop)=>valueOrDefault(value[prop], value[props[prop]]) : (prop)=>value[prop] : ()=>value;
3031
    for (const prop of keys){
3032
        ret[prop] = numberOrZero(read(prop));
3033
    }
3034
    return ret;
3035
}
3036
/**
3037
 * Converts the given value into a TRBL object.
3038
 * @param value - If a number, set the value to all TRBL component,
3039
 *  else, if an object, use defined properties and sets undefined ones to 0.
3040
 *  x / y are shorthands for same value for left/right and top/bottom.
3041
 * @returns The padding values (top, right, bottom, left)
3042
 * @since 3.0.0
3043
 */ function toTRBL(value) {
3044
    return _readValueToProps(value, {
3045
        top: 'y',
3046
        right: 'x',
3047
        bottom: 'y',
3048
        left: 'x'
3049
    });
3050
}
3051
/**
3052
 * Converts the given value into a TRBL corners object (similar with css border-radius).
3053
 * @param value - If a number, set the value to all TRBL corner components,
3054
 *  else, if an object, use defined properties and sets undefined ones to 0.
3055
 * @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight)
3056
 * @since 3.0.0
3057
 */ function toTRBLCorners(value) {
3058
    return _readValueToProps(value, [
3059
        'topLeft',
3060
        'topRight',
3061
        'bottomLeft',
3062
        'bottomRight'
3063
    ]);
3064
}
3065
/**
3066
 * Converts the given value into a padding object with pre-computed width/height.
3067
 * @param value - If a number, set the value to all TRBL component,
3068
 *  else, if an object, use defined properties and sets undefined ones to 0.
3069
 *  x / y are shorthands for same value for left/right and top/bottom.
3070
 * @returns The padding values (top, right, bottom, left, width, height)
3071
 * @since 2.7.0
3072
 */ function toPadding(value) {
3073
    const obj = toTRBL(value);
3074
    obj.width = obj.left + obj.right;
3075
    obj.height = obj.top + obj.bottom;
3076
    return obj;
3077
}
3078
/**
3079
 * Parses font options and returns the font object.
3080
 * @param options - A object that contains font options to be parsed.
3081
 * @param fallback - A object that contains fallback font options.
3082
 * @return The font object.
3083
 * @private
3084
 */ function toFont(options, fallback) {
3085
    options = options || {};
3086
    fallback = fallback || defaults.font;
3087
    let size = valueOrDefault(options.size, fallback.size);
3088
    if (typeof size === 'string') {
3089
        size = parseInt(size, 10);
3090
    }
3091
    let style = valueOrDefault(options.style, fallback.style);
3092
    if (style && !('' + style).match(FONT_STYLE)) {
3093
        console.warn('Invalid font style specified: "' + style + '"');
3094
        style = undefined;
3095
    }
3096
    const font = {
3097
        family: valueOrDefault(options.family, fallback.family),
3098
        lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size),
3099
        size,
3100
        style,
3101
        weight: valueOrDefault(options.weight, fallback.weight),
3102
        string: ''
3103
    };
3104
    font.string = toFontString(font);
3105
    return font;
3106
}
3107
/**
3108
 * Evaluates the given `inputs` sequentially and returns the first defined value.
3109
 * @param inputs - An array of values, falling back to the last value.
3110
 * @param context - If defined and the current value is a function, the value
3111
 * is called with `context` as first argument and the result becomes the new input.
3112
 * @param index - If defined and the current value is an array, the value
3113
 * at `index` become the new input.
3114
 * @param info - object to return information about resolution in
3115
 * @param info.cacheable - Will be set to `false` if option is not cacheable.
3116
 * @since 2.7.0
3117
 */ function resolve(inputs, context, index, info) {
3118
    let cacheable = true;
3119
    let i, ilen, value;
3120
    for(i = 0, ilen = inputs.length; i < ilen; ++i){
3121
        value = inputs[i];
3122
        if (value === undefined) {
3123
            continue;
3124
        }
3125
        if (context !== undefined && typeof value === 'function') {
3126
            value = value(context);
3127
            cacheable = false;
3128
        }
3129
        if (index !== undefined && isArray(value)) {
3130
            value = value[index % value.length];
3131
            cacheable = false;
3132
        }
3133
        if (value !== undefined) {
3134
            if (info && !cacheable) {
3135
                info.cacheable = false;
3136
            }
3137
            return value;
3138
        }
3139
    }
3140
}
3141
/**
3142
 * @param minmax
3143
 * @param grace
3144
 * @param beginAtZero
3145
 * @private
3146
 */ function _addGrace(minmax, grace, beginAtZero) {
3147
    const { min , max  } = minmax;
3148
    const change = toDimension(grace, (max - min) / 2);
3149
    const keepZero = (value, add)=>beginAtZero && value === 0 ? 0 : value + add;
3150
    return {
3151
        min: keepZero(min, -Math.abs(change)),
3152
        max: keepZero(max, change)
3153
    };
3154
}
3155
function createContext(parentContext, context) {
3156
    return Object.assign(Object.create(parentContext), context);
3157
}
3158
 
3159
const getRightToLeftAdapter = function(rectX, width) {
3160
    return {
3161
        x (x) {
3162
            return rectX + rectX + width - x;
3163
        },
3164
        setWidth (w) {
3165
            width = w;
3166
        },
3167
        textAlign (align) {
3168
            if (align === 'center') {
3169
                return align;
3170
            }
3171
            return align === 'right' ? 'left' : 'right';
3172
        },
3173
        xPlus (x, value) {
3174
            return x - value;
3175
        },
3176
        leftForLtr (x, itemWidth) {
3177
            return x - itemWidth;
3178
        }
3179
    };
3180
};
3181
const getLeftToRightAdapter = function() {
3182
    return {
3183
        x (x) {
3184
            return x;
3185
        },
3186
        setWidth (w) {},
3187
        textAlign (align) {
3188
            return align;
3189
        },
3190
        xPlus (x, value) {
3191
            return x + value;
3192
        },
3193
        leftForLtr (x, _itemWidth) {
3194
            return x;
3195
        }
3196
    };
3197
};
3198
function getRtlAdapter(rtl, rectX, width) {
3199
    return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter();
3200
}
3201
function overrideTextDirection(ctx, direction) {
3202
    let style, original;
3203
    if (direction === 'ltr' || direction === 'rtl') {
3204
        style = ctx.canvas.style;
3205
        original = [
3206
            style.getPropertyValue('direction'),
3207
            style.getPropertyPriority('direction')
3208
        ];
3209
        style.setProperty('direction', direction, 'important');
3210
        ctx.prevTextDirection = original;
3211
    }
3212
}
3213
function restoreTextDirection(ctx, original) {
3214
    if (original !== undefined) {
3215
        delete ctx.prevTextDirection;
3216
        ctx.canvas.style.setProperty('direction', original[0], original[1]);
3217
    }
3218
}
3219
 
3220
function propertyFn(property) {
3221
    if (property === 'angle') {
3222
        return {
3223
            between: _angleBetween,
3224
            compare: _angleDiff,
3225
            normalize: _normalizeAngle
3226
        };
3227
    }
3228
    return {
3229
        between: _isBetween,
3230
        compare: (a, b)=>a - b,
3231
        normalize: (x)=>x
3232
    };
3233
}
3234
function normalizeSegment({ start , end , count , loop , style  }) {
3235
    return {
3236
        start: start % count,
3237
        end: end % count,
3238
        loop: loop && (end - start + 1) % count === 0,
3239
        style
3240
    };
3241
}
3242
function getSegment(segment, points, bounds) {
3243
    const { property , start: startBound , end: endBound  } = bounds;
3244
    const { between , normalize  } = propertyFn(property);
3245
    const count = points.length;
3246
    let { start , end , loop  } = segment;
3247
    let i, ilen;
3248
    if (loop) {
3249
        start += count;
3250
        end += count;
3251
        for(i = 0, ilen = count; i < ilen; ++i){
3252
            if (!between(normalize(points[start % count][property]), startBound, endBound)) {
3253
                break;
3254
            }
3255
            start--;
3256
            end--;
3257
        }
3258
        start %= count;
3259
        end %= count;
3260
    }
3261
    if (end < start) {
3262
        end += count;
3263
    }
3264
    return {
3265
        start,
3266
        end,
3267
        loop,
3268
        style: segment.style
3269
    };
3270
}
3271
 function _boundSegment(segment, points, bounds) {
3272
    if (!bounds) {
3273
        return [
3274
            segment
3275
        ];
3276
    }
3277
    const { property , start: startBound , end: endBound  } = bounds;
3278
    const count = points.length;
3279
    const { compare , between , normalize  } = propertyFn(property);
3280
    const { start , end , loop , style  } = getSegment(segment, points, bounds);
3281
    const result = [];
3282
    let inside = false;
3283
    let subStart = null;
3284
    let value, point, prevValue;
3285
    const startIsBefore = ()=>between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0;
3286
    const endIsBefore = ()=>compare(endBound, value) === 0 || between(endBound, prevValue, value);
3287
    const shouldStart = ()=>inside || startIsBefore();
3288
    const shouldStop = ()=>!inside || endIsBefore();
3289
    for(let i = start, prev = start; i <= end; ++i){
3290
        point = points[i % count];
3291
        if (point.skip) {
3292
            continue;
3293
        }
3294
        value = normalize(point[property]);
3295
        if (value === prevValue) {
3296
            continue;
3297
        }
3298
        inside = between(value, startBound, endBound);
3299
        if (subStart === null && shouldStart()) {
3300
            subStart = compare(value, startBound) === 0 ? i : prev;
3301
        }
3302
        if (subStart !== null && shouldStop()) {
3303
            result.push(normalizeSegment({
3304
                start: subStart,
3305
                end: i,
3306
                loop,
3307
                count,
3308
                style
3309
            }));
3310
            subStart = null;
3311
        }
3312
        prev = i;
3313
        prevValue = value;
3314
    }
3315
    if (subStart !== null) {
3316
        result.push(normalizeSegment({
3317
            start: subStart,
3318
            end,
3319
            loop,
3320
            count,
3321
            style
3322
        }));
3323
    }
3324
    return result;
3325
}
3326
 function _boundSegments(line, bounds) {
3327
    const result = [];
3328
    const segments = line.segments;
3329
    for(let i = 0; i < segments.length; i++){
3330
        const sub = _boundSegment(segments[i], line.points, bounds);
3331
        if (sub.length) {
3332
            result.push(...sub);
3333
        }
3334
    }
3335
    return result;
3336
}
3337
 function findStartAndEnd(points, count, loop, spanGaps) {
3338
    let start = 0;
3339
    let end = count - 1;
3340
    if (loop && !spanGaps) {
3341
        while(start < count && !points[start].skip){
3342
            start++;
3343
        }
3344
    }
3345
    while(start < count && points[start].skip){
3346
        start++;
3347
    }
3348
    start %= count;
3349
    if (loop) {
3350
        end += start;
3351
    }
3352
    while(end > start && points[end % count].skip){
3353
        end--;
3354
    }
3355
    end %= count;
3356
    return {
3357
        start,
3358
        end
3359
    };
3360
}
3361
 function solidSegments(points, start, max, loop) {
3362
    const count = points.length;
3363
    const result = [];
3364
    let last = start;
3365
    let prev = points[start];
3366
    let end;
3367
    for(end = start + 1; end <= max; ++end){
3368
        const cur = points[end % count];
3369
        if (cur.skip || cur.stop) {
3370
            if (!prev.skip) {
3371
                loop = false;
3372
                result.push({
3373
                    start: start % count,
3374
                    end: (end - 1) % count,
3375
                    loop
3376
                });
3377
                start = last = cur.stop ? end : null;
3378
            }
3379
        } else {
3380
            last = end;
3381
            if (prev.skip) {
3382
                start = end;
3383
            }
3384
        }
3385
        prev = cur;
3386
    }
3387
    if (last !== null) {
3388
        result.push({
3389
            start: start % count,
3390
            end: last % count,
3391
            loop
3392
        });
3393
    }
3394
    return result;
3395
}
3396
 function _computeSegments(line, segmentOptions) {
3397
    const points = line.points;
3398
    const spanGaps = line.options.spanGaps;
3399
    const count = points.length;
3400
    if (!count) {
3401
        return [];
3402
    }
3403
    const loop = !!line._loop;
3404
    const { start , end  } = findStartAndEnd(points, count, loop, spanGaps);
3405
    if (spanGaps === true) {
3406
        return splitByStyles(line, [
3407
            {
3408
                start,
3409
                end,
3410
                loop
3411
            }
3412
        ], points, segmentOptions);
3413
    }
3414
    const max = end < start ? end + count : end;
3415
    const completeLoop = !!line._fullLoop && start === 0 && end === count - 1;
3416
    return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions);
3417
}
3418
 function splitByStyles(line, segments, points, segmentOptions) {
3419
    if (!segmentOptions || !segmentOptions.setContext || !points) {
3420
        return segments;
3421
    }
3422
    return doSplitByStyles(line, segments, points, segmentOptions);
3423
}
3424
 function doSplitByStyles(line, segments, points, segmentOptions) {
3425
    const chartContext = line._chart.getContext();
3426
    const baseStyle = readStyle(line.options);
3427
    const { _datasetIndex: datasetIndex , options: { spanGaps  }  } = line;
3428
    const count = points.length;
3429
    const result = [];
3430
    let prevStyle = baseStyle;
3431
    let start = segments[0].start;
3432
    let i = start;
3433
    function addStyle(s, e, l, st) {
3434
        const dir = spanGaps ? -1 : 1;
3435
        if (s === e) {
3436
            return;
3437
        }
3438
        s += count;
3439
        while(points[s % count].skip){
3440
            s -= dir;
3441
        }
3442
        while(points[e % count].skip){
3443
            e += dir;
3444
        }
3445
        if (s % count !== e % count) {
3446
            result.push({
3447
                start: s % count,
3448
                end: e % count,
3449
                loop: l,
3450
                style: st
3451
            });
3452
            prevStyle = st;
3453
            start = e % count;
3454
        }
3455
    }
3456
    for (const segment of segments){
3457
        start = spanGaps ? start : segment.start;
3458
        let prev = points[start % count];
3459
        let style;
3460
        for(i = start + 1; i <= segment.end; i++){
3461
            const pt = points[i % count];
3462
            style = readStyle(segmentOptions.setContext(createContext(chartContext, {
3463
                type: 'segment',
3464
                p0: prev,
3465
                p1: pt,
3466
                p0DataIndex: (i - 1) % count,
3467
                p1DataIndex: i % count,
3468
                datasetIndex
3469
            })));
3470
            if (styleChanged(style, prevStyle)) {
3471
                addStyle(start, i - 1, segment.loop, prevStyle);
3472
            }
3473
            prev = pt;
3474
            prevStyle = style;
3475
        }
3476
        if (start < i - 1) {
3477
            addStyle(start, i - 1, segment.loop, prevStyle);
3478
        }
3479
    }
3480
    return result;
3481
}
3482
function readStyle(options) {
3483
    return {
3484
        backgroundColor: options.backgroundColor,
3485
        borderCapStyle: options.borderCapStyle,
3486
        borderDash: options.borderDash,
3487
        borderDashOffset: options.borderDashOffset,
3488
        borderJoinStyle: options.borderJoinStyle,
3489
        borderWidth: options.borderWidth,
3490
        borderColor: options.borderColor
3491
    };
3492
}
3493
function styleChanged(style, prevStyle) {
3494
    if (!prevStyle) {
3495
        return false;
3496
    }
3497
    const cache = [];
3498
    const replacer = function(key, value) {
3499
        if (!isPatternOrGradient(value)) {
3500
            return value;
3501
        }
3502
        if (!cache.includes(value)) {
3503
            cache.push(value);
3504
        }
3505
        return cache.indexOf(value);
3506
    };
3507
    return JSON.stringify(style, replacer) !== JSON.stringify(prevStyle, replacer);
3508
}
3509
 
3510
var helpers = /*#__PURE__*/Object.freeze({
3511
__proto__: null,
3512
HALF_PI: HALF_PI,
3513
INFINITY: INFINITY,
3514
PI: PI,
3515
PITAU: PITAU,
3516
QUARTER_PI: QUARTER_PI,
3517
RAD_PER_DEG: RAD_PER_DEG,
3518
TAU: TAU,
3519
TWO_THIRDS_PI: TWO_THIRDS_PI,
3520
_addGrace: _addGrace,
3521
_alignPixel: _alignPixel,
3522
_alignStartEnd: _alignStartEnd,
3523
_angleBetween: _angleBetween,
3524
_angleDiff: _angleDiff,
3525
_arrayUnique: _arrayUnique,
3526
_attachContext: _attachContext,
3527
_bezierCurveTo: _bezierCurveTo,
3528
_bezierInterpolation: _bezierInterpolation,
3529
_boundSegment: _boundSegment,
3530
_boundSegments: _boundSegments,
3531
_capitalize: _capitalize,
3532
_computeSegments: _computeSegments,
3533
_createResolver: _createResolver,
3534
_decimalPlaces: _decimalPlaces,
3535
_deprecated: _deprecated,
3536
_descriptors: _descriptors,
3537
_elementsEqual: _elementsEqual,
3538
_factorize: _factorize,
3539
_filterBetween: _filterBetween,
3540
_getParentNode: _getParentNode,
3541
_getStartAndCountOfVisiblePoints: _getStartAndCountOfVisiblePoints,
3542
_int16Range: _int16Range,
3543
_isBetween: _isBetween,
3544
_isClickEvent: _isClickEvent,
3545
_isDomSupported: _isDomSupported,
3546
_isPointInArea: _isPointInArea,
3547
_limitValue: _limitValue,
3548
_longestText: _longestText,
3549
_lookup: _lookup,
3550
_lookupByKey: _lookupByKey,
3551
_measureText: _measureText,
3552
_merger: _merger,
3553
_mergerIf: _mergerIf,
3554
_normalizeAngle: _normalizeAngle,
3555
_parseObjectDataRadialScale: _parseObjectDataRadialScale,
3556
_pointInLine: _pointInLine,
3557
_readValueToProps: _readValueToProps,
3558
_rlookupByKey: _rlookupByKey,
3559
_scaleRangesChanged: _scaleRangesChanged,
3560
_setMinAndMaxByKey: _setMinAndMaxByKey,
3561
_splitKey: _splitKey,
3562
_steppedInterpolation: _steppedInterpolation,
3563
_steppedLineTo: _steppedLineTo,
3564
_textX: _textX,
3565
_toLeftRightCenter: _toLeftRightCenter,
3566
_updateBezierControlPoints: _updateBezierControlPoints,
3567
addRoundedRectPath: addRoundedRectPath,
3568
almostEquals: almostEquals,
3569
almostWhole: almostWhole,
3570
callback: callback,
3571
clearCanvas: clearCanvas,
3572
clipArea: clipArea,
3573
clone: clone$1,
3574
color: color,
3575
createContext: createContext,
3576
debounce: debounce,
3577
defined: defined,
3578
distanceBetweenPoints: distanceBetweenPoints,
3579
drawPoint: drawPoint,
3580
drawPointLegend: drawPointLegend,
3581
each: each,
3582
easingEffects: effects,
3583
finiteOrDefault: finiteOrDefault,
3584
fontString: fontString,
3585
formatNumber: formatNumber,
3586
getAngleFromPoint: getAngleFromPoint,
3587
getHoverColor: getHoverColor,
3588
getMaximumSize: getMaximumSize,
3589
getRelativePosition: getRelativePosition,
3590
getRtlAdapter: getRtlAdapter,
3591
getStyle: getStyle,
3592
isArray: isArray,
3593
isFinite: isNumberFinite,
3594
isFunction: isFunction,
3595
isNullOrUndef: isNullOrUndef,
3596
isNumber: isNumber,
3597
isObject: isObject,
3598
isPatternOrGradient: isPatternOrGradient,
3599
listenArrayEvents: listenArrayEvents,
3600
log10: log10,
3601
merge: merge,
3602
mergeIf: mergeIf,
3603
niceNum: niceNum,
3604
noop: noop,
3605
overrideTextDirection: overrideTextDirection,
3606
readUsedSize: readUsedSize,
3607
renderText: renderText,
3608
requestAnimFrame: requestAnimFrame,
3609
resolve: resolve,
3610
resolveObjectKey: resolveObjectKey,
3611
restoreTextDirection: restoreTextDirection,
3612
retinaScale: retinaScale,
3613
setsEqual: setsEqual,
3614
sign: sign,
3615
splineCurve: splineCurve,
3616
splineCurveMonotone: splineCurveMonotone,
3617
supportsEventListenerOptions: supportsEventListenerOptions,
3618
throttled: throttled,
3619
toDegrees: toDegrees,
3620
toDimension: toDimension,
3621
toFont: toFont,
3622
toFontString: toFontString,
3623
toLineHeight: toLineHeight,
3624
toPadding: toPadding,
3625
toPercentage: toPercentage,
3626
toRadians: toRadians,
3627
toTRBL: toTRBL,
3628
toTRBLCorners: toTRBLCorners,
3629
uid: uid,
3630
unclipArea: unclipArea,
3631
unlistenArrayEvents: unlistenArrayEvents,
3632
valueOrDefault: valueOrDefault
3633
});
3634
 
3635
function binarySearch(metaset, axis, value, intersect) {
3636
    const { controller , data , _sorted  } = metaset;
3637
    const iScale = controller._cachedMeta.iScale;
3638
    if (iScale && axis === iScale.axis && axis !== 'r' && _sorted && data.length) {
3639
        const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey;
3640
        if (!intersect) {
3641
            return lookupMethod(data, axis, value);
3642
        } else if (controller._sharedOptions) {
3643
            const el = data[0];
3644
            const range = typeof el.getRange === 'function' && el.getRange(axis);
3645
            if (range) {
3646
                const start = lookupMethod(data, axis, value - range);
3647
                const end = lookupMethod(data, axis, value + range);
3648
                return {
3649
                    lo: start.lo,
3650
                    hi: end.hi
3651
                };
3652
            }
3653
        }
3654
    }
3655
    return {
3656
        lo: 0,
3657
        hi: data.length - 1
3658
    };
3659
}
3660
 function evaluateInteractionItems(chart, axis, position, handler, intersect) {
3661
    const metasets = chart.getSortedVisibleDatasetMetas();
3662
    const value = position[axis];
3663
    for(let i = 0, ilen = metasets.length; i < ilen; ++i){
3664
        const { index , data  } = metasets[i];
3665
        const { lo , hi  } = binarySearch(metasets[i], axis, value, intersect);
3666
        for(let j = lo; j <= hi; ++j){
3667
            const element = data[j];
3668
            if (!element.skip) {
3669
                handler(element, index, j);
3670
            }
3671
        }
3672
    }
3673
}
3674
 function getDistanceMetricForAxis(axis) {
3675
    const useX = axis.indexOf('x') !== -1;
3676
    const useY = axis.indexOf('y') !== -1;
3677
    return function(pt1, pt2) {
3678
        const deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
3679
        const deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
3680
        return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
3681
    };
3682
}
3683
 function getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) {
3684
    const items = [];
3685
    if (!includeInvisible && !chart.isPointInArea(position)) {
3686
        return items;
3687
    }
3688
    const evaluationFunc = function(element, datasetIndex, index) {
3689
        if (!includeInvisible && !_isPointInArea(element, chart.chartArea, 0)) {
3690
            return;
3691
        }
3692
        if (element.inRange(position.x, position.y, useFinalPosition)) {
3693
            items.push({
3694
                element,
3695
                datasetIndex,
3696
                index
3697
            });
3698
        }
3699
    };
3700
    evaluateInteractionItems(chart, axis, position, evaluationFunc, true);
3701
    return items;
3702
}
3703
 function getNearestRadialItems(chart, position, axis, useFinalPosition) {
3704
    let items = [];
3705
    function evaluationFunc(element, datasetIndex, index) {
3706
        const { startAngle , endAngle  } = element.getProps([
3707
            'startAngle',
3708
            'endAngle'
3709
        ], useFinalPosition);
3710
        const { angle  } = getAngleFromPoint(element, {
3711
            x: position.x,
3712
            y: position.y
3713
        });
3714
        if (_angleBetween(angle, startAngle, endAngle)) {
3715
            items.push({
3716
                element,
3717
                datasetIndex,
3718
                index
3719
            });
3720
        }
3721
    }
3722
    evaluateInteractionItems(chart, axis, position, evaluationFunc);
3723
    return items;
3724
}
3725
 function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {
3726
    let items = [];
3727
    const distanceMetric = getDistanceMetricForAxis(axis);
3728
    let minDistance = Number.POSITIVE_INFINITY;
3729
    function evaluationFunc(element, datasetIndex, index) {
3730
        const inRange = element.inRange(position.x, position.y, useFinalPosition);
3731
        if (intersect && !inRange) {
3732
            return;
3733
        }
3734
        const center = element.getCenterPoint(useFinalPosition);
3735
        const pointInArea = !!includeInvisible || chart.isPointInArea(center);
3736
        if (!pointInArea && !inRange) {
3737
            return;
3738
        }
3739
        const distance = distanceMetric(position, center);
3740
        if (distance < minDistance) {
3741
            items = [
3742
                {
3743
                    element,
3744
                    datasetIndex,
3745
                    index
3746
                }
3747
            ];
3748
            minDistance = distance;
3749
        } else if (distance === minDistance) {
3750
            items.push({
3751
                element,
3752
                datasetIndex,
3753
                index
3754
            });
3755
        }
3756
    }
3757
    evaluateInteractionItems(chart, axis, position, evaluationFunc);
3758
    return items;
3759
}
3760
 function getNearestItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {
3761
    if (!includeInvisible && !chart.isPointInArea(position)) {
3762
        return [];
3763
    }
3764
    return axis === 'r' && !intersect ? getNearestRadialItems(chart, position, axis, useFinalPosition) : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible);
3765
}
3766
 function getAxisItems(chart, position, axis, intersect, useFinalPosition) {
3767
    const items = [];
3768
    const rangeMethod = axis === 'x' ? 'inXRange' : 'inYRange';
3769
    let intersectsItem = false;
3770
    evaluateInteractionItems(chart, axis, position, (element, datasetIndex, index)=>{
3771
        if (element[rangeMethod](position[axis], useFinalPosition)) {
3772
            items.push({
3773
                element,
3774
                datasetIndex,
3775
                index
3776
            });
3777
            intersectsItem = intersectsItem || element.inRange(position.x, position.y, useFinalPosition);
3778
        }
3779
    });
3780
    if (intersect && !intersectsItem) {
3781
        return [];
3782
    }
3783
    return items;
3784
}
3785
 var Interaction = {
3786
    evaluateInteractionItems,
3787
    modes: {
3788
 index (chart, e, options, useFinalPosition) {
3789
            const position = getRelativePosition(e, chart);
3790
            const axis = options.axis || 'x';
3791
            const includeInvisible = options.includeInvisible || false;
3792
            const items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);
3793
            const elements = [];
3794
            if (!items.length) {
3795
                return [];
3796
            }
3797
            chart.getSortedVisibleDatasetMetas().forEach((meta)=>{
3798
                const index = items[0].index;
3799
                const element = meta.data[index];
3800
                if (element && !element.skip) {
3801
                    elements.push({
3802
                        element,
3803
                        datasetIndex: meta.index,
3804
                        index
3805
                    });
3806
                }
3807
            });
3808
            return elements;
3809
        },
3810
 dataset (chart, e, options, useFinalPosition) {
3811
            const position = getRelativePosition(e, chart);
3812
            const axis = options.axis || 'xy';
3813
            const includeInvisible = options.includeInvisible || false;
3814
            let items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);
3815
            if (items.length > 0) {
3816
                const datasetIndex = items[0].datasetIndex;
3817
                const data = chart.getDatasetMeta(datasetIndex).data;
3818
                items = [];
3819
                for(let i = 0; i < data.length; ++i){
3820
                    items.push({
3821
                        element: data[i],
3822
                        datasetIndex,
3823
                        index: i
3824
                    });
3825
                }
3826
            }
3827
            return items;
3828
        },
3829
 point (chart, e, options, useFinalPosition) {
3830
            const position = getRelativePosition(e, chart);
3831
            const axis = options.axis || 'xy';
3832
            const includeInvisible = options.includeInvisible || false;
3833
            return getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible);
3834
        },
3835
 nearest (chart, e, options, useFinalPosition) {
3836
            const position = getRelativePosition(e, chart);
3837
            const axis = options.axis || 'xy';
3838
            const includeInvisible = options.includeInvisible || false;
3839
            return getNearestItems(chart, position, axis, options.intersect, useFinalPosition, includeInvisible);
3840
        },
3841
 x (chart, e, options, useFinalPosition) {
3842
            const position = getRelativePosition(e, chart);
3843
            return getAxisItems(chart, position, 'x', options.intersect, useFinalPosition);
3844
        },
3845
 y (chart, e, options, useFinalPosition) {
3846
            const position = getRelativePosition(e, chart);
3847
            return getAxisItems(chart, position, 'y', options.intersect, useFinalPosition);
3848
        }
3849
    }
3850
};
3851
 
3852
const STATIC_POSITIONS = [
3853
    'left',
3854
    'top',
3855
    'right',
3856
    'bottom'
3857
];
3858
function filterByPosition(array, position) {
3859
    return array.filter((v)=>v.pos === position);
3860
}
3861
function filterDynamicPositionByAxis(array, axis) {
3862
    return array.filter((v)=>STATIC_POSITIONS.indexOf(v.pos) === -1 && v.box.axis === axis);
3863
}
3864
function sortByWeight(array, reverse) {
3865
    return array.sort((a, b)=>{
3866
        const v0 = reverse ? b : a;
3867
        const v1 = reverse ? a : b;
3868
        return v0.weight === v1.weight ? v0.index - v1.index : v0.weight - v1.weight;
3869
    });
3870
}
3871
function wrapBoxes(boxes) {
3872
    const layoutBoxes = [];
3873
    let i, ilen, box, pos, stack, stackWeight;
3874
    for(i = 0, ilen = (boxes || []).length; i < ilen; ++i){
3875
        box = boxes[i];
3876
        ({ position: pos , options: { stack , stackWeight =1  }  } = box);
3877
        layoutBoxes.push({
3878
            index: i,
3879
            box,
3880
            pos,
3881
            horizontal: box.isHorizontal(),
3882
            weight: box.weight,
3883
            stack: stack && pos + stack,
3884
            stackWeight
3885
        });
3886
    }
3887
    return layoutBoxes;
3888
}
3889
function buildStacks(layouts) {
3890
    const stacks = {};
3891
    for (const wrap of layouts){
3892
        const { stack , pos , stackWeight  } = wrap;
3893
        if (!stack || !STATIC_POSITIONS.includes(pos)) {
3894
            continue;
3895
        }
3896
        const _stack = stacks[stack] || (stacks[stack] = {
3897
            count: 0,
3898
            placed: 0,
3899
            weight: 0,
3900
            size: 0
3901
        });
3902
        _stack.count++;
3903
        _stack.weight += stackWeight;
3904
    }
3905
    return stacks;
3906
}
3907
 function setLayoutDims(layouts, params) {
3908
    const stacks = buildStacks(layouts);
3909
    const { vBoxMaxWidth , hBoxMaxHeight  } = params;
3910
    let i, ilen, layout;
3911
    for(i = 0, ilen = layouts.length; i < ilen; ++i){
3912
        layout = layouts[i];
3913
        const { fullSize  } = layout.box;
3914
        const stack = stacks[layout.stack];
3915
        const factor = stack && layout.stackWeight / stack.weight;
3916
        if (layout.horizontal) {
3917
            layout.width = factor ? factor * vBoxMaxWidth : fullSize && params.availableWidth;
3918
            layout.height = hBoxMaxHeight;
3919
        } else {
3920
            layout.width = vBoxMaxWidth;
3921
            layout.height = factor ? factor * hBoxMaxHeight : fullSize && params.availableHeight;
3922
        }
3923
    }
3924
    return stacks;
3925
}
3926
function buildLayoutBoxes(boxes) {
3927
    const layoutBoxes = wrapBoxes(boxes);
3928
    const fullSize = sortByWeight(layoutBoxes.filter((wrap)=>wrap.box.fullSize), true);
3929
    const left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);
3930
    const right = sortByWeight(filterByPosition(layoutBoxes, 'right'));
3931
    const top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);
3932
    const bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom'));
3933
    const centerHorizontal = filterDynamicPositionByAxis(layoutBoxes, 'x');
3934
    const centerVertical = filterDynamicPositionByAxis(layoutBoxes, 'y');
3935
    return {
3936
        fullSize,
3937
        leftAndTop: left.concat(top),
3938
        rightAndBottom: right.concat(centerVertical).concat(bottom).concat(centerHorizontal),
3939
        chartArea: filterByPosition(layoutBoxes, 'chartArea'),
3940
        vertical: left.concat(right).concat(centerVertical),
3941
        horizontal: top.concat(bottom).concat(centerHorizontal)
3942
    };
3943
}
3944
function getCombinedMax(maxPadding, chartArea, a, b) {
3945
    return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);
3946
}
3947
function updateMaxPadding(maxPadding, boxPadding) {
3948
    maxPadding.top = Math.max(maxPadding.top, boxPadding.top);
3949
    maxPadding.left = Math.max(maxPadding.left, boxPadding.left);
3950
    maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);
3951
    maxPadding.right = Math.max(maxPadding.right, boxPadding.right);
3952
}
3953
function updateDims(chartArea, params, layout, stacks) {
3954
    const { pos , box  } = layout;
3955
    const maxPadding = chartArea.maxPadding;
3956
    if (!isObject(pos)) {
3957
        if (layout.size) {
3958
            chartArea[pos] -= layout.size;
3959
        }
3960
        const stack = stacks[layout.stack] || {
3961
            size: 0,
3962
            count: 1
3963
        };
3964
        stack.size = Math.max(stack.size, layout.horizontal ? box.height : box.width);
3965
        layout.size = stack.size / stack.count;
3966
        chartArea[pos] += layout.size;
3967
    }
3968
    if (box.getPadding) {
3969
        updateMaxPadding(maxPadding, box.getPadding());
3970
    }
3971
    const newWidth = Math.max(0, params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right'));
3972
    const newHeight = Math.max(0, params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom'));
3973
    const widthChanged = newWidth !== chartArea.w;
3974
    const heightChanged = newHeight !== chartArea.h;
3975
    chartArea.w = newWidth;
3976
    chartArea.h = newHeight;
3977
    return layout.horizontal ? {
3978
        same: widthChanged,
3979
        other: heightChanged
3980
    } : {
3981
        same: heightChanged,
3982
        other: widthChanged
3983
    };
3984
}
3985
function handleMaxPadding(chartArea) {
3986
    const maxPadding = chartArea.maxPadding;
3987
    function updatePos(pos) {
3988
        const change = Math.max(maxPadding[pos] - chartArea[pos], 0);
3989
        chartArea[pos] += change;
3990
        return change;
3991
    }
3992
    chartArea.y += updatePos('top');
3993
    chartArea.x += updatePos('left');
3994
    updatePos('right');
3995
    updatePos('bottom');
3996
}
3997
function getMargins(horizontal, chartArea) {
3998
    const maxPadding = chartArea.maxPadding;
3999
    function marginForPositions(positions) {
4000
        const margin = {
4001
            left: 0,
4002
            top: 0,
4003
            right: 0,
4004
            bottom: 0
4005
        };
4006
        positions.forEach((pos)=>{
4007
            margin[pos] = Math.max(chartArea[pos], maxPadding[pos]);
4008
        });
4009
        return margin;
4010
    }
4011
    return horizontal ? marginForPositions([
4012
        'left',
4013
        'right'
4014
    ]) : marginForPositions([
4015
        'top',
4016
        'bottom'
4017
    ]);
4018
}
4019
function fitBoxes(boxes, chartArea, params, stacks) {
4020
    const refitBoxes = [];
4021
    let i, ilen, layout, box, refit, changed;
4022
    for(i = 0, ilen = boxes.length, refit = 0; i < ilen; ++i){
4023
        layout = boxes[i];
4024
        box = layout.box;
4025
        box.update(layout.width || chartArea.w, layout.height || chartArea.h, getMargins(layout.horizontal, chartArea));
4026
        const { same , other  } = updateDims(chartArea, params, layout, stacks);
4027
        refit |= same && refitBoxes.length;
4028
        changed = changed || other;
4029
        if (!box.fullSize) {
4030
            refitBoxes.push(layout);
4031
        }
4032
    }
4033
    return refit && fitBoxes(refitBoxes, chartArea, params, stacks) || changed;
4034
}
4035
function setBoxDims(box, left, top, width, height) {
4036
    box.top = top;
4037
    box.left = left;
4038
    box.right = left + width;
4039
    box.bottom = top + height;
4040
    box.width = width;
4041
    box.height = height;
4042
}
4043
function placeBoxes(boxes, chartArea, params, stacks) {
4044
    const userPadding = params.padding;
4045
    let { x , y  } = chartArea;
4046
    for (const layout of boxes){
4047
        const box = layout.box;
4048
        const stack = stacks[layout.stack] || {
4049
            count: 1,
4050
            placed: 0,
4051
            weight: 1
4052
        };
4053
        const weight = layout.stackWeight / stack.weight || 1;
4054
        if (layout.horizontal) {
4055
            const width = chartArea.w * weight;
4056
            const height = stack.size || box.height;
4057
            if (defined(stack.start)) {
4058
                y = stack.start;
4059
            }
4060
            if (box.fullSize) {
4061
                setBoxDims(box, userPadding.left, y, params.outerWidth - userPadding.right - userPadding.left, height);
4062
            } else {
4063
                setBoxDims(box, chartArea.left + stack.placed, y, width, height);
4064
            }
4065
            stack.start = y;
4066
            stack.placed += width;
4067
            y = box.bottom;
4068
        } else {
4069
            const height = chartArea.h * weight;
4070
            const width = stack.size || box.width;
4071
            if (defined(stack.start)) {
4072
                x = stack.start;
4073
            }
4074
            if (box.fullSize) {
4075
                setBoxDims(box, x, userPadding.top, width, params.outerHeight - userPadding.bottom - userPadding.top);
4076
            } else {
4077
                setBoxDims(box, x, chartArea.top + stack.placed, width, height);
4078
            }
4079
            stack.start = x;
4080
            stack.placed += height;
4081
            x = box.right;
4082
        }
4083
    }
4084
    chartArea.x = x;
4085
    chartArea.y = y;
4086
}
4087
var layouts = {
4088
 addBox (chart, item) {
4089
        if (!chart.boxes) {
4090
            chart.boxes = [];
4091
        }
4092
        item.fullSize = item.fullSize || false;
4093
        item.position = item.position || 'top';
4094
        item.weight = item.weight || 0;
4095
        item._layers = item._layers || function() {
4096
            return [
4097
                {
4098
                    z: 0,
4099
                    draw (chartArea) {
4100
                        item.draw(chartArea);
4101
                    }
4102
                }
4103
            ];
4104
        };
4105
        chart.boxes.push(item);
4106
    },
4107
 removeBox (chart, layoutItem) {
4108
        const index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
4109
        if (index !== -1) {
4110
            chart.boxes.splice(index, 1);
4111
        }
4112
    },
4113
 configure (chart, item, options) {
4114
        item.fullSize = options.fullSize;
4115
        item.position = options.position;
4116
        item.weight = options.weight;
4117
    },
4118
 update (chart, width, height, minPadding) {
4119
        if (!chart) {
4120
            return;
4121
        }
4122
        const padding = toPadding(chart.options.layout.padding);
4123
        const availableWidth = Math.max(width - padding.width, 0);
4124
        const availableHeight = Math.max(height - padding.height, 0);
4125
        const boxes = buildLayoutBoxes(chart.boxes);
4126
        const verticalBoxes = boxes.vertical;
4127
        const horizontalBoxes = boxes.horizontal;
4128
        each(chart.boxes, (box)=>{
4129
            if (typeof box.beforeLayout === 'function') {
4130
                box.beforeLayout();
4131
            }
4132
        });
4133
        const visibleVerticalBoxCount = verticalBoxes.reduce((total, wrap)=>wrap.box.options && wrap.box.options.display === false ? total : total + 1, 0) || 1;
4134
        const params = Object.freeze({
4135
            outerWidth: width,
4136
            outerHeight: height,
4137
            padding,
4138
            availableWidth,
4139
            availableHeight,
4140
            vBoxMaxWidth: availableWidth / 2 / visibleVerticalBoxCount,
4141
            hBoxMaxHeight: availableHeight / 2
4142
        });
4143
        const maxPadding = Object.assign({}, padding);
4144
        updateMaxPadding(maxPadding, toPadding(minPadding));
4145
        const chartArea = Object.assign({
4146
            maxPadding,
4147
            w: availableWidth,
4148
            h: availableHeight,
4149
            x: padding.left,
4150
            y: padding.top
4151
        }, padding);
4152
        const stacks = setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);
4153
        fitBoxes(boxes.fullSize, chartArea, params, stacks);
4154
        fitBoxes(verticalBoxes, chartArea, params, stacks);
4155
        if (fitBoxes(horizontalBoxes, chartArea, params, stacks)) {
4156
            fitBoxes(verticalBoxes, chartArea, params, stacks);
4157
        }
4158
        handleMaxPadding(chartArea);
4159
        placeBoxes(boxes.leftAndTop, chartArea, params, stacks);
4160
        chartArea.x += chartArea.w;
4161
        chartArea.y += chartArea.h;
4162
        placeBoxes(boxes.rightAndBottom, chartArea, params, stacks);
4163
        chart.chartArea = {
4164
            left: chartArea.left,
4165
            top: chartArea.top,
4166
            right: chartArea.left + chartArea.w,
4167
            bottom: chartArea.top + chartArea.h,
4168
            height: chartArea.h,
4169
            width: chartArea.w
4170
        };
4171
        each(boxes.chartArea, (layout)=>{
4172
            const box = layout.box;
4173
            Object.assign(box, chart.chartArea);
4174
            box.update(chartArea.w, chartArea.h, {
4175
                left: 0,
4176
                top: 0,
4177
                right: 0,
4178
                bottom: 0
4179
            });
4180
        });
4181
    }
4182
};
4183
 
4184
class BasePlatform {
4185
 acquireContext(canvas, aspectRatio) {}
4186
 releaseContext(context) {
4187
        return false;
4188
    }
4189
 addEventListener(chart, type, listener) {}
4190
 removeEventListener(chart, type, listener) {}
4191
 getDevicePixelRatio() {
4192
        return 1;
4193
    }
4194
 getMaximumSize(element, width, height, aspectRatio) {
4195
        width = Math.max(0, width || element.width);
4196
        height = height || element.height;
4197
        return {
4198
            width,
4199
            height: Math.max(0, aspectRatio ? Math.floor(width / aspectRatio) : height)
4200
        };
4201
    }
4202
 isAttached(canvas) {
4203
        return true;
4204
    }
4205
 updateConfig(config) {
4206
    }
4207
}
4208
 
4209
class BasicPlatform extends BasePlatform {
4210
    acquireContext(item) {
4211
        return item && item.getContext && item.getContext('2d') || null;
4212
    }
4213
    updateConfig(config) {
4214
        config.options.animation = false;
4215
    }
4216
}
4217
 
4218
const EXPANDO_KEY = '$chartjs';
4219
 const EVENT_TYPES = {
4220
    touchstart: 'mousedown',
4221
    touchmove: 'mousemove',
4222
    touchend: 'mouseup',
4223
    pointerenter: 'mouseenter',
4224
    pointerdown: 'mousedown',
4225
    pointermove: 'mousemove',
4226
    pointerup: 'mouseup',
4227
    pointerleave: 'mouseout',
4228
    pointerout: 'mouseout'
4229
};
4230
const isNullOrEmpty = (value)=>value === null || value === '';
4231
 function initCanvas(canvas, aspectRatio) {
4232
    const style = canvas.style;
4233
    const renderHeight = canvas.getAttribute('height');
4234
    const renderWidth = canvas.getAttribute('width');
4235
    canvas[EXPANDO_KEY] = {
4236
        initial: {
4237
            height: renderHeight,
4238
            width: renderWidth,
4239
            style: {
4240
                display: style.display,
4241
                height: style.height,
4242
                width: style.width
4243
            }
4244
        }
4245
    };
4246
    style.display = style.display || 'block';
4247
    style.boxSizing = style.boxSizing || 'border-box';
4248
    if (isNullOrEmpty(renderWidth)) {
4249
        const displayWidth = readUsedSize(canvas, 'width');
4250
        if (displayWidth !== undefined) {
4251
            canvas.width = displayWidth;
4252
        }
4253
    }
4254
    if (isNullOrEmpty(renderHeight)) {
4255
        if (canvas.style.height === '') {
4256
            canvas.height = canvas.width / (aspectRatio || 2);
4257
        } else {
4258
            const displayHeight = readUsedSize(canvas, 'height');
4259
            if (displayHeight !== undefined) {
4260
                canvas.height = displayHeight;
4261
            }
4262
        }
4263
    }
4264
    return canvas;
4265
}
4266
const eventListenerOptions = supportsEventListenerOptions ? {
4267
    passive: true
4268
} : false;
4269
function addListener(node, type, listener) {
4270
    if (node) {
4271
        node.addEventListener(type, listener, eventListenerOptions);
4272
    }
4273
}
4274
function removeListener(chart, type, listener) {
4275
    if (chart && chart.canvas) {
4276
        chart.canvas.removeEventListener(type, listener, eventListenerOptions);
4277
    }
4278
}
4279
function fromNativeEvent(event, chart) {
4280
    const type = EVENT_TYPES[event.type] || event.type;
4281
    const { x , y  } = getRelativePosition(event, chart);
4282
    return {
4283
        type,
4284
        chart,
4285
        native: event,
4286
        x: x !== undefined ? x : null,
4287
        y: y !== undefined ? y : null
4288
    };
4289
}
4290
function nodeListContains(nodeList, canvas) {
4291
    for (const node of nodeList){
4292
        if (node === canvas || node.contains(canvas)) {
4293
            return true;
4294
        }
4295
    }
4296
}
4297
function createAttachObserver(chart, type, listener) {
4298
    const canvas = chart.canvas;
4299
    const observer = new MutationObserver((entries)=>{
4300
        let trigger = false;
4301
        for (const entry of entries){
4302
            trigger = trigger || nodeListContains(entry.addedNodes, canvas);
4303
            trigger = trigger && !nodeListContains(entry.removedNodes, canvas);
4304
        }
4305
        if (trigger) {
4306
            listener();
4307
        }
4308
    });
4309
    observer.observe(document, {
4310
        childList: true,
4311
        subtree: true
4312
    });
4313
    return observer;
4314
}
4315
function createDetachObserver(chart, type, listener) {
4316
    const canvas = chart.canvas;
4317
    const observer = new MutationObserver((entries)=>{
4318
        let trigger = false;
4319
        for (const entry of entries){
4320
            trigger = trigger || nodeListContains(entry.removedNodes, canvas);
4321
            trigger = trigger && !nodeListContains(entry.addedNodes, canvas);
4322
        }
4323
        if (trigger) {
4324
            listener();
4325
        }
4326
    });
4327
    observer.observe(document, {
4328
        childList: true,
4329
        subtree: true
4330
    });
4331
    return observer;
4332
}
4333
const drpListeningCharts = new Map();
4334
let oldDevicePixelRatio = 0;
4335
function onWindowResize() {
4336
    const dpr = window.devicePixelRatio;
4337
    if (dpr === oldDevicePixelRatio) {
4338
        return;
4339
    }
4340
    oldDevicePixelRatio = dpr;
4341
    drpListeningCharts.forEach((resize, chart)=>{
4342
        if (chart.currentDevicePixelRatio !== dpr) {
4343
            resize();
4344
        }
4345
    });
4346
}
4347
function listenDevicePixelRatioChanges(chart, resize) {
4348
    if (!drpListeningCharts.size) {
4349
        window.addEventListener('resize', onWindowResize);
4350
    }
4351
    drpListeningCharts.set(chart, resize);
4352
}
4353
function unlistenDevicePixelRatioChanges(chart) {
4354
    drpListeningCharts.delete(chart);
4355
    if (!drpListeningCharts.size) {
4356
        window.removeEventListener('resize', onWindowResize);
4357
    }
4358
}
4359
function createResizeObserver(chart, type, listener) {
4360
    const canvas = chart.canvas;
4361
    const container = canvas && _getParentNode(canvas);
4362
    if (!container) {
4363
        return;
4364
    }
4365
    const resize = throttled((width, height)=>{
4366
        const w = container.clientWidth;
4367
        listener(width, height);
4368
        if (w < container.clientWidth) {
4369
            listener();
4370
        }
4371
    }, window);
4372
    const observer = new ResizeObserver((entries)=>{
4373
        const entry = entries[0];
4374
        const width = entry.contentRect.width;
4375
        const height = entry.contentRect.height;
4376
        if (width === 0 && height === 0) {
4377
            return;
4378
        }
4379
        resize(width, height);
4380
    });
4381
    observer.observe(container);
4382
    listenDevicePixelRatioChanges(chart, resize);
4383
    return observer;
4384
}
4385
function releaseObserver(chart, type, observer) {
4386
    if (observer) {
4387
        observer.disconnect();
4388
    }
4389
    if (type === 'resize') {
4390
        unlistenDevicePixelRatioChanges(chart);
4391
    }
4392
}
4393
function createProxyAndListen(chart, type, listener) {
4394
    const canvas = chart.canvas;
4395
    const proxy = throttled((event)=>{
4396
        if (chart.ctx !== null) {
4397
            listener(fromNativeEvent(event, chart));
4398
        }
4399
    }, chart);
4400
    addListener(canvas, type, proxy);
4401
    return proxy;
4402
}
4403
 class DomPlatform extends BasePlatform {
4404
 acquireContext(canvas, aspectRatio) {
4405
        const context = canvas && canvas.getContext && canvas.getContext('2d');
4406
        if (context && context.canvas === canvas) {
4407
            initCanvas(canvas, aspectRatio);
4408
            return context;
4409
        }
4410
        return null;
4411
    }
4412
 releaseContext(context) {
4413
        const canvas = context.canvas;
4414
        if (!canvas[EXPANDO_KEY]) {
4415
            return false;
4416
        }
4417
        const initial = canvas[EXPANDO_KEY].initial;
4418
        [
4419
            'height',
4420
            'width'
4421
        ].forEach((prop)=>{
4422
            const value = initial[prop];
4423
            if (isNullOrUndef(value)) {
4424
                canvas.removeAttribute(prop);
4425
            } else {
4426
                canvas.setAttribute(prop, value);
4427
            }
4428
        });
4429
        const style = initial.style || {};
4430
        Object.keys(style).forEach((key)=>{
4431
            canvas.style[key] = style[key];
4432
        });
4433
        canvas.width = canvas.width;
4434
        delete canvas[EXPANDO_KEY];
4435
        return true;
4436
    }
4437
 addEventListener(chart, type, listener) {
4438
        this.removeEventListener(chart, type);
4439
        const proxies = chart.$proxies || (chart.$proxies = {});
4440
        const handlers = {
4441
            attach: createAttachObserver,
4442
            detach: createDetachObserver,
4443
            resize: createResizeObserver
4444
        };
4445
        const handler = handlers[type] || createProxyAndListen;
4446
        proxies[type] = handler(chart, type, listener);
4447
    }
4448
 removeEventListener(chart, type) {
4449
        const proxies = chart.$proxies || (chart.$proxies = {});
4450
        const proxy = proxies[type];
4451
        if (!proxy) {
4452
            return;
4453
        }
4454
        const handlers = {
4455
            attach: releaseObserver,
4456
            detach: releaseObserver,
4457
            resize: releaseObserver
4458
        };
4459
        const handler = handlers[type] || removeListener;
4460
        handler(chart, type, proxy);
4461
        proxies[type] = undefined;
4462
    }
4463
    getDevicePixelRatio() {
4464
        return window.devicePixelRatio;
4465
    }
4466
 getMaximumSize(canvas, width, height, aspectRatio) {
4467
        return getMaximumSize(canvas, width, height, aspectRatio);
4468
    }
4469
 isAttached(canvas) {
4470
        const container = _getParentNode(canvas);
4471
        return !!(container && container.isConnected);
4472
    }
4473
}
4474
 
4475
function _detectPlatform(canvas) {
4476
    if (!_isDomSupported() || typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
4477
        return BasicPlatform;
4478
    }
4479
    return DomPlatform;
4480
}
4481
 
4482
var platforms = /*#__PURE__*/Object.freeze({
4483
__proto__: null,
4484
BasePlatform: BasePlatform,
4485
BasicPlatform: BasicPlatform,
4486
DomPlatform: DomPlatform,
4487
_detectPlatform: _detectPlatform
4488
});
4489
 
4490
const transparent = 'transparent';
4491
const interpolators = {
4492
    boolean (from, to, factor) {
4493
        return factor > 0.5 ? to : from;
4494
    },
4495
 color (from, to, factor) {
4496
        const c0 = color(from || transparent);
4497
        const c1 = c0.valid && color(to || transparent);
4498
        return c1 && c1.valid ? c1.mix(c0, factor).hexString() : to;
4499
    },
4500
    number (from, to, factor) {
4501
        return from + (to - from) * factor;
4502
    }
4503
};
4504
class Animation {
4505
    constructor(cfg, target, prop, to){
4506
        const currentValue = target[prop];
4507
        to = resolve([
4508
            cfg.to,
4509
            to,
4510
            currentValue,
4511
            cfg.from
4512
        ]);
4513
        const from = resolve([
4514
            cfg.from,
4515
            currentValue,
4516
            to
4517
        ]);
4518
        this._active = true;
4519
        this._fn = cfg.fn || interpolators[cfg.type || typeof from];
4520
        this._easing = effects[cfg.easing] || effects.linear;
4521
        this._start = Math.floor(Date.now() + (cfg.delay || 0));
4522
        this._duration = this._total = Math.floor(cfg.duration);
4523
        this._loop = !!cfg.loop;
4524
        this._target = target;
4525
        this._prop = prop;
4526
        this._from = from;
4527
        this._to = to;
4528
        this._promises = undefined;
4529
    }
4530
    active() {
4531
        return this._active;
4532
    }
4533
    update(cfg, to, date) {
4534
        if (this._active) {
4535
            this._notify(false);
4536
            const currentValue = this._target[this._prop];
4537
            const elapsed = date - this._start;
4538
            const remain = this._duration - elapsed;
4539
            this._start = date;
4540
            this._duration = Math.floor(Math.max(remain, cfg.duration));
4541
            this._total += elapsed;
4542
            this._loop = !!cfg.loop;
4543
            this._to = resolve([
4544
                cfg.to,
4545
                to,
4546
                currentValue,
4547
                cfg.from
4548
            ]);
4549
            this._from = resolve([
4550
                cfg.from,
4551
                currentValue,
4552
                to
4553
            ]);
4554
        }
4555
    }
4556
    cancel() {
4557
        if (this._active) {
4558
            this.tick(Date.now());
4559
            this._active = false;
4560
            this._notify(false);
4561
        }
4562
    }
4563
    tick(date) {
4564
        const elapsed = date - this._start;
4565
        const duration = this._duration;
4566
        const prop = this._prop;
4567
        const from = this._from;
4568
        const loop = this._loop;
4569
        const to = this._to;
4570
        let factor;
4571
        this._active = from !== to && (loop || elapsed < duration);
4572
        if (!this._active) {
4573
            this._target[prop] = to;
4574
            this._notify(true);
4575
            return;
4576
        }
4577
        if (elapsed < 0) {
4578
            this._target[prop] = from;
4579
            return;
4580
        }
4581
        factor = elapsed / duration % 2;
4582
        factor = loop && factor > 1 ? 2 - factor : factor;
4583
        factor = this._easing(Math.min(1, Math.max(0, factor)));
4584
        this._target[prop] = this._fn(from, to, factor);
4585
    }
4586
    wait() {
4587
        const promises = this._promises || (this._promises = []);
4588
        return new Promise((res, rej)=>{
4589
            promises.push({
4590
                res,
4591
                rej
4592
            });
4593
        });
4594
    }
4595
    _notify(resolved) {
4596
        const method = resolved ? 'res' : 'rej';
4597
        const promises = this._promises || [];
4598
        for(let i = 0; i < promises.length; i++){
4599
            promises[i][method]();
4600
        }
4601
    }
4602
}
4603
 
4604
class Animations {
4605
    constructor(chart, config){
4606
        this._chart = chart;
4607
        this._properties = new Map();
4608
        this.configure(config);
4609
    }
4610
    configure(config) {
4611
        if (!isObject(config)) {
4612
            return;
4613
        }
4614
        const animationOptions = Object.keys(defaults.animation);
4615
        const animatedProps = this._properties;
4616
        Object.getOwnPropertyNames(config).forEach((key)=>{
4617
            const cfg = config[key];
4618
            if (!isObject(cfg)) {
4619
                return;
4620
            }
4621
            const resolved = {};
4622
            for (const option of animationOptions){
4623
                resolved[option] = cfg[option];
4624
            }
4625
            (isArray(cfg.properties) && cfg.properties || [
4626
                key
4627
            ]).forEach((prop)=>{
4628
                if (prop === key || !animatedProps.has(prop)) {
4629
                    animatedProps.set(prop, resolved);
4630
                }
4631
            });
4632
        });
4633
    }
4634
 _animateOptions(target, values) {
4635
        const newOptions = values.options;
4636
        const options = resolveTargetOptions(target, newOptions);
4637
        if (!options) {
4638
            return [];
4639
        }
4640
        const animations = this._createAnimations(options, newOptions);
4641
        if (newOptions.$shared) {
4642
            awaitAll(target.options.$animations, newOptions).then(()=>{
4643
                target.options = newOptions;
4644
            }, ()=>{
4645
            });
4646
        }
4647
        return animations;
4648
    }
4649
 _createAnimations(target, values) {
4650
        const animatedProps = this._properties;
4651
        const animations = [];
4652
        const running = target.$animations || (target.$animations = {});
4653
        const props = Object.keys(values);
4654
        const date = Date.now();
4655
        let i;
4656
        for(i = props.length - 1; i >= 0; --i){
4657
            const prop = props[i];
4658
            if (prop.charAt(0) === '$') {
4659
                continue;
4660
            }
4661
            if (prop === 'options') {
4662
                animations.push(...this._animateOptions(target, values));
4663
                continue;
4664
            }
4665
            const value = values[prop];
4666
            let animation = running[prop];
4667
            const cfg = animatedProps.get(prop);
4668
            if (animation) {
4669
                if (cfg && animation.active()) {
4670
                    animation.update(cfg, value, date);
4671
                    continue;
4672
                } else {
4673
                    animation.cancel();
4674
                }
4675
            }
4676
            if (!cfg || !cfg.duration) {
4677
                target[prop] = value;
4678
                continue;
4679
            }
4680
            running[prop] = animation = new Animation(cfg, target, prop, value);
4681
            animations.push(animation);
4682
        }
4683
        return animations;
4684
    }
4685
 update(target, values) {
4686
        if (this._properties.size === 0) {
4687
            Object.assign(target, values);
4688
            return;
4689
        }
4690
        const animations = this._createAnimations(target, values);
4691
        if (animations.length) {
4692
            animator.add(this._chart, animations);
4693
            return true;
4694
        }
4695
    }
4696
}
4697
function awaitAll(animations, properties) {
4698
    const running = [];
4699
    const keys = Object.keys(properties);
4700
    for(let i = 0; i < keys.length; i++){
4701
        const anim = animations[keys[i]];
4702
        if (anim && anim.active()) {
4703
            running.push(anim.wait());
4704
        }
4705
    }
4706
    return Promise.all(running);
4707
}
4708
function resolveTargetOptions(target, newOptions) {
4709
    if (!newOptions) {
4710
        return;
4711
    }
4712
    let options = target.options;
4713
    if (!options) {
4714
        target.options = newOptions;
4715
        return;
4716
    }
4717
    if (options.$shared) {
4718
        target.options = options = Object.assign({}, options, {
4719
            $shared: false,
4720
            $animations: {}
4721
        });
4722
    }
4723
    return options;
4724
}
4725
 
4726
function scaleClip(scale, allowedOverflow) {
4727
    const opts = scale && scale.options || {};
4728
    const reverse = opts.reverse;
4729
    const min = opts.min === undefined ? allowedOverflow : 0;
4730
    const max = opts.max === undefined ? allowedOverflow : 0;
4731
    return {
4732
        start: reverse ? max : min,
4733
        end: reverse ? min : max
4734
    };
4735
}
4736
function defaultClip(xScale, yScale, allowedOverflow) {
4737
    if (allowedOverflow === false) {
4738
        return false;
4739
    }
4740
    const x = scaleClip(xScale, allowedOverflow);
4741
    const y = scaleClip(yScale, allowedOverflow);
4742
    return {
4743
        top: y.end,
4744
        right: x.end,
4745
        bottom: y.start,
4746
        left: x.start
4747
    };
4748
}
4749
function toClip(value) {
4750
    let t, r, b, l;
4751
    if (isObject(value)) {
4752
        t = value.top;
4753
        r = value.right;
4754
        b = value.bottom;
4755
        l = value.left;
4756
    } else {
4757
        t = r = b = l = value;
4758
    }
4759
    return {
4760
        top: t,
4761
        right: r,
4762
        bottom: b,
4763
        left: l,
4764
        disabled: value === false
4765
    };
4766
}
4767
function getSortedDatasetIndices(chart, filterVisible) {
4768
    const keys = [];
4769
    const metasets = chart._getSortedDatasetMetas(filterVisible);
4770
    let i, ilen;
4771
    for(i = 0, ilen = metasets.length; i < ilen; ++i){
4772
        keys.push(metasets[i].index);
4773
    }
4774
    return keys;
4775
}
4776
function applyStack(stack, value, dsIndex, options = {}) {
4777
    const keys = stack.keys;
4778
    const singleMode = options.mode === 'single';
4779
    let i, ilen, datasetIndex, otherValue;
4780
    if (value === null) {
4781
        return;
4782
    }
4783
    for(i = 0, ilen = keys.length; i < ilen; ++i){
4784
        datasetIndex = +keys[i];
4785
        if (datasetIndex === dsIndex) {
4786
            if (options.all) {
4787
                continue;
4788
            }
4789
            break;
4790
        }
4791
        otherValue = stack.values[datasetIndex];
4792
        if (isNumberFinite(otherValue) && (singleMode || value === 0 || sign(value) === sign(otherValue))) {
4793
            value += otherValue;
4794
        }
4795
    }
4796
    return value;
4797
}
4798
function convertObjectDataToArray(data) {
4799
    const keys = Object.keys(data);
4800
    const adata = new Array(keys.length);
4801
    let i, ilen, key;
4802
    for(i = 0, ilen = keys.length; i < ilen; ++i){
4803
        key = keys[i];
4804
        adata[i] = {
4805
            x: key,
4806
            y: data[key]
4807
        };
4808
    }
4809
    return adata;
4810
}
4811
function isStacked(scale, meta) {
4812
    const stacked = scale && scale.options.stacked;
4813
    return stacked || stacked === undefined && meta.stack !== undefined;
4814
}
4815
function getStackKey(indexScale, valueScale, meta) {
4816
    return `${indexScale.id}.${valueScale.id}.${meta.stack || meta.type}`;
4817
}
4818
function getUserBounds(scale) {
4819
    const { min , max , minDefined , maxDefined  } = scale.getUserBounds();
4820
    return {
4821
        min: minDefined ? min : Number.NEGATIVE_INFINITY,
4822
        max: maxDefined ? max : Number.POSITIVE_INFINITY
4823
    };
4824
}
4825
function getOrCreateStack(stacks, stackKey, indexValue) {
4826
    const subStack = stacks[stackKey] || (stacks[stackKey] = {});
4827
    return subStack[indexValue] || (subStack[indexValue] = {});
4828
}
4829
function getLastIndexInStack(stack, vScale, positive, type) {
4830
    for (const meta of vScale.getMatchingVisibleMetas(type).reverse()){
4831
        const value = stack[meta.index];
4832
        if (positive && value > 0 || !positive && value < 0) {
4833
            return meta.index;
4834
        }
4835
    }
4836
    return null;
4837
}
4838
function updateStacks(controller, parsed) {
4839
    const { chart , _cachedMeta: meta  } = controller;
4840
    const stacks = chart._stacks || (chart._stacks = {});
4841
    const { iScale , vScale , index: datasetIndex  } = meta;
4842
    const iAxis = iScale.axis;
4843
    const vAxis = vScale.axis;
4844
    const key = getStackKey(iScale, vScale, meta);
4845
    const ilen = parsed.length;
4846
    let stack;
4847
    for(let i = 0; i < ilen; ++i){
4848
        const item = parsed[i];
4849
        const { [iAxis]: index , [vAxis]: value  } = item;
4850
        const itemStacks = item._stacks || (item._stacks = {});
4851
        stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index);
4852
        stack[datasetIndex] = value;
4853
        stack._top = getLastIndexInStack(stack, vScale, true, meta.type);
4854
        stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type);
4855
        const visualValues = stack._visualValues || (stack._visualValues = {});
4856
        visualValues[datasetIndex] = value;
4857
    }
4858
}
4859
function getFirstScaleId(chart, axis) {
4860
    const scales = chart.scales;
4861
    return Object.keys(scales).filter((key)=>scales[key].axis === axis).shift();
4862
}
4863
function createDatasetContext(parent, index) {
4864
    return createContext(parent, {
4865
        active: false,
4866
        dataset: undefined,
4867
        datasetIndex: index,
4868
        index,
4869
        mode: 'default',
4870
        type: 'dataset'
4871
    });
4872
}
4873
function createDataContext(parent, index, element) {
4874
    return createContext(parent, {
4875
        active: false,
4876
        dataIndex: index,
4877
        parsed: undefined,
4878
        raw: undefined,
4879
        element,
4880
        index,
4881
        mode: 'default',
4882
        type: 'data'
4883
    });
4884
}
4885
function clearStacks(meta, items) {
4886
    const datasetIndex = meta.controller.index;
4887
    const axis = meta.vScale && meta.vScale.axis;
4888
    if (!axis) {
4889
        return;
4890
    }
4891
    items = items || meta._parsed;
4892
    for (const parsed of items){
4893
        const stacks = parsed._stacks;
4894
        if (!stacks || stacks[axis] === undefined || stacks[axis][datasetIndex] === undefined) {
4895
            return;
4896
        }
4897
        delete stacks[axis][datasetIndex];
4898
        if (stacks[axis]._visualValues !== undefined && stacks[axis]._visualValues[datasetIndex] !== undefined) {
4899
            delete stacks[axis]._visualValues[datasetIndex];
4900
        }
4901
    }
4902
}
4903
const isDirectUpdateMode = (mode)=>mode === 'reset' || mode === 'none';
4904
const cloneIfNotShared = (cached, shared)=>shared ? cached : Object.assign({}, cached);
4905
const createStack = (canStack, meta, chart)=>canStack && !meta.hidden && meta._stacked && {
4906
        keys: getSortedDatasetIndices(chart, true),
4907
        values: null
4908
    };
4909
class DatasetController {
4910
 static defaults = {};
4911
 static datasetElementType = null;
4912
 static dataElementType = null;
4913
 constructor(chart, datasetIndex){
4914
        this.chart = chart;
4915
        this._ctx = chart.ctx;
4916
        this.index = datasetIndex;
4917
        this._cachedDataOpts = {};
4918
        this._cachedMeta = this.getMeta();
4919
        this._type = this._cachedMeta.type;
4920
        this.options = undefined;
4921
         this._parsing = false;
4922
        this._data = undefined;
4923
        this._objectData = undefined;
4924
        this._sharedOptions = undefined;
4925
        this._drawStart = undefined;
4926
        this._drawCount = undefined;
4927
        this.enableOptionSharing = false;
4928
        this.supportsDecimation = false;
4929
        this.$context = undefined;
4930
        this._syncList = [];
4931
        this.datasetElementType = new.target.datasetElementType;
4932
        this.dataElementType = new.target.dataElementType;
4933
        this.initialize();
4934
    }
4935
    initialize() {
4936
        const meta = this._cachedMeta;
4937
        this.configure();
4938
        this.linkScales();
4939
        meta._stacked = isStacked(meta.vScale, meta);
4940
        this.addElements();
4941
        if (this.options.fill && !this.chart.isPluginEnabled('filler')) {
4942
            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");
4943
        }
4944
    }
4945
    updateIndex(datasetIndex) {
4946
        if (this.index !== datasetIndex) {
4947
            clearStacks(this._cachedMeta);
4948
        }
4949
        this.index = datasetIndex;
4950
    }
4951
    linkScales() {
4952
        const chart = this.chart;
4953
        const meta = this._cachedMeta;
4954
        const dataset = this.getDataset();
4955
        const chooseId = (axis, x, y, r)=>axis === 'x' ? x : axis === 'r' ? r : y;
4956
        const xid = meta.xAxisID = valueOrDefault(dataset.xAxisID, getFirstScaleId(chart, 'x'));
4957
        const yid = meta.yAxisID = valueOrDefault(dataset.yAxisID, getFirstScaleId(chart, 'y'));
4958
        const rid = meta.rAxisID = valueOrDefault(dataset.rAxisID, getFirstScaleId(chart, 'r'));
4959
        const indexAxis = meta.indexAxis;
4960
        const iid = meta.iAxisID = chooseId(indexAxis, xid, yid, rid);
4961
        const vid = meta.vAxisID = chooseId(indexAxis, yid, xid, rid);
4962
        meta.xScale = this.getScaleForId(xid);
4963
        meta.yScale = this.getScaleForId(yid);
4964
        meta.rScale = this.getScaleForId(rid);
4965
        meta.iScale = this.getScaleForId(iid);
4966
        meta.vScale = this.getScaleForId(vid);
4967
    }
4968
    getDataset() {
4969
        return this.chart.data.datasets[this.index];
4970
    }
4971
    getMeta() {
4972
        return this.chart.getDatasetMeta(this.index);
4973
    }
4974
 getScaleForId(scaleID) {
4975
        return this.chart.scales[scaleID];
4976
    }
4977
 _getOtherScale(scale) {
4978
        const meta = this._cachedMeta;
4979
        return scale === meta.iScale ? meta.vScale : meta.iScale;
4980
    }
4981
    reset() {
4982
        this._update('reset');
4983
    }
4984
 _destroy() {
4985
        const meta = this._cachedMeta;
4986
        if (this._data) {
4987
            unlistenArrayEvents(this._data, this);
4988
        }
4989
        if (meta._stacked) {
4990
            clearStacks(meta);
4991
        }
4992
    }
4993
 _dataCheck() {
4994
        const dataset = this.getDataset();
4995
        const data = dataset.data || (dataset.data = []);
4996
        const _data = this._data;
4997
        if (isObject(data)) {
4998
            this._data = convertObjectDataToArray(data);
4999
        } else if (_data !== data) {
5000
            if (_data) {
5001
                unlistenArrayEvents(_data, this);
5002
                const meta = this._cachedMeta;
5003
                clearStacks(meta);
5004
                meta._parsed = [];
5005
            }
5006
            if (data && Object.isExtensible(data)) {
5007
                listenArrayEvents(data, this);
5008
            }
5009
            this._syncList = [];
5010
            this._data = data;
5011
        }
5012
    }
5013
    addElements() {
5014
        const meta = this._cachedMeta;
5015
        this._dataCheck();
5016
        if (this.datasetElementType) {
5017
            meta.dataset = new this.datasetElementType();
5018
        }
5019
    }
5020
    buildOrUpdateElements(resetNewElements) {
5021
        const meta = this._cachedMeta;
5022
        const dataset = this.getDataset();
5023
        let stackChanged = false;
5024
        this._dataCheck();
5025
        const oldStacked = meta._stacked;
5026
        meta._stacked = isStacked(meta.vScale, meta);
5027
        if (meta.stack !== dataset.stack) {
5028
            stackChanged = true;
5029
            clearStacks(meta);
5030
            meta.stack = dataset.stack;
5031
        }
5032
        this._resyncElements(resetNewElements);
5033
        if (stackChanged || oldStacked !== meta._stacked) {
5034
            updateStacks(this, meta._parsed);
5035
        }
5036
    }
5037
 configure() {
5038
        const config = this.chart.config;
5039
        const scopeKeys = config.datasetScopeKeys(this._type);
5040
        const scopes = config.getOptionScopes(this.getDataset(), scopeKeys, true);
5041
        this.options = config.createResolver(scopes, this.getContext());
5042
        this._parsing = this.options.parsing;
5043
        this._cachedDataOpts = {};
5044
    }
5045
 parse(start, count) {
5046
        const { _cachedMeta: meta , _data: data  } = this;
5047
        const { iScale , _stacked  } = meta;
5048
        const iAxis = iScale.axis;
5049
        let sorted = start === 0 && count === data.length ? true : meta._sorted;
5050
        let prev = start > 0 && meta._parsed[start - 1];
5051
        let i, cur, parsed;
5052
        if (this._parsing === false) {
5053
            meta._parsed = data;
5054
            meta._sorted = true;
5055
            parsed = data;
5056
        } else {
5057
            if (isArray(data[start])) {
5058
                parsed = this.parseArrayData(meta, data, start, count);
5059
            } else if (isObject(data[start])) {
5060
                parsed = this.parseObjectData(meta, data, start, count);
5061
            } else {
5062
                parsed = this.parsePrimitiveData(meta, data, start, count);
5063
            }
5064
            const isNotInOrderComparedToPrev = ()=>cur[iAxis] === null || prev && cur[iAxis] < prev[iAxis];
5065
            for(i = 0; i < count; ++i){
5066
                meta._parsed[i + start] = cur = parsed[i];
5067
                if (sorted) {
5068
                    if (isNotInOrderComparedToPrev()) {
5069
                        sorted = false;
5070
                    }
5071
                    prev = cur;
5072
                }
5073
            }
5074
            meta._sorted = sorted;
5075
        }
5076
        if (_stacked) {
5077
            updateStacks(this, parsed);
5078
        }
5079
    }
5080
 parsePrimitiveData(meta, data, start, count) {
5081
        const { iScale , vScale  } = meta;
5082
        const iAxis = iScale.axis;
5083
        const vAxis = vScale.axis;
5084
        const labels = iScale.getLabels();
5085
        const singleScale = iScale === vScale;
5086
        const parsed = new Array(count);
5087
        let i, ilen, index;
5088
        for(i = 0, ilen = count; i < ilen; ++i){
5089
            index = i + start;
5090
            parsed[i] = {
5091
                [iAxis]: singleScale || iScale.parse(labels[index], index),
5092
                [vAxis]: vScale.parse(data[index], index)
5093
            };
5094
        }
5095
        return parsed;
5096
    }
5097
 parseArrayData(meta, data, start, count) {
5098
        const { xScale , yScale  } = meta;
5099
        const parsed = new Array(count);
5100
        let i, ilen, index, item;
5101
        for(i = 0, ilen = count; i < ilen; ++i){
5102
            index = i + start;
5103
            item = data[index];
5104
            parsed[i] = {
5105
                x: xScale.parse(item[0], index),
5106
                y: yScale.parse(item[1], index)
5107
            };
5108
        }
5109
        return parsed;
5110
    }
5111
 parseObjectData(meta, data, start, count) {
5112
        const { xScale , yScale  } = meta;
5113
        const { xAxisKey ='x' , yAxisKey ='y'  } = this._parsing;
5114
        const parsed = new Array(count);
5115
        let i, ilen, index, item;
5116
        for(i = 0, ilen = count; i < ilen; ++i){
5117
            index = i + start;
5118
            item = data[index];
5119
            parsed[i] = {
5120
                x: xScale.parse(resolveObjectKey(item, xAxisKey), index),
5121
                y: yScale.parse(resolveObjectKey(item, yAxisKey), index)
5122
            };
5123
        }
5124
        return parsed;
5125
    }
5126
 getParsed(index) {
5127
        return this._cachedMeta._parsed[index];
5128
    }
5129
 getDataElement(index) {
5130
        return this._cachedMeta.data[index];
5131
    }
5132
 applyStack(scale, parsed, mode) {
5133
        const chart = this.chart;
5134
        const meta = this._cachedMeta;
5135
        const value = parsed[scale.axis];
5136
        const stack = {
5137
            keys: getSortedDatasetIndices(chart, true),
5138
            values: parsed._stacks[scale.axis]._visualValues
5139
        };
5140
        return applyStack(stack, value, meta.index, {
5141
            mode
5142
        });
5143
    }
5144
 updateRangeFromParsed(range, scale, parsed, stack) {
5145
        const parsedValue = parsed[scale.axis];
5146
        let value = parsedValue === null ? NaN : parsedValue;
5147
        const values = stack && parsed._stacks[scale.axis];
5148
        if (stack && values) {
5149
            stack.values = values;
5150
            value = applyStack(stack, parsedValue, this._cachedMeta.index);
5151
        }
5152
        range.min = Math.min(range.min, value);
5153
        range.max = Math.max(range.max, value);
5154
    }
5155
 getMinMax(scale, canStack) {
5156
        const meta = this._cachedMeta;
5157
        const _parsed = meta._parsed;
5158
        const sorted = meta._sorted && scale === meta.iScale;
5159
        const ilen = _parsed.length;
5160
        const otherScale = this._getOtherScale(scale);
5161
        const stack = createStack(canStack, meta, this.chart);
5162
        const range = {
5163
            min: Number.POSITIVE_INFINITY,
5164
            max: Number.NEGATIVE_INFINITY
5165
        };
5166
        const { min: otherMin , max: otherMax  } = getUserBounds(otherScale);
5167
        let i, parsed;
5168
        function _skip() {
5169
            parsed = _parsed[i];
5170
            const otherValue = parsed[otherScale.axis];
5171
            return !isNumberFinite(parsed[scale.axis]) || otherMin > otherValue || otherMax < otherValue;
5172
        }
5173
        for(i = 0; i < ilen; ++i){
5174
            if (_skip()) {
5175
                continue;
5176
            }
5177
            this.updateRangeFromParsed(range, scale, parsed, stack);
5178
            if (sorted) {
5179
                break;
5180
            }
5181
        }
5182
        if (sorted) {
5183
            for(i = ilen - 1; i >= 0; --i){
5184
                if (_skip()) {
5185
                    continue;
5186
                }
5187
                this.updateRangeFromParsed(range, scale, parsed, stack);
5188
                break;
5189
            }
5190
        }
5191
        return range;
5192
    }
5193
    getAllParsedValues(scale) {
5194
        const parsed = this._cachedMeta._parsed;
5195
        const values = [];
5196
        let i, ilen, value;
5197
        for(i = 0, ilen = parsed.length; i < ilen; ++i){
5198
            value = parsed[i][scale.axis];
5199
            if (isNumberFinite(value)) {
5200
                values.push(value);
5201
            }
5202
        }
5203
        return values;
5204
    }
5205
 getMaxOverflow() {
5206
        return false;
5207
    }
5208
 getLabelAndValue(index) {
5209
        const meta = this._cachedMeta;
5210
        const iScale = meta.iScale;
5211
        const vScale = meta.vScale;
5212
        const parsed = this.getParsed(index);
5213
        return {
5214
            label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.axis]) : '',
5215
            value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.axis]) : ''
5216
        };
5217
    }
5218
 _update(mode) {
5219
        const meta = this._cachedMeta;
5220
        this.update(mode || 'default');
5221
        meta._clip = toClip(valueOrDefault(this.options.clip, defaultClip(meta.xScale, meta.yScale, this.getMaxOverflow())));
5222
    }
5223
 update(mode) {}
5224
    draw() {
5225
        const ctx = this._ctx;
5226
        const chart = this.chart;
5227
        const meta = this._cachedMeta;
5228
        const elements = meta.data || [];
5229
        const area = chart.chartArea;
5230
        const active = [];
5231
        const start = this._drawStart || 0;
5232
        const count = this._drawCount || elements.length - start;
5233
        const drawActiveElementsOnTop = this.options.drawActiveElementsOnTop;
5234
        let i;
5235
        if (meta.dataset) {
5236
            meta.dataset.draw(ctx, area, start, count);
5237
        }
5238
        for(i = start; i < start + count; ++i){
5239
            const element = elements[i];
5240
            if (element.hidden) {
5241
                continue;
5242
            }
5243
            if (element.active && drawActiveElementsOnTop) {
5244
                active.push(element);
5245
            } else {
5246
                element.draw(ctx, area);
5247
            }
5248
        }
5249
        for(i = 0; i < active.length; ++i){
5250
            active[i].draw(ctx, area);
5251
        }
5252
    }
5253
 getStyle(index, active) {
5254
        const mode = active ? 'active' : 'default';
5255
        return index === undefined && this._cachedMeta.dataset ? this.resolveDatasetElementOptions(mode) : this.resolveDataElementOptions(index || 0, mode);
5256
    }
5257
 getContext(index, active, mode) {
5258
        const dataset = this.getDataset();
5259
        let context;
5260
        if (index >= 0 && index < this._cachedMeta.data.length) {
5261
            const element = this._cachedMeta.data[index];
5262
            context = element.$context || (element.$context = createDataContext(this.getContext(), index, element));
5263
            context.parsed = this.getParsed(index);
5264
            context.raw = dataset.data[index];
5265
            context.index = context.dataIndex = index;
5266
        } else {
5267
            context = this.$context || (this.$context = createDatasetContext(this.chart.getContext(), this.index));
5268
            context.dataset = dataset;
5269
            context.index = context.datasetIndex = this.index;
5270
        }
5271
        context.active = !!active;
5272
        context.mode = mode;
5273
        return context;
5274
    }
5275
 resolveDatasetElementOptions(mode) {
5276
        return this._resolveElementOptions(this.datasetElementType.id, mode);
5277
    }
5278
 resolveDataElementOptions(index, mode) {
5279
        return this._resolveElementOptions(this.dataElementType.id, mode, index);
5280
    }
5281
 _resolveElementOptions(elementType, mode = 'default', index) {
5282
        const active = mode === 'active';
5283
        const cache = this._cachedDataOpts;
5284
        const cacheKey = elementType + '-' + mode;
5285
        const cached = cache[cacheKey];
5286
        const sharing = this.enableOptionSharing && defined(index);
5287
        if (cached) {
5288
            return cloneIfNotShared(cached, sharing);
5289
        }
5290
        const config = this.chart.config;
5291
        const scopeKeys = config.datasetElementScopeKeys(this._type, elementType);
5292
        const prefixes = active ? [
5293
            `${elementType}Hover`,
5294
            'hover',
5295
            elementType,
5296
            ''
5297
        ] : [
5298
            elementType,
5299
            ''
5300
        ];
5301
        const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);
5302
        const names = Object.keys(defaults.elements[elementType]);
5303
        const context = ()=>this.getContext(index, active, mode);
5304
        const values = config.resolveNamedOptions(scopes, names, context, prefixes);
5305
        if (values.$shared) {
5306
            values.$shared = sharing;
5307
            cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing));
5308
        }
5309
        return values;
5310
    }
5311
 _resolveAnimations(index, transition, active) {
5312
        const chart = this.chart;
5313
        const cache = this._cachedDataOpts;
5314
        const cacheKey = `animation-${transition}`;
5315
        const cached = cache[cacheKey];
5316
        if (cached) {
5317
            return cached;
5318
        }
5319
        let options;
5320
        if (chart.options.animation !== false) {
5321
            const config = this.chart.config;
5322
            const scopeKeys = config.datasetAnimationScopeKeys(this._type, transition);
5323
            const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);
5324
            options = config.createResolver(scopes, this.getContext(index, active, transition));
5325
        }
5326
        const animations = new Animations(chart, options && options.animations);
5327
        if (options && options._cacheable) {
5328
            cache[cacheKey] = Object.freeze(animations);
5329
        }
5330
        return animations;
5331
    }
5332
 getSharedOptions(options) {
5333
        if (!options.$shared) {
5334
            return;
5335
        }
5336
        return this._sharedOptions || (this._sharedOptions = Object.assign({}, options));
5337
    }
5338
 includeOptions(mode, sharedOptions) {
5339
        return !sharedOptions || isDirectUpdateMode(mode) || this.chart._animationsDisabled;
5340
    }
5341
 _getSharedOptions(start, mode) {
5342
        const firstOpts = this.resolveDataElementOptions(start, mode);
5343
        const previouslySharedOptions = this._sharedOptions;
5344
        const sharedOptions = this.getSharedOptions(firstOpts);
5345
        const includeOptions = this.includeOptions(mode, sharedOptions) || sharedOptions !== previouslySharedOptions;
5346
        this.updateSharedOptions(sharedOptions, mode, firstOpts);
5347
        return {
5348
            sharedOptions,
5349
            includeOptions
5350
        };
5351
    }
5352
 updateElement(element, index, properties, mode) {
5353
        if (isDirectUpdateMode(mode)) {
5354
            Object.assign(element, properties);
5355
        } else {
5356
            this._resolveAnimations(index, mode).update(element, properties);
5357
        }
5358
    }
5359
 updateSharedOptions(sharedOptions, mode, newOptions) {
5360
        if (sharedOptions && !isDirectUpdateMode(mode)) {
5361
            this._resolveAnimations(undefined, mode).update(sharedOptions, newOptions);
5362
        }
5363
    }
5364
 _setStyle(element, index, mode, active) {
5365
        element.active = active;
5366
        const options = this.getStyle(index, active);
5367
        this._resolveAnimations(index, mode, active).update(element, {
5368
            options: !active && this.getSharedOptions(options) || options
5369
        });
5370
    }
5371
    removeHoverStyle(element, datasetIndex, index) {
5372
        this._setStyle(element, index, 'active', false);
5373
    }
5374
    setHoverStyle(element, datasetIndex, index) {
5375
        this._setStyle(element, index, 'active', true);
5376
    }
5377
 _removeDatasetHoverStyle() {
5378
        const element = this._cachedMeta.dataset;
5379
        if (element) {
5380
            this._setStyle(element, undefined, 'active', false);
5381
        }
5382
    }
5383
 _setDatasetHoverStyle() {
5384
        const element = this._cachedMeta.dataset;
5385
        if (element) {
5386
            this._setStyle(element, undefined, 'active', true);
5387
        }
5388
    }
5389
 _resyncElements(resetNewElements) {
5390
        const data = this._data;
5391
        const elements = this._cachedMeta.data;
5392
        for (const [method, arg1, arg2] of this._syncList){
5393
            this[method](arg1, arg2);
5394
        }
5395
        this._syncList = [];
5396
        const numMeta = elements.length;
5397
        const numData = data.length;
5398
        const count = Math.min(numData, numMeta);
5399
        if (count) {
5400
            this.parse(0, count);
5401
        }
5402
        if (numData > numMeta) {
5403
            this._insertElements(numMeta, numData - numMeta, resetNewElements);
5404
        } else if (numData < numMeta) {
5405
            this._removeElements(numData, numMeta - numData);
5406
        }
5407
    }
5408
 _insertElements(start, count, resetNewElements = true) {
5409
        const meta = this._cachedMeta;
5410
        const data = meta.data;
5411
        const end = start + count;
5412
        let i;
5413
        const move = (arr)=>{
5414
            arr.length += count;
5415
            for(i = arr.length - 1; i >= end; i--){
5416
                arr[i] = arr[i - count];
5417
            }
5418
        };
5419
        move(data);
5420
        for(i = start; i < end; ++i){
5421
            data[i] = new this.dataElementType();
5422
        }
5423
        if (this._parsing) {
5424
            move(meta._parsed);
5425
        }
5426
        this.parse(start, count);
5427
        if (resetNewElements) {
5428
            this.updateElements(data, start, count, 'reset');
5429
        }
5430
    }
5431
    updateElements(element, start, count, mode) {}
5432
 _removeElements(start, count) {
5433
        const meta = this._cachedMeta;
5434
        if (this._parsing) {
5435
            const removed = meta._parsed.splice(start, count);
5436
            if (meta._stacked) {
5437
                clearStacks(meta, removed);
5438
            }
5439
        }
5440
        meta.data.splice(start, count);
5441
    }
5442
 _sync(args) {
5443
        if (this._parsing) {
5444
            this._syncList.push(args);
5445
        } else {
5446
            const [method, arg1, arg2] = args;
5447
            this[method](arg1, arg2);
5448
        }
5449
        this.chart._dataChanges.push([
5450
            this.index,
5451
            ...args
5452
        ]);
5453
    }
5454
    _onDataPush() {
5455
        const count = arguments.length;
5456
        this._sync([
5457
            '_insertElements',
5458
            this.getDataset().data.length - count,
5459
            count
5460
        ]);
5461
    }
5462
    _onDataPop() {
5463
        this._sync([
5464
            '_removeElements',
5465
            this._cachedMeta.data.length - 1,
5466
            1
5467
        ]);
5468
    }
5469
    _onDataShift() {
5470
        this._sync([
5471
            '_removeElements',
5472
            0,
5473
            1
5474
        ]);
5475
    }
5476
    _onDataSplice(start, count) {
5477
        if (count) {
5478
            this._sync([
5479
                '_removeElements',
5480
                start,
5481
                count
5482
            ]);
5483
        }
5484
        const newCount = arguments.length - 2;
5485
        if (newCount) {
5486
            this._sync([
5487
                '_insertElements',
5488
                start,
5489
                newCount
5490
            ]);
5491
        }
5492
    }
5493
    _onDataUnshift() {
5494
        this._sync([
5495
            '_insertElements',
5496
            0,
5497
            arguments.length
5498
        ]);
5499
    }
5500
}
5501
 
5502
class Element {
5503
    static defaults = {};
5504
    static defaultRoutes = undefined;
5505
    x;
5506
    y;
5507
    active = false;
5508
    options;
5509
    $animations;
5510
    tooltipPosition(useFinalPosition) {
5511
        const { x , y  } = this.getProps([
5512
            'x',
5513
            'y'
5514
        ], useFinalPosition);
5515
        return {
5516
            x,
5517
            y
5518
        };
5519
    }
5520
    hasValue() {
5521
        return isNumber(this.x) && isNumber(this.y);
5522
    }
5523
    getProps(props, final) {
5524
        const anims = this.$animations;
5525
        if (!final || !anims) {
5526
            // let's not create an object, if not needed
5527
            return this;
5528
        }
5529
        const ret = {};
5530
        props.forEach((prop)=>{
5531
            ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : this[prop];
5532
        });
5533
        return ret;
5534
    }
5535
}
5536
 
5537
function autoSkip(scale, ticks) {
5538
    const tickOpts = scale.options.ticks;
5539
    const determinedMaxTicks = determineMaxTicks(scale);
5540
    const ticksLimit = Math.min(tickOpts.maxTicksLimit || determinedMaxTicks, determinedMaxTicks);
5541
    const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];
5542
    const numMajorIndices = majorIndices.length;
5543
    const first = majorIndices[0];
5544
    const last = majorIndices[numMajorIndices - 1];
5545
    const newTicks = [];
5546
    if (numMajorIndices > ticksLimit) {
5547
        skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit);
5548
        return newTicks;
5549
    }
5550
    const spacing = calculateSpacing(majorIndices, ticks, ticksLimit);
5551
    if (numMajorIndices > 0) {
5552
        let i, ilen;
5553
        const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null;
5554
        skip(ticks, newTicks, spacing, isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
5555
        for(i = 0, ilen = numMajorIndices - 1; i < ilen; i++){
5556
            skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]);
5557
        }
5558
        skip(ticks, newTicks, spacing, last, isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
5559
        return newTicks;
5560
    }
5561
    skip(ticks, newTicks, spacing);
5562
    return newTicks;
5563
}
5564
function determineMaxTicks(scale) {
5565
    const offset = scale.options.offset;
5566
    const tickLength = scale._tickSize();
5567
    const maxScale = scale._length / tickLength + (offset ? 0 : 1);
5568
    const maxChart = scale._maxLength / tickLength;
5569
    return Math.floor(Math.min(maxScale, maxChart));
5570
}
5571
 function calculateSpacing(majorIndices, ticks, ticksLimit) {
5572
    const evenMajorSpacing = getEvenSpacing(majorIndices);
5573
    const spacing = ticks.length / ticksLimit;
5574
    if (!evenMajorSpacing) {
5575
        return Math.max(spacing, 1);
5576
    }
5577
    const factors = _factorize(evenMajorSpacing);
5578
    for(let i = 0, ilen = factors.length - 1; i < ilen; i++){
5579
        const factor = factors[i];
5580
        if (factor > spacing) {
5581
            return factor;
5582
        }
5583
    }
5584
    return Math.max(spacing, 1);
5585
}
5586
 function getMajorIndices(ticks) {
5587
    const result = [];
5588
    let i, ilen;
5589
    for(i = 0, ilen = ticks.length; i < ilen; i++){
5590
        if (ticks[i].major) {
5591
            result.push(i);
5592
        }
5593
    }
5594
    return result;
5595
}
5596
 function skipMajors(ticks, newTicks, majorIndices, spacing) {
5597
    let count = 0;
5598
    let next = majorIndices[0];
5599
    let i;
5600
    spacing = Math.ceil(spacing);
5601
    for(i = 0; i < ticks.length; i++){
5602
        if (i === next) {
5603
            newTicks.push(ticks[i]);
5604
            count++;
5605
            next = majorIndices[count * spacing];
5606
        }
5607
    }
5608
}
5609
 function skip(ticks, newTicks, spacing, majorStart, majorEnd) {
5610
    const start = valueOrDefault(majorStart, 0);
5611
    const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length);
5612
    let count = 0;
5613
    let length, i, next;
5614
    spacing = Math.ceil(spacing);
5615
    if (majorEnd) {
5616
        length = majorEnd - majorStart;
5617
        spacing = length / Math.floor(length / spacing);
5618
    }
5619
    next = start;
5620
    while(next < 0){
5621
        count++;
5622
        next = Math.round(start + count * spacing);
5623
    }
5624
    for(i = Math.max(start, 0); i < end; i++){
5625
        if (i === next) {
5626
            newTicks.push(ticks[i]);
5627
            count++;
5628
            next = Math.round(start + count * spacing);
5629
        }
5630
    }
5631
}
5632
 function getEvenSpacing(arr) {
5633
    const len = arr.length;
5634
    let i, diff;
5635
    if (len < 2) {
5636
        return false;
5637
    }
5638
    for(diff = arr[0], i = 1; i < len; ++i){
5639
        if (arr[i] - arr[i - 1] !== diff) {
5640
            return false;
5641
        }
5642
    }
5643
    return diff;
5644
}
5645
 
5646
const reverseAlign = (align)=>align === 'left' ? 'right' : align === 'right' ? 'left' : align;
5647
const offsetFromEdge = (scale, edge, offset)=>edge === 'top' || edge === 'left' ? scale[edge] + offset : scale[edge] - offset;
5648
const getTicksLimit = (ticksLength, maxTicksLimit)=>Math.min(maxTicksLimit || ticksLength, ticksLength);
5649
 function sample(arr, numItems) {
5650
    const result = [];
5651
    const increment = arr.length / numItems;
5652
    const len = arr.length;
5653
    let i = 0;
5654
    for(; i < len; i += increment){
5655
        result.push(arr[Math.floor(i)]);
5656
    }
5657
    return result;
5658
}
5659
 function getPixelForGridLine(scale, index, offsetGridLines) {
5660
    const length = scale.ticks.length;
5661
    const validIndex = Math.min(index, length - 1);
5662
    const start = scale._startPixel;
5663
    const end = scale._endPixel;
5664
    const epsilon = 1e-6;
5665
    let lineValue = scale.getPixelForTick(validIndex);
5666
    let offset;
5667
    if (offsetGridLines) {
5668
        if (length === 1) {
5669
            offset = Math.max(lineValue - start, end - lineValue);
5670
        } else if (index === 0) {
5671
            offset = (scale.getPixelForTick(1) - lineValue) / 2;
5672
        } else {
5673
            offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2;
5674
        }
5675
        lineValue += validIndex < index ? offset : -offset;
5676
        if (lineValue < start - epsilon || lineValue > end + epsilon) {
5677
            return;
5678
        }
5679
    }
5680
    return lineValue;
5681
}
5682
 function garbageCollect(caches, length) {
5683
    each(caches, (cache)=>{
5684
        const gc = cache.gc;
5685
        const gcLen = gc.length / 2;
5686
        let i;
5687
        if (gcLen > length) {
5688
            for(i = 0; i < gcLen; ++i){
5689
                delete cache.data[gc[i]];
5690
            }
5691
            gc.splice(0, gcLen);
5692
        }
5693
    });
5694
}
5695
 function getTickMarkLength(options) {
5696
    return options.drawTicks ? options.tickLength : 0;
5697
}
5698
 function getTitleHeight(options, fallback) {
5699
    if (!options.display) {
5700
        return 0;
5701
    }
5702
    const font = toFont(options.font, fallback);
5703
    const padding = toPadding(options.padding);
5704
    const lines = isArray(options.text) ? options.text.length : 1;
5705
    return lines * font.lineHeight + padding.height;
5706
}
5707
function createScaleContext(parent, scale) {
5708
    return createContext(parent, {
5709
        scale,
5710
        type: 'scale'
5711
    });
5712
}
5713
function createTickContext(parent, index, tick) {
5714
    return createContext(parent, {
5715
        tick,
5716
        index,
5717
        type: 'tick'
5718
    });
5719
}
5720
function titleAlign(align, position, reverse) {
5721
     let ret = _toLeftRightCenter(align);
5722
    if (reverse && position !== 'right' || !reverse && position === 'right') {
5723
        ret = reverseAlign(ret);
5724
    }
5725
    return ret;
5726
}
5727
function titleArgs(scale, offset, position, align) {
5728
    const { top , left , bottom , right , chart  } = scale;
5729
    const { chartArea , scales  } = chart;
5730
    let rotation = 0;
5731
    let maxWidth, titleX, titleY;
5732
    const height = bottom - top;
5733
    const width = right - left;
5734
    if (scale.isHorizontal()) {
5735
        titleX = _alignStartEnd(align, left, right);
5736
        if (isObject(position)) {
5737
            const positionAxisID = Object.keys(position)[0];
5738
            const value = position[positionAxisID];
5739
            titleY = scales[positionAxisID].getPixelForValue(value) + height - offset;
5740
        } else if (position === 'center') {
5741
            titleY = (chartArea.bottom + chartArea.top) / 2 + height - offset;
5742
        } else {
5743
            titleY = offsetFromEdge(scale, position, offset);
5744
        }
5745
        maxWidth = right - left;
5746
    } else {
5747
        if (isObject(position)) {
5748
            const positionAxisID = Object.keys(position)[0];
5749
            const value = position[positionAxisID];
5750
            titleX = scales[positionAxisID].getPixelForValue(value) - width + offset;
5751
        } else if (position === 'center') {
5752
            titleX = (chartArea.left + chartArea.right) / 2 - width + offset;
5753
        } else {
5754
            titleX = offsetFromEdge(scale, position, offset);
5755
        }
5756
        titleY = _alignStartEnd(align, bottom, top);
5757
        rotation = position === 'left' ? -HALF_PI : HALF_PI;
5758
    }
5759
    return {
5760
        titleX,
5761
        titleY,
5762
        maxWidth,
5763
        rotation
5764
    };
5765
}
5766
class Scale extends Element {
5767
    constructor(cfg){
5768
        super();
5769
         this.id = cfg.id;
5770
         this.type = cfg.type;
5771
         this.options = undefined;
5772
         this.ctx = cfg.ctx;
5773
         this.chart = cfg.chart;
5774
         this.top = undefined;
5775
         this.bottom = undefined;
5776
         this.left = undefined;
5777
         this.right = undefined;
5778
         this.width = undefined;
5779
         this.height = undefined;
5780
        this._margins = {
5781
            left: 0,
5782
            right: 0,
5783
            top: 0,
5784
            bottom: 0
5785
        };
5786
         this.maxWidth = undefined;
5787
         this.maxHeight = undefined;
5788
         this.paddingTop = undefined;
5789
         this.paddingBottom = undefined;
5790
         this.paddingLeft = undefined;
5791
         this.paddingRight = undefined;
5792
         this.axis = undefined;
5793
         this.labelRotation = undefined;
5794
        this.min = undefined;
5795
        this.max = undefined;
5796
        this._range = undefined;
5797
         this.ticks = [];
5798
         this._gridLineItems = null;
5799
         this._labelItems = null;
5800
         this._labelSizes = null;
5801
        this._length = 0;
5802
        this._maxLength = 0;
5803
        this._longestTextCache = {};
5804
         this._startPixel = undefined;
5805
         this._endPixel = undefined;
5806
        this._reversePixels = false;
5807
        this._userMax = undefined;
5808
        this._userMin = undefined;
5809
        this._suggestedMax = undefined;
5810
        this._suggestedMin = undefined;
5811
        this._ticksLength = 0;
5812
        this._borderValue = 0;
5813
        this._cache = {};
5814
        this._dataLimitsCached = false;
5815
        this.$context = undefined;
5816
    }
5817
 init(options) {
5818
        this.options = options.setContext(this.getContext());
5819
        this.axis = options.axis;
5820
        this._userMin = this.parse(options.min);
5821
        this._userMax = this.parse(options.max);
5822
        this._suggestedMin = this.parse(options.suggestedMin);
5823
        this._suggestedMax = this.parse(options.suggestedMax);
5824
    }
5825
 parse(raw, index) {
5826
        return raw;
5827
    }
5828
 getUserBounds() {
5829
        let { _userMin , _userMax , _suggestedMin , _suggestedMax  } = this;
5830
        _userMin = finiteOrDefault(_userMin, Number.POSITIVE_INFINITY);
5831
        _userMax = finiteOrDefault(_userMax, Number.NEGATIVE_INFINITY);
5832
        _suggestedMin = finiteOrDefault(_suggestedMin, Number.POSITIVE_INFINITY);
5833
        _suggestedMax = finiteOrDefault(_suggestedMax, Number.NEGATIVE_INFINITY);
5834
        return {
5835
            min: finiteOrDefault(_userMin, _suggestedMin),
5836
            max: finiteOrDefault(_userMax, _suggestedMax),
5837
            minDefined: isNumberFinite(_userMin),
5838
            maxDefined: isNumberFinite(_userMax)
5839
        };
5840
    }
5841
 getMinMax(canStack) {
5842
        let { min , max , minDefined , maxDefined  } = this.getUserBounds();
5843
        let range;
5844
        if (minDefined && maxDefined) {
5845
            return {
5846
                min,
5847
                max
5848
            };
5849
        }
5850
        const metas = this.getMatchingVisibleMetas();
5851
        for(let i = 0, ilen = metas.length; i < ilen; ++i){
5852
            range = metas[i].controller.getMinMax(this, canStack);
5853
            if (!minDefined) {
5854
                min = Math.min(min, range.min);
5855
            }
5856
            if (!maxDefined) {
5857
                max = Math.max(max, range.max);
5858
            }
5859
        }
5860
        min = maxDefined && min > max ? max : min;
5861
        max = minDefined && min > max ? min : max;
5862
        return {
5863
            min: finiteOrDefault(min, finiteOrDefault(max, min)),
5864
            max: finiteOrDefault(max, finiteOrDefault(min, max))
5865
        };
5866
    }
5867
 getPadding() {
5868
        return {
5869
            left: this.paddingLeft || 0,
5870
            top: this.paddingTop || 0,
5871
            right: this.paddingRight || 0,
5872
            bottom: this.paddingBottom || 0
5873
        };
5874
    }
5875
 getTicks() {
5876
        return this.ticks;
5877
    }
5878
 getLabels() {
5879
        const data = this.chart.data;
5880
        return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || [];
5881
    }
5882
 getLabelItems(chartArea = this.chart.chartArea) {
5883
        const items = this._labelItems || (this._labelItems = this._computeLabelItems(chartArea));
5884
        return items;
5885
    }
5886
    beforeLayout() {
5887
        this._cache = {};
5888
        this._dataLimitsCached = false;
5889
    }
5890
    beforeUpdate() {
5891
        callback(this.options.beforeUpdate, [
5892
            this
5893
        ]);
5894
    }
5895
 update(maxWidth, maxHeight, margins) {
5896
        const { beginAtZero , grace , ticks: tickOpts  } = this.options;
5897
        const sampleSize = tickOpts.sampleSize;
5898
        this.beforeUpdate();
5899
        this.maxWidth = maxWidth;
5900
        this.maxHeight = maxHeight;
5901
        this._margins = margins = Object.assign({
5902
            left: 0,
5903
            right: 0,
5904
            top: 0,
5905
            bottom: 0
5906
        }, margins);
5907
        this.ticks = null;
5908
        this._labelSizes = null;
5909
        this._gridLineItems = null;
5910
        this._labelItems = null;
5911
        this.beforeSetDimensions();
5912
        this.setDimensions();
5913
        this.afterSetDimensions();
5914
        this._maxLength = this.isHorizontal() ? this.width + margins.left + margins.right : this.height + margins.top + margins.bottom;
5915
        if (!this._dataLimitsCached) {
5916
            this.beforeDataLimits();
5917
            this.determineDataLimits();
5918
            this.afterDataLimits();
5919
            this._range = _addGrace(this, grace, beginAtZero);
5920
            this._dataLimitsCached = true;
5921
        }
5922
        this.beforeBuildTicks();
5923
        this.ticks = this.buildTicks() || [];
5924
        this.afterBuildTicks();
5925
        const samplingEnabled = sampleSize < this.ticks.length;
5926
        this._convertTicksToLabels(samplingEnabled ? sample(this.ticks, sampleSize) : this.ticks);
5927
        this.configure();
5928
        this.beforeCalculateLabelRotation();
5929
        this.calculateLabelRotation();
5930
        this.afterCalculateLabelRotation();
5931
        if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto')) {
5932
            this.ticks = autoSkip(this, this.ticks);
5933
            this._labelSizes = null;
5934
            this.afterAutoSkip();
5935
        }
5936
        if (samplingEnabled) {
5937
            this._convertTicksToLabels(this.ticks);
5938
        }
5939
        this.beforeFit();
5940
        this.fit();
5941
        this.afterFit();
5942
        this.afterUpdate();
5943
    }
5944
 configure() {
5945
        let reversePixels = this.options.reverse;
5946
        let startPixel, endPixel;
5947
        if (this.isHorizontal()) {
5948
            startPixel = this.left;
5949
            endPixel = this.right;
5950
        } else {
5951
            startPixel = this.top;
5952
            endPixel = this.bottom;
5953
            reversePixels = !reversePixels;
5954
        }
5955
        this._startPixel = startPixel;
5956
        this._endPixel = endPixel;
5957
        this._reversePixels = reversePixels;
5958
        this._length = endPixel - startPixel;
5959
        this._alignToPixels = this.options.alignToPixels;
5960
    }
5961
    afterUpdate() {
5962
        callback(this.options.afterUpdate, [
5963
            this
5964
        ]);
5965
    }
5966
    beforeSetDimensions() {
5967
        callback(this.options.beforeSetDimensions, [
5968
            this
5969
        ]);
5970
    }
5971
    setDimensions() {
5972
        if (this.isHorizontal()) {
5973
            this.width = this.maxWidth;
5974
            this.left = 0;
5975
            this.right = this.width;
5976
        } else {
5977
            this.height = this.maxHeight;
5978
            this.top = 0;
5979
            this.bottom = this.height;
5980
        }
5981
        this.paddingLeft = 0;
5982
        this.paddingTop = 0;
5983
        this.paddingRight = 0;
5984
        this.paddingBottom = 0;
5985
    }
5986
    afterSetDimensions() {
5987
        callback(this.options.afterSetDimensions, [
5988
            this
5989
        ]);
5990
    }
5991
    _callHooks(name) {
5992
        this.chart.notifyPlugins(name, this.getContext());
5993
        callback(this.options[name], [
5994
            this
5995
        ]);
5996
    }
5997
    beforeDataLimits() {
5998
        this._callHooks('beforeDataLimits');
5999
    }
6000
    determineDataLimits() {}
6001
    afterDataLimits() {
6002
        this._callHooks('afterDataLimits');
6003
    }
6004
    beforeBuildTicks() {
6005
        this._callHooks('beforeBuildTicks');
6006
    }
6007
 buildTicks() {
6008
        return [];
6009
    }
6010
    afterBuildTicks() {
6011
        this._callHooks('afterBuildTicks');
6012
    }
6013
    beforeTickToLabelConversion() {
6014
        callback(this.options.beforeTickToLabelConversion, [
6015
            this
6016
        ]);
6017
    }
6018
 generateTickLabels(ticks) {
6019
        const tickOpts = this.options.ticks;
6020
        let i, ilen, tick;
6021
        for(i = 0, ilen = ticks.length; i < ilen; i++){
6022
            tick = ticks[i];
6023
            tick.label = callback(tickOpts.callback, [
6024
                tick.value,
6025
                i,
6026
                ticks
6027
            ], this);
6028
        }
6029
    }
6030
    afterTickToLabelConversion() {
6031
        callback(this.options.afterTickToLabelConversion, [
6032
            this
6033
        ]);
6034
    }
6035
    beforeCalculateLabelRotation() {
6036
        callback(this.options.beforeCalculateLabelRotation, [
6037
            this
6038
        ]);
6039
    }
6040
    calculateLabelRotation() {
6041
        const options = this.options;
6042
        const tickOpts = options.ticks;
6043
        const numTicks = getTicksLimit(this.ticks.length, options.ticks.maxTicksLimit);
6044
        const minRotation = tickOpts.minRotation || 0;
6045
        const maxRotation = tickOpts.maxRotation;
6046
        let labelRotation = minRotation;
6047
        let tickWidth, maxHeight, maxLabelDiagonal;
6048
        if (!this._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !this.isHorizontal()) {
6049
            this.labelRotation = minRotation;
6050
            return;
6051
        }
6052
        const labelSizes = this._getLabelSizes();
6053
        const maxLabelWidth = labelSizes.widest.width;
6054
        const maxLabelHeight = labelSizes.highest.height;
6055
        const maxWidth = _limitValue(this.chart.width - maxLabelWidth, 0, this.maxWidth);
6056
        tickWidth = options.offset ? this.maxWidth / numTicks : maxWidth / (numTicks - 1);
6057
        if (maxLabelWidth + 6 > tickWidth) {
6058
            tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1));
6059
            maxHeight = this.maxHeight - getTickMarkLength(options.grid) - tickOpts.padding - getTitleHeight(options.title, this.chart.options.font);
6060
            maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight);
6061
            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))));
6062
            labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation));
6063
        }
6064
        this.labelRotation = labelRotation;
6065
    }
6066
    afterCalculateLabelRotation() {
6067
        callback(this.options.afterCalculateLabelRotation, [
6068
            this
6069
        ]);
6070
    }
6071
    afterAutoSkip() {}
6072
    beforeFit() {
6073
        callback(this.options.beforeFit, [
6074
            this
6075
        ]);
6076
    }
6077
    fit() {
6078
        const minSize = {
6079
            width: 0,
6080
            height: 0
6081
        };
6082
        const { chart , options: { ticks: tickOpts , title: titleOpts , grid: gridOpts  }  } = this;
6083
        const display = this._isVisible();
6084
        const isHorizontal = this.isHorizontal();
6085
        if (display) {
6086
            const titleHeight = getTitleHeight(titleOpts, chart.options.font);
6087
            if (isHorizontal) {
6088
                minSize.width = this.maxWidth;
6089
                minSize.height = getTickMarkLength(gridOpts) + titleHeight;
6090
            } else {
6091
                minSize.height = this.maxHeight;
6092
                minSize.width = getTickMarkLength(gridOpts) + titleHeight;
6093
            }
6094
            if (tickOpts.display && this.ticks.length) {
6095
                const { first , last , widest , highest  } = this._getLabelSizes();
6096
                const tickPadding = tickOpts.padding * 2;
6097
                const angleRadians = toRadians(this.labelRotation);
6098
                const cos = Math.cos(angleRadians);
6099
                const sin = Math.sin(angleRadians);
6100
                if (isHorizontal) {
6101
                    const labelHeight = tickOpts.mirror ? 0 : sin * widest.width + cos * highest.height;
6102
                    minSize.height = Math.min(this.maxHeight, minSize.height + labelHeight + tickPadding);
6103
                } else {
6104
                    const labelWidth = tickOpts.mirror ? 0 : cos * widest.width + sin * highest.height;
6105
                    minSize.width = Math.min(this.maxWidth, minSize.width + labelWidth + tickPadding);
6106
                }
6107
                this._calculatePadding(first, last, sin, cos);
6108
            }
6109
        }
6110
        this._handleMargins();
6111
        if (isHorizontal) {
6112
            this.width = this._length = chart.width - this._margins.left - this._margins.right;
6113
            this.height = minSize.height;
6114
        } else {
6115
            this.width = minSize.width;
6116
            this.height = this._length = chart.height - this._margins.top - this._margins.bottom;
6117
        }
6118
    }
6119
    _calculatePadding(first, last, sin, cos) {
6120
        const { ticks: { align , padding  } , position  } = this.options;
6121
        const isRotated = this.labelRotation !== 0;
6122
        const labelsBelowTicks = position !== 'top' && this.axis === 'x';
6123
        if (this.isHorizontal()) {
6124
            const offsetLeft = this.getPixelForTick(0) - this.left;
6125
            const offsetRight = this.right - this.getPixelForTick(this.ticks.length - 1);
6126
            let paddingLeft = 0;
6127
            let paddingRight = 0;
6128
            if (isRotated) {
6129
                if (labelsBelowTicks) {
6130
                    paddingLeft = cos * first.width;
6131
                    paddingRight = sin * last.height;
6132
                } else {
6133
                    paddingLeft = sin * first.height;
6134
                    paddingRight = cos * last.width;
6135
                }
6136
            } else if (align === 'start') {
6137
                paddingRight = last.width;
6138
            } else if (align === 'end') {
6139
                paddingLeft = first.width;
6140
            } else if (align !== 'inner') {
6141
                paddingLeft = first.width / 2;
6142
                paddingRight = last.width / 2;
6143
            }
6144
            this.paddingLeft = Math.max((paddingLeft - offsetLeft + padding) * this.width / (this.width - offsetLeft), 0);
6145
            this.paddingRight = Math.max((paddingRight - offsetRight + padding) * this.width / (this.width - offsetRight), 0);
6146
        } else {
6147
            let paddingTop = last.height / 2;
6148
            let paddingBottom = first.height / 2;
6149
            if (align === 'start') {
6150
                paddingTop = 0;
6151
                paddingBottom = first.height;
6152
            } else if (align === 'end') {
6153
                paddingTop = last.height;
6154
                paddingBottom = 0;
6155
            }
6156
            this.paddingTop = paddingTop + padding;
6157
            this.paddingBottom = paddingBottom + padding;
6158
        }
6159
    }
6160
 _handleMargins() {
6161
        if (this._margins) {
6162
            this._margins.left = Math.max(this.paddingLeft, this._margins.left);
6163
            this._margins.top = Math.max(this.paddingTop, this._margins.top);
6164
            this._margins.right = Math.max(this.paddingRight, this._margins.right);
6165
            this._margins.bottom = Math.max(this.paddingBottom, this._margins.bottom);
6166
        }
6167
    }
6168
    afterFit() {
6169
        callback(this.options.afterFit, [
6170
            this
6171
        ]);
6172
    }
6173
 isHorizontal() {
6174
        const { axis , position  } = this.options;
6175
        return position === 'top' || position === 'bottom' || axis === 'x';
6176
    }
6177
 isFullSize() {
6178
        return this.options.fullSize;
6179
    }
6180
 _convertTicksToLabels(ticks) {
6181
        this.beforeTickToLabelConversion();
6182
        this.generateTickLabels(ticks);
6183
        let i, ilen;
6184
        for(i = 0, ilen = ticks.length; i < ilen; i++){
6185
            if (isNullOrUndef(ticks[i].label)) {
6186
                ticks.splice(i, 1);
6187
                ilen--;
6188
                i--;
6189
            }
6190
        }
6191
        this.afterTickToLabelConversion();
6192
    }
6193
 _getLabelSizes() {
6194
        let labelSizes = this._labelSizes;
6195
        if (!labelSizes) {
6196
            const sampleSize = this.options.ticks.sampleSize;
6197
            let ticks = this.ticks;
6198
            if (sampleSize < ticks.length) {
6199
                ticks = sample(ticks, sampleSize);
6200
            }
6201
            this._labelSizes = labelSizes = this._computeLabelSizes(ticks, ticks.length, this.options.ticks.maxTicksLimit);
6202
        }
6203
        return labelSizes;
6204
    }
6205
 _computeLabelSizes(ticks, length, maxTicksLimit) {
6206
        const { ctx , _longestTextCache: caches  } = this;
6207
        const widths = [];
6208
        const heights = [];
6209
        const increment = Math.floor(length / getTicksLimit(length, maxTicksLimit));
6210
        let widestLabelSize = 0;
6211
        let highestLabelSize = 0;
6212
        let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel;
6213
        for(i = 0; i < length; i += increment){
6214
            label = ticks[i].label;
6215
            tickFont = this._resolveTickFontOptions(i);
6216
            ctx.font = fontString = tickFont.string;
6217
            cache = caches[fontString] = caches[fontString] || {
6218
                data: {},
6219
                gc: []
6220
            };
6221
            lineHeight = tickFont.lineHeight;
6222
            width = height = 0;
6223
            if (!isNullOrUndef(label) && !isArray(label)) {
6224
                width = _measureText(ctx, cache.data, cache.gc, width, label);
6225
                height = lineHeight;
6226
            } else if (isArray(label)) {
6227
                for(j = 0, jlen = label.length; j < jlen; ++j){
6228
                    nestedLabel =  label[j];
6229
                    if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) {
6230
                        width = _measureText(ctx, cache.data, cache.gc, width, nestedLabel);
6231
                        height += lineHeight;
6232
                    }
6233
                }
6234
            }
6235
            widths.push(width);
6236
            heights.push(height);
6237
            widestLabelSize = Math.max(width, widestLabelSize);
6238
            highestLabelSize = Math.max(height, highestLabelSize);
6239
        }
6240
        garbageCollect(caches, length);
6241
        const widest = widths.indexOf(widestLabelSize);
6242
        const highest = heights.indexOf(highestLabelSize);
6243
        const valueAt = (idx)=>({
6244
                width: widths[idx] || 0,
6245
                height: heights[idx] || 0
6246
            });
6247
        return {
6248
            first: valueAt(0),
6249
            last: valueAt(length - 1),
6250
            widest: valueAt(widest),
6251
            highest: valueAt(highest),
6252
            widths,
6253
            heights
6254
        };
6255
    }
6256
 getLabelForValue(value) {
6257
        return value;
6258
    }
6259
 getPixelForValue(value, index) {
6260
        return NaN;
6261
    }
6262
 getValueForPixel(pixel) {}
6263
 getPixelForTick(index) {
6264
        const ticks = this.ticks;
6265
        if (index < 0 || index > ticks.length - 1) {
6266
            return null;
6267
        }
6268
        return this.getPixelForValue(ticks[index].value);
6269
    }
6270
 getPixelForDecimal(decimal) {
6271
        if (this._reversePixels) {
6272
            decimal = 1 - decimal;
6273
        }
6274
        const pixel = this._startPixel + decimal * this._length;
6275
        return _int16Range(this._alignToPixels ? _alignPixel(this.chart, pixel, 0) : pixel);
6276
    }
6277
 getDecimalForPixel(pixel) {
6278
        const decimal = (pixel - this._startPixel) / this._length;
6279
        return this._reversePixels ? 1 - decimal : decimal;
6280
    }
6281
 getBasePixel() {
6282
        return this.getPixelForValue(this.getBaseValue());
6283
    }
6284
 getBaseValue() {
6285
        const { min , max  } = this;
6286
        return min < 0 && max < 0 ? max : min > 0 && max > 0 ? min : 0;
6287
    }
6288
 getContext(index) {
6289
        const ticks = this.ticks || [];
6290
        if (index >= 0 && index < ticks.length) {
6291
            const tick = ticks[index];
6292
            return tick.$context || (tick.$context = createTickContext(this.getContext(), index, tick));
6293
        }
6294
        return this.$context || (this.$context = createScaleContext(this.chart.getContext(), this));
6295
    }
6296
 _tickSize() {
6297
        const optionTicks = this.options.ticks;
6298
        const rot = toRadians(this.labelRotation);
6299
        const cos = Math.abs(Math.cos(rot));
6300
        const sin = Math.abs(Math.sin(rot));
6301
        const labelSizes = this._getLabelSizes();
6302
        const padding = optionTicks.autoSkipPadding || 0;
6303
        const w = labelSizes ? labelSizes.widest.width + padding : 0;
6304
        const h = labelSizes ? labelSizes.highest.height + padding : 0;
6305
        return this.isHorizontal() ? h * cos > w * sin ? w / cos : h / sin : h * sin < w * cos ? h / cos : w / sin;
6306
    }
6307
 _isVisible() {
6308
        const display = this.options.display;
6309
        if (display !== 'auto') {
6310
            return !!display;
6311
        }
6312
        return this.getMatchingVisibleMetas().length > 0;
6313
    }
6314
 _computeGridLineItems(chartArea) {
6315
        const axis = this.axis;
6316
        const chart = this.chart;
6317
        const options = this.options;
6318
        const { grid , position , border  } = options;
6319
        const offset = grid.offset;
6320
        const isHorizontal = this.isHorizontal();
6321
        const ticks = this.ticks;
6322
        const ticksLength = ticks.length + (offset ? 1 : 0);
6323
        const tl = getTickMarkLength(grid);
6324
        const items = [];
6325
        const borderOpts = border.setContext(this.getContext());
6326
        const axisWidth = borderOpts.display ? borderOpts.width : 0;
6327
        const axisHalfWidth = axisWidth / 2;
6328
        const alignBorderValue = function(pixel) {
6329
            return _alignPixel(chart, pixel, axisWidth);
6330
        };
6331
        let borderValue, i, lineValue, alignedLineValue;
6332
        let tx1, ty1, tx2, ty2, x1, y1, x2, y2;
6333
        if (position === 'top') {
6334
            borderValue = alignBorderValue(this.bottom);
6335
            ty1 = this.bottom - tl;
6336
            ty2 = borderValue - axisHalfWidth;
6337
            y1 = alignBorderValue(chartArea.top) + axisHalfWidth;
6338
            y2 = chartArea.bottom;
6339
        } else if (position === 'bottom') {
6340
            borderValue = alignBorderValue(this.top);
6341
            y1 = chartArea.top;
6342
            y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth;
6343
            ty1 = borderValue + axisHalfWidth;
6344
            ty2 = this.top + tl;
6345
        } else if (position === 'left') {
6346
            borderValue = alignBorderValue(this.right);
6347
            tx1 = this.right - tl;
6348
            tx2 = borderValue - axisHalfWidth;
6349
            x1 = alignBorderValue(chartArea.left) + axisHalfWidth;
6350
            x2 = chartArea.right;
6351
        } else if (position === 'right') {
6352
            borderValue = alignBorderValue(this.left);
6353
            x1 = chartArea.left;
6354
            x2 = alignBorderValue(chartArea.right) - axisHalfWidth;
6355
            tx1 = borderValue + axisHalfWidth;
6356
            tx2 = this.left + tl;
6357
        } else if (axis === 'x') {
6358
            if (position === 'center') {
6359
                borderValue = alignBorderValue((chartArea.top + chartArea.bottom) / 2 + 0.5);
6360
            } else if (isObject(position)) {
6361
                const positionAxisID = Object.keys(position)[0];
6362
                const value = position[positionAxisID];
6363
                borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));
6364
            }
6365
            y1 = chartArea.top;
6366
            y2 = chartArea.bottom;
6367
            ty1 = borderValue + axisHalfWidth;
6368
            ty2 = ty1 + tl;
6369
        } else if (axis === 'y') {
6370
            if (position === 'center') {
6371
                borderValue = alignBorderValue((chartArea.left + chartArea.right) / 2);
6372
            } else if (isObject(position)) {
6373
                const positionAxisID = Object.keys(position)[0];
6374
                const value = position[positionAxisID];
6375
                borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));
6376
            }
6377
            tx1 = borderValue - axisHalfWidth;
6378
            tx2 = tx1 - tl;
6379
            x1 = chartArea.left;
6380
            x2 = chartArea.right;
6381
        }
6382
        const limit = valueOrDefault(options.ticks.maxTicksLimit, ticksLength);
6383
        const step = Math.max(1, Math.ceil(ticksLength / limit));
6384
        for(i = 0; i < ticksLength; i += step){
6385
            const context = this.getContext(i);
6386
            const optsAtIndex = grid.setContext(context);
6387
            const optsAtIndexBorder = border.setContext(context);
6388
            const lineWidth = optsAtIndex.lineWidth;
6389
            const lineColor = optsAtIndex.color;
6390
            const borderDash = optsAtIndexBorder.dash || [];
6391
            const borderDashOffset = optsAtIndexBorder.dashOffset;
6392
            const tickWidth = optsAtIndex.tickWidth;
6393
            const tickColor = optsAtIndex.tickColor;
6394
            const tickBorderDash = optsAtIndex.tickBorderDash || [];
6395
            const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset;
6396
            lineValue = getPixelForGridLine(this, i, offset);
6397
            if (lineValue === undefined) {
6398
                continue;
6399
            }
6400
            alignedLineValue = _alignPixel(chart, lineValue, lineWidth);
6401
            if (isHorizontal) {
6402
                tx1 = tx2 = x1 = x2 = alignedLineValue;
6403
            } else {
6404
                ty1 = ty2 = y1 = y2 = alignedLineValue;
6405
            }
6406
            items.push({
6407
                tx1,
6408
                ty1,
6409
                tx2,
6410
                ty2,
6411
                x1,
6412
                y1,
6413
                x2,
6414
                y2,
6415
                width: lineWidth,
6416
                color: lineColor,
6417
                borderDash,
6418
                borderDashOffset,
6419
                tickWidth,
6420
                tickColor,
6421
                tickBorderDash,
6422
                tickBorderDashOffset
6423
            });
6424
        }
6425
        this._ticksLength = ticksLength;
6426
        this._borderValue = borderValue;
6427
        return items;
6428
    }
6429
 _computeLabelItems(chartArea) {
6430
        const axis = this.axis;
6431
        const options = this.options;
6432
        const { position , ticks: optionTicks  } = options;
6433
        const isHorizontal = this.isHorizontal();
6434
        const ticks = this.ticks;
6435
        const { align , crossAlign , padding , mirror  } = optionTicks;
6436
        const tl = getTickMarkLength(options.grid);
6437
        const tickAndPadding = tl + padding;
6438
        const hTickAndPadding = mirror ? -padding : tickAndPadding;
6439
        const rotation = -toRadians(this.labelRotation);
6440
        const items = [];
6441
        let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;
6442
        let textBaseline = 'middle';
6443
        if (position === 'top') {
6444
            y = this.bottom - hTickAndPadding;
6445
            textAlign = this._getXAxisLabelAlignment();
6446
        } else if (position === 'bottom') {
6447
            y = this.top + hTickAndPadding;
6448
            textAlign = this._getXAxisLabelAlignment();
6449
        } else if (position === 'left') {
6450
            const ret = this._getYAxisLabelAlignment(tl);
6451
            textAlign = ret.textAlign;
6452
            x = ret.x;
6453
        } else if (position === 'right') {
6454
            const ret = this._getYAxisLabelAlignment(tl);
6455
            textAlign = ret.textAlign;
6456
            x = ret.x;
6457
        } else if (axis === 'x') {
6458
            if (position === 'center') {
6459
                y = (chartArea.top + chartArea.bottom) / 2 + tickAndPadding;
6460
            } else if (isObject(position)) {
6461
                const positionAxisID = Object.keys(position)[0];
6462
                const value = position[positionAxisID];
6463
                y = this.chart.scales[positionAxisID].getPixelForValue(value) + tickAndPadding;
6464
            }
6465
            textAlign = this._getXAxisLabelAlignment();
6466
        } else if (axis === 'y') {
6467
            if (position === 'center') {
6468
                x = (chartArea.left + chartArea.right) / 2 - tickAndPadding;
6469
            } else if (isObject(position)) {
6470
                const positionAxisID = Object.keys(position)[0];
6471
                const value = position[positionAxisID];
6472
                x = this.chart.scales[positionAxisID].getPixelForValue(value);
6473
            }
6474
            textAlign = this._getYAxisLabelAlignment(tl).textAlign;
6475
        }
6476
        if (axis === 'y') {
6477
            if (align === 'start') {
6478
                textBaseline = 'top';
6479
            } else if (align === 'end') {
6480
                textBaseline = 'bottom';
6481
            }
6482
        }
6483
        const labelSizes = this._getLabelSizes();
6484
        for(i = 0, ilen = ticks.length; i < ilen; ++i){
6485
            tick = ticks[i];
6486
            label = tick.label;
6487
            const optsAtIndex = optionTicks.setContext(this.getContext(i));
6488
            pixel = this.getPixelForTick(i) + optionTicks.labelOffset;
6489
            font = this._resolveTickFontOptions(i);
6490
            lineHeight = font.lineHeight;
6491
            lineCount = isArray(label) ? label.length : 1;
6492
            const halfCount = lineCount / 2;
6493
            const color = optsAtIndex.color;
6494
            const strokeColor = optsAtIndex.textStrokeColor;
6495
            const strokeWidth = optsAtIndex.textStrokeWidth;
6496
            let tickTextAlign = textAlign;
6497
            if (isHorizontal) {
6498
                x = pixel;
6499
                if (textAlign === 'inner') {
6500
                    if (i === ilen - 1) {
6501
                        tickTextAlign = !this.options.reverse ? 'right' : 'left';
6502
                    } else if (i === 0) {
6503
                        tickTextAlign = !this.options.reverse ? 'left' : 'right';
6504
                    } else {
6505
                        tickTextAlign = 'center';
6506
                    }
6507
                }
6508
                if (position === 'top') {
6509
                    if (crossAlign === 'near' || rotation !== 0) {
6510
                        textOffset = -lineCount * lineHeight + lineHeight / 2;
6511
                    } else if (crossAlign === 'center') {
6512
                        textOffset = -labelSizes.highest.height / 2 - halfCount * lineHeight + lineHeight;
6513
                    } else {
6514
                        textOffset = -labelSizes.highest.height + lineHeight / 2;
6515
                    }
6516
                } else {
6517
                    if (crossAlign === 'near' || rotation !== 0) {
6518
                        textOffset = lineHeight / 2;
6519
                    } else if (crossAlign === 'center') {
6520
                        textOffset = labelSizes.highest.height / 2 - halfCount * lineHeight;
6521
                    } else {
6522
                        textOffset = labelSizes.highest.height - lineCount * lineHeight;
6523
                    }
6524
                }
6525
                if (mirror) {
6526
                    textOffset *= -1;
6527
                }
6528
                if (rotation !== 0 && !optsAtIndex.showLabelBackdrop) {
6529
                    x += lineHeight / 2 * Math.sin(rotation);
6530
                }
6531
            } else {
6532
                y = pixel;
6533
                textOffset = (1 - lineCount) * lineHeight / 2;
6534
            }
6535
            let backdrop;
6536
            if (optsAtIndex.showLabelBackdrop) {
6537
                const labelPadding = toPadding(optsAtIndex.backdropPadding);
6538
                const height = labelSizes.heights[i];
6539
                const width = labelSizes.widths[i];
6540
                let top = textOffset - labelPadding.top;
6541
                let left = 0 - labelPadding.left;
6542
                switch(textBaseline){
6543
                    case 'middle':
6544
                        top -= height / 2;
6545
                        break;
6546
                    case 'bottom':
6547
                        top -= height;
6548
                        break;
6549
                }
6550
                switch(textAlign){
6551
                    case 'center':
6552
                        left -= width / 2;
6553
                        break;
6554
                    case 'right':
6555
                        left -= width;
6556
                        break;
6557
                    case 'inner':
6558
                        if (i === ilen - 1) {
6559
                            left -= width;
6560
                        } else if (i > 0) {
6561
                            left -= width / 2;
6562
                        }
6563
                        break;
6564
                }
6565
                backdrop = {
6566
                    left,
6567
                    top,
6568
                    width: width + labelPadding.width,
6569
                    height: height + labelPadding.height,
6570
                    color: optsAtIndex.backdropColor
6571
                };
6572
            }
6573
            items.push({
6574
                label,
6575
                font,
6576
                textOffset,
6577
                options: {
6578
                    rotation,
6579
                    color,
6580
                    strokeColor,
6581
                    strokeWidth,
6582
                    textAlign: tickTextAlign,
6583
                    textBaseline,
6584
                    translation: [
6585
                        x,
6586
                        y
6587
                    ],
6588
                    backdrop
6589
                }
6590
            });
6591
        }
6592
        return items;
6593
    }
6594
    _getXAxisLabelAlignment() {
6595
        const { position , ticks  } = this.options;
6596
        const rotation = -toRadians(this.labelRotation);
6597
        if (rotation) {
6598
            return position === 'top' ? 'left' : 'right';
6599
        }
6600
        let align = 'center';
6601
        if (ticks.align === 'start') {
6602
            align = 'left';
6603
        } else if (ticks.align === 'end') {
6604
            align = 'right';
6605
        } else if (ticks.align === 'inner') {
6606
            align = 'inner';
6607
        }
6608
        return align;
6609
    }
6610
    _getYAxisLabelAlignment(tl) {
6611
        const { position , ticks: { crossAlign , mirror , padding  }  } = this.options;
6612
        const labelSizes = this._getLabelSizes();
6613
        const tickAndPadding = tl + padding;
6614
        const widest = labelSizes.widest.width;
6615
        let textAlign;
6616
        let x;
6617
        if (position === 'left') {
6618
            if (mirror) {
6619
                x = this.right + padding;
6620
                if (crossAlign === 'near') {
6621
                    textAlign = 'left';
6622
                } else if (crossAlign === 'center') {
6623
                    textAlign = 'center';
6624
                    x += widest / 2;
6625
                } else {
6626
                    textAlign = 'right';
6627
                    x += widest;
6628
                }
6629
            } else {
6630
                x = this.right - tickAndPadding;
6631
                if (crossAlign === 'near') {
6632
                    textAlign = 'right';
6633
                } else if (crossAlign === 'center') {
6634
                    textAlign = 'center';
6635
                    x -= widest / 2;
6636
                } else {
6637
                    textAlign = 'left';
6638
                    x = this.left;
6639
                }
6640
            }
6641
        } else if (position === 'right') {
6642
            if (mirror) {
6643
                x = this.left + padding;
6644
                if (crossAlign === 'near') {
6645
                    textAlign = 'right';
6646
                } else if (crossAlign === 'center') {
6647
                    textAlign = 'center';
6648
                    x -= widest / 2;
6649
                } else {
6650
                    textAlign = 'left';
6651
                    x -= widest;
6652
                }
6653
            } else {
6654
                x = this.left + tickAndPadding;
6655
                if (crossAlign === 'near') {
6656
                    textAlign = 'left';
6657
                } else if (crossAlign === 'center') {
6658
                    textAlign = 'center';
6659
                    x += widest / 2;
6660
                } else {
6661
                    textAlign = 'right';
6662
                    x = this.right;
6663
                }
6664
            }
6665
        } else {
6666
            textAlign = 'right';
6667
        }
6668
        return {
6669
            textAlign,
6670
            x
6671
        };
6672
    }
6673
 _computeLabelArea() {
6674
        if (this.options.ticks.mirror) {
6675
            return;
6676
        }
6677
        const chart = this.chart;
6678
        const position = this.options.position;
6679
        if (position === 'left' || position === 'right') {
6680
            return {
6681
                top: 0,
6682
                left: this.left,
6683
                bottom: chart.height,
6684
                right: this.right
6685
            };
6686
        }
6687
        if (position === 'top' || position === 'bottom') {
6688
            return {
6689
                top: this.top,
6690
                left: 0,
6691
                bottom: this.bottom,
6692
                right: chart.width
6693
            };
6694
        }
6695
    }
6696
 drawBackground() {
6697
        const { ctx , options: { backgroundColor  } , left , top , width , height  } = this;
6698
        if (backgroundColor) {
6699
            ctx.save();
6700
            ctx.fillStyle = backgroundColor;
6701
            ctx.fillRect(left, top, width, height);
6702
            ctx.restore();
6703
        }
6704
    }
6705
    getLineWidthForValue(value) {
6706
        const grid = this.options.grid;
6707
        if (!this._isVisible() || !grid.display) {
6708
            return 0;
6709
        }
6710
        const ticks = this.ticks;
6711
        const index = ticks.findIndex((t)=>t.value === value);
6712
        if (index >= 0) {
6713
            const opts = grid.setContext(this.getContext(index));
6714
            return opts.lineWidth;
6715
        }
6716
        return 0;
6717
    }
6718
 drawGrid(chartArea) {
6719
        const grid = this.options.grid;
6720
        const ctx = this.ctx;
6721
        const items = this._gridLineItems || (this._gridLineItems = this._computeGridLineItems(chartArea));
6722
        let i, ilen;
6723
        const drawLine = (p1, p2, style)=>{
6724
            if (!style.width || !style.color) {
6725
                return;
6726
            }
6727
            ctx.save();
6728
            ctx.lineWidth = style.width;
6729
            ctx.strokeStyle = style.color;
6730
            ctx.setLineDash(style.borderDash || []);
6731
            ctx.lineDashOffset = style.borderDashOffset;
6732
            ctx.beginPath();
6733
            ctx.moveTo(p1.x, p1.y);
6734
            ctx.lineTo(p2.x, p2.y);
6735
            ctx.stroke();
6736
            ctx.restore();
6737
        };
6738
        if (grid.display) {
6739
            for(i = 0, ilen = items.length; i < ilen; ++i){
6740
                const item = items[i];
6741
                if (grid.drawOnChartArea) {
6742
                    drawLine({
6743
                        x: item.x1,
6744
                        y: item.y1
6745
                    }, {
6746
                        x: item.x2,
6747
                        y: item.y2
6748
                    }, item);
6749
                }
6750
                if (grid.drawTicks) {
6751
                    drawLine({
6752
                        x: item.tx1,
6753
                        y: item.ty1
6754
                    }, {
6755
                        x: item.tx2,
6756
                        y: item.ty2
6757
                    }, {
6758
                        color: item.tickColor,
6759
                        width: item.tickWidth,
6760
                        borderDash: item.tickBorderDash,
6761
                        borderDashOffset: item.tickBorderDashOffset
6762
                    });
6763
                }
6764
            }
6765
        }
6766
    }
6767
 drawBorder() {
6768
        const { chart , ctx , options: { border , grid  }  } = this;
6769
        const borderOpts = border.setContext(this.getContext());
6770
        const axisWidth = border.display ? borderOpts.width : 0;
6771
        if (!axisWidth) {
6772
            return;
6773
        }
6774
        const lastLineWidth = grid.setContext(this.getContext(0)).lineWidth;
6775
        const borderValue = this._borderValue;
6776
        let x1, x2, y1, y2;
6777
        if (this.isHorizontal()) {
6778
            x1 = _alignPixel(chart, this.left, axisWidth) - axisWidth / 2;
6779
            x2 = _alignPixel(chart, this.right, lastLineWidth) + lastLineWidth / 2;
6780
            y1 = y2 = borderValue;
6781
        } else {
6782
            y1 = _alignPixel(chart, this.top, axisWidth) - axisWidth / 2;
6783
            y2 = _alignPixel(chart, this.bottom, lastLineWidth) + lastLineWidth / 2;
6784
            x1 = x2 = borderValue;
6785
        }
6786
        ctx.save();
6787
        ctx.lineWidth = borderOpts.width;
6788
        ctx.strokeStyle = borderOpts.color;
6789
        ctx.beginPath();
6790
        ctx.moveTo(x1, y1);
6791
        ctx.lineTo(x2, y2);
6792
        ctx.stroke();
6793
        ctx.restore();
6794
    }
6795
 drawLabels(chartArea) {
6796
        const optionTicks = this.options.ticks;
6797
        if (!optionTicks.display) {
6798
            return;
6799
        }
6800
        const ctx = this.ctx;
6801
        const area = this._computeLabelArea();
6802
        if (area) {
6803
            clipArea(ctx, area);
6804
        }
6805
        const items = this.getLabelItems(chartArea);
6806
        for (const item of items){
6807
            const renderTextOptions = item.options;
6808
            const tickFont = item.font;
6809
            const label = item.label;
6810
            const y = item.textOffset;
6811
            renderText(ctx, label, 0, y, tickFont, renderTextOptions);
6812
        }
6813
        if (area) {
6814
            unclipArea(ctx);
6815
        }
6816
    }
6817
 drawTitle() {
6818
        const { ctx , options: { position , title , reverse  }  } = this;
6819
        if (!title.display) {
6820
            return;
6821
        }
6822
        const font = toFont(title.font);
6823
        const padding = toPadding(title.padding);
6824
        const align = title.align;
6825
        let offset = font.lineHeight / 2;
6826
        if (position === 'bottom' || position === 'center' || isObject(position)) {
6827
            offset += padding.bottom;
6828
            if (isArray(title.text)) {
6829
                offset += font.lineHeight * (title.text.length - 1);
6830
            }
6831
        } else {
6832
            offset += padding.top;
6833
        }
6834
        const { titleX , titleY , maxWidth , rotation  } = titleArgs(this, offset, position, align);
6835
        renderText(ctx, title.text, 0, 0, font, {
6836
            color: title.color,
6837
            maxWidth,
6838
            rotation,
6839
            textAlign: titleAlign(align, position, reverse),
6840
            textBaseline: 'middle',
6841
            translation: [
6842
                titleX,
6843
                titleY
6844
            ]
6845
        });
6846
    }
6847
    draw(chartArea) {
6848
        if (!this._isVisible()) {
6849
            return;
6850
        }
6851
        this.drawBackground();
6852
        this.drawGrid(chartArea);
6853
        this.drawBorder();
6854
        this.drawTitle();
6855
        this.drawLabels(chartArea);
6856
    }
6857
 _layers() {
6858
        const opts = this.options;
6859
        const tz = opts.ticks && opts.ticks.z || 0;
6860
        const gz = valueOrDefault(opts.grid && opts.grid.z, -1);
6861
        const bz = valueOrDefault(opts.border && opts.border.z, 0);
6862
        if (!this._isVisible() || this.draw !== Scale.prototype.draw) {
6863
            return [
6864
                {
6865
                    z: tz,
6866
                    draw: (chartArea)=>{
6867
                        this.draw(chartArea);
6868
                    }
6869
                }
6870
            ];
6871
        }
6872
        return [
6873
            {
6874
                z: gz,
6875
                draw: (chartArea)=>{
6876
                    this.drawBackground();
6877
                    this.drawGrid(chartArea);
6878
                    this.drawTitle();
6879
                }
6880
            },
6881
            {
6882
                z: bz,
6883
                draw: ()=>{
6884
                    this.drawBorder();
6885
                }
6886
            },
6887
            {
6888
                z: tz,
6889
                draw: (chartArea)=>{
6890
                    this.drawLabels(chartArea);
6891
                }
6892
            }
6893
        ];
6894
    }
6895
 getMatchingVisibleMetas(type) {
6896
        const metas = this.chart.getSortedVisibleDatasetMetas();
6897
        const axisID = this.axis + 'AxisID';
6898
        const result = [];
6899
        let i, ilen;
6900
        for(i = 0, ilen = metas.length; i < ilen; ++i){
6901
            const meta = metas[i];
6902
            if (meta[axisID] === this.id && (!type || meta.type === type)) {
6903
                result.push(meta);
6904
            }
6905
        }
6906
        return result;
6907
    }
6908
 _resolveTickFontOptions(index) {
6909
        const opts = this.options.ticks.setContext(this.getContext(index));
6910
        return toFont(opts.font);
6911
    }
6912
 _maxDigits() {
6913
        const fontSize = this._resolveTickFontOptions(0).lineHeight;
6914
        return (this.isHorizontal() ? this.width : this.height) / fontSize;
6915
    }
6916
}
6917
 
6918
class TypedRegistry {
6919
    constructor(type, scope, override){
6920
        this.type = type;
6921
        this.scope = scope;
6922
        this.override = override;
6923
        this.items = Object.create(null);
6924
    }
6925
    isForType(type) {
6926
        return Object.prototype.isPrototypeOf.call(this.type.prototype, type.prototype);
6927
    }
6928
 register(item) {
6929
        const proto = Object.getPrototypeOf(item);
6930
        let parentScope;
6931
        if (isIChartComponent(proto)) {
6932
            parentScope = this.register(proto);
6933
        }
6934
        const items = this.items;
6935
        const id = item.id;
6936
        const scope = this.scope + '.' + id;
6937
        if (!id) {
6938
            throw new Error('class does not have id: ' + item);
6939
        }
6940
        if (id in items) {
6941
            return scope;
6942
        }
6943
        items[id] = item;
6944
        registerDefaults(item, scope, parentScope);
6945
        if (this.override) {
6946
            defaults.override(item.id, item.overrides);
6947
        }
6948
        return scope;
6949
    }
6950
 get(id) {
6951
        return this.items[id];
6952
    }
6953
 unregister(item) {
6954
        const items = this.items;
6955
        const id = item.id;
6956
        const scope = this.scope;
6957
        if (id in items) {
6958
            delete items[id];
6959
        }
6960
        if (scope && id in defaults[scope]) {
6961
            delete defaults[scope][id];
6962
            if (this.override) {
6963
                delete overrides[id];
6964
            }
6965
        }
6966
    }
6967
}
6968
function registerDefaults(item, scope, parentScope) {
6969
    const itemDefaults = merge(Object.create(null), [
6970
        parentScope ? defaults.get(parentScope) : {},
6971
        defaults.get(scope),
6972
        item.defaults
6973
    ]);
6974
    defaults.set(scope, itemDefaults);
6975
    if (item.defaultRoutes) {
6976
        routeDefaults(scope, item.defaultRoutes);
6977
    }
6978
    if (item.descriptors) {
6979
        defaults.describe(scope, item.descriptors);
6980
    }
6981
}
6982
function routeDefaults(scope, routes) {
6983
    Object.keys(routes).forEach((property)=>{
6984
        const propertyParts = property.split('.');
6985
        const sourceName = propertyParts.pop();
6986
        const sourceScope = [
6987
            scope
6988
        ].concat(propertyParts).join('.');
6989
        const parts = routes[property].split('.');
6990
        const targetName = parts.pop();
6991
        const targetScope = parts.join('.');
6992
        defaults.route(sourceScope, sourceName, targetScope, targetName);
6993
    });
6994
}
6995
function isIChartComponent(proto) {
6996
    return 'id' in proto && 'defaults' in proto;
6997
}
6998
 
6999
class Registry {
7000
    constructor(){
7001
        this.controllers = new TypedRegistry(DatasetController, 'datasets', true);
7002
        this.elements = new TypedRegistry(Element, 'elements');
7003
        this.plugins = new TypedRegistry(Object, 'plugins');
7004
        this.scales = new TypedRegistry(Scale, 'scales');
7005
        this._typedRegistries = [
7006
            this.controllers,
7007
            this.scales,
7008
            this.elements
7009
        ];
7010
    }
7011
 add(...args) {
7012
        this._each('register', args);
7013
    }
7014
    remove(...args) {
7015
        this._each('unregister', args);
7016
    }
7017
 addControllers(...args) {
7018
        this._each('register', args, this.controllers);
7019
    }
7020
 addElements(...args) {
7021
        this._each('register', args, this.elements);
7022
    }
7023
 addPlugins(...args) {
7024
        this._each('register', args, this.plugins);
7025
    }
7026
 addScales(...args) {
7027
        this._each('register', args, this.scales);
7028
    }
7029
 getController(id) {
7030
        return this._get(id, this.controllers, 'controller');
7031
    }
7032
 getElement(id) {
7033
        return this._get(id, this.elements, 'element');
7034
    }
7035
 getPlugin(id) {
7036
        return this._get(id, this.plugins, 'plugin');
7037
    }
7038
 getScale(id) {
7039
        return this._get(id, this.scales, 'scale');
7040
    }
7041
 removeControllers(...args) {
7042
        this._each('unregister', args, this.controllers);
7043
    }
7044
 removeElements(...args) {
7045
        this._each('unregister', args, this.elements);
7046
    }
7047
 removePlugins(...args) {
7048
        this._each('unregister', args, this.plugins);
7049
    }
7050
 removeScales(...args) {
7051
        this._each('unregister', args, this.scales);
7052
    }
7053
 _each(method, args, typedRegistry) {
7054
        [
7055
            ...args
7056
        ].forEach((arg)=>{
7057
            const reg = typedRegistry || this._getRegistryForType(arg);
7058
            if (typedRegistry || reg.isForType(arg) || reg === this.plugins && arg.id) {
7059
                this._exec(method, reg, arg);
7060
            } else {
7061
                each(arg, (item)=>{
7062
                    const itemReg = typedRegistry || this._getRegistryForType(item);
7063
                    this._exec(method, itemReg, item);
7064
                });
7065
            }
7066
        });
7067
    }
7068
 _exec(method, registry, component) {
7069
        const camelMethod = _capitalize(method);
7070
        callback(component['before' + camelMethod], [], component);
7071
        registry[method](component);
7072
        callback(component['after' + camelMethod], [], component);
7073
    }
7074
 _getRegistryForType(type) {
7075
        for(let i = 0; i < this._typedRegistries.length; i++){
7076
            const reg = this._typedRegistries[i];
7077
            if (reg.isForType(type)) {
7078
                return reg;
7079
            }
7080
        }
7081
        return this.plugins;
7082
    }
7083
 _get(id, typedRegistry, type) {
7084
        const item = typedRegistry.get(id);
7085
        if (item === undefined) {
7086
            throw new Error('"' + id + '" is not a registered ' + type + '.');
7087
        }
7088
        return item;
7089
    }
7090
}
7091
var registry = /* #__PURE__ */ new Registry();
7092
 
7093
class PluginService {
7094
    constructor(){
7095
        this._init = [];
7096
    }
7097
 notify(chart, hook, args, filter) {
7098
        if (hook === 'beforeInit') {
7099
            this._init = this._createDescriptors(chart, true);
7100
            this._notify(this._init, chart, 'install');
7101
        }
7102
        const descriptors = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart);
7103
        const result = this._notify(descriptors, chart, hook, args);
7104
        if (hook === 'afterDestroy') {
7105
            this._notify(descriptors, chart, 'stop');
7106
            this._notify(this._init, chart, 'uninstall');
7107
        }
7108
        return result;
7109
    }
7110
 _notify(descriptors, chart, hook, args) {
7111
        args = args || {};
7112
        for (const descriptor of descriptors){
7113
            const plugin = descriptor.plugin;
7114
            const method = plugin[hook];
7115
            const params = [
7116
                chart,
7117
                args,
7118
                descriptor.options
7119
            ];
7120
            if (callback(method, params, plugin) === false && args.cancelable) {
7121
                return false;
7122
            }
7123
        }
7124
        return true;
7125
    }
7126
    invalidate() {
7127
        if (!isNullOrUndef(this._cache)) {
7128
            this._oldCache = this._cache;
7129
            this._cache = undefined;
7130
        }
7131
    }
7132
 _descriptors(chart) {
7133
        if (this._cache) {
7134
            return this._cache;
7135
        }
7136
        const descriptors = this._cache = this._createDescriptors(chart);
7137
        this._notifyStateChanges(chart);
7138
        return descriptors;
7139
    }
7140
    _createDescriptors(chart, all) {
7141
        const config = chart && chart.config;
7142
        const options = valueOrDefault(config.options && config.options.plugins, {});
7143
        const plugins = allPlugins(config);
7144
        return options === false && !all ? [] : createDescriptors(chart, plugins, options, all);
7145
    }
7146
 _notifyStateChanges(chart) {
7147
        const previousDescriptors = this._oldCache || [];
7148
        const descriptors = this._cache;
7149
        const diff = (a, b)=>a.filter((x)=>!b.some((y)=>x.plugin.id === y.plugin.id));
7150
        this._notify(diff(previousDescriptors, descriptors), chart, 'stop');
7151
        this._notify(diff(descriptors, previousDescriptors), chart, 'start');
7152
    }
7153
}
7154
 function allPlugins(config) {
7155
    const localIds = {};
7156
    const plugins = [];
7157
    const keys = Object.keys(registry.plugins.items);
7158
    for(let i = 0; i < keys.length; i++){
7159
        plugins.push(registry.getPlugin(keys[i]));
7160
    }
7161
    const local = config.plugins || [];
7162
    for(let i = 0; i < local.length; i++){
7163
        const plugin = local[i];
7164
        if (plugins.indexOf(plugin) === -1) {
7165
            plugins.push(plugin);
7166
            localIds[plugin.id] = true;
7167
        }
7168
    }
7169
    return {
7170
        plugins,
7171
        localIds
7172
    };
7173
}
7174
function getOpts(options, all) {
7175
    if (!all && options === false) {
7176
        return null;
7177
    }
7178
    if (options === true) {
7179
        return {};
7180
    }
7181
    return options;
7182
}
7183
function createDescriptors(chart, { plugins , localIds  }, options, all) {
7184
    const result = [];
7185
    const context = chart.getContext();
7186
    for (const plugin of plugins){
7187
        const id = plugin.id;
7188
        const opts = getOpts(options[id], all);
7189
        if (opts === null) {
7190
            continue;
7191
        }
7192
        result.push({
7193
            plugin,
7194
            options: pluginOpts(chart.config, {
7195
                plugin,
7196
                local: localIds[id]
7197
            }, opts, context)
7198
        });
7199
    }
7200
    return result;
7201
}
7202
function pluginOpts(config, { plugin , local  }, opts, context) {
7203
    const keys = config.pluginScopeKeys(plugin);
7204
    const scopes = config.getOptionScopes(opts, keys);
7205
    if (local && plugin.defaults) {
7206
        scopes.push(plugin.defaults);
7207
    }
7208
    return config.createResolver(scopes, context, [
7209
        ''
7210
    ], {
7211
        scriptable: false,
7212
        indexable: false,
7213
        allKeys: true
7214
    });
7215
}
7216
 
7217
function getIndexAxis(type, options) {
7218
    const datasetDefaults = defaults.datasets[type] || {};
7219
    const datasetOptions = (options.datasets || {})[type] || {};
7220
    return datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || 'x';
7221
}
7222
function getAxisFromDefaultScaleID(id, indexAxis) {
7223
    let axis = id;
7224
    if (id === '_index_') {
7225
        axis = indexAxis;
7226
    } else if (id === '_value_') {
7227
        axis = indexAxis === 'x' ? 'y' : 'x';
7228
    }
7229
    return axis;
7230
}
7231
function getDefaultScaleIDFromAxis(axis, indexAxis) {
7232
    return axis === indexAxis ? '_index_' : '_value_';
7233
}
7234
function idMatchesAxis(id) {
7235
    if (id === 'x' || id === 'y' || id === 'r') {
7236
        return id;
7237
    }
7238
}
7239
function axisFromPosition(position) {
7240
    if (position === 'top' || position === 'bottom') {
7241
        return 'x';
7242
    }
7243
    if (position === 'left' || position === 'right') {
7244
        return 'y';
7245
    }
7246
}
7247
function determineAxis(id, ...scaleOptions) {
7248
    if (idMatchesAxis(id)) {
7249
        return id;
7250
    }
7251
    for (const opts of scaleOptions){
7252
        const axis = opts.axis || axisFromPosition(opts.position) || id.length > 1 && idMatchesAxis(id[0].toLowerCase());
7253
        if (axis) {
7254
            return axis;
7255
        }
7256
    }
7257
    throw new Error(`Cannot determine type of '${id}' axis. Please provide 'axis' or 'position' option.`);
7258
}
7259
function getAxisFromDataset(id, axis, dataset) {
7260
    if (dataset[axis + 'AxisID'] === id) {
7261
        return {
7262
            axis
7263
        };
7264
    }
7265
}
7266
function retrieveAxisFromDatasets(id, config) {
7267
    if (config.data && config.data.datasets) {
7268
        const boundDs = config.data.datasets.filter((d)=>d.xAxisID === id || d.yAxisID === id);
7269
        if (boundDs.length) {
7270
            return getAxisFromDataset(id, 'x', boundDs[0]) || getAxisFromDataset(id, 'y', boundDs[0]);
7271
        }
7272
    }
7273
    return {};
7274
}
7275
function mergeScaleConfig(config, options) {
7276
    const chartDefaults = overrides[config.type] || {
7277
        scales: {}
7278
    };
7279
    const configScales = options.scales || {};
7280
    const chartIndexAxis = getIndexAxis(config.type, options);
7281
    const scales = Object.create(null);
7282
    Object.keys(configScales).forEach((id)=>{
7283
        const scaleConf = configScales[id];
7284
        if (!isObject(scaleConf)) {
7285
            return console.error(`Invalid scale configuration for scale: ${id}`);
7286
        }
7287
        if (scaleConf._proxy) {
7288
            return console.warn(`Ignoring resolver passed as options for scale: ${id}`);
7289
        }
7290
        const axis = determineAxis(id, scaleConf, retrieveAxisFromDatasets(id, config), defaults.scales[scaleConf.type]);
7291
        const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis);
7292
        const defaultScaleOptions = chartDefaults.scales || {};
7293
        scales[id] = mergeIf(Object.create(null), [
7294
            {
7295
                axis
7296
            },
7297
            scaleConf,
7298
            defaultScaleOptions[axis],
7299
            defaultScaleOptions[defaultId]
7300
        ]);
7301
    });
7302
    config.data.datasets.forEach((dataset)=>{
7303
        const type = dataset.type || config.type;
7304
        const indexAxis = dataset.indexAxis || getIndexAxis(type, options);
7305
        const datasetDefaults = overrides[type] || {};
7306
        const defaultScaleOptions = datasetDefaults.scales || {};
7307
        Object.keys(defaultScaleOptions).forEach((defaultID)=>{
7308
            const axis = getAxisFromDefaultScaleID(defaultID, indexAxis);
7309
            const id = dataset[axis + 'AxisID'] || axis;
7310
            scales[id] = scales[id] || Object.create(null);
7311
            mergeIf(scales[id], [
7312
                {
7313
                    axis
7314
                },
7315
                configScales[id],
7316
                defaultScaleOptions[defaultID]
7317
            ]);
7318
        });
7319
    });
7320
    Object.keys(scales).forEach((key)=>{
7321
        const scale = scales[key];
7322
        mergeIf(scale, [
7323
            defaults.scales[scale.type],
7324
            defaults.scale
7325
        ]);
7326
    });
7327
    return scales;
7328
}
7329
function initOptions(config) {
7330
    const options = config.options || (config.options = {});
7331
    options.plugins = valueOrDefault(options.plugins, {});
7332
    options.scales = mergeScaleConfig(config, options);
7333
}
7334
function initData(data) {
7335
    data = data || {};
7336
    data.datasets = data.datasets || [];
7337
    data.labels = data.labels || [];
7338
    return data;
7339
}
7340
function initConfig(config) {
7341
    config = config || {};
7342
    config.data = initData(config.data);
7343
    initOptions(config);
7344
    return config;
7345
}
7346
const keyCache = new Map();
7347
const keysCached = new Set();
7348
function cachedKeys(cacheKey, generate) {
7349
    let keys = keyCache.get(cacheKey);
7350
    if (!keys) {
7351
        keys = generate();
7352
        keyCache.set(cacheKey, keys);
7353
        keysCached.add(keys);
7354
    }
7355
    return keys;
7356
}
7357
const addIfFound = (set, obj, key)=>{
7358
    const opts = resolveObjectKey(obj, key);
7359
    if (opts !== undefined) {
7360
        set.add(opts);
7361
    }
7362
};
7363
class Config {
7364
    constructor(config){
7365
        this._config = initConfig(config);
7366
        this._scopeCache = new Map();
7367
        this._resolverCache = new Map();
7368
    }
7369
    get platform() {
7370
        return this._config.platform;
7371
    }
7372
    get type() {
7373
        return this._config.type;
7374
    }
7375
    set type(type) {
7376
        this._config.type = type;
7377
    }
7378
    get data() {
7379
        return this._config.data;
7380
    }
7381
    set data(data) {
7382
        this._config.data = initData(data);
7383
    }
7384
    get options() {
7385
        return this._config.options;
7386
    }
7387
    set options(options) {
7388
        this._config.options = options;
7389
    }
7390
    get plugins() {
7391
        return this._config.plugins;
7392
    }
7393
    update() {
7394
        const config = this._config;
7395
        this.clearCache();
7396
        initOptions(config);
7397
    }
7398
    clearCache() {
7399
        this._scopeCache.clear();
7400
        this._resolverCache.clear();
7401
    }
7402
 datasetScopeKeys(datasetType) {
7403
        return cachedKeys(datasetType, ()=>[
7404
                [
7405
                    `datasets.${datasetType}`,
7406
                    ''
7407
                ]
7408
            ]);
7409
    }
7410
 datasetAnimationScopeKeys(datasetType, transition) {
7411
        return cachedKeys(`${datasetType}.transition.${transition}`, ()=>[
7412
                [
7413
                    `datasets.${datasetType}.transitions.${transition}`,
7414
                    `transitions.${transition}`
7415
                ],
7416
                [
7417
                    `datasets.${datasetType}`,
7418
                    ''
7419
                ]
7420
            ]);
7421
    }
7422
 datasetElementScopeKeys(datasetType, elementType) {
7423
        return cachedKeys(`${datasetType}-${elementType}`, ()=>[
7424
                [
7425
                    `datasets.${datasetType}.elements.${elementType}`,
7426
                    `datasets.${datasetType}`,
7427
                    `elements.${elementType}`,
7428
                    ''
7429
                ]
7430
            ]);
7431
    }
7432
 pluginScopeKeys(plugin) {
7433
        const id = plugin.id;
7434
        const type = this.type;
7435
        return cachedKeys(`${type}-plugin-${id}`, ()=>[
7436
                [
7437
                    `plugins.${id}`,
7438
                    ...plugin.additionalOptionScopes || []
7439
                ]
7440
            ]);
7441
    }
7442
 _cachedScopes(mainScope, resetCache) {
7443
        const _scopeCache = this._scopeCache;
7444
        let cache = _scopeCache.get(mainScope);
7445
        if (!cache || resetCache) {
7446
            cache = new Map();
7447
            _scopeCache.set(mainScope, cache);
7448
        }
7449
        return cache;
7450
    }
7451
 getOptionScopes(mainScope, keyLists, resetCache) {
7452
        const { options , type  } = this;
7453
        const cache = this._cachedScopes(mainScope, resetCache);
7454
        const cached = cache.get(keyLists);
7455
        if (cached) {
7456
            return cached;
7457
        }
7458
        const scopes = new Set();
7459
        keyLists.forEach((keys)=>{
7460
            if (mainScope) {
7461
                scopes.add(mainScope);
7462
                keys.forEach((key)=>addIfFound(scopes, mainScope, key));
7463
            }
7464
            keys.forEach((key)=>addIfFound(scopes, options, key));
7465
            keys.forEach((key)=>addIfFound(scopes, overrides[type] || {}, key));
7466
            keys.forEach((key)=>addIfFound(scopes, defaults, key));
7467
            keys.forEach((key)=>addIfFound(scopes, descriptors, key));
7468
        });
7469
        const array = Array.from(scopes);
7470
        if (array.length === 0) {
7471
            array.push(Object.create(null));
7472
        }
7473
        if (keysCached.has(keyLists)) {
7474
            cache.set(keyLists, array);
7475
        }
7476
        return array;
7477
    }
7478
 chartOptionScopes() {
7479
        const { options , type  } = this;
7480
        return [
7481
            options,
7482
            overrides[type] || {},
7483
            defaults.datasets[type] || {},
7484
            {
7485
                type
7486
            },
7487
            defaults,
7488
            descriptors
7489
        ];
7490
    }
7491
 resolveNamedOptions(scopes, names, context, prefixes = [
7492
        ''
7493
    ]) {
7494
        const result = {
7495
            $shared: true
7496
        };
7497
        const { resolver , subPrefixes  } = getResolver(this._resolverCache, scopes, prefixes);
7498
        let options = resolver;
7499
        if (needContext(resolver, names)) {
7500
            result.$shared = false;
7501
            context = isFunction(context) ? context() : context;
7502
            const subResolver = this.createResolver(scopes, context, subPrefixes);
7503
            options = _attachContext(resolver, context, subResolver);
7504
        }
7505
        for (const prop of names){
7506
            result[prop] = options[prop];
7507
        }
7508
        return result;
7509
    }
7510
 createResolver(scopes, context, prefixes = [
7511
        ''
7512
    ], descriptorDefaults) {
7513
        const { resolver  } = getResolver(this._resolverCache, scopes, prefixes);
7514
        return isObject(context) ? _attachContext(resolver, context, undefined, descriptorDefaults) : resolver;
7515
    }
7516
}
7517
function getResolver(resolverCache, scopes, prefixes) {
7518
    let cache = resolverCache.get(scopes);
7519
    if (!cache) {
7520
        cache = new Map();
7521
        resolverCache.set(scopes, cache);
7522
    }
7523
    const cacheKey = prefixes.join();
7524
    let cached = cache.get(cacheKey);
7525
    if (!cached) {
7526
        const resolver = _createResolver(scopes, prefixes);
7527
        cached = {
7528
            resolver,
7529
            subPrefixes: prefixes.filter((p)=>!p.toLowerCase().includes('hover'))
7530
        };
7531
        cache.set(cacheKey, cached);
7532
    }
7533
    return cached;
7534
}
7535
const hasFunction = (value)=>isObject(value) && Object.getOwnPropertyNames(value).some((key)=>isFunction(value[key]));
7536
function needContext(proxy, names) {
7537
    const { isScriptable , isIndexable  } = _descriptors(proxy);
7538
    for (const prop of names){
7539
        const scriptable = isScriptable(prop);
7540
        const indexable = isIndexable(prop);
7541
        const value = (indexable || scriptable) && proxy[prop];
7542
        if (scriptable && (isFunction(value) || hasFunction(value)) || indexable && isArray(value)) {
7543
            return true;
7544
        }
7545
    }
7546
    return false;
7547
}
7548
 
7549
var version = "4.4.2";
7550
 
7551
const KNOWN_POSITIONS = [
7552
    'top',
7553
    'bottom',
7554
    'left',
7555
    'right',
7556
    'chartArea'
7557
];
7558
function positionIsHorizontal(position, axis) {
7559
    return position === 'top' || position === 'bottom' || KNOWN_POSITIONS.indexOf(position) === -1 && axis === 'x';
7560
}
7561
function compare2Level(l1, l2) {
7562
    return function(a, b) {
7563
        return a[l1] === b[l1] ? a[l2] - b[l2] : a[l1] - b[l1];
7564
    };
7565
}
7566
function onAnimationsComplete(context) {
7567
    const chart = context.chart;
7568
    const animationOptions = chart.options.animation;
7569
    chart.notifyPlugins('afterRender');
7570
    callback(animationOptions && animationOptions.onComplete, [
7571
        context
7572
    ], chart);
7573
}
7574
function onAnimationProgress(context) {
7575
    const chart = context.chart;
7576
    const animationOptions = chart.options.animation;
7577
    callback(animationOptions && animationOptions.onProgress, [
7578
        context
7579
    ], chart);
7580
}
7581
 function getCanvas(item) {
7582
    if (_isDomSupported() && typeof item === 'string') {
7583
        item = document.getElementById(item);
7584
    } else if (item && item.length) {
7585
        item = item[0];
7586
    }
7587
    if (item && item.canvas) {
7588
        item = item.canvas;
7589
    }
7590
    return item;
7591
}
7592
const instances = {};
7593
const getChart = (key)=>{
7594
    const canvas = getCanvas(key);
7595
    return Object.values(instances).filter((c)=>c.canvas === canvas).pop();
7596
};
7597
function moveNumericKeys(obj, start, move) {
7598
    const keys = Object.keys(obj);
7599
    for (const key of keys){
7600
        const intKey = +key;
7601
        if (intKey >= start) {
7602
            const value = obj[key];
7603
            delete obj[key];
7604
            if (move > 0 || intKey > start) {
7605
                obj[intKey + move] = value;
7606
            }
7607
        }
7608
    }
7609
}
7610
 function determineLastEvent(e, lastEvent, inChartArea, isClick) {
7611
    if (!inChartArea || e.type === 'mouseout') {
7612
        return null;
7613
    }
7614
    if (isClick) {
7615
        return lastEvent;
7616
    }
7617
    return e;
7618
}
7619
function getSizeForArea(scale, chartArea, field) {
7620
    return scale.options.clip ? scale[field] : chartArea[field];
7621
}
7622
function getDatasetArea(meta, chartArea) {
7623
    const { xScale , yScale  } = meta;
7624
    if (xScale && yScale) {
7625
        return {
7626
            left: getSizeForArea(xScale, chartArea, 'left'),
7627
            right: getSizeForArea(xScale, chartArea, 'right'),
7628
            top: getSizeForArea(yScale, chartArea, 'top'),
7629
            bottom: getSizeForArea(yScale, chartArea, 'bottom')
7630
        };
7631
    }
7632
    return chartArea;
7633
}
7634
class Chart {
7635
    static defaults = defaults;
7636
    static instances = instances;
7637
    static overrides = overrides;
7638
    static registry = registry;
7639
    static version = version;
7640
    static getChart = getChart;
7641
    static register(...items) {
7642
        registry.add(...items);
7643
        invalidatePlugins();
7644
    }
7645
    static unregister(...items) {
7646
        registry.remove(...items);
7647
        invalidatePlugins();
7648
    }
7649
    constructor(item, userConfig){
7650
        const config = this.config = new Config(userConfig);
7651
        const initialCanvas = getCanvas(item);
7652
        const existingChart = getChart(initialCanvas);
7653
        if (existingChart) {
7654
            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.');
7655
        }
7656
        const options = config.createResolver(config.chartOptionScopes(), this.getContext());
7657
        this.platform = new (config.platform || _detectPlatform(initialCanvas))();
7658
        this.platform.updateConfig(config);
7659
        const context = this.platform.acquireContext(initialCanvas, options.aspectRatio);
7660
        const canvas = context && context.canvas;
7661
        const height = canvas && canvas.height;
7662
        const width = canvas && canvas.width;
7663
        this.id = uid();
7664
        this.ctx = context;
7665
        this.canvas = canvas;
7666
        this.width = width;
7667
        this.height = height;
7668
        this._options = options;
7669
        this._aspectRatio = this.aspectRatio;
7670
        this._layers = [];
7671
        this._metasets = [];
7672
        this._stacks = undefined;
7673
        this.boxes = [];
7674
        this.currentDevicePixelRatio = undefined;
7675
        this.chartArea = undefined;
7676
        this._active = [];
7677
        this._lastEvent = undefined;
7678
        this._listeners = {};
7679
         this._responsiveListeners = undefined;
7680
        this._sortedMetasets = [];
7681
        this.scales = {};
7682
        this._plugins = new PluginService();
7683
        this.$proxies = {};
7684
        this._hiddenIndices = {};
7685
        this.attached = false;
7686
        this._animationsDisabled = undefined;
7687
        this.$context = undefined;
7688
        this._doResize = debounce((mode)=>this.update(mode), options.resizeDelay || 0);
7689
        this._dataChanges = [];
7690
        instances[this.id] = this;
7691
        if (!context || !canvas) {
7692
            console.error("Failed to create chart: can't acquire context from the given item");
7693
            return;
7694
        }
7695
        animator.listen(this, 'complete', onAnimationsComplete);
7696
        animator.listen(this, 'progress', onAnimationProgress);
7697
        this._initialize();
7698
        if (this.attached) {
7699
            this.update();
7700
        }
7701
    }
7702
    get aspectRatio() {
7703
        const { options: { aspectRatio , maintainAspectRatio  } , width , height , _aspectRatio  } = this;
7704
        if (!isNullOrUndef(aspectRatio)) {
7705
            return aspectRatio;
7706
        }
7707
        if (maintainAspectRatio && _aspectRatio) {
7708
            return _aspectRatio;
7709
        }
7710
        return height ? width / height : null;
7711
    }
7712
    get data() {
7713
        return this.config.data;
7714
    }
7715
    set data(data) {
7716
        this.config.data = data;
7717
    }
7718
    get options() {
7719
        return this._options;
7720
    }
7721
    set options(options) {
7722
        this.config.options = options;
7723
    }
7724
    get registry() {
7725
        return registry;
7726
    }
7727
 _initialize() {
7728
        this.notifyPlugins('beforeInit');
7729
        if (this.options.responsive) {
7730
            this.resize();
7731
        } else {
7732
            retinaScale(this, this.options.devicePixelRatio);
7733
        }
7734
        this.bindEvents();
7735
        this.notifyPlugins('afterInit');
7736
        return this;
7737
    }
7738
    clear() {
7739
        clearCanvas(this.canvas, this.ctx);
7740
        return this;
7741
    }
7742
    stop() {
7743
        animator.stop(this);
7744
        return this;
7745
    }
7746
 resize(width, height) {
7747
        if (!animator.running(this)) {
7748
            this._resize(width, height);
7749
        } else {
7750
            this._resizeBeforeDraw = {
7751
                width,
7752
                height
7753
            };
7754
        }
7755
    }
7756
    _resize(width, height) {
7757
        const options = this.options;
7758
        const canvas = this.canvas;
7759
        const aspectRatio = options.maintainAspectRatio && this.aspectRatio;
7760
        const newSize = this.platform.getMaximumSize(canvas, width, height, aspectRatio);
7761
        const newRatio = options.devicePixelRatio || this.platform.getDevicePixelRatio();
7762
        const mode = this.width ? 'resize' : 'attach';
7763
        this.width = newSize.width;
7764
        this.height = newSize.height;
7765
        this._aspectRatio = this.aspectRatio;
7766
        if (!retinaScale(this, newRatio, true)) {
7767
            return;
7768
        }
7769
        this.notifyPlugins('resize', {
7770
            size: newSize
7771
        });
7772
        callback(options.onResize, [
7773
            this,
7774
            newSize
7775
        ], this);
7776
        if (this.attached) {
7777
            if (this._doResize(mode)) {
7778
                this.render();
7779
            }
7780
        }
7781
    }
7782
    ensureScalesHaveIDs() {
7783
        const options = this.options;
7784
        const scalesOptions = options.scales || {};
7785
        each(scalesOptions, (axisOptions, axisID)=>{
7786
            axisOptions.id = axisID;
7787
        });
7788
    }
7789
 buildOrUpdateScales() {
7790
        const options = this.options;
7791
        const scaleOpts = options.scales;
7792
        const scales = this.scales;
7793
        const updated = Object.keys(scales).reduce((obj, id)=>{
7794
            obj[id] = false;
7795
            return obj;
7796
        }, {});
7797
        let items = [];
7798
        if (scaleOpts) {
7799
            items = items.concat(Object.keys(scaleOpts).map((id)=>{
7800
                const scaleOptions = scaleOpts[id];
7801
                const axis = determineAxis(id, scaleOptions);
7802
                const isRadial = axis === 'r';
7803
                const isHorizontal = axis === 'x';
7804
                return {
7805
                    options: scaleOptions,
7806
                    dposition: isRadial ? 'chartArea' : isHorizontal ? 'bottom' : 'left',
7807
                    dtype: isRadial ? 'radialLinear' : isHorizontal ? 'category' : 'linear'
7808
                };
7809
            }));
7810
        }
7811
        each(items, (item)=>{
7812
            const scaleOptions = item.options;
7813
            const id = scaleOptions.id;
7814
            const axis = determineAxis(id, scaleOptions);
7815
            const scaleType = valueOrDefault(scaleOptions.type, item.dtype);
7816
            if (scaleOptions.position === undefined || positionIsHorizontal(scaleOptions.position, axis) !== positionIsHorizontal(item.dposition)) {
7817
                scaleOptions.position = item.dposition;
7818
            }
7819
            updated[id] = true;
7820
            let scale = null;
7821
            if (id in scales && scales[id].type === scaleType) {
7822
                scale = scales[id];
7823
            } else {
7824
                const scaleClass = registry.getScale(scaleType);
7825
                scale = new scaleClass({
7826
                    id,
7827
                    type: scaleType,
7828
                    ctx: this.ctx,
7829
                    chart: this
7830
                });
7831
                scales[scale.id] = scale;
7832
            }
7833
            scale.init(scaleOptions, options);
7834
        });
7835
        each(updated, (hasUpdated, id)=>{
7836
            if (!hasUpdated) {
7837
                delete scales[id];
7838
            }
7839
        });
7840
        each(scales, (scale)=>{
7841
            layouts.configure(this, scale, scale.options);
7842
            layouts.addBox(this, scale);
7843
        });
7844
    }
7845
 _updateMetasets() {
7846
        const metasets = this._metasets;
7847
        const numData = this.data.datasets.length;
7848
        const numMeta = metasets.length;
7849
        metasets.sort((a, b)=>a.index - b.index);
7850
        if (numMeta > numData) {
7851
            for(let i = numData; i < numMeta; ++i){
7852
                this._destroyDatasetMeta(i);
7853
            }
7854
            metasets.splice(numData, numMeta - numData);
7855
        }
7856
        this._sortedMetasets = metasets.slice(0).sort(compare2Level('order', 'index'));
7857
    }
7858
 _removeUnreferencedMetasets() {
7859
        const { _metasets: metasets , data: { datasets  }  } = this;
7860
        if (metasets.length > datasets.length) {
7861
            delete this._stacks;
7862
        }
7863
        metasets.forEach((meta, index)=>{
7864
            if (datasets.filter((x)=>x === meta._dataset).length === 0) {
7865
                this._destroyDatasetMeta(index);
7866
            }
7867
        });
7868
    }
7869
    buildOrUpdateControllers() {
7870
        const newControllers = [];
7871
        const datasets = this.data.datasets;
7872
        let i, ilen;
7873
        this._removeUnreferencedMetasets();
7874
        for(i = 0, ilen = datasets.length; i < ilen; i++){
7875
            const dataset = datasets[i];
7876
            let meta = this.getDatasetMeta(i);
7877
            const type = dataset.type || this.config.type;
7878
            if (meta.type && meta.type !== type) {
7879
                this._destroyDatasetMeta(i);
7880
                meta = this.getDatasetMeta(i);
7881
            }
7882
            meta.type = type;
7883
            meta.indexAxis = dataset.indexAxis || getIndexAxis(type, this.options);
7884
            meta.order = dataset.order || 0;
7885
            meta.index = i;
7886
            meta.label = '' + dataset.label;
7887
            meta.visible = this.isDatasetVisible(i);
7888
            if (meta.controller) {
7889
                meta.controller.updateIndex(i);
7890
                meta.controller.linkScales();
7891
            } else {
7892
                const ControllerClass = registry.getController(type);
7893
                const { datasetElementType , dataElementType  } = defaults.datasets[type];
7894
                Object.assign(ControllerClass, {
7895
                    dataElementType: registry.getElement(dataElementType),
7896
                    datasetElementType: datasetElementType && registry.getElement(datasetElementType)
7897
                });
7898
                meta.controller = new ControllerClass(this, i);
7899
                newControllers.push(meta.controller);
7900
            }
7901
        }
7902
        this._updateMetasets();
7903
        return newControllers;
7904
    }
7905
 _resetElements() {
7906
        each(this.data.datasets, (dataset, datasetIndex)=>{
7907
            this.getDatasetMeta(datasetIndex).controller.reset();
7908
        }, this);
7909
    }
7910
 reset() {
7911
        this._resetElements();
7912
        this.notifyPlugins('reset');
7913
    }
7914
    update(mode) {
7915
        const config = this.config;
7916
        config.update();
7917
        const options = this._options = config.createResolver(config.chartOptionScopes(), this.getContext());
7918
        const animsDisabled = this._animationsDisabled = !options.animation;
7919
        this._updateScales();
7920
        this._checkEventBindings();
7921
        this._updateHiddenIndices();
7922
        this._plugins.invalidate();
7923
        if (this.notifyPlugins('beforeUpdate', {
7924
            mode,
7925
            cancelable: true
7926
        }) === false) {
7927
            return;
7928
        }
7929
        const newControllers = this.buildOrUpdateControllers();
7930
        this.notifyPlugins('beforeElementsUpdate');
7931
        let minPadding = 0;
7932
        for(let i = 0, ilen = this.data.datasets.length; i < ilen; i++){
7933
            const { controller  } = this.getDatasetMeta(i);
7934
            const reset = !animsDisabled && newControllers.indexOf(controller) === -1;
7935
            controller.buildOrUpdateElements(reset);
7936
            minPadding = Math.max(+controller.getMaxOverflow(), minPadding);
7937
        }
7938
        minPadding = this._minPadding = options.layout.autoPadding ? minPadding : 0;
7939
        this._updateLayout(minPadding);
7940
        if (!animsDisabled) {
7941
            each(newControllers, (controller)=>{
7942
                controller.reset();
7943
            });
7944
        }
7945
        this._updateDatasets(mode);
7946
        this.notifyPlugins('afterUpdate', {
7947
            mode
7948
        });
7949
        this._layers.sort(compare2Level('z', '_idx'));
7950
        const { _active , _lastEvent  } = this;
7951
        if (_lastEvent) {
7952
            this._eventHandler(_lastEvent, true);
7953
        } else if (_active.length) {
7954
            this._updateHoverStyles(_active, _active, true);
7955
        }
7956
        this.render();
7957
    }
7958
 _updateScales() {
7959
        each(this.scales, (scale)=>{
7960
            layouts.removeBox(this, scale);
7961
        });
7962
        this.ensureScalesHaveIDs();
7963
        this.buildOrUpdateScales();
7964
    }
7965
 _checkEventBindings() {
7966
        const options = this.options;
7967
        const existingEvents = new Set(Object.keys(this._listeners));
7968
        const newEvents = new Set(options.events);
7969
        if (!setsEqual(existingEvents, newEvents) || !!this._responsiveListeners !== options.responsive) {
7970
            this.unbindEvents();
7971
            this.bindEvents();
7972
        }
7973
    }
7974
 _updateHiddenIndices() {
7975
        const { _hiddenIndices  } = this;
7976
        const changes = this._getUniformDataChanges() || [];
7977
        for (const { method , start , count  } of changes){
7978
            const move = method === '_removeElements' ? -count : count;
7979
            moveNumericKeys(_hiddenIndices, start, move);
7980
        }
7981
    }
7982
 _getUniformDataChanges() {
7983
        const _dataChanges = this._dataChanges;
7984
        if (!_dataChanges || !_dataChanges.length) {
7985
            return;
7986
        }
7987
        this._dataChanges = [];
7988
        const datasetCount = this.data.datasets.length;
7989
        const makeSet = (idx)=>new Set(_dataChanges.filter((c)=>c[0] === idx).map((c, i)=>i + ',' + c.splice(1).join(',')));
7990
        const changeSet = makeSet(0);
7991
        for(let i = 1; i < datasetCount; i++){
7992
            if (!setsEqual(changeSet, makeSet(i))) {
7993
                return;
7994
            }
7995
        }
7996
        return Array.from(changeSet).map((c)=>c.split(',')).map((a)=>({
7997
                method: a[1],
7998
                start: +a[2],
7999
                count: +a[3]
8000
            }));
8001
    }
8002
 _updateLayout(minPadding) {
8003
        if (this.notifyPlugins('beforeLayout', {
8004
            cancelable: true
8005
        }) === false) {
8006
            return;
8007
        }
8008
        layouts.update(this, this.width, this.height, minPadding);
8009
        const area = this.chartArea;
8010
        const noArea = area.width <= 0 || area.height <= 0;
8011
        this._layers = [];
8012
        each(this.boxes, (box)=>{
8013
            if (noArea && box.position === 'chartArea') {
8014
                return;
8015
            }
8016
            if (box.configure) {
8017
                box.configure();
8018
            }
8019
            this._layers.push(...box._layers());
8020
        }, this);
8021
        this._layers.forEach((item, index)=>{
8022
            item._idx = index;
8023
        });
8024
        this.notifyPlugins('afterLayout');
8025
    }
8026
 _updateDatasets(mode) {
8027
        if (this.notifyPlugins('beforeDatasetsUpdate', {
8028
            mode,
8029
            cancelable: true
8030
        }) === false) {
8031
            return;
8032
        }
8033
        for(let i = 0, ilen = this.data.datasets.length; i < ilen; ++i){
8034
            this.getDatasetMeta(i).controller.configure();
8035
        }
8036
        for(let i = 0, ilen = this.data.datasets.length; i < ilen; ++i){
8037
            this._updateDataset(i, isFunction(mode) ? mode({
8038
                datasetIndex: i
8039
            }) : mode);
8040
        }
8041
        this.notifyPlugins('afterDatasetsUpdate', {
8042
            mode
8043
        });
8044
    }
8045
 _updateDataset(index, mode) {
8046
        const meta = this.getDatasetMeta(index);
8047
        const args = {
8048
            meta,
8049
            index,
8050
            mode,
8051
            cancelable: true
8052
        };
8053
        if (this.notifyPlugins('beforeDatasetUpdate', args) === false) {
8054
            return;
8055
        }
8056
        meta.controller._update(mode);
8057
        args.cancelable = false;
8058
        this.notifyPlugins('afterDatasetUpdate', args);
8059
    }
8060
    render() {
8061
        if (this.notifyPlugins('beforeRender', {
8062
            cancelable: true
8063
        }) === false) {
8064
            return;
8065
        }
8066
        if (animator.has(this)) {
8067
            if (this.attached && !animator.running(this)) {
8068
                animator.start(this);
8069
            }
8070
        } else {
8071
            this.draw();
8072
            onAnimationsComplete({
8073
                chart: this
8074
            });
8075
        }
8076
    }
8077
    draw() {
8078
        let i;
8079
        if (this._resizeBeforeDraw) {
8080
            const { width , height  } = this._resizeBeforeDraw;
8081
            this._resize(width, height);
8082
            this._resizeBeforeDraw = null;
8083
        }
8084
        this.clear();
8085
        if (this.width <= 0 || this.height <= 0) {
8086
            return;
8087
        }
8088
        if (this.notifyPlugins('beforeDraw', {
8089
            cancelable: true
8090
        }) === false) {
8091
            return;
8092
        }
8093
        const layers = this._layers;
8094
        for(i = 0; i < layers.length && layers[i].z <= 0; ++i){
8095
            layers[i].draw(this.chartArea);
8096
        }
8097
        this._drawDatasets();
8098
        for(; i < layers.length; ++i){
8099
            layers[i].draw(this.chartArea);
8100
        }
8101
        this.notifyPlugins('afterDraw');
8102
    }
8103
 _getSortedDatasetMetas(filterVisible) {
8104
        const metasets = this._sortedMetasets;
8105
        const result = [];
8106
        let i, ilen;
8107
        for(i = 0, ilen = metasets.length; i < ilen; ++i){
8108
            const meta = metasets[i];
8109
            if (!filterVisible || meta.visible) {
8110
                result.push(meta);
8111
            }
8112
        }
8113
        return result;
8114
    }
8115
 getSortedVisibleDatasetMetas() {
8116
        return this._getSortedDatasetMetas(true);
8117
    }
8118
 _drawDatasets() {
8119
        if (this.notifyPlugins('beforeDatasetsDraw', {
8120
            cancelable: true
8121
        }) === false) {
8122
            return;
8123
        }
8124
        const metasets = this.getSortedVisibleDatasetMetas();
8125
        for(let i = metasets.length - 1; i >= 0; --i){
8126
            this._drawDataset(metasets[i]);
8127
        }
8128
        this.notifyPlugins('afterDatasetsDraw');
8129
    }
8130
 _drawDataset(meta) {
8131
        const ctx = this.ctx;
8132
        const clip = meta._clip;
8133
        const useClip = !clip.disabled;
8134
        const area = getDatasetArea(meta, this.chartArea);
8135
        const args = {
8136
            meta,
8137
            index: meta.index,
8138
            cancelable: true
8139
        };
8140
        if (this.notifyPlugins('beforeDatasetDraw', args) === false) {
8141
            return;
8142
        }
8143
        if (useClip) {
8144
            clipArea(ctx, {
8145
                left: clip.left === false ? 0 : area.left - clip.left,
8146
                right: clip.right === false ? this.width : area.right + clip.right,
8147
                top: clip.top === false ? 0 : area.top - clip.top,
8148
                bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom
8149
            });
8150
        }
8151
        meta.controller.draw();
8152
        if (useClip) {
8153
            unclipArea(ctx);
8154
        }
8155
        args.cancelable = false;
8156
        this.notifyPlugins('afterDatasetDraw', args);
8157
    }
8158
 isPointInArea(point) {
8159
        return _isPointInArea(point, this.chartArea, this._minPadding);
8160
    }
8161
    getElementsAtEventForMode(e, mode, options, useFinalPosition) {
8162
        const method = Interaction.modes[mode];
8163
        if (typeof method === 'function') {
8164
            return method(this, e, options, useFinalPosition);
8165
        }
8166
        return [];
8167
    }
8168
    getDatasetMeta(datasetIndex) {
8169
        const dataset = this.data.datasets[datasetIndex];
8170
        const metasets = this._metasets;
8171
        let meta = metasets.filter((x)=>x && x._dataset === dataset).pop();
8172
        if (!meta) {
8173
            meta = {
8174
                type: null,
8175
                data: [],
8176
                dataset: null,
8177
                controller: null,
8178
                hidden: null,
8179
                xAxisID: null,
8180
                yAxisID: null,
8181
                order: dataset && dataset.order || 0,
8182
                index: datasetIndex,
8183
                _dataset: dataset,
8184
                _parsed: [],
8185
                _sorted: false
8186
            };
8187
            metasets.push(meta);
8188
        }
8189
        return meta;
8190
    }
8191
    getContext() {
8192
        return this.$context || (this.$context = createContext(null, {
8193
            chart: this,
8194
            type: 'chart'
8195
        }));
8196
    }
8197
    getVisibleDatasetCount() {
8198
        return this.getSortedVisibleDatasetMetas().length;
8199
    }
8200
    isDatasetVisible(datasetIndex) {
8201
        const dataset = this.data.datasets[datasetIndex];
8202
        if (!dataset) {
8203
            return false;
8204
        }
8205
        const meta = this.getDatasetMeta(datasetIndex);
8206
        return typeof meta.hidden === 'boolean' ? !meta.hidden : !dataset.hidden;
8207
    }
8208
    setDatasetVisibility(datasetIndex, visible) {
8209
        const meta = this.getDatasetMeta(datasetIndex);
8210
        meta.hidden = !visible;
8211
    }
8212
    toggleDataVisibility(index) {
8213
        this._hiddenIndices[index] = !this._hiddenIndices[index];
8214
    }
8215
    getDataVisibility(index) {
8216
        return !this._hiddenIndices[index];
8217
    }
8218
 _updateVisibility(datasetIndex, dataIndex, visible) {
8219
        const mode = visible ? 'show' : 'hide';
8220
        const meta = this.getDatasetMeta(datasetIndex);
8221
        const anims = meta.controller._resolveAnimations(undefined, mode);
8222
        if (defined(dataIndex)) {
8223
            meta.data[dataIndex].hidden = !visible;
8224
            this.update();
8225
        } else {
8226
            this.setDatasetVisibility(datasetIndex, visible);
8227
            anims.update(meta, {
8228
                visible
8229
            });
8230
            this.update((ctx)=>ctx.datasetIndex === datasetIndex ? mode : undefined);
8231
        }
8232
    }
8233
    hide(datasetIndex, dataIndex) {
8234
        this._updateVisibility(datasetIndex, dataIndex, false);
8235
    }
8236
    show(datasetIndex, dataIndex) {
8237
        this._updateVisibility(datasetIndex, dataIndex, true);
8238
    }
8239
 _destroyDatasetMeta(datasetIndex) {
8240
        const meta = this._metasets[datasetIndex];
8241
        if (meta && meta.controller) {
8242
            meta.controller._destroy();
8243
        }
8244
        delete this._metasets[datasetIndex];
8245
    }
8246
    _stop() {
8247
        let i, ilen;
8248
        this.stop();
8249
        animator.remove(this);
8250
        for(i = 0, ilen = this.data.datasets.length; i < ilen; ++i){
8251
            this._destroyDatasetMeta(i);
8252
        }
8253
    }
8254
    destroy() {
8255
        this.notifyPlugins('beforeDestroy');
8256
        const { canvas , ctx  } = this;
8257
        this._stop();
8258
        this.config.clearCache();
8259
        if (canvas) {
8260
            this.unbindEvents();
8261
            clearCanvas(canvas, ctx);
8262
            this.platform.releaseContext(ctx);
8263
            this.canvas = null;
8264
            this.ctx = null;
8265
        }
8266
        delete instances[this.id];
8267
        this.notifyPlugins('afterDestroy');
8268
    }
8269
    toBase64Image(...args) {
8270
        return this.canvas.toDataURL(...args);
8271
    }
8272
 bindEvents() {
8273
        this.bindUserEvents();
8274
        if (this.options.responsive) {
8275
            this.bindResponsiveEvents();
8276
        } else {
8277
            this.attached = true;
8278
        }
8279
    }
8280
 bindUserEvents() {
8281
        const listeners = this._listeners;
8282
        const platform = this.platform;
8283
        const _add = (type, listener)=>{
8284
            platform.addEventListener(this, type, listener);
8285
            listeners[type] = listener;
8286
        };
8287
        const listener = (e, x, y)=>{
8288
            e.offsetX = x;
8289
            e.offsetY = y;
8290
            this._eventHandler(e);
8291
        };
8292
        each(this.options.events, (type)=>_add(type, listener));
8293
    }
8294
 bindResponsiveEvents() {
8295
        if (!this._responsiveListeners) {
8296
            this._responsiveListeners = {};
8297
        }
8298
        const listeners = this._responsiveListeners;
8299
        const platform = this.platform;
8300
        const _add = (type, listener)=>{
8301
            platform.addEventListener(this, type, listener);
8302
            listeners[type] = listener;
8303
        };
8304
        const _remove = (type, listener)=>{
8305
            if (listeners[type]) {
8306
                platform.removeEventListener(this, type, listener);
8307
                delete listeners[type];
8308
            }
8309
        };
8310
        const listener = (width, height)=>{
8311
            if (this.canvas) {
8312
                this.resize(width, height);
8313
            }
8314
        };
8315
        let detached;
8316
        const attached = ()=>{
8317
            _remove('attach', attached);
8318
            this.attached = true;
8319
            this.resize();
8320
            _add('resize', listener);
8321
            _add('detach', detached);
8322
        };
8323
        detached = ()=>{
8324
            this.attached = false;
8325
            _remove('resize', listener);
8326
            this._stop();
8327
            this._resize(0, 0);
8328
            _add('attach', attached);
8329
        };
8330
        if (platform.isAttached(this.canvas)) {
8331
            attached();
8332
        } else {
8333
            detached();
8334
        }
8335
    }
8336
 unbindEvents() {
8337
        each(this._listeners, (listener, type)=>{
8338
            this.platform.removeEventListener(this, type, listener);
8339
        });
8340
        this._listeners = {};
8341
        each(this._responsiveListeners, (listener, type)=>{
8342
            this.platform.removeEventListener(this, type, listener);
8343
        });
8344
        this._responsiveListeners = undefined;
8345
    }
8346
    updateHoverStyle(items, mode, enabled) {
8347
        const prefix = enabled ? 'set' : 'remove';
8348
        let meta, item, i, ilen;
8349
        if (mode === 'dataset') {
8350
            meta = this.getDatasetMeta(items[0].datasetIndex);
8351
            meta.controller['_' + prefix + 'DatasetHoverStyle']();
8352
        }
8353
        for(i = 0, ilen = items.length; i < ilen; ++i){
8354
            item = items[i];
8355
            const controller = item && this.getDatasetMeta(item.datasetIndex).controller;
8356
            if (controller) {
8357
                controller[prefix + 'HoverStyle'](item.element, item.datasetIndex, item.index);
8358
            }
8359
        }
8360
    }
8361
 getActiveElements() {
8362
        return this._active || [];
8363
    }
8364
 setActiveElements(activeElements) {
8365
        const lastActive = this._active || [];
8366
        const active = activeElements.map(({ datasetIndex , index  })=>{
8367
            const meta = this.getDatasetMeta(datasetIndex);
8368
            if (!meta) {
8369
                throw new Error('No dataset found at index ' + datasetIndex);
8370
            }
8371
            return {
8372
                datasetIndex,
8373
                element: meta.data[index],
8374
                index
8375
            };
8376
        });
8377
        const changed = !_elementsEqual(active, lastActive);
8378
        if (changed) {
8379
            this._active = active;
8380
            this._lastEvent = null;
8381
            this._updateHoverStyles(active, lastActive);
8382
        }
8383
    }
8384
 notifyPlugins(hook, args, filter) {
8385
        return this._plugins.notify(this, hook, args, filter);
8386
    }
8387
 isPluginEnabled(pluginId) {
8388
        return this._plugins._cache.filter((p)=>p.plugin.id === pluginId).length === 1;
8389
    }
8390
 _updateHoverStyles(active, lastActive, replay) {
8391
        const hoverOptions = this.options.hover;
8392
        const diff = (a, b)=>a.filter((x)=>!b.some((y)=>x.datasetIndex === y.datasetIndex && x.index === y.index));
8393
        const deactivated = diff(lastActive, active);
8394
        const activated = replay ? active : diff(active, lastActive);
8395
        if (deactivated.length) {
8396
            this.updateHoverStyle(deactivated, hoverOptions.mode, false);
8397
        }
8398
        if (activated.length && hoverOptions.mode) {
8399
            this.updateHoverStyle(activated, hoverOptions.mode, true);
8400
        }
8401
    }
8402
 _eventHandler(e, replay) {
8403
        const args = {
8404
            event: e,
8405
            replay,
8406
            cancelable: true,
8407
            inChartArea: this.isPointInArea(e)
8408
        };
8409
        const eventFilter = (plugin)=>(plugin.options.events || this.options.events).includes(e.native.type);
8410
        if (this.notifyPlugins('beforeEvent', args, eventFilter) === false) {
8411
            return;
8412
        }
8413
        const changed = this._handleEvent(e, replay, args.inChartArea);
8414
        args.cancelable = false;
8415
        this.notifyPlugins('afterEvent', args, eventFilter);
8416
        if (changed || args.changed) {
8417
            this.render();
8418
        }
8419
        return this;
8420
    }
8421
 _handleEvent(e, replay, inChartArea) {
8422
        const { _active: lastActive = [] , options  } = this;
8423
        const useFinalPosition = replay;
8424
        const active = this._getActiveElements(e, lastActive, inChartArea, useFinalPosition);
8425
        const isClick = _isClickEvent(e);
8426
        const lastEvent = determineLastEvent(e, this._lastEvent, inChartArea, isClick);
8427
        if (inChartArea) {
8428
            this._lastEvent = null;
8429
            callback(options.onHover, [
8430
                e,
8431
                active,
8432
                this
8433
            ], this);
8434
            if (isClick) {
8435
                callback(options.onClick, [
8436
                    e,
8437
                    active,
8438
                    this
8439
                ], this);
8440
            }
8441
        }
8442
        const changed = !_elementsEqual(active, lastActive);
8443
        if (changed || replay) {
8444
            this._active = active;
8445
            this._updateHoverStyles(active, lastActive, replay);
8446
        }
8447
        this._lastEvent = lastEvent;
8448
        return changed;
8449
    }
8450
 _getActiveElements(e, lastActive, inChartArea, useFinalPosition) {
8451
        if (e.type === 'mouseout') {
8452
            return [];
8453
        }
8454
        if (!inChartArea) {
8455
            return lastActive;
8456
        }
8457
        const hoverOptions = this.options.hover;
8458
        return this.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition);
8459
    }
8460
}
8461
function invalidatePlugins() {
8462
    return each(Chart.instances, (chart)=>chart._plugins.invalidate());
8463
}
8464
 
8465
/**
8466
 * @namespace Chart._adapters
8467
 * @since 2.8.0
8468
 * @private
8469
 */ function abstract() {
8470
    throw new Error('This method is not implemented: Check that a complete date adapter is provided.');
8471
}
8472
/**
8473
 * Date adapter (current used by the time scale)
8474
 * @namespace Chart._adapters._date
8475
 * @memberof Chart._adapters
8476
 * @private
8477
 */ class DateAdapterBase {
8478
    /**
8479
   * Override default date adapter methods.
8480
   * Accepts type parameter to define options type.
8481
   * @example
8482
   * Chart._adapters._date.override<{myAdapterOption: string}>({
8483
   *   init() {
8484
   *     console.log(this.options.myAdapterOption);
8485
   *   }
8486
   * })
8487
   */ static override(members) {
8488
        Object.assign(DateAdapterBase.prototype, members);
8489
    }
8490
    options;
8491
    constructor(options){
8492
        this.options = options || {};
8493
    }
8494
    // eslint-disable-next-line @typescript-eslint/no-empty-function
8495
    init() {}
8496
    formats() {
8497
        return abstract();
8498
    }
8499
    parse() {
8500
        return abstract();
8501
    }
8502
    format() {
8503
        return abstract();
8504
    }
8505
    add() {
8506
        return abstract();
8507
    }
8508
    diff() {
8509
        return abstract();
8510
    }
8511
    startOf() {
8512
        return abstract();
8513
    }
8514
    endOf() {
8515
        return abstract();
8516
    }
8517
}
8518
var _adapters = {
8519
    _date: DateAdapterBase
8520
};
8521
 
8522
function getAllScaleValues(scale, type) {
8523
    if (!scale._cache.$bar) {
8524
        const visibleMetas = scale.getMatchingVisibleMetas(type);
8525
        let values = [];
8526
        for(let i = 0, ilen = visibleMetas.length; i < ilen; i++){
8527
            values = values.concat(visibleMetas[i].controller.getAllParsedValues(scale));
8528
        }
8529
        scale._cache.$bar = _arrayUnique(values.sort((a, b)=>a - b));
8530
    }
8531
    return scale._cache.$bar;
8532
}
8533
 function computeMinSampleSize(meta) {
8534
    const scale = meta.iScale;
8535
    const values = getAllScaleValues(scale, meta.type);
8536
    let min = scale._length;
8537
    let i, ilen, curr, prev;
8538
    const updateMinAndPrev = ()=>{
8539
        if (curr === 32767 || curr === -32768) {
8540
            return;
8541
        }
8542
        if (defined(prev)) {
8543
            min = Math.min(min, Math.abs(curr - prev) || min);
8544
        }
8545
        prev = curr;
8546
    };
8547
    for(i = 0, ilen = values.length; i < ilen; ++i){
8548
        curr = scale.getPixelForValue(values[i]);
8549
        updateMinAndPrev();
8550
    }
8551
    prev = undefined;
8552
    for(i = 0, ilen = scale.ticks.length; i < ilen; ++i){
8553
        curr = scale.getPixelForTick(i);
8554
        updateMinAndPrev();
8555
    }
8556
    return min;
8557
}
8558
 function computeFitCategoryTraits(index, ruler, options, stackCount) {
8559
    const thickness = options.barThickness;
8560
    let size, ratio;
8561
    if (isNullOrUndef(thickness)) {
8562
        size = ruler.min * options.categoryPercentage;
8563
        ratio = options.barPercentage;
8564
    } else {
8565
        size = thickness * stackCount;
8566
        ratio = 1;
8567
    }
8568
    return {
8569
        chunk: size / stackCount,
8570
        ratio,
8571
        start: ruler.pixels[index] - size / 2
8572
    };
8573
}
8574
 function computeFlexCategoryTraits(index, ruler, options, stackCount) {
8575
    const pixels = ruler.pixels;
8576
    const curr = pixels[index];
8577
    let prev = index > 0 ? pixels[index - 1] : null;
8578
    let next = index < pixels.length - 1 ? pixels[index + 1] : null;
8579
    const percent = options.categoryPercentage;
8580
    if (prev === null) {
8581
        prev = curr - (next === null ? ruler.end - ruler.start : next - curr);
8582
    }
8583
    if (next === null) {
8584
        next = curr + curr - prev;
8585
    }
8586
    const start = curr - (curr - Math.min(prev, next)) / 2 * percent;
8587
    const size = Math.abs(next - prev) / 2 * percent;
8588
    return {
8589
        chunk: size / stackCount,
8590
        ratio: options.barPercentage,
8591
        start
8592
    };
8593
}
8594
function parseFloatBar(entry, item, vScale, i) {
8595
    const startValue = vScale.parse(entry[0], i);
8596
    const endValue = vScale.parse(entry[1], i);
8597
    const min = Math.min(startValue, endValue);
8598
    const max = Math.max(startValue, endValue);
8599
    let barStart = min;
8600
    let barEnd = max;
8601
    if (Math.abs(min) > Math.abs(max)) {
8602
        barStart = max;
8603
        barEnd = min;
8604
    }
8605
    item[vScale.axis] = barEnd;
8606
    item._custom = {
8607
        barStart,
8608
        barEnd,
8609
        start: startValue,
8610
        end: endValue,
8611
        min,
8612
        max
8613
    };
8614
}
8615
function parseValue(entry, item, vScale, i) {
8616
    if (isArray(entry)) {
8617
        parseFloatBar(entry, item, vScale, i);
8618
    } else {
8619
        item[vScale.axis] = vScale.parse(entry, i);
8620
    }
8621
    return item;
8622
}
8623
function parseArrayOrPrimitive(meta, data, start, count) {
8624
    const iScale = meta.iScale;
8625
    const vScale = meta.vScale;
8626
    const labels = iScale.getLabels();
8627
    const singleScale = iScale === vScale;
8628
    const parsed = [];
8629
    let i, ilen, item, entry;
8630
    for(i = start, ilen = start + count; i < ilen; ++i){
8631
        entry = data[i];
8632
        item = {};
8633
        item[iScale.axis] = singleScale || iScale.parse(labels[i], i);
8634
        parsed.push(parseValue(entry, item, vScale, i));
8635
    }
8636
    return parsed;
8637
}
8638
function isFloatBar(custom) {
8639
    return custom && custom.barStart !== undefined && custom.barEnd !== undefined;
8640
}
8641
function barSign(size, vScale, actualBase) {
8642
    if (size !== 0) {
8643
        return sign(size);
8644
    }
8645
    return (vScale.isHorizontal() ? 1 : -1) * (vScale.min >= actualBase ? 1 : -1);
8646
}
8647
function borderProps(properties) {
8648
    let reverse, start, end, top, bottom;
8649
    if (properties.horizontal) {
8650
        reverse = properties.base > properties.x;
8651
        start = 'left';
8652
        end = 'right';
8653
    } else {
8654
        reverse = properties.base < properties.y;
8655
        start = 'bottom';
8656
        end = 'top';
8657
    }
8658
    if (reverse) {
8659
        top = 'end';
8660
        bottom = 'start';
8661
    } else {
8662
        top = 'start';
8663
        bottom = 'end';
8664
    }
8665
    return {
8666
        start,
8667
        end,
8668
        reverse,
8669
        top,
8670
        bottom
8671
    };
8672
}
8673
function setBorderSkipped(properties, options, stack, index) {
8674
    let edge = options.borderSkipped;
8675
    const res = {};
8676
    if (!edge) {
8677
        properties.borderSkipped = res;
8678
        return;
8679
    }
8680
    if (edge === true) {
8681
        properties.borderSkipped = {
8682
            top: true,
8683
            right: true,
8684
            bottom: true,
8685
            left: true
8686
        };
8687
        return;
8688
    }
8689
    const { start , end , reverse , top , bottom  } = borderProps(properties);
8690
    if (edge === 'middle' && stack) {
8691
        properties.enableBorderRadius = true;
8692
        if ((stack._top || 0) === index) {
8693
            edge = top;
8694
        } else if ((stack._bottom || 0) === index) {
8695
            edge = bottom;
8696
        } else {
8697
            res[parseEdge(bottom, start, end, reverse)] = true;
8698
            edge = top;
8699
        }
8700
    }
8701
    res[parseEdge(edge, start, end, reverse)] = true;
8702
    properties.borderSkipped = res;
8703
}
8704
function parseEdge(edge, a, b, reverse) {
8705
    if (reverse) {
8706
        edge = swap(edge, a, b);
8707
        edge = startEnd(edge, b, a);
8708
    } else {
8709
        edge = startEnd(edge, a, b);
8710
    }
8711
    return edge;
8712
}
8713
function swap(orig, v1, v2) {
8714
    return orig === v1 ? v2 : orig === v2 ? v1 : orig;
8715
}
8716
function startEnd(v, start, end) {
8717
    return v === 'start' ? start : v === 'end' ? end : v;
8718
}
8719
function setInflateAmount(properties, { inflateAmount  }, ratio) {
8720
    properties.inflateAmount = inflateAmount === 'auto' ? ratio === 1 ? 0.33 : 0 : inflateAmount;
8721
}
8722
class BarController extends DatasetController {
8723
    static id = 'bar';
8724
 static defaults = {
8725
        datasetElementType: false,
8726
        dataElementType: 'bar',
8727
        categoryPercentage: 0.8,
8728
        barPercentage: 0.9,
8729
        grouped: true,
8730
        animations: {
8731
            numbers: {
8732
                type: 'number',
8733
                properties: [
8734
                    'x',
8735
                    'y',
8736
                    'base',
8737
                    'width',
8738
                    'height'
8739
                ]
8740
            }
8741
        }
8742
    };
8743
 static overrides = {
8744
        scales: {
8745
            _index_: {
8746
                type: 'category',
8747
                offset: true,
8748
                grid: {
8749
                    offset: true
8750
                }
8751
            },
8752
            _value_: {
8753
                type: 'linear',
8754
                beginAtZero: true
8755
            }
8756
        }
8757
    };
8758
 parsePrimitiveData(meta, data, start, count) {
8759
        return parseArrayOrPrimitive(meta, data, start, count);
8760
    }
8761
 parseArrayData(meta, data, start, count) {
8762
        return parseArrayOrPrimitive(meta, data, start, count);
8763
    }
8764
 parseObjectData(meta, data, start, count) {
8765
        const { iScale , vScale  } = meta;
8766
        const { xAxisKey ='x' , yAxisKey ='y'  } = this._parsing;
8767
        const iAxisKey = iScale.axis === 'x' ? xAxisKey : yAxisKey;
8768
        const vAxisKey = vScale.axis === 'x' ? xAxisKey : yAxisKey;
8769
        const parsed = [];
8770
        let i, ilen, item, obj;
8771
        for(i = start, ilen = start + count; i < ilen; ++i){
8772
            obj = data[i];
8773
            item = {};
8774
            item[iScale.axis] = iScale.parse(resolveObjectKey(obj, iAxisKey), i);
8775
            parsed.push(parseValue(resolveObjectKey(obj, vAxisKey), item, vScale, i));
8776
        }
8777
        return parsed;
8778
    }
8779
 updateRangeFromParsed(range, scale, parsed, stack) {
8780
        super.updateRangeFromParsed(range, scale, parsed, stack);
8781
        const custom = parsed._custom;
8782
        if (custom && scale === this._cachedMeta.vScale) {
8783
            range.min = Math.min(range.min, custom.min);
8784
            range.max = Math.max(range.max, custom.max);
8785
        }
8786
    }
8787
 getMaxOverflow() {
8788
        return 0;
8789
    }
8790
 getLabelAndValue(index) {
8791
        const meta = this._cachedMeta;
8792
        const { iScale , vScale  } = meta;
8793
        const parsed = this.getParsed(index);
8794
        const custom = parsed._custom;
8795
        const value = isFloatBar(custom) ? '[' + custom.start + ', ' + custom.end + ']' : '' + vScale.getLabelForValue(parsed[vScale.axis]);
8796
        return {
8797
            label: '' + iScale.getLabelForValue(parsed[iScale.axis]),
8798
            value
8799
        };
8800
    }
8801
    initialize() {
8802
        this.enableOptionSharing = true;
8803
        super.initialize();
8804
        const meta = this._cachedMeta;
8805
        meta.stack = this.getDataset().stack;
8806
    }
8807
    update(mode) {
8808
        const meta = this._cachedMeta;
8809
        this.updateElements(meta.data, 0, meta.data.length, mode);
8810
    }
8811
    updateElements(bars, start, count, mode) {
8812
        const reset = mode === 'reset';
8813
        const { index , _cachedMeta: { vScale  }  } = this;
8814
        const base = vScale.getBasePixel();
8815
        const horizontal = vScale.isHorizontal();
8816
        const ruler = this._getRuler();
8817
        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);
8818
        for(let i = start; i < start + count; i++){
8819
            const parsed = this.getParsed(i);
8820
            const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {
8821
                base,
8822
                head: base
8823
            } : this._calculateBarValuePixels(i);
8824
            const ipixels = this._calculateBarIndexPixels(i, ruler);
8825
            const stack = (parsed._stacks || {})[vScale.axis];
8826
            const properties = {
8827
                horizontal,
8828
                base: vpixels.base,
8829
                enableBorderRadius: !stack || isFloatBar(parsed._custom) || index === stack._top || index === stack._bottom,
8830
                x: horizontal ? vpixels.head : ipixels.center,
8831
                y: horizontal ? ipixels.center : vpixels.head,
8832
                height: horizontal ? ipixels.size : Math.abs(vpixels.size),
8833
                width: horizontal ? Math.abs(vpixels.size) : ipixels.size
8834
            };
8835
            if (includeOptions) {
8836
                properties.options = sharedOptions || this.resolveDataElementOptions(i, bars[i].active ? 'active' : mode);
8837
            }
8838
            const options = properties.options || bars[i].options;
8839
            setBorderSkipped(properties, options, stack, index);
8840
            setInflateAmount(properties, options, ruler.ratio);
8841
            this.updateElement(bars[i], i, properties, mode);
8842
        }
8843
    }
8844
 _getStacks(last, dataIndex) {
8845
        const { iScale  } = this._cachedMeta;
8846
        const metasets = iScale.getMatchingVisibleMetas(this._type).filter((meta)=>meta.controller.options.grouped);
8847
        const stacked = iScale.options.stacked;
8848
        const stacks = [];
8849
        const skipNull = (meta)=>{
8850
            const parsed = meta.controller.getParsed(dataIndex);
8851
            const val = parsed && parsed[meta.vScale.axis];
8852
            if (isNullOrUndef(val) || isNaN(val)) {
8853
                return true;
8854
            }
8855
        };
8856
        for (const meta of metasets){
8857
            if (dataIndex !== undefined && skipNull(meta)) {
8858
                continue;
8859
            }
8860
            if (stacked === false || stacks.indexOf(meta.stack) === -1 || stacked === undefined && meta.stack === undefined) {
8861
                stacks.push(meta.stack);
8862
            }
8863
            if (meta.index === last) {
8864
                break;
8865
            }
8866
        }
8867
        if (!stacks.length) {
8868
            stacks.push(undefined);
8869
        }
8870
        return stacks;
8871
    }
8872
 _getStackCount(index) {
8873
        return this._getStacks(undefined, index).length;
8874
    }
8875
 _getStackIndex(datasetIndex, name, dataIndex) {
8876
        const stacks = this._getStacks(datasetIndex, dataIndex);
8877
        const index = name !== undefined ? stacks.indexOf(name) : -1;
8878
        return index === -1 ? stacks.length - 1 : index;
8879
    }
8880
 _getRuler() {
8881
        const opts = this.options;
8882
        const meta = this._cachedMeta;
8883
        const iScale = meta.iScale;
8884
        const pixels = [];
8885
        let i, ilen;
8886
        for(i = 0, ilen = meta.data.length; i < ilen; ++i){
8887
            pixels.push(iScale.getPixelForValue(this.getParsed(i)[iScale.axis], i));
8888
        }
8889
        const barThickness = opts.barThickness;
8890
        const min = barThickness || computeMinSampleSize(meta);
8891
        return {
8892
            min,
8893
            pixels,
8894
            start: iScale._startPixel,
8895
            end: iScale._endPixel,
8896
            stackCount: this._getStackCount(),
8897
            scale: iScale,
8898
            grouped: opts.grouped,
8899
            ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage
8900
        };
8901
    }
8902
 _calculateBarValuePixels(index) {
8903
        const { _cachedMeta: { vScale , _stacked , index: datasetIndex  } , options: { base: baseValue , minBarLength  }  } = this;
8904
        const actualBase = baseValue || 0;
8905
        const parsed = this.getParsed(index);
8906
        const custom = parsed._custom;
8907
        const floating = isFloatBar(custom);
8908
        let value = parsed[vScale.axis];
8909
        let start = 0;
8910
        let length = _stacked ? this.applyStack(vScale, parsed, _stacked) : value;
8911
        let head, size;
8912
        if (length !== value) {
8913
            start = length - value;
8914
            length = value;
8915
        }
8916
        if (floating) {
8917
            value = custom.barStart;
8918
            length = custom.barEnd - custom.barStart;
8919
            if (value !== 0 && sign(value) !== sign(custom.barEnd)) {
8920
                start = 0;
8921
            }
8922
            start += value;
8923
        }
8924
        const startValue = !isNullOrUndef(baseValue) && !floating ? baseValue : start;
8925
        let base = vScale.getPixelForValue(startValue);
8926
        if (this.chart.getDataVisibility(index)) {
8927
            head = vScale.getPixelForValue(start + length);
8928
        } else {
8929
            head = base;
8930
        }
8931
        size = head - base;
8932
        if (Math.abs(size) < minBarLength) {
8933
            size = barSign(size, vScale, actualBase) * minBarLength;
8934
            if (value === actualBase) {
8935
                base -= size / 2;
8936
            }
8937
            const startPixel = vScale.getPixelForDecimal(0);
8938
            const endPixel = vScale.getPixelForDecimal(1);
8939
            const min = Math.min(startPixel, endPixel);
8940
            const max = Math.max(startPixel, endPixel);
8941
            base = Math.max(Math.min(base, max), min);
8942
            head = base + size;
8943
            if (_stacked && !floating) {
8944
                parsed._stacks[vScale.axis]._visualValues[datasetIndex] = vScale.getValueForPixel(head) - vScale.getValueForPixel(base);
8945
            }
8946
        }
8947
        if (base === vScale.getPixelForValue(actualBase)) {
8948
            const halfGrid = sign(size) * vScale.getLineWidthForValue(actualBase) / 2;
8949
            base += halfGrid;
8950
            size -= halfGrid;
8951
        }
8952
        return {
8953
            size,
8954
            base,
8955
            head,
8956
            center: head + size / 2
8957
        };
8958
    }
8959
 _calculateBarIndexPixels(index, ruler) {
8960
        const scale = ruler.scale;
8961
        const options = this.options;
8962
        const skipNull = options.skipNull;
8963
        const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity);
8964
        let center, size;
8965
        if (ruler.grouped) {
8966
            const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount;
8967
            const range = options.barThickness === 'flex' ? computeFlexCategoryTraits(index, ruler, options, stackCount) : computeFitCategoryTraits(index, ruler, options, stackCount);
8968
            const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined);
8969
            center = range.start + range.chunk * stackIndex + range.chunk / 2;
8970
            size = Math.min(maxBarThickness, range.chunk * range.ratio);
8971
        } else {
8972
            center = scale.getPixelForValue(this.getParsed(index)[scale.axis], index);
8973
            size = Math.min(maxBarThickness, ruler.min * ruler.ratio);
8974
        }
8975
        return {
8976
            base: center - size / 2,
8977
            head: center + size / 2,
8978
            center,
8979
            size
8980
        };
8981
    }
8982
    draw() {
8983
        const meta = this._cachedMeta;
8984
        const vScale = meta.vScale;
8985
        const rects = meta.data;
8986
        const ilen = rects.length;
8987
        let i = 0;
8988
        for(; i < ilen; ++i){
8989
            if (this.getParsed(i)[vScale.axis] !== null) {
8990
                rects[i].draw(this._ctx);
8991
            }
8992
        }
8993
    }
8994
}
8995
 
8996
class BubbleController extends DatasetController {
8997
    static id = 'bubble';
8998
 static defaults = {
8999
        datasetElementType: false,
9000
        dataElementType: 'point',
9001
        animations: {
9002
            numbers: {
9003
                type: 'number',
9004
                properties: [
9005
                    'x',
9006
                    'y',
9007
                    'borderWidth',
9008
                    'radius'
9009
                ]
9010
            }
9011
        }
9012
    };
9013
 static overrides = {
9014
        scales: {
9015
            x: {
9016
                type: 'linear'
9017
            },
9018
            y: {
9019
                type: 'linear'
9020
            }
9021
        }
9022
    };
9023
    initialize() {
9024
        this.enableOptionSharing = true;
9025
        super.initialize();
9026
    }
9027
 parsePrimitiveData(meta, data, start, count) {
9028
        const parsed = super.parsePrimitiveData(meta, data, start, count);
9029
        for(let i = 0; i < parsed.length; i++){
9030
            parsed[i]._custom = this.resolveDataElementOptions(i + start).radius;
9031
        }
9032
        return parsed;
9033
    }
9034
 parseArrayData(meta, data, start, count) {
9035
        const parsed = super.parseArrayData(meta, data, start, count);
9036
        for(let i = 0; i < parsed.length; i++){
9037
            const item = data[start + i];
9038
            parsed[i]._custom = valueOrDefault(item[2], this.resolveDataElementOptions(i + start).radius);
9039
        }
9040
        return parsed;
9041
    }
9042
 parseObjectData(meta, data, start, count) {
9043
        const parsed = super.parseObjectData(meta, data, start, count);
9044
        for(let i = 0; i < parsed.length; i++){
9045
            const item = data[start + i];
9046
            parsed[i]._custom = valueOrDefault(item && item.r && +item.r, this.resolveDataElementOptions(i + start).radius);
9047
        }
9048
        return parsed;
9049
    }
9050
 getMaxOverflow() {
9051
        const data = this._cachedMeta.data;
9052
        let max = 0;
9053
        for(let i = data.length - 1; i >= 0; --i){
9054
            max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);
9055
        }
9056
        return max > 0 && max;
9057
    }
9058
 getLabelAndValue(index) {
9059
        const meta = this._cachedMeta;
9060
        const labels = this.chart.data.labels || [];
9061
        const { xScale , yScale  } = meta;
9062
        const parsed = this.getParsed(index);
9063
        const x = xScale.getLabelForValue(parsed.x);
9064
        const y = yScale.getLabelForValue(parsed.y);
9065
        const r = parsed._custom;
9066
        return {
9067
            label: labels[index] || '',
9068
            value: '(' + x + ', ' + y + (r ? ', ' + r : '') + ')'
9069
        };
9070
    }
9071
    update(mode) {
9072
        const points = this._cachedMeta.data;
9073
        this.updateElements(points, 0, points.length, mode);
9074
    }
9075
    updateElements(points, start, count, mode) {
9076
        const reset = mode === 'reset';
9077
        const { iScale , vScale  } = this._cachedMeta;
9078
        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);
9079
        const iAxis = iScale.axis;
9080
        const vAxis = vScale.axis;
9081
        for(let i = start; i < start + count; i++){
9082
            const point = points[i];
9083
            const parsed = !reset && this.getParsed(i);
9084
            const properties = {};
9085
            const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]);
9086
            const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]);
9087
            properties.skip = isNaN(iPixel) || isNaN(vPixel);
9088
            if (includeOptions) {
9089
                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);
9090
                if (reset) {
9091
                    properties.options.radius = 0;
9092
                }
9093
            }
9094
            this.updateElement(point, i, properties, mode);
9095
        }
9096
    }
9097
 resolveDataElementOptions(index, mode) {
9098
        const parsed = this.getParsed(index);
9099
        let values = super.resolveDataElementOptions(index, mode);
9100
        if (values.$shared) {
9101
            values = Object.assign({}, values, {
9102
                $shared: false
9103
            });
9104
        }
9105
        const radius = values.radius;
9106
        if (mode !== 'active') {
9107
            values.radius = 0;
9108
        }
9109
        values.radius += valueOrDefault(parsed && parsed._custom, radius);
9110
        return values;
9111
    }
9112
}
9113
 
9114
function getRatioAndOffset(rotation, circumference, cutout) {
9115
    let ratioX = 1;
9116
    let ratioY = 1;
9117
    let offsetX = 0;
9118
    let offsetY = 0;
9119
    if (circumference < TAU) {
9120
        const startAngle = rotation;
9121
        const endAngle = startAngle + circumference;
9122
        const startX = Math.cos(startAngle);
9123
        const startY = Math.sin(startAngle);
9124
        const endX = Math.cos(endAngle);
9125
        const endY = Math.sin(endAngle);
9126
        const calcMax = (angle, a, b)=>_angleBetween(angle, startAngle, endAngle, true) ? 1 : Math.max(a, a * cutout, b, b * cutout);
9127
        const calcMin = (angle, a, b)=>_angleBetween(angle, startAngle, endAngle, true) ? -1 : Math.min(a, a * cutout, b, b * cutout);
9128
        const maxX = calcMax(0, startX, endX);
9129
        const maxY = calcMax(HALF_PI, startY, endY);
9130
        const minX = calcMin(PI, startX, endX);
9131
        const minY = calcMin(PI + HALF_PI, startY, endY);
9132
        ratioX = (maxX - minX) / 2;
9133
        ratioY = (maxY - minY) / 2;
9134
        offsetX = -(maxX + minX) / 2;
9135
        offsetY = -(maxY + minY) / 2;
9136
    }
9137
    return {
9138
        ratioX,
9139
        ratioY,
9140
        offsetX,
9141
        offsetY
9142
    };
9143
}
9144
class DoughnutController extends DatasetController {
9145
    static id = 'doughnut';
9146
 static defaults = {
9147
        datasetElementType: false,
9148
        dataElementType: 'arc',
9149
        animation: {
9150
            animateRotate: true,
9151
            animateScale: false
9152
        },
9153
        animations: {
9154
            numbers: {
9155
                type: 'number',
9156
                properties: [
9157
                    'circumference',
9158
                    'endAngle',
9159
                    'innerRadius',
9160
                    'outerRadius',
9161
                    'startAngle',
9162
                    'x',
9163
                    'y',
9164
                    'offset',
9165
                    'borderWidth',
9166
                    'spacing'
9167
                ]
9168
            }
9169
        },
9170
        cutout: '50%',
9171
        rotation: 0,
9172
        circumference: 360,
9173
        radius: '100%',
9174
        spacing: 0,
9175
        indexAxis: 'r'
9176
    };
9177
    static descriptors = {
9178
        _scriptable: (name)=>name !== 'spacing',
9179
        _indexable: (name)=>name !== 'spacing' && !name.startsWith('borderDash') && !name.startsWith('hoverBorderDash')
9180
    };
9181
 static overrides = {
9182
        aspectRatio: 1,
9183
        plugins: {
9184
            legend: {
9185
                labels: {
9186
                    generateLabels (chart) {
9187
                        const data = chart.data;
9188
                        if (data.labels.length && data.datasets.length) {
9189
                            const { labels: { pointStyle , color  }  } = chart.legend.options;
9190
                            return data.labels.map((label, i)=>{
9191
                                const meta = chart.getDatasetMeta(0);
9192
                                const style = meta.controller.getStyle(i);
9193
                                return {
9194
                                    text: label,
9195
                                    fillStyle: style.backgroundColor,
9196
                                    strokeStyle: style.borderColor,
9197
                                    fontColor: color,
9198
                                    lineWidth: style.borderWidth,
9199
                                    pointStyle: pointStyle,
9200
                                    hidden: !chart.getDataVisibility(i),
9201
                                    index: i
9202
                                };
9203
                            });
9204
                        }
9205
                        return [];
9206
                    }
9207
                },
9208
                onClick (e, legendItem, legend) {
9209
                    legend.chart.toggleDataVisibility(legendItem.index);
9210
                    legend.chart.update();
9211
                }
9212
            }
9213
        }
9214
    };
9215
    constructor(chart, datasetIndex){
9216
        super(chart, datasetIndex);
9217
        this.enableOptionSharing = true;
9218
        this.innerRadius = undefined;
9219
        this.outerRadius = undefined;
9220
        this.offsetX = undefined;
9221
        this.offsetY = undefined;
9222
    }
9223
    linkScales() {}
9224
 parse(start, count) {
9225
        const data = this.getDataset().data;
9226
        const meta = this._cachedMeta;
9227
        if (this._parsing === false) {
9228
            meta._parsed = data;
9229
        } else {
9230
            let getter = (i)=>+data[i];
9231
            if (isObject(data[start])) {
9232
                const { key ='value'  } = this._parsing;
9233
                getter = (i)=>+resolveObjectKey(data[i], key);
9234
            }
9235
            let i, ilen;
9236
            for(i = start, ilen = start + count; i < ilen; ++i){
9237
                meta._parsed[i] = getter(i);
9238
            }
9239
        }
9240
    }
9241
 _getRotation() {
9242
        return toRadians(this.options.rotation - 90);
9243
    }
9244
 _getCircumference() {
9245
        return toRadians(this.options.circumference);
9246
    }
9247
 _getRotationExtents() {
9248
        let min = TAU;
9249
        let max = -TAU;
9250
        for(let i = 0; i < this.chart.data.datasets.length; ++i){
9251
            if (this.chart.isDatasetVisible(i) && this.chart.getDatasetMeta(i).type === this._type) {
9252
                const controller = this.chart.getDatasetMeta(i).controller;
9253
                const rotation = controller._getRotation();
9254
                const circumference = controller._getCircumference();
9255
                min = Math.min(min, rotation);
9256
                max = Math.max(max, rotation + circumference);
9257
            }
9258
        }
9259
        return {
9260
            rotation: min,
9261
            circumference: max - min
9262
        };
9263
    }
9264
 update(mode) {
9265
        const chart = this.chart;
9266
        const { chartArea  } = chart;
9267
        const meta = this._cachedMeta;
9268
        const arcs = meta.data;
9269
        const spacing = this.getMaxBorderWidth() + this.getMaxOffset(arcs) + this.options.spacing;
9270
        const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0);
9271
        const cutout = Math.min(toPercentage(this.options.cutout, maxSize), 1);
9272
        const chartWeight = this._getRingWeight(this.index);
9273
        const { circumference , rotation  } = this._getRotationExtents();
9274
        const { ratioX , ratioY , offsetX , offsetY  } = getRatioAndOffset(rotation, circumference, cutout);
9275
        const maxWidth = (chartArea.width - spacing) / ratioX;
9276
        const maxHeight = (chartArea.height - spacing) / ratioY;
9277
        const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
9278
        const outerRadius = toDimension(this.options.radius, maxRadius);
9279
        const innerRadius = Math.max(outerRadius * cutout, 0);
9280
        const radiusLength = (outerRadius - innerRadius) / this._getVisibleDatasetWeightTotal();
9281
        this.offsetX = offsetX * outerRadius;
9282
        this.offsetY = offsetY * outerRadius;
9283
        meta.total = this.calculateTotal();
9284
        this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index);
9285
        this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0);
9286
        this.updateElements(arcs, 0, arcs.length, mode);
9287
    }
9288
 _circumference(i, reset) {
9289
        const opts = this.options;
9290
        const meta = this._cachedMeta;
9291
        const circumference = this._getCircumference();
9292
        if (reset && opts.animation.animateRotate || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) {
9293
            return 0;
9294
        }
9295
        return this.calculateCircumference(meta._parsed[i] * circumference / TAU);
9296
    }
9297
    updateElements(arcs, start, count, mode) {
9298
        const reset = mode === 'reset';
9299
        const chart = this.chart;
9300
        const chartArea = chart.chartArea;
9301
        const opts = chart.options;
9302
        const animationOpts = opts.animation;
9303
        const centerX = (chartArea.left + chartArea.right) / 2;
9304
        const centerY = (chartArea.top + chartArea.bottom) / 2;
9305
        const animateScale = reset && animationOpts.animateScale;
9306
        const innerRadius = animateScale ? 0 : this.innerRadius;
9307
        const outerRadius = animateScale ? 0 : this.outerRadius;
9308
        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);
9309
        let startAngle = this._getRotation();
9310
        let i;
9311
        for(i = 0; i < start; ++i){
9312
            startAngle += this._circumference(i, reset);
9313
        }
9314
        for(i = start; i < start + count; ++i){
9315
            const circumference = this._circumference(i, reset);
9316
            const arc = arcs[i];
9317
            const properties = {
9318
                x: centerX + this.offsetX,
9319
                y: centerY + this.offsetY,
9320
                startAngle,
9321
                endAngle: startAngle + circumference,
9322
                circumference,
9323
                outerRadius,
9324
                innerRadius
9325
            };
9326
            if (includeOptions) {
9327
                properties.options = sharedOptions || this.resolveDataElementOptions(i, arc.active ? 'active' : mode);
9328
            }
9329
            startAngle += circumference;
9330
            this.updateElement(arc, i, properties, mode);
9331
        }
9332
    }
9333
    calculateTotal() {
9334
        const meta = this._cachedMeta;
9335
        const metaData = meta.data;
9336
        let total = 0;
9337
        let i;
9338
        for(i = 0; i < metaData.length; i++){
9339
            const value = meta._parsed[i];
9340
            if (value !== null && !isNaN(value) && this.chart.getDataVisibility(i) && !metaData[i].hidden) {
9341
                total += Math.abs(value);
9342
            }
9343
        }
9344
        return total;
9345
    }
9346
    calculateCircumference(value) {
9347
        const total = this._cachedMeta.total;
9348
        if (total > 0 && !isNaN(value)) {
9349
            return TAU * (Math.abs(value) / total);
9350
        }
9351
        return 0;
9352
    }
9353
    getLabelAndValue(index) {
9354
        const meta = this._cachedMeta;
9355
        const chart = this.chart;
9356
        const labels = chart.data.labels || [];
9357
        const value = formatNumber(meta._parsed[index], chart.options.locale);
9358
        return {
9359
            label: labels[index] || '',
9360
            value
9361
        };
9362
    }
9363
    getMaxBorderWidth(arcs) {
9364
        let max = 0;
9365
        const chart = this.chart;
9366
        let i, ilen, meta, controller, options;
9367
        if (!arcs) {
9368
            for(i = 0, ilen = chart.data.datasets.length; i < ilen; ++i){
9369
                if (chart.isDatasetVisible(i)) {
9370
                    meta = chart.getDatasetMeta(i);
9371
                    arcs = meta.data;
9372
                    controller = meta.controller;
9373
                    break;
9374
                }
9375
            }
9376
        }
9377
        if (!arcs) {
9378
            return 0;
9379
        }
9380
        for(i = 0, ilen = arcs.length; i < ilen; ++i){
9381
            options = controller.resolveDataElementOptions(i);
9382
            if (options.borderAlign !== 'inner') {
9383
                max = Math.max(max, options.borderWidth || 0, options.hoverBorderWidth || 0);
9384
            }
9385
        }
9386
        return max;
9387
    }
9388
    getMaxOffset(arcs) {
9389
        let max = 0;
9390
        for(let i = 0, ilen = arcs.length; i < ilen; ++i){
9391
            const options = this.resolveDataElementOptions(i);
9392
            max = Math.max(max, options.offset || 0, options.hoverOffset || 0);
9393
        }
9394
        return max;
9395
    }
9396
 _getRingWeightOffset(datasetIndex) {
9397
        let ringWeightOffset = 0;
9398
        for(let i = 0; i < datasetIndex; ++i){
9399
            if (this.chart.isDatasetVisible(i)) {
9400
                ringWeightOffset += this._getRingWeight(i);
9401
            }
9402
        }
9403
        return ringWeightOffset;
9404
    }
9405
 _getRingWeight(datasetIndex) {
9406
        return Math.max(valueOrDefault(this.chart.data.datasets[datasetIndex].weight, 1), 0);
9407
    }
9408
 _getVisibleDatasetWeightTotal() {
9409
        return this._getRingWeightOffset(this.chart.data.datasets.length) || 1;
9410
    }
9411
}
9412
 
9413
class LineController extends DatasetController {
9414
    static id = 'line';
9415
 static defaults = {
9416
        datasetElementType: 'line',
9417
        dataElementType: 'point',
9418
        showLine: true,
9419
        spanGaps: false
9420
    };
9421
 static overrides = {
9422
        scales: {
9423
            _index_: {
9424
                type: 'category'
9425
            },
9426
            _value_: {
9427
                type: 'linear'
9428
            }
9429
        }
9430
    };
9431
    initialize() {
9432
        this.enableOptionSharing = true;
9433
        this.supportsDecimation = true;
9434
        super.initialize();
9435
    }
9436
    update(mode) {
9437
        const meta = this._cachedMeta;
9438
        const { dataset: line , data: points = [] , _dataset  } = meta;
9439
        const animationsDisabled = this.chart._animationsDisabled;
9440
        let { start , count  } = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
9441
        this._drawStart = start;
9442
        this._drawCount = count;
9443
        if (_scaleRangesChanged(meta)) {
9444
            start = 0;
9445
            count = points.length;
9446
        }
9447
        line._chart = this.chart;
9448
        line._datasetIndex = this.index;
9449
        line._decimated = !!_dataset._decimated;
9450
        line.points = points;
9451
        const options = this.resolveDatasetElementOptions(mode);
9452
        if (!this.options.showLine) {
9453
            options.borderWidth = 0;
9454
        }
9455
        options.segment = this.options.segment;
9456
        this.updateElement(line, undefined, {
9457
            animated: !animationsDisabled,
9458
            options
9459
        }, mode);
9460
        this.updateElements(points, start, count, mode);
9461
    }
9462
    updateElements(points, start, count, mode) {
9463
        const reset = mode === 'reset';
9464
        const { iScale , vScale , _stacked , _dataset  } = this._cachedMeta;
9465
        const { sharedOptions , includeOptions  } = this._getSharedOptions(start, mode);
9466
        const iAxis = iScale.axis;
9467
        const vAxis = vScale.axis;
9468
        const { spanGaps , segment  } = this.options;
9469
        const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
9470
        const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';
9471
        const end = start + count;
9472
        const pointsCount = points.length;
9473
        let prevParsed = start > 0 && this.getParsed(start - 1);
9474
        for(let i = 0; i < pointsCount; ++i){
9475
            const point = points[i];
9476
            const properties = directUpdate ? point : {};
9477
            if (i < start || i >= end) {
9478
                properties.skip = true;
9479
                continue;
9480
            }
9481
            const parsed = this.getParsed(i);
9482
            const nullData = isNullOrUndef(parsed[vAxis]);
9483
            const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);
9484
            const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);
9485
            properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;
9486
            properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;
9487
            if (segment) {
9488
                properties.parsed = parsed;
9489
                properties.raw = _dataset.data[i];
9490
            }
9491
            if (includeOptions) {
9492
                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);
9493
            }
9494
            if (!directUpdate) {
9495
                this.updateElement(point, i, properties, mode);
9496
            }
9497
            prevParsed = parsed;
9498
        }
9499
    }
9500
 getMaxOverflow() {
9501
        const meta = this._cachedMeta;
9502
        const dataset = meta.dataset;
9503
        const border = dataset.options && dataset.options.borderWidth || 0;
9504
        const data = meta.data || [];
9505
        if (!data.length) {
9506
            return border;
9507
        }
9508
        const firstPoint = data[0].size(this.resolveDataElementOptions(0));
9509
        const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));
9510
        return Math.max(border, firstPoint, lastPoint) / 2;
9511
    }
9512
    draw() {
9513
        const meta = this._cachedMeta;
9514
        meta.dataset.updateControlPoints(this.chart.chartArea, meta.iScale.axis);
9515
        super.draw();
9516
    }
9517
}
9518
 
9519
class PolarAreaController extends DatasetController {
9520
    static id = 'polarArea';
9521
 static defaults = {
9522
        dataElementType: 'arc',
9523
        animation: {
9524
            animateRotate: true,
9525
            animateScale: true
9526
        },
9527
        animations: {
9528
            numbers: {
9529
                type: 'number',
9530
                properties: [
9531
                    'x',
9532
                    'y',
9533
                    'startAngle',
9534
                    'endAngle',
9535
                    'innerRadius',
9536
                    'outerRadius'
9537
                ]
9538
            }
9539
        },
9540
        indexAxis: 'r',
9541
        startAngle: 0
9542
    };
9543
 static overrides = {
9544
        aspectRatio: 1,
9545
        plugins: {
9546
            legend: {
9547
                labels: {
9548
                    generateLabels (chart) {
9549
                        const data = chart.data;
9550
                        if (data.labels.length && data.datasets.length) {
9551
                            const { labels: { pointStyle , color  }  } = chart.legend.options;
9552
                            return data.labels.map((label, i)=>{
9553
                                const meta = chart.getDatasetMeta(0);
9554
                                const style = meta.controller.getStyle(i);
9555
                                return {
9556
                                    text: label,
9557
                                    fillStyle: style.backgroundColor,
9558
                                    strokeStyle: style.borderColor,
9559
                                    fontColor: color,
9560
                                    lineWidth: style.borderWidth,
9561
                                    pointStyle: pointStyle,
9562
                                    hidden: !chart.getDataVisibility(i),
9563
                                    index: i
9564
                                };
9565
                            });
9566
                        }
9567
                        return [];
9568
                    }
9569
                },
9570
                onClick (e, legendItem, legend) {
9571
                    legend.chart.toggleDataVisibility(legendItem.index);
9572
                    legend.chart.update();
9573
                }
9574
            }
9575
        },
9576
        scales: {
9577
            r: {
9578
                type: 'radialLinear',
9579
                angleLines: {
9580
                    display: false
9581
                },
9582
                beginAtZero: true,
9583
                grid: {
9584
                    circular: true
9585
                },
9586
                pointLabels: {
9587
                    display: false
9588
                },
9589
                startAngle: 0
9590
            }
9591
        }
9592
    };
9593
    constructor(chart, datasetIndex){
9594
        super(chart, datasetIndex);
9595
        this.innerRadius = undefined;
9596
        this.outerRadius = undefined;
9597
    }
9598
    getLabelAndValue(index) {
9599
        const meta = this._cachedMeta;
9600
        const chart = this.chart;
9601
        const labels = chart.data.labels || [];
9602
        const value = formatNumber(meta._parsed[index].r, chart.options.locale);
9603
        return {
9604
            label: labels[index] || '',
9605
            value
9606
        };
9607
    }
9608
    parseObjectData(meta, data, start, count) {
9609
        return _parseObjectDataRadialScale.bind(this)(meta, data, start, count);
9610
    }
9611
    update(mode) {
9612
        const arcs = this._cachedMeta.data;
9613
        this._updateRadius();
9614
        this.updateElements(arcs, 0, arcs.length, mode);
9615
    }
9616
 getMinMax() {
9617
        const meta = this._cachedMeta;
9618
        const range = {
9619
            min: Number.POSITIVE_INFINITY,
9620
            max: Number.NEGATIVE_INFINITY
9621
        };
9622
        meta.data.forEach((element, index)=>{
9623
            const parsed = this.getParsed(index).r;
9624
            if (!isNaN(parsed) && this.chart.getDataVisibility(index)) {
9625
                if (parsed < range.min) {
9626
                    range.min = parsed;
9627
                }
9628
                if (parsed > range.max) {
9629
                    range.max = parsed;
9630
                }
9631
            }
9632
        });
9633
        return range;
9634
    }
9635
 _updateRadius() {
9636
        const chart = this.chart;
9637
        const chartArea = chart.chartArea;
9638
        const opts = chart.options;
9639
        const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
9640
        const outerRadius = Math.max(minSize / 2, 0);
9641
        const innerRadius = Math.max(opts.cutoutPercentage ? outerRadius / 100 * opts.cutoutPercentage : 1, 0);
9642
        const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount();
9643
        this.outerRadius = outerRadius - radiusLength * this.index;
9644
        this.innerRadius = this.outerRadius - radiusLength;
9645
    }
9646
    updateElements(arcs, start, count, mode) {
9647
        const reset = mode === 'reset';
9648
        const chart = this.chart;
9649
        const opts = chart.options;
9650
        const animationOpts = opts.animation;
9651
        const scale = this._cachedMeta.rScale;
9652
        const centerX = scale.xCenter;
9653
        const centerY = scale.yCenter;
9654
        const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI;
9655
        let angle = datasetStartAngle;
9656
        let i;
9657
        const defaultAngle = 360 / this.countVisibleElements();
9658
        for(i = 0; i < start; ++i){
9659
            angle += this._computeAngle(i, mode, defaultAngle);
9660
        }
9661
        for(i = start; i < start + count; i++){
9662
            const arc = arcs[i];
9663
            let startAngle = angle;
9664
            let endAngle = angle + this._computeAngle(i, mode, defaultAngle);
9665
            let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(this.getParsed(i).r) : 0;
9666
            angle = endAngle;
9667
            if (reset) {
9668
                if (animationOpts.animateScale) {
9669
                    outerRadius = 0;
9670
                }
9671
                if (animationOpts.animateRotate) {
9672
                    startAngle = endAngle = datasetStartAngle;
9673
                }
9674
            }
9675
            const properties = {
9676
                x: centerX,
9677
                y: centerY,
9678
                innerRadius: 0,
9679
                outerRadius,
9680
                startAngle,
9681
                endAngle,
9682
                options: this.resolveDataElementOptions(i, arc.active ? 'active' : mode)
9683
            };
9684
            this.updateElement(arc, i, properties, mode);
9685
        }
9686
    }
9687
    countVisibleElements() {
9688
        const meta = this._cachedMeta;
9689
        let count = 0;
9690
        meta.data.forEach((element, index)=>{
9691
            if (!isNaN(this.getParsed(index).r) && this.chart.getDataVisibility(index)) {
9692
                count++;
9693
            }
9694
        });
9695
        return count;
9696
    }
9697
 _computeAngle(index, mode, defaultAngle) {
9698
        return this.chart.getDataVisibility(index) ? toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle) : 0;
9699
    }
9700
}
9701
 
9702
class PieController extends DoughnutController {
9703
    static id = 'pie';
9704
 static defaults = {
9705
        cutout: 0,
9706
        rotation: 0,
9707
        circumference: 360,
9708
        radius: '100%'
9709
    };
9710
}
9711
 
9712
class RadarController extends DatasetController {
9713
    static id = 'radar';
9714
 static defaults = {
9715
        datasetElementType: 'line',
9716
        dataElementType: 'point',
9717
        indexAxis: 'r',
9718
        showLine: true,
9719
        elements: {
9720
            line: {
9721
                fill: 'start'
9722
            }
9723
        }
9724
    };
9725
 static overrides = {
9726
        aspectRatio: 1,
9727
        scales: {
9728
            r: {
9729
                type: 'radialLinear'
9730
            }
9731
        }
9732
    };
9733
 getLabelAndValue(index) {
9734
        const vScale = this._cachedMeta.vScale;
9735
        const parsed = this.getParsed(index);
9736
        return {
9737
            label: vScale.getLabels()[index],
9738
            value: '' + vScale.getLabelForValue(parsed[vScale.axis])
9739
        };
9740
    }
9741
    parseObjectData(meta, data, start, count) {
9742
        return _parseObjectDataRadialScale.bind(this)(meta, data, start, count);
9743
    }
9744
    update(mode) {
9745
        const meta = this._cachedMeta;
9746
        const line = meta.dataset;
9747
        const points = meta.data || [];
9748
        const labels = meta.iScale.getLabels();
9749
        line.points = points;
9750
        if (mode !== 'resize') {
9751
            const options = this.resolveDatasetElementOptions(mode);
9752
            if (!this.options.showLine) {
9753
                options.borderWidth = 0;
9754
            }
9755
            const properties = {
9756
                _loop: true,
9757
                _fullLoop: labels.length === points.length,
9758
                options
9759
            };
9760
            this.updateElement(line, undefined, properties, mode);
9761
        }
9762
        this.updateElements(points, 0, points.length, mode);
9763
    }
9764
    updateElements(points, start, count, mode) {
9765
        const scale = this._cachedMeta.rScale;
9766
        const reset = mode === 'reset';
9767
        for(let i = start; i < start + count; i++){
9768
            const point = points[i];
9769
            const options = this.resolveDataElementOptions(i, point.active ? 'active' : mode);
9770
            const pointPosition = scale.getPointPositionForValue(i, this.getParsed(i).r);
9771
            const x = reset ? scale.xCenter : pointPosition.x;
9772
            const y = reset ? scale.yCenter : pointPosition.y;
9773
            const properties = {
9774
                x,
9775
                y,
9776
                angle: pointPosition.angle,
9777
                skip: isNaN(x) || isNaN(y),
9778
                options
9779
            };
9780
            this.updateElement(point, i, properties, mode);
9781
        }
9782
    }
9783
}
9784
 
9785
class ScatterController extends DatasetController {
9786
    static id = 'scatter';
9787
 static defaults = {
9788
        datasetElementType: false,
9789
        dataElementType: 'point',
9790
        showLine: false,
9791
        fill: false
9792
    };
9793
 static overrides = {
9794
        interaction: {
9795
            mode: 'point'
9796
        },
9797
        scales: {
9798
            x: {
9799
                type: 'linear'
9800
            },
9801
            y: {
9802
                type: 'linear'
9803
            }
9804
        }
9805
    };
9806
 getLabelAndValue(index) {
9807
        const meta = this._cachedMeta;
9808
        const labels = this.chart.data.labels || [];
9809
        const { xScale , yScale  } = meta;
9810
        const parsed = this.getParsed(index);
9811
        const x = xScale.getLabelForValue(parsed.x);
9812
        const y = yScale.getLabelForValue(parsed.y);
9813
        return {
9814
            label: labels[index] || '',
9815
            value: '(' + x + ', ' + y + ')'
9816
        };
9817
    }
9818
    update(mode) {
9819
        const meta = this._cachedMeta;
9820
        const { data: points = []  } = meta;
9821
        const animationsDisabled = this.chart._animationsDisabled;
9822
        let { start , count  } = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
9823
        this._drawStart = start;
9824
        this._drawCount = count;
9825
        if (_scaleRangesChanged(meta)) {
9826
            start = 0;
9827
            count = points.length;
9828
        }
9829
        if (this.options.showLine) {
9830
            if (!this.datasetElementType) {
9831
                this.addElements();
9832
            }
9833
            const { dataset: line , _dataset  } = meta;
9834
            line._chart = this.chart;
9835
            line._datasetIndex = this.index;
9836
            line._decimated = !!_dataset._decimated;
9837
            line.points = points;
9838
            const options = this.resolveDatasetElementOptions(mode);
9839
            options.segment = this.options.segment;
9840
            this.updateElement(line, undefined, {
9841
                animated: !animationsDisabled,
9842
                options
9843
            }, mode);
9844
        } else if (this.datasetElementType) {
9845
            delete meta.dataset;
9846
            this.datasetElementType = false;
9847
        }
9848
        this.updateElements(points, start, count, mode);
9849
    }
9850
    addElements() {
9851
        const { showLine  } = this.options;
9852
        if (!this.datasetElementType && showLine) {
9853
            this.datasetElementType = this.chart.registry.getElement('line');
9854
        }
9855
        super.addElements();
9856
    }
9857
    updateElements(points, start, count, mode) {
9858
        const reset = mode === 'reset';
9859
        const { iScale , vScale , _stacked , _dataset  } = this._cachedMeta;
9860
        const firstOpts = this.resolveDataElementOptions(start, mode);
9861
        const sharedOptions = this.getSharedOptions(firstOpts);
9862
        const includeOptions = this.includeOptions(mode, sharedOptions);
9863
        const iAxis = iScale.axis;
9864
        const vAxis = vScale.axis;
9865
        const { spanGaps , segment  } = this.options;
9866
        const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
9867
        const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';
9868
        let prevParsed = start > 0 && this.getParsed(start - 1);
9869
        for(let i = start; i < start + count; ++i){
9870
            const point = points[i];
9871
            const parsed = this.getParsed(i);
9872
            const properties = directUpdate ? point : {};
9873
            const nullData = isNullOrUndef(parsed[vAxis]);
9874
            const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);
9875
            const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);
9876
            properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;
9877
            properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;
9878
            if (segment) {
9879
                properties.parsed = parsed;
9880
                properties.raw = _dataset.data[i];
9881
            }
9882
            if (includeOptions) {
9883
                properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);
9884
            }
9885
            if (!directUpdate) {
9886
                this.updateElement(point, i, properties, mode);
9887
            }
9888
            prevParsed = parsed;
9889
        }
9890
        this.updateSharedOptions(sharedOptions, mode, firstOpts);
9891
    }
9892
 getMaxOverflow() {
9893
        const meta = this._cachedMeta;
9894
        const data = meta.data || [];
9895
        if (!this.options.showLine) {
9896
            let max = 0;
9897
            for(let i = data.length - 1; i >= 0; --i){
9898
                max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);
9899
            }
9900
            return max > 0 && max;
9901
        }
9902
        const dataset = meta.dataset;
9903
        const border = dataset.options && dataset.options.borderWidth || 0;
9904
        if (!data.length) {
9905
            return border;
9906
        }
9907
        const firstPoint = data[0].size(this.resolveDataElementOptions(0));
9908
        const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));
9909
        return Math.max(border, firstPoint, lastPoint) / 2;
9910
    }
9911
}
9912
 
9913
var controllers = /*#__PURE__*/Object.freeze({
9914
__proto__: null,
9915
BarController: BarController,
9916
BubbleController: BubbleController,
9917
DoughnutController: DoughnutController,
9918
LineController: LineController,
9919
PieController: PieController,
9920
PolarAreaController: PolarAreaController,
9921
RadarController: RadarController,
9922
ScatterController: ScatterController
9923
});
9924
 
9925
function clipArc(ctx, element, endAngle) {
9926
    const { startAngle , pixelMargin , x , y , outerRadius , innerRadius  } = element;
9927
    let angleMargin = pixelMargin / outerRadius;
9928
    // Draw an inner border by clipping the arc and drawing a double-width border
9929
    // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders
9930
    ctx.beginPath();
9931
    ctx.arc(x, y, outerRadius, startAngle - angleMargin, endAngle + angleMargin);
9932
    if (innerRadius > pixelMargin) {
9933
        angleMargin = pixelMargin / innerRadius;
9934
        ctx.arc(x, y, innerRadius, endAngle + angleMargin, startAngle - angleMargin, true);
9935
    } else {
9936
        ctx.arc(x, y, pixelMargin, endAngle + HALF_PI, startAngle - HALF_PI);
9937
    }
9938
    ctx.closePath();
9939
    ctx.clip();
9940
}
9941
function toRadiusCorners(value) {
9942
    return _readValueToProps(value, [
9943
        'outerStart',
9944
        'outerEnd',
9945
        'innerStart',
9946
        'innerEnd'
9947
    ]);
9948
}
9949
/**
9950
 * Parse border radius from the provided options
9951
 */ function parseBorderRadius$1(arc, innerRadius, outerRadius, angleDelta) {
9952
    const o = toRadiusCorners(arc.options.borderRadius);
9953
    const halfThickness = (outerRadius - innerRadius) / 2;
9954
    const innerLimit = Math.min(halfThickness, angleDelta * innerRadius / 2);
9955
    // Outer limits are complicated. We want to compute the available angular distance at
9956
    // a radius of outerRadius - borderRadius because for small angular distances, this term limits.
9957
    // We compute at r = outerRadius - borderRadius because this circle defines the center of the border corners.
9958
    //
9959
    // If the borderRadius is large, that value can become negative.
9960
    // This causes the outer borders to lose their radius entirely, which is rather unexpected. To solve that, if borderRadius > outerRadius
9961
    // we know that the thickness term will dominate and compute the limits at that point
9962
    const computeOuterLimit = (val)=>{
9963
        const outerArcLimit = (outerRadius - Math.min(halfThickness, val)) * angleDelta / 2;
9964
        return _limitValue(val, 0, Math.min(halfThickness, outerArcLimit));
9965
    };
9966
    return {
9967
        outerStart: computeOuterLimit(o.outerStart),
9968
        outerEnd: computeOuterLimit(o.outerEnd),
9969
        innerStart: _limitValue(o.innerStart, 0, innerLimit),
9970
        innerEnd: _limitValue(o.innerEnd, 0, innerLimit)
9971
    };
9972
}
9973
/**
9974
 * Convert (r, 𝜃) to (x, y)
9975
 */ function rThetaToXY(r, theta, x, y) {
9976
    return {
9977
        x: x + r * Math.cos(theta),
9978
        y: y + r * Math.sin(theta)
9979
    };
9980
}
9981
/**
9982
 * Path the arc, respecting border radius by separating into left and right halves.
9983
 *
9984
 *   Start      End
9985
 *
9986
 *    1--->a--->2    Outer
9987
 *   /           \
9988
 *   8           3
9989
 *   |           |
9990
 *   |           |
9991
 *   7           4
9992
 *   \           /
9993
 *    6<---b<---5    Inner
9994
 */ function pathArc(ctx, element, offset, spacing, end, circular) {
9995
    const { x , y , startAngle: start , pixelMargin , innerRadius: innerR  } = element;
9996
    const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0);
9997
    const innerRadius = innerR > 0 ? innerR + spacing + offset + pixelMargin : 0;
9998
    let spacingOffset = 0;
9999
    const alpha = end - start;
10000
    if (spacing) {
10001
        // When spacing is present, it is the same for all items
10002
        // So we adjust the start and end angle of the arc such that
10003
        // the distance is the same as it would be without the spacing
10004
        const noSpacingInnerRadius = innerR > 0 ? innerR - spacing : 0;
10005
        const noSpacingOuterRadius = outerRadius > 0 ? outerRadius - spacing : 0;
10006
        const avNogSpacingRadius = (noSpacingInnerRadius + noSpacingOuterRadius) / 2;
10007
        const adjustedAngle = avNogSpacingRadius !== 0 ? alpha * avNogSpacingRadius / (avNogSpacingRadius + spacing) : alpha;
10008
        spacingOffset = (alpha - adjustedAngle) / 2;
10009
    }
10010
    const beta = Math.max(0.001, alpha * outerRadius - offset / PI) / outerRadius;
10011
    const angleOffset = (alpha - beta) / 2;
10012
    const startAngle = start + angleOffset + spacingOffset;
10013
    const endAngle = end - angleOffset - spacingOffset;
10014
    const { outerStart , outerEnd , innerStart , innerEnd  } = parseBorderRadius$1(element, innerRadius, outerRadius, endAngle - startAngle);
10015
    const outerStartAdjustedRadius = outerRadius - outerStart;
10016
    const outerEndAdjustedRadius = outerRadius - outerEnd;
10017
    const outerStartAdjustedAngle = startAngle + outerStart / outerStartAdjustedRadius;
10018
    const outerEndAdjustedAngle = endAngle - outerEnd / outerEndAdjustedRadius;
10019
    const innerStartAdjustedRadius = innerRadius + innerStart;
10020
    const innerEndAdjustedRadius = innerRadius + innerEnd;
10021
    const innerStartAdjustedAngle = startAngle + innerStart / innerStartAdjustedRadius;
10022
    const innerEndAdjustedAngle = endAngle - innerEnd / innerEndAdjustedRadius;
10023
    ctx.beginPath();
10024
    if (circular) {
10025
        // The first arc segments from point 1 to point a to point 2
10026
        const outerMidAdjustedAngle = (outerStartAdjustedAngle + outerEndAdjustedAngle) / 2;
10027
        ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerMidAdjustedAngle);
10028
        ctx.arc(x, y, outerRadius, outerMidAdjustedAngle, outerEndAdjustedAngle);
10029
        // The corner segment from point 2 to point 3
10030
        if (outerEnd > 0) {
10031
            const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y);
10032
            ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI);
10033
        }
10034
        // The line from point 3 to point 4
10035
        const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y);
10036
        ctx.lineTo(p4.x, p4.y);
10037
        // The corner segment from point 4 to point 5
10038
        if (innerEnd > 0) {
10039
            const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y);
10040
            ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI);
10041
        }
10042
        // The inner arc from point 5 to point b to point 6
10043
        const innerMidAdjustedAngle = (endAngle - innerEnd / innerRadius + (startAngle + innerStart / innerRadius)) / 2;
10044
        ctx.arc(x, y, innerRadius, endAngle - innerEnd / innerRadius, innerMidAdjustedAngle, true);
10045
        ctx.arc(x, y, innerRadius, innerMidAdjustedAngle, startAngle + innerStart / innerRadius, true);
10046
        // The corner segment from point 6 to point 7
10047
        if (innerStart > 0) {
10048
            const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y);
10049
            ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI);
10050
        }
10051
        // The line from point 7 to point 8
10052
        const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y);
10053
        ctx.lineTo(p8.x, p8.y);
10054
        // The corner segment from point 8 to point 1
10055
        if (outerStart > 0) {
10056
            const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y);
10057
            ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle);
10058
        }
10059
    } else {
10060
        ctx.moveTo(x, y);
10061
        const outerStartX = Math.cos(outerStartAdjustedAngle) * outerRadius + x;
10062
        const outerStartY = Math.sin(outerStartAdjustedAngle) * outerRadius + y;
10063
        ctx.lineTo(outerStartX, outerStartY);
10064
        const outerEndX = Math.cos(outerEndAdjustedAngle) * outerRadius + x;
10065
        const outerEndY = Math.sin(outerEndAdjustedAngle) * outerRadius + y;
10066
        ctx.lineTo(outerEndX, outerEndY);
10067
    }
10068
    ctx.closePath();
10069
}
10070
function drawArc(ctx, element, offset, spacing, circular) {
10071
    const { fullCircles , startAngle , circumference  } = element;
10072
    let endAngle = element.endAngle;
10073
    if (fullCircles) {
10074
        pathArc(ctx, element, offset, spacing, endAngle, circular);
10075
        for(let i = 0; i < fullCircles; ++i){
10076
            ctx.fill();
10077
        }
10078
        if (!isNaN(circumference)) {
10079
            endAngle = startAngle + (circumference % TAU || TAU);
10080
        }
10081
    }
10082
    pathArc(ctx, element, offset, spacing, endAngle, circular);
10083
    ctx.fill();
10084
    return endAngle;
10085
}
10086
function drawBorder(ctx, element, offset, spacing, circular) {
10087
    const { fullCircles , startAngle , circumference , options  } = element;
10088
    const { borderWidth , borderJoinStyle , borderDash , borderDashOffset  } = options;
10089
    const inner = options.borderAlign === 'inner';
10090
    if (!borderWidth) {
10091
        return;
10092
    }
10093
    ctx.setLineDash(borderDash || []);
10094
    ctx.lineDashOffset = borderDashOffset;
10095
    if (inner) {
10096
        ctx.lineWidth = borderWidth * 2;
10097
        ctx.lineJoin = borderJoinStyle || 'round';
10098
    } else {
10099
        ctx.lineWidth = borderWidth;
10100
        ctx.lineJoin = borderJoinStyle || 'bevel';
10101
    }
10102
    let endAngle = element.endAngle;
10103
    if (fullCircles) {
10104
        pathArc(ctx, element, offset, spacing, endAngle, circular);
10105
        for(let i = 0; i < fullCircles; ++i){
10106
            ctx.stroke();
10107
        }
10108
        if (!isNaN(circumference)) {
10109
            endAngle = startAngle + (circumference % TAU || TAU);
10110
        }
10111
    }
10112
    if (inner) {
10113
        clipArc(ctx, element, endAngle);
10114
    }
10115
    if (!fullCircles) {
10116
        pathArc(ctx, element, offset, spacing, endAngle, circular);
10117
        ctx.stroke();
10118
    }
10119
}
10120
class ArcElement extends Element {
10121
    static id = 'arc';
10122
    static defaults = {
10123
        borderAlign: 'center',
10124
        borderColor: '#fff',
10125
        borderDash: [],
10126
        borderDashOffset: 0,
10127
        borderJoinStyle: undefined,
10128
        borderRadius: 0,
10129
        borderWidth: 2,
10130
        offset: 0,
10131
        spacing: 0,
10132
        angle: undefined,
10133
        circular: true
10134
    };
10135
    static defaultRoutes = {
10136
        backgroundColor: 'backgroundColor'
10137
    };
10138
    static descriptors = {
10139
        _scriptable: true,
10140
        _indexable: (name)=>name !== 'borderDash'
10141
    };
10142
    circumference;
10143
    endAngle;
10144
    fullCircles;
10145
    innerRadius;
10146
    outerRadius;
10147
    pixelMargin;
10148
    startAngle;
10149
    constructor(cfg){
10150
        super();
10151
        this.options = undefined;
10152
        this.circumference = undefined;
10153
        this.startAngle = undefined;
10154
        this.endAngle = undefined;
10155
        this.innerRadius = undefined;
10156
        this.outerRadius = undefined;
10157
        this.pixelMargin = 0;
10158
        this.fullCircles = 0;
10159
        if (cfg) {
10160
            Object.assign(this, cfg);
10161
        }
10162
    }
10163
    inRange(chartX, chartY, useFinalPosition) {
10164
        const point = this.getProps([
10165
            'x',
10166
            'y'
10167
        ], useFinalPosition);
10168
        const { angle , distance  } = getAngleFromPoint(point, {
10169
            x: chartX,
10170
            y: chartY
10171
        });
10172
        const { startAngle , endAngle , innerRadius , outerRadius , circumference  } = this.getProps([
10173
            'startAngle',
10174
            'endAngle',
10175
            'innerRadius',
10176
            'outerRadius',
10177
            'circumference'
10178
        ], useFinalPosition);
10179
        const rAdjust = (this.options.spacing + this.options.borderWidth) / 2;
10180
        const _circumference = valueOrDefault(circumference, endAngle - startAngle);
10181
        const betweenAngles = _circumference >= TAU || _angleBetween(angle, startAngle, endAngle);
10182
        const withinRadius = _isBetween(distance, innerRadius + rAdjust, outerRadius + rAdjust);
10183
        return betweenAngles && withinRadius;
10184
    }
10185
    getCenterPoint(useFinalPosition) {
10186
        const { x , y , startAngle , endAngle , innerRadius , outerRadius  } = this.getProps([
10187
            'x',
10188
            'y',
10189
            'startAngle',
10190
            'endAngle',
10191
            'innerRadius',
10192
            'outerRadius'
10193
        ], useFinalPosition);
10194
        const { offset , spacing  } = this.options;
10195
        const halfAngle = (startAngle + endAngle) / 2;
10196
        const halfRadius = (innerRadius + outerRadius + spacing + offset) / 2;
10197
        return {
10198
            x: x + Math.cos(halfAngle) * halfRadius,
10199
            y: y + Math.sin(halfAngle) * halfRadius
10200
        };
10201
    }
10202
    tooltipPosition(useFinalPosition) {
10203
        return this.getCenterPoint(useFinalPosition);
10204
    }
10205
    draw(ctx) {
10206
        const { options , circumference  } = this;
10207
        const offset = (options.offset || 0) / 4;
10208
        const spacing = (options.spacing || 0) / 2;
10209
        const circular = options.circular;
10210
        this.pixelMargin = options.borderAlign === 'inner' ? 0.33 : 0;
10211
        this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0;
10212
        if (circumference === 0 || this.innerRadius < 0 || this.outerRadius < 0) {
10213
            return;
10214
        }
10215
        ctx.save();
10216
        const halfAngle = (this.startAngle + this.endAngle) / 2;
10217
        ctx.translate(Math.cos(halfAngle) * offset, Math.sin(halfAngle) * offset);
10218
        const fix = 1 - Math.sin(Math.min(PI, circumference || 0));
10219
        const radiusOffset = offset * fix;
10220
        ctx.fillStyle = options.backgroundColor;
10221
        ctx.strokeStyle = options.borderColor;
10222
        drawArc(ctx, this, radiusOffset, spacing, circular);
10223
        drawBorder(ctx, this, radiusOffset, spacing, circular);
10224
        ctx.restore();
10225
    }
10226
}
10227
 
10228
function setStyle(ctx, options, style = options) {
10229
    ctx.lineCap = valueOrDefault(style.borderCapStyle, options.borderCapStyle);
10230
    ctx.setLineDash(valueOrDefault(style.borderDash, options.borderDash));
10231
    ctx.lineDashOffset = valueOrDefault(style.borderDashOffset, options.borderDashOffset);
10232
    ctx.lineJoin = valueOrDefault(style.borderJoinStyle, options.borderJoinStyle);
10233
    ctx.lineWidth = valueOrDefault(style.borderWidth, options.borderWidth);
10234
    ctx.strokeStyle = valueOrDefault(style.borderColor, options.borderColor);
10235
}
10236
function lineTo(ctx, previous, target) {
10237
    ctx.lineTo(target.x, target.y);
10238
}
10239
 function getLineMethod(options) {
10240
    if (options.stepped) {
10241
        return _steppedLineTo;
10242
    }
10243
    if (options.tension || options.cubicInterpolationMode === 'monotone') {
10244
        return _bezierCurveTo;
10245
    }
10246
    return lineTo;
10247
}
10248
function pathVars(points, segment, params = {}) {
10249
    const count = points.length;
10250
    const { start: paramsStart = 0 , end: paramsEnd = count - 1  } = params;
10251
    const { start: segmentStart , end: segmentEnd  } = segment;
10252
    const start = Math.max(paramsStart, segmentStart);
10253
    const end = Math.min(paramsEnd, segmentEnd);
10254
    const outside = paramsStart < segmentStart && paramsEnd < segmentStart || paramsStart > segmentEnd && paramsEnd > segmentEnd;
10255
    return {
10256
        count,
10257
        start,
10258
        loop: segment.loop,
10259
        ilen: end < start && !outside ? count + end - start : end - start
10260
    };
10261
}
10262
 function pathSegment(ctx, line, segment, params) {
10263
    const { points , options  } = line;
10264
    const { count , start , loop , ilen  } = pathVars(points, segment, params);
10265
    const lineMethod = getLineMethod(options);
10266
    let { move =true , reverse  } = params || {};
10267
    let i, point, prev;
10268
    for(i = 0; i <= ilen; ++i){
10269
        point = points[(start + (reverse ? ilen - i : i)) % count];
10270
        if (point.skip) {
10271
            continue;
10272
        } else if (move) {
10273
            ctx.moveTo(point.x, point.y);
10274
            move = false;
10275
        } else {
10276
            lineMethod(ctx, prev, point, reverse, options.stepped);
10277
        }
10278
        prev = point;
10279
    }
10280
    if (loop) {
10281
        point = points[(start + (reverse ? ilen : 0)) % count];
10282
        lineMethod(ctx, prev, point, reverse, options.stepped);
10283
    }
10284
    return !!loop;
10285
}
10286
 function fastPathSegment(ctx, line, segment, params) {
10287
    const points = line.points;
10288
    const { count , start , ilen  } = pathVars(points, segment, params);
10289
    const { move =true , reverse  } = params || {};
10290
    let avgX = 0;
10291
    let countX = 0;
10292
    let i, point, prevX, minY, maxY, lastY;
10293
    const pointIndex = (index)=>(start + (reverse ? ilen - index : index)) % count;
10294
    const drawX = ()=>{
10295
        if (minY !== maxY) {
10296
            ctx.lineTo(avgX, maxY);
10297
            ctx.lineTo(avgX, minY);
10298
            ctx.lineTo(avgX, lastY);
10299
        }
10300
    };
10301
    if (move) {
10302
        point = points[pointIndex(0)];
10303
        ctx.moveTo(point.x, point.y);
10304
    }
10305
    for(i = 0; i <= ilen; ++i){
10306
        point = points[pointIndex(i)];
10307
        if (point.skip) {
10308
            continue;
10309
        }
10310
        const x = point.x;
10311
        const y = point.y;
10312
        const truncX = x | 0;
10313
        if (truncX === prevX) {
10314
            if (y < minY) {
10315
                minY = y;
10316
            } else if (y > maxY) {
10317
                maxY = y;
10318
            }
10319
            avgX = (countX * avgX + x) / ++countX;
10320
        } else {
10321
            drawX();
10322
            ctx.lineTo(x, y);
10323
            prevX = truncX;
10324
            countX = 0;
10325
            minY = maxY = y;
10326
        }
10327
        lastY = y;
10328
    }
10329
    drawX();
10330
}
10331
 function _getSegmentMethod(line) {
10332
    const opts = line.options;
10333
    const borderDash = opts.borderDash && opts.borderDash.length;
10334
    const useFastPath = !line._decimated && !line._loop && !opts.tension && opts.cubicInterpolationMode !== 'monotone' && !opts.stepped && !borderDash;
10335
    return useFastPath ? fastPathSegment : pathSegment;
10336
}
10337
 function _getInterpolationMethod(options) {
10338
    if (options.stepped) {
10339
        return _steppedInterpolation;
10340
    }
10341
    if (options.tension || options.cubicInterpolationMode === 'monotone') {
10342
        return _bezierInterpolation;
10343
    }
10344
    return _pointInLine;
10345
}
10346
function strokePathWithCache(ctx, line, start, count) {
10347
    let path = line._path;
10348
    if (!path) {
10349
        path = line._path = new Path2D();
10350
        if (line.path(path, start, count)) {
10351
            path.closePath();
10352
        }
10353
    }
10354
    setStyle(ctx, line.options);
10355
    ctx.stroke(path);
10356
}
10357
function strokePathDirect(ctx, line, start, count) {
10358
    const { segments , options  } = line;
10359
    const segmentMethod = _getSegmentMethod(line);
10360
    for (const segment of segments){
10361
        setStyle(ctx, options, segment.style);
10362
        ctx.beginPath();
10363
        if (segmentMethod(ctx, line, segment, {
10364
            start,
10365
            end: start + count - 1
10366
        })) {
10367
            ctx.closePath();
10368
        }
10369
        ctx.stroke();
10370
    }
10371
}
10372
const usePath2D = typeof Path2D === 'function';
10373
function draw(ctx, line, start, count) {
10374
    if (usePath2D && !line.options.segment) {
10375
        strokePathWithCache(ctx, line, start, count);
10376
    } else {
10377
        strokePathDirect(ctx, line, start, count);
10378
    }
10379
}
10380
class LineElement extends Element {
10381
    static id = 'line';
10382
 static defaults = {
10383
        borderCapStyle: 'butt',
10384
        borderDash: [],
10385
        borderDashOffset: 0,
10386
        borderJoinStyle: 'miter',
10387
        borderWidth: 3,
10388
        capBezierPoints: true,
10389
        cubicInterpolationMode: 'default',
10390
        fill: false,
10391
        spanGaps: false,
10392
        stepped: false,
10393
        tension: 0
10394
    };
10395
 static defaultRoutes = {
10396
        backgroundColor: 'backgroundColor',
10397
        borderColor: 'borderColor'
10398
    };
10399
    static descriptors = {
10400
        _scriptable: true,
10401
        _indexable: (name)=>name !== 'borderDash' && name !== 'fill'
10402
    };
10403
    constructor(cfg){
10404
        super();
10405
        this.animated = true;
10406
        this.options = undefined;
10407
        this._chart = undefined;
10408
        this._loop = undefined;
10409
        this._fullLoop = undefined;
10410
        this._path = undefined;
10411
        this._points = undefined;
10412
        this._segments = undefined;
10413
        this._decimated = false;
10414
        this._pointsUpdated = false;
10415
        this._datasetIndex = undefined;
10416
        if (cfg) {
10417
            Object.assign(this, cfg);
10418
        }
10419
    }
10420
    updateControlPoints(chartArea, indexAxis) {
10421
        const options = this.options;
10422
        if ((options.tension || options.cubicInterpolationMode === 'monotone') && !options.stepped && !this._pointsUpdated) {
10423
            const loop = options.spanGaps ? this._loop : this._fullLoop;
10424
            _updateBezierControlPoints(this._points, options, chartArea, loop, indexAxis);
10425
            this._pointsUpdated = true;
10426
        }
10427
    }
10428
    set points(points) {
10429
        this._points = points;
10430
        delete this._segments;
10431
        delete this._path;
10432
        this._pointsUpdated = false;
10433
    }
10434
    get points() {
10435
        return this._points;
10436
    }
10437
    get segments() {
10438
        return this._segments || (this._segments = _computeSegments(this, this.options.segment));
10439
    }
10440
 first() {
10441
        const segments = this.segments;
10442
        const points = this.points;
10443
        return segments.length && points[segments[0].start];
10444
    }
10445
 last() {
10446
        const segments = this.segments;
10447
        const points = this.points;
10448
        const count = segments.length;
10449
        return count && points[segments[count - 1].end];
10450
    }
10451
 interpolate(point, property) {
10452
        const options = this.options;
10453
        const value = point[property];
10454
        const points = this.points;
10455
        const segments = _boundSegments(this, {
10456
            property,
10457
            start: value,
10458
            end: value
10459
        });
10460
        if (!segments.length) {
10461
            return;
10462
        }
10463
        const result = [];
10464
        const _interpolate = _getInterpolationMethod(options);
10465
        let i, ilen;
10466
        for(i = 0, ilen = segments.length; i < ilen; ++i){
10467
            const { start , end  } = segments[i];
10468
            const p1 = points[start];
10469
            const p2 = points[end];
10470
            if (p1 === p2) {
10471
                result.push(p1);
10472
                continue;
10473
            }
10474
            const t = Math.abs((value - p1[property]) / (p2[property] - p1[property]));
10475
            const interpolated = _interpolate(p1, p2, t, options.stepped);
10476
            interpolated[property] = point[property];
10477
            result.push(interpolated);
10478
        }
10479
        return result.length === 1 ? result[0] : result;
10480
    }
10481
 pathSegment(ctx, segment, params) {
10482
        const segmentMethod = _getSegmentMethod(this);
10483
        return segmentMethod(ctx, this, segment, params);
10484
    }
10485
 path(ctx, start, count) {
10486
        const segments = this.segments;
10487
        const segmentMethod = _getSegmentMethod(this);
10488
        let loop = this._loop;
10489
        start = start || 0;
10490
        count = count || this.points.length - start;
10491
        for (const segment of segments){
10492
            loop &= segmentMethod(ctx, this, segment, {
10493
                start,
10494
                end: start + count - 1
10495
            });
10496
        }
10497
        return !!loop;
10498
    }
10499
 draw(ctx, chartArea, start, count) {
10500
        const options = this.options || {};
10501
        const points = this.points || [];
10502
        if (points.length && options.borderWidth) {
10503
            ctx.save();
10504
            draw(ctx, this, start, count);
10505
            ctx.restore();
10506
        }
10507
        if (this.animated) {
10508
            this._pointsUpdated = false;
10509
            this._path = undefined;
10510
        }
10511
    }
10512
}
10513
 
10514
function inRange$1(el, pos, axis, useFinalPosition) {
10515
    const options = el.options;
10516
    const { [axis]: value  } = el.getProps([
10517
        axis
10518
    ], useFinalPosition);
10519
    return Math.abs(pos - value) < options.radius + options.hitRadius;
10520
}
10521
class PointElement extends Element {
10522
    static id = 'point';
10523
    parsed;
10524
    skip;
10525
    stop;
10526
    /**
10527
   * @type {any}
10528
   */ static defaults = {
10529
        borderWidth: 1,
10530
        hitRadius: 1,
10531
        hoverBorderWidth: 1,
10532
        hoverRadius: 4,
10533
        pointStyle: 'circle',
10534
        radius: 3,
10535
        rotation: 0
10536
    };
10537
    /**
10538
   * @type {any}
10539
   */ static defaultRoutes = {
10540
        backgroundColor: 'backgroundColor',
10541
        borderColor: 'borderColor'
10542
    };
10543
    constructor(cfg){
10544
        super();
10545
        this.options = undefined;
10546
        this.parsed = undefined;
10547
        this.skip = undefined;
10548
        this.stop = undefined;
10549
        if (cfg) {
10550
            Object.assign(this, cfg);
10551
        }
10552
    }
10553
    inRange(mouseX, mouseY, useFinalPosition) {
10554
        const options = this.options;
10555
        const { x , y  } = this.getProps([
10556
            'x',
10557
            'y'
10558
        ], useFinalPosition);
10559
        return Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2) < Math.pow(options.hitRadius + options.radius, 2);
10560
    }
10561
    inXRange(mouseX, useFinalPosition) {
10562
        return inRange$1(this, mouseX, 'x', useFinalPosition);
10563
    }
10564
    inYRange(mouseY, useFinalPosition) {
10565
        return inRange$1(this, mouseY, 'y', useFinalPosition);
10566
    }
10567
    getCenterPoint(useFinalPosition) {
10568
        const { x , y  } = this.getProps([
10569
            'x',
10570
            'y'
10571
        ], useFinalPosition);
10572
        return {
10573
            x,
10574
            y
10575
        };
10576
    }
10577
    size(options) {
10578
        options = options || this.options || {};
10579
        let radius = options.radius || 0;
10580
        radius = Math.max(radius, radius && options.hoverRadius || 0);
10581
        const borderWidth = radius && options.borderWidth || 0;
10582
        return (radius + borderWidth) * 2;
10583
    }
10584
    draw(ctx, area) {
10585
        const options = this.options;
10586
        if (this.skip || options.radius < 0.1 || !_isPointInArea(this, area, this.size(options) / 2)) {
10587
            return;
10588
        }
10589
        ctx.strokeStyle = options.borderColor;
10590
        ctx.lineWidth = options.borderWidth;
10591
        ctx.fillStyle = options.backgroundColor;
10592
        drawPoint(ctx, options, this.x, this.y);
10593
    }
10594
    getRange() {
10595
        const options = this.options || {};
10596
        // @ts-expect-error Fallbacks should never be hit in practice
10597
        return options.radius + options.hitRadius;
10598
    }
10599
}
10600
 
10601
function getBarBounds(bar, useFinalPosition) {
10602
    const { x , y , base , width , height  } =  bar.getProps([
10603
        'x',
10604
        'y',
10605
        'base',
10606
        'width',
10607
        'height'
10608
    ], useFinalPosition);
10609
    let left, right, top, bottom, half;
10610
    if (bar.horizontal) {
10611
        half = height / 2;
10612
        left = Math.min(x, base);
10613
        right = Math.max(x, base);
10614
        top = y - half;
10615
        bottom = y + half;
10616
    } else {
10617
        half = width / 2;
10618
        left = x - half;
10619
        right = x + half;
10620
        top = Math.min(y, base);
10621
        bottom = Math.max(y, base);
10622
    }
10623
    return {
10624
        left,
10625
        top,
10626
        right,
10627
        bottom
10628
    };
10629
}
10630
function skipOrLimit(skip, value, min, max) {
10631
    return skip ? 0 : _limitValue(value, min, max);
10632
}
10633
function parseBorderWidth(bar, maxW, maxH) {
10634
    const value = bar.options.borderWidth;
10635
    const skip = bar.borderSkipped;
10636
    const o = toTRBL(value);
10637
    return {
10638
        t: skipOrLimit(skip.top, o.top, 0, maxH),
10639
        r: skipOrLimit(skip.right, o.right, 0, maxW),
10640
        b: skipOrLimit(skip.bottom, o.bottom, 0, maxH),
10641
        l: skipOrLimit(skip.left, o.left, 0, maxW)
10642
    };
10643
}
10644
function parseBorderRadius(bar, maxW, maxH) {
10645
    const { enableBorderRadius  } = bar.getProps([
10646
        'enableBorderRadius'
10647
    ]);
10648
    const value = bar.options.borderRadius;
10649
    const o = toTRBLCorners(value);
10650
    const maxR = Math.min(maxW, maxH);
10651
    const skip = bar.borderSkipped;
10652
    const enableBorder = enableBorderRadius || isObject(value);
10653
    return {
10654
        topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR),
10655
        topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR),
10656
        bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR),
10657
        bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR)
10658
    };
10659
}
10660
function boundingRects(bar) {
10661
    const bounds = getBarBounds(bar);
10662
    const width = bounds.right - bounds.left;
10663
    const height = bounds.bottom - bounds.top;
10664
    const border = parseBorderWidth(bar, width / 2, height / 2);
10665
    const radius = parseBorderRadius(bar, width / 2, height / 2);
10666
    return {
10667
        outer: {
10668
            x: bounds.left,
10669
            y: bounds.top,
10670
            w: width,
10671
            h: height,
10672
            radius
10673
        },
10674
        inner: {
10675
            x: bounds.left + border.l,
10676
            y: bounds.top + border.t,
10677
            w: width - border.l - border.r,
10678
            h: height - border.t - border.b,
10679
            radius: {
10680
                topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)),
10681
                topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)),
10682
                bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)),
10683
                bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r))
10684
            }
10685
        }
10686
    };
10687
}
10688
function inRange(bar, x, y, useFinalPosition) {
10689
    const skipX = x === null;
10690
    const skipY = y === null;
10691
    const skipBoth = skipX && skipY;
10692
    const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition);
10693
    return bounds && (skipX || _isBetween(x, bounds.left, bounds.right)) && (skipY || _isBetween(y, bounds.top, bounds.bottom));
10694
}
10695
function hasRadius(radius) {
10696
    return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;
10697
}
10698
 function addNormalRectPath(ctx, rect) {
10699
    ctx.rect(rect.x, rect.y, rect.w, rect.h);
10700
}
10701
function inflateRect(rect, amount, refRect = {}) {
10702
    const x = rect.x !== refRect.x ? -amount : 0;
10703
    const y = rect.y !== refRect.y ? -amount : 0;
10704
    const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x;
10705
    const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y;
10706
    return {
10707
        x: rect.x + x,
10708
        y: rect.y + y,
10709
        w: rect.w + w,
10710
        h: rect.h + h,
10711
        radius: rect.radius
10712
    };
10713
}
10714
class BarElement extends Element {
10715
    static id = 'bar';
10716
 static defaults = {
10717
        borderSkipped: 'start',
10718
        borderWidth: 0,
10719
        borderRadius: 0,
10720
        inflateAmount: 'auto',
10721
        pointStyle: undefined
10722
    };
10723
 static defaultRoutes = {
10724
        backgroundColor: 'backgroundColor',
10725
        borderColor: 'borderColor'
10726
    };
10727
    constructor(cfg){
10728
        super();
10729
        this.options = undefined;
10730
        this.horizontal = undefined;
10731
        this.base = undefined;
10732
        this.width = undefined;
10733
        this.height = undefined;
10734
        this.inflateAmount = undefined;
10735
        if (cfg) {
10736
            Object.assign(this, cfg);
10737
        }
10738
    }
10739
    draw(ctx) {
10740
        const { inflateAmount , options: { borderColor , backgroundColor  }  } = this;
10741
        const { inner , outer  } = boundingRects(this);
10742
        const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath;
10743
        ctx.save();
10744
        if (outer.w !== inner.w || outer.h !== inner.h) {
10745
            ctx.beginPath();
10746
            addRectPath(ctx, inflateRect(outer, inflateAmount, inner));
10747
            ctx.clip();
10748
            addRectPath(ctx, inflateRect(inner, -inflateAmount, outer));
10749
            ctx.fillStyle = borderColor;
10750
            ctx.fill('evenodd');
10751
        }
10752
        ctx.beginPath();
10753
        addRectPath(ctx, inflateRect(inner, inflateAmount));
10754
        ctx.fillStyle = backgroundColor;
10755
        ctx.fill();
10756
        ctx.restore();
10757
    }
10758
    inRange(mouseX, mouseY, useFinalPosition) {
10759
        return inRange(this, mouseX, mouseY, useFinalPosition);
10760
    }
10761
    inXRange(mouseX, useFinalPosition) {
10762
        return inRange(this, mouseX, null, useFinalPosition);
10763
    }
10764
    inYRange(mouseY, useFinalPosition) {
10765
        return inRange(this, null, mouseY, useFinalPosition);
10766
    }
10767
    getCenterPoint(useFinalPosition) {
10768
        const { x , y , base , horizontal  } =  this.getProps([
10769
            'x',
10770
            'y',
10771
            'base',
10772
            'horizontal'
10773
        ], useFinalPosition);
10774
        return {
10775
            x: horizontal ? (x + base) / 2 : x,
10776
            y: horizontal ? y : (y + base) / 2
10777
        };
10778
    }
10779
    getRange(axis) {
10780
        return axis === 'x' ? this.width / 2 : this.height / 2;
10781
    }
10782
}
10783
 
10784
var elements = /*#__PURE__*/Object.freeze({
10785
__proto__: null,
10786
ArcElement: ArcElement,
10787
BarElement: BarElement,
10788
LineElement: LineElement,
10789
PointElement: PointElement
10790
});
10791
 
10792
const addIfString = (labels, raw, index, addedLabels)=>{
10793
    if (typeof raw === 'string') {
10794
        index = labels.push(raw) - 1;
10795
        addedLabels.unshift({
10796
            index,
10797
            label: raw
10798
        });
10799
    } else if (isNaN(raw)) {
10800
        index = null;
10801
    }
10802
    return index;
10803
};
10804
function findOrAddLabel(labels, raw, index, addedLabels) {
10805
    const first = labels.indexOf(raw);
10806
    if (first === -1) {
10807
        return addIfString(labels, raw, index, addedLabels);
10808
    }
10809
    const last = labels.lastIndexOf(raw);
10810
    return first !== last ? index : first;
10811
}
10812
const validIndex = (index, max)=>index === null ? null : _limitValue(Math.round(index), 0, max);
10813
function _getLabelForValue(value) {
10814
    const labels = this.getLabels();
10815
    if (value >= 0 && value < labels.length) {
10816
        return labels[value];
10817
    }
10818
    return value;
10819
}
10820
class CategoryScale extends Scale {
10821
    static id = 'category';
10822
 static defaults = {
10823
        ticks: {
10824
            callback: _getLabelForValue
10825
        }
10826
    };
10827
    constructor(cfg){
10828
        super(cfg);
10829
         this._startValue = undefined;
10830
        this._valueRange = 0;
10831
        this._addedLabels = [];
10832
    }
10833
    init(scaleOptions) {
10834
        const added = this._addedLabels;
10835
        if (added.length) {
10836
            const labels = this.getLabels();
10837
            for (const { index , label  } of added){
10838
                if (labels[index] === label) {
10839
                    labels.splice(index, 1);
10840
                }
10841
            }
10842
            this._addedLabels = [];
10843
        }
10844
        super.init(scaleOptions);
10845
    }
10846
    parse(raw, index) {
10847
        if (isNullOrUndef(raw)) {
10848
            return null;
10849
        }
10850
        const labels = this.getLabels();
10851
        index = isFinite(index) && labels[index] === raw ? index : findOrAddLabel(labels, raw, valueOrDefault(index, raw), this._addedLabels);
10852
        return validIndex(index, labels.length - 1);
10853
    }
10854
    determineDataLimits() {
10855
        const { minDefined , maxDefined  } = this.getUserBounds();
10856
        let { min , max  } = this.getMinMax(true);
10857
        if (this.options.bounds === 'ticks') {
10858
            if (!minDefined) {
10859
                min = 0;
10860
            }
10861
            if (!maxDefined) {
10862
                max = this.getLabels().length - 1;
10863
            }
10864
        }
10865
        this.min = min;
10866
        this.max = max;
10867
    }
10868
    buildTicks() {
10869
        const min = this.min;
10870
        const max = this.max;
10871
        const offset = this.options.offset;
10872
        const ticks = [];
10873
        let labels = this.getLabels();
10874
        labels = min === 0 && max === labels.length - 1 ? labels : labels.slice(min, max + 1);
10875
        this._valueRange = Math.max(labels.length - (offset ? 0 : 1), 1);
10876
        this._startValue = this.min - (offset ? 0.5 : 0);
10877
        for(let value = min; value <= max; value++){
10878
            ticks.push({
10879
                value
10880
            });
10881
        }
10882
        return ticks;
10883
    }
10884
    getLabelForValue(value) {
10885
        return _getLabelForValue.call(this, value);
10886
    }
10887
 configure() {
10888
        super.configure();
10889
        if (!this.isHorizontal()) {
10890
            this._reversePixels = !this._reversePixels;
10891
        }
10892
    }
10893
    getPixelForValue(value) {
10894
        if (typeof value !== 'number') {
10895
            value = this.parse(value);
10896
        }
10897
        return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);
10898
    }
10899
    getPixelForTick(index) {
10900
        const ticks = this.ticks;
10901
        if (index < 0 || index > ticks.length - 1) {
10902
            return null;
10903
        }
10904
        return this.getPixelForValue(ticks[index].value);
10905
    }
10906
    getValueForPixel(pixel) {
10907
        return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange);
10908
    }
10909
    getBasePixel() {
10910
        return this.bottom;
10911
    }
10912
}
10913
 
10914
function generateTicks$1(generationOptions, dataRange) {
10915
    const ticks = [];
10916
    const MIN_SPACING = 1e-14;
10917
    const { bounds , step , min , max , precision , count , maxTicks , maxDigits , includeBounds  } = generationOptions;
10918
    const unit = step || 1;
10919
    const maxSpaces = maxTicks - 1;
10920
    const { min: rmin , max: rmax  } = dataRange;
10921
    const minDefined = !isNullOrUndef(min);
10922
    const maxDefined = !isNullOrUndef(max);
10923
    const countDefined = !isNullOrUndef(count);
10924
    const minSpacing = (rmax - rmin) / (maxDigits + 1);
10925
    let spacing = niceNum((rmax - rmin) / maxSpaces / unit) * unit;
10926
    let factor, niceMin, niceMax, numSpaces;
10927
    if (spacing < MIN_SPACING && !minDefined && !maxDefined) {
10928
        return [
10929
            {
10930
                value: rmin
10931
            },
10932
            {
10933
                value: rmax
10934
            }
10935
        ];
10936
    }
10937
    numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);
10938
    if (numSpaces > maxSpaces) {
10939
        spacing = niceNum(numSpaces * spacing / maxSpaces / unit) * unit;
10940
    }
10941
    if (!isNullOrUndef(precision)) {
10942
        factor = Math.pow(10, precision);
10943
        spacing = Math.ceil(spacing * factor) / factor;
10944
    }
10945
    if (bounds === 'ticks') {
10946
        niceMin = Math.floor(rmin / spacing) * spacing;
10947
        niceMax = Math.ceil(rmax / spacing) * spacing;
10948
    } else {
10949
        niceMin = rmin;
10950
        niceMax = rmax;
10951
    }
10952
    if (minDefined && maxDefined && step && almostWhole((max - min) / step, spacing / 1000)) {
10953
        numSpaces = Math.round(Math.min((max - min) / spacing, maxTicks));
10954
        spacing = (max - min) / numSpaces;
10955
        niceMin = min;
10956
        niceMax = max;
10957
    } else if (countDefined) {
10958
        niceMin = minDefined ? min : niceMin;
10959
        niceMax = maxDefined ? max : niceMax;
10960
        numSpaces = count - 1;
10961
        spacing = (niceMax - niceMin) / numSpaces;
10962
    } else {
10963
        numSpaces = (niceMax - niceMin) / spacing;
10964
        if (almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
10965
            numSpaces = Math.round(numSpaces);
10966
        } else {
10967
            numSpaces = Math.ceil(numSpaces);
10968
        }
10969
    }
10970
    const decimalPlaces = Math.max(_decimalPlaces(spacing), _decimalPlaces(niceMin));
10971
    factor = Math.pow(10, isNullOrUndef(precision) ? decimalPlaces : precision);
10972
    niceMin = Math.round(niceMin * factor) / factor;
10973
    niceMax = Math.round(niceMax * factor) / factor;
10974
    let j = 0;
10975
    if (minDefined) {
10976
        if (includeBounds && niceMin !== min) {
10977
            ticks.push({
10978
                value: min
10979
            });
10980
            if (niceMin < min) {
10981
                j++;
10982
            }
10983
            if (almostEquals(Math.round((niceMin + j * spacing) * factor) / factor, min, relativeLabelSize(min, minSpacing, generationOptions))) {
10984
                j++;
10985
            }
10986
        } else if (niceMin < min) {
10987
            j++;
10988
        }
10989
    }
10990
    for(; j < numSpaces; ++j){
10991
        const tickValue = Math.round((niceMin + j * spacing) * factor) / factor;
10992
        if (maxDefined && tickValue > max) {
10993
            break;
10994
        }
10995
        ticks.push({
10996
            value: tickValue
10997
        });
10998
    }
10999
    if (maxDefined && includeBounds && niceMax !== max) {
11000
        if (ticks.length && almostEquals(ticks[ticks.length - 1].value, max, relativeLabelSize(max, minSpacing, generationOptions))) {
11001
            ticks[ticks.length - 1].value = max;
11002
        } else {
11003
            ticks.push({
11004
                value: max
11005
            });
11006
        }
11007
    } else if (!maxDefined || niceMax === max) {
11008
        ticks.push({
11009
            value: niceMax
11010
        });
11011
    }
11012
    return ticks;
11013
}
11014
function relativeLabelSize(value, minSpacing, { horizontal , minRotation  }) {
11015
    const rad = toRadians(minRotation);
11016
    const ratio = (horizontal ? Math.sin(rad) : Math.cos(rad)) || 0.001;
11017
    const length = 0.75 * minSpacing * ('' + value).length;
11018
    return Math.min(minSpacing / ratio, length);
11019
}
11020
class LinearScaleBase extends Scale {
11021
    constructor(cfg){
11022
        super(cfg);
11023
         this.start = undefined;
11024
         this.end = undefined;
11025
         this._startValue = undefined;
11026
         this._endValue = undefined;
11027
        this._valueRange = 0;
11028
    }
11029
    parse(raw, index) {
11030
        if (isNullOrUndef(raw)) {
11031
            return null;
11032
        }
11033
        if ((typeof raw === 'number' || raw instanceof Number) && !isFinite(+raw)) {
11034
            return null;
11035
        }
11036
        return +raw;
11037
    }
11038
    handleTickRangeOptions() {
11039
        const { beginAtZero  } = this.options;
11040
        const { minDefined , maxDefined  } = this.getUserBounds();
11041
        let { min , max  } = this;
11042
        const setMin = (v)=>min = minDefined ? min : v;
11043
        const setMax = (v)=>max = maxDefined ? max : v;
11044
        if (beginAtZero) {
11045
            const minSign = sign(min);
11046
            const maxSign = sign(max);
11047
            if (minSign < 0 && maxSign < 0) {
11048
                setMax(0);
11049
            } else if (minSign > 0 && maxSign > 0) {
11050
                setMin(0);
11051
            }
11052
        }
11053
        if (min === max) {
11054
            let offset = max === 0 ? 1 : Math.abs(max * 0.05);
11055
            setMax(max + offset);
11056
            if (!beginAtZero) {
11057
                setMin(min - offset);
11058
            }
11059
        }
11060
        this.min = min;
11061
        this.max = max;
11062
    }
11063
    getTickLimit() {
11064
        const tickOpts = this.options.ticks;
11065
        let { maxTicksLimit , stepSize  } = tickOpts;
11066
        let maxTicks;
11067
        if (stepSize) {
11068
            maxTicks = Math.ceil(this.max / stepSize) - Math.floor(this.min / stepSize) + 1;
11069
            if (maxTicks > 1000) {
11070
                console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`);
11071
                maxTicks = 1000;
11072
            }
11073
        } else {
11074
            maxTicks = this.computeTickLimit();
11075
            maxTicksLimit = maxTicksLimit || 11;
11076
        }
11077
        if (maxTicksLimit) {
11078
            maxTicks = Math.min(maxTicksLimit, maxTicks);
11079
        }
11080
        return maxTicks;
11081
    }
11082
 computeTickLimit() {
11083
        return Number.POSITIVE_INFINITY;
11084
    }
11085
    buildTicks() {
11086
        const opts = this.options;
11087
        const tickOpts = opts.ticks;
11088
        let maxTicks = this.getTickLimit();
11089
        maxTicks = Math.max(2, maxTicks);
11090
        const numericGeneratorOptions = {
11091
            maxTicks,
11092
            bounds: opts.bounds,
11093
            min: opts.min,
11094
            max: opts.max,
11095
            precision: tickOpts.precision,
11096
            step: tickOpts.stepSize,
11097
            count: tickOpts.count,
11098
            maxDigits: this._maxDigits(),
11099
            horizontal: this.isHorizontal(),
11100
            minRotation: tickOpts.minRotation || 0,
11101
            includeBounds: tickOpts.includeBounds !== false
11102
        };
11103
        const dataRange = this._range || this;
11104
        const ticks = generateTicks$1(numericGeneratorOptions, dataRange);
11105
        if (opts.bounds === 'ticks') {
11106
            _setMinAndMaxByKey(ticks, this, 'value');
11107
        }
11108
        if (opts.reverse) {
11109
            ticks.reverse();
11110
            this.start = this.max;
11111
            this.end = this.min;
11112
        } else {
11113
            this.start = this.min;
11114
            this.end = this.max;
11115
        }
11116
        return ticks;
11117
    }
11118
 configure() {
11119
        const ticks = this.ticks;
11120
        let start = this.min;
11121
        let end = this.max;
11122
        super.configure();
11123
        if (this.options.offset && ticks.length) {
11124
            const offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;
11125
            start -= offset;
11126
            end += offset;
11127
        }
11128
        this._startValue = start;
11129
        this._endValue = end;
11130
        this._valueRange = end - start;
11131
    }
11132
    getLabelForValue(value) {
11133
        return formatNumber(value, this.chart.options.locale, this.options.ticks.format);
11134
    }
11135
}
11136
 
11137
class LinearScale extends LinearScaleBase {
11138
    static id = 'linear';
11139
 static defaults = {
11140
        ticks: {
11141
            callback: Ticks.formatters.numeric
11142
        }
11143
    };
11144
    determineDataLimits() {
11145
        const { min , max  } = this.getMinMax(true);
11146
        this.min = isNumberFinite(min) ? min : 0;
11147
        this.max = isNumberFinite(max) ? max : 1;
11148
        this.handleTickRangeOptions();
11149
    }
11150
 computeTickLimit() {
11151
        const horizontal = this.isHorizontal();
11152
        const length = horizontal ? this.width : this.height;
11153
        const minRotation = toRadians(this.options.ticks.minRotation);
11154
        const ratio = (horizontal ? Math.sin(minRotation) : Math.cos(minRotation)) || 0.001;
11155
        const tickFont = this._resolveTickFontOptions(0);
11156
        return Math.ceil(length / Math.min(40, tickFont.lineHeight / ratio));
11157
    }
11158
    getPixelForValue(value) {
11159
        return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);
11160
    }
11161
    getValueForPixel(pixel) {
11162
        return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange;
11163
    }
11164
}
11165
 
11166
const log10Floor = (v)=>Math.floor(log10(v));
11167
const changeExponent = (v, m)=>Math.pow(10, log10Floor(v) + m);
11168
function isMajor(tickVal) {
11169
    const remain = tickVal / Math.pow(10, log10Floor(tickVal));
11170
    return remain === 1;
11171
}
11172
function steps(min, max, rangeExp) {
11173
    const rangeStep = Math.pow(10, rangeExp);
11174
    const start = Math.floor(min / rangeStep);
11175
    const end = Math.ceil(max / rangeStep);
11176
    return end - start;
11177
}
11178
function startExp(min, max) {
11179
    const range = max - min;
11180
    let rangeExp = log10Floor(range);
11181
    while(steps(min, max, rangeExp) > 10){
11182
        rangeExp++;
11183
    }
11184
    while(steps(min, max, rangeExp) < 10){
11185
        rangeExp--;
11186
    }
11187
    return Math.min(rangeExp, log10Floor(min));
11188
}
11189
 function generateTicks(generationOptions, { min , max  }) {
11190
    min = finiteOrDefault(generationOptions.min, min);
11191
    const ticks = [];
11192
    const minExp = log10Floor(min);
11193
    let exp = startExp(min, max);
11194
    let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;
11195
    const stepSize = Math.pow(10, exp);
11196
    const base = minExp > exp ? Math.pow(10, minExp) : 0;
11197
    const start = Math.round((min - base) * precision) / precision;
11198
    const offset = Math.floor((min - base) / stepSize / 10) * stepSize * 10;
11199
    let significand = Math.floor((start - offset) / Math.pow(10, exp));
11200
    let value = finiteOrDefault(generationOptions.min, Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision);
11201
    while(value < max){
11202
        ticks.push({
11203
            value,
11204
            major: isMajor(value),
11205
            significand
11206
        });
11207
        if (significand >= 10) {
11208
            significand = significand < 15 ? 15 : 20;
11209
        } else {
11210
            significand++;
11211
        }
11212
        if (significand >= 20) {
11213
            exp++;
11214
            significand = 2;
11215
            precision = exp >= 0 ? 1 : precision;
11216
        }
11217
        value = Math.round((base + offset + significand * Math.pow(10, exp)) * precision) / precision;
11218
    }
11219
    const lastTick = finiteOrDefault(generationOptions.max, value);
11220
    ticks.push({
11221
        value: lastTick,
11222
        major: isMajor(lastTick),
11223
        significand
11224
    });
11225
    return ticks;
11226
}
11227
class LogarithmicScale extends Scale {
11228
    static id = 'logarithmic';
11229
 static defaults = {
11230
        ticks: {
11231
            callback: Ticks.formatters.logarithmic,
11232
            major: {
11233
                enabled: true
11234
            }
11235
        }
11236
    };
11237
    constructor(cfg){
11238
        super(cfg);
11239
         this.start = undefined;
11240
         this.end = undefined;
11241
         this._startValue = undefined;
11242
        this._valueRange = 0;
11243
    }
11244
    parse(raw, index) {
11245
        const value = LinearScaleBase.prototype.parse.apply(this, [
11246
            raw,
11247
            index
11248
        ]);
11249
        if (value === 0) {
11250
            this._zero = true;
11251
            return undefined;
11252
        }
11253
        return isNumberFinite(value) && value > 0 ? value : null;
11254
    }
11255
    determineDataLimits() {
11256
        const { min , max  } = this.getMinMax(true);
11257
        this.min = isNumberFinite(min) ? Math.max(0, min) : null;
11258
        this.max = isNumberFinite(max) ? Math.max(0, max) : null;
11259
        if (this.options.beginAtZero) {
11260
            this._zero = true;
11261
        }
11262
        if (this._zero && this.min !== this._suggestedMin && !isNumberFinite(this._userMin)) {
11263
            this.min = min === changeExponent(this.min, 0) ? changeExponent(this.min, -1) : changeExponent(this.min, 0);
11264
        }
11265
        this.handleTickRangeOptions();
11266
    }
11267
    handleTickRangeOptions() {
11268
        const { minDefined , maxDefined  } = this.getUserBounds();
11269
        let min = this.min;
11270
        let max = this.max;
11271
        const setMin = (v)=>min = minDefined ? min : v;
11272
        const setMax = (v)=>max = maxDefined ? max : v;
11273
        if (min === max) {
11274
            if (min <= 0) {
11275
                setMin(1);
11276
                setMax(10);
11277
            } else {
11278
                setMin(changeExponent(min, -1));
11279
                setMax(changeExponent(max, +1));
11280
            }
11281
        }
11282
        if (min <= 0) {
11283
            setMin(changeExponent(max, -1));
11284
        }
11285
        if (max <= 0) {
11286
            setMax(changeExponent(min, +1));
11287
        }
11288
        this.min = min;
11289
        this.max = max;
11290
    }
11291
    buildTicks() {
11292
        const opts = this.options;
11293
        const generationOptions = {
11294
            min: this._userMin,
11295
            max: this._userMax
11296
        };
11297
        const ticks = generateTicks(generationOptions, this);
11298
        if (opts.bounds === 'ticks') {
11299
            _setMinAndMaxByKey(ticks, this, 'value');
11300
        }
11301
        if (opts.reverse) {
11302
            ticks.reverse();
11303
            this.start = this.max;
11304
            this.end = this.min;
11305
        } else {
11306
            this.start = this.min;
11307
            this.end = this.max;
11308
        }
11309
        return ticks;
11310
    }
11311
 getLabelForValue(value) {
11312
        return value === undefined ? '0' : formatNumber(value, this.chart.options.locale, this.options.ticks.format);
11313
    }
11314
 configure() {
11315
        const start = this.min;
11316
        super.configure();
11317
        this._startValue = log10(start);
11318
        this._valueRange = log10(this.max) - log10(start);
11319
    }
11320
    getPixelForValue(value) {
11321
        if (value === undefined || value === 0) {
11322
            value = this.min;
11323
        }
11324
        if (value === null || isNaN(value)) {
11325
            return NaN;
11326
        }
11327
        return this.getPixelForDecimal(value === this.min ? 0 : (log10(value) - this._startValue) / this._valueRange);
11328
    }
11329
    getValueForPixel(pixel) {
11330
        const decimal = this.getDecimalForPixel(pixel);
11331
        return Math.pow(10, this._startValue + decimal * this._valueRange);
11332
    }
11333
}
11334
 
11335
function getTickBackdropHeight(opts) {
11336
    const tickOpts = opts.ticks;
11337
    if (tickOpts.display && opts.display) {
11338
        const padding = toPadding(tickOpts.backdropPadding);
11339
        return valueOrDefault(tickOpts.font && tickOpts.font.size, defaults.font.size) + padding.height;
11340
    }
11341
    return 0;
11342
}
11343
function measureLabelSize(ctx, font, label) {
11344
    label = isArray(label) ? label : [
11345
        label
11346
    ];
11347
    return {
11348
        w: _longestText(ctx, font.string, label),
11349
        h: label.length * font.lineHeight
11350
    };
11351
}
11352
function determineLimits(angle, pos, size, min, max) {
11353
    if (angle === min || angle === max) {
11354
        return {
11355
            start: pos - size / 2,
11356
            end: pos + size / 2
11357
        };
11358
    } else if (angle < min || angle > max) {
11359
        return {
11360
            start: pos - size,
11361
            end: pos
11362
        };
11363
    }
11364
    return {
11365
        start: pos,
11366
        end: pos + size
11367
    };
11368
}
11369
 function fitWithPointLabels(scale) {
11370
    const orig = {
11371
        l: scale.left + scale._padding.left,
11372
        r: scale.right - scale._padding.right,
11373
        t: scale.top + scale._padding.top,
11374
        b: scale.bottom - scale._padding.bottom
11375
    };
11376
    const limits = Object.assign({}, orig);
11377
    const labelSizes = [];
11378
    const padding = [];
11379
    const valueCount = scale._pointLabels.length;
11380
    const pointLabelOpts = scale.options.pointLabels;
11381
    const additionalAngle = pointLabelOpts.centerPointLabels ? PI / valueCount : 0;
11382
    for(let i = 0; i < valueCount; i++){
11383
        const opts = pointLabelOpts.setContext(scale.getPointLabelContext(i));
11384
        padding[i] = opts.padding;
11385
        const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i], additionalAngle);
11386
        const plFont = toFont(opts.font);
11387
        const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]);
11388
        labelSizes[i] = textSize;
11389
        const angleRadians = _normalizeAngle(scale.getIndexAngle(i) + additionalAngle);
11390
        const angle = Math.round(toDegrees(angleRadians));
11391
        const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
11392
        const vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
11393
        updateLimits(limits, orig, angleRadians, hLimits, vLimits);
11394
    }
11395
    scale.setCenterPoint(orig.l - limits.l, limits.r - orig.r, orig.t - limits.t, limits.b - orig.b);
11396
    scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding);
11397
}
11398
function updateLimits(limits, orig, angle, hLimits, vLimits) {
11399
    const sin = Math.abs(Math.sin(angle));
11400
    const cos = Math.abs(Math.cos(angle));
11401
    let x = 0;
11402
    let y = 0;
11403
    if (hLimits.start < orig.l) {
11404
        x = (orig.l - hLimits.start) / sin;
11405
        limits.l = Math.min(limits.l, orig.l - x);
11406
    } else if (hLimits.end > orig.r) {
11407
        x = (hLimits.end - orig.r) / sin;
11408
        limits.r = Math.max(limits.r, orig.r + x);
11409
    }
11410
    if (vLimits.start < orig.t) {
11411
        y = (orig.t - vLimits.start) / cos;
11412
        limits.t = Math.min(limits.t, orig.t - y);
11413
    } else if (vLimits.end > orig.b) {
11414
        y = (vLimits.end - orig.b) / cos;
11415
        limits.b = Math.max(limits.b, orig.b + y);
11416
    }
11417
}
11418
function createPointLabelItem(scale, index, itemOpts) {
11419
    const outerDistance = scale.drawingArea;
11420
    const { extra , additionalAngle , padding , size  } = itemOpts;
11421
    const pointLabelPosition = scale.getPointPosition(index, outerDistance + extra + padding, additionalAngle);
11422
    const angle = Math.round(toDegrees(_normalizeAngle(pointLabelPosition.angle + HALF_PI)));
11423
    const y = yForAngle(pointLabelPosition.y, size.h, angle);
11424
    const textAlign = getTextAlignForAngle(angle);
11425
    const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign);
11426
    return {
11427
        visible: true,
11428
        x: pointLabelPosition.x,
11429
        y,
11430
        textAlign,
11431
        left,
11432
        top: y,
11433
        right: left + size.w,
11434
        bottom: y + size.h
11435
    };
11436
}
11437
function isNotOverlapped(item, area) {
11438
    if (!area) {
11439
        return true;
11440
    }
11441
    const { left , top , right , bottom  } = item;
11442
    const apexesInArea = _isPointInArea({
11443
        x: left,
11444
        y: top
11445
    }, area) || _isPointInArea({
11446
        x: left,
11447
        y: bottom
11448
    }, area) || _isPointInArea({
11449
        x: right,
11450
        y: top
11451
    }, area) || _isPointInArea({
11452
        x: right,
11453
        y: bottom
11454
    }, area);
11455
    return !apexesInArea;
11456
}
11457
function buildPointLabelItems(scale, labelSizes, padding) {
11458
    const items = [];
11459
    const valueCount = scale._pointLabels.length;
11460
    const opts = scale.options;
11461
    const { centerPointLabels , display  } = opts.pointLabels;
11462
    const itemOpts = {
11463
        extra: getTickBackdropHeight(opts) / 2,
11464
        additionalAngle: centerPointLabels ? PI / valueCount : 0
11465
    };
11466
    let area;
11467
    for(let i = 0; i < valueCount; i++){
11468
        itemOpts.padding = padding[i];
11469
        itemOpts.size = labelSizes[i];
11470
        const item = createPointLabelItem(scale, i, itemOpts);
11471
        items.push(item);
11472
        if (display === 'auto') {
11473
            item.visible = isNotOverlapped(item, area);
11474
            if (item.visible) {
11475
                area = item;
11476
            }
11477
        }
11478
    }
11479
    return items;
11480
}
11481
function getTextAlignForAngle(angle) {
11482
    if (angle === 0 || angle === 180) {
11483
        return 'center';
11484
    } else if (angle < 180) {
11485
        return 'left';
11486
    }
11487
    return 'right';
11488
}
11489
function leftForTextAlign(x, w, align) {
11490
    if (align === 'right') {
11491
        x -= w;
11492
    } else if (align === 'center') {
11493
        x -= w / 2;
11494
    }
11495
    return x;
11496
}
11497
function yForAngle(y, h, angle) {
11498
    if (angle === 90 || angle === 270) {
11499
        y -= h / 2;
11500
    } else if (angle > 270 || angle < 90) {
11501
        y -= h;
11502
    }
11503
    return y;
11504
}
11505
function drawPointLabelBox(ctx, opts, item) {
11506
    const { left , top , right , bottom  } = item;
11507
    const { backdropColor  } = opts;
11508
    if (!isNullOrUndef(backdropColor)) {
11509
        const borderRadius = toTRBLCorners(opts.borderRadius);
11510
        const padding = toPadding(opts.backdropPadding);
11511
        ctx.fillStyle = backdropColor;
11512
        const backdropLeft = left - padding.left;
11513
        const backdropTop = top - padding.top;
11514
        const backdropWidth = right - left + padding.width;
11515
        const backdropHeight = bottom - top + padding.height;
11516
        if (Object.values(borderRadius).some((v)=>v !== 0)) {
11517
            ctx.beginPath();
11518
            addRoundedRectPath(ctx, {
11519
                x: backdropLeft,
11520
                y: backdropTop,
11521
                w: backdropWidth,
11522
                h: backdropHeight,
11523
                radius: borderRadius
11524
            });
11525
            ctx.fill();
11526
        } else {
11527
            ctx.fillRect(backdropLeft, backdropTop, backdropWidth, backdropHeight);
11528
        }
11529
    }
11530
}
11531
function drawPointLabels(scale, labelCount) {
11532
    const { ctx , options: { pointLabels  }  } = scale;
11533
    for(let i = labelCount - 1; i >= 0; i--){
11534
        const item = scale._pointLabelItems[i];
11535
        if (!item.visible) {
11536
            continue;
11537
        }
11538
        const optsAtIndex = pointLabels.setContext(scale.getPointLabelContext(i));
11539
        drawPointLabelBox(ctx, optsAtIndex, item);
11540
        const plFont = toFont(optsAtIndex.font);
11541
        const { x , y , textAlign  } = item;
11542
        renderText(ctx, scale._pointLabels[i], x, y + plFont.lineHeight / 2, plFont, {
11543
            color: optsAtIndex.color,
11544
            textAlign: textAlign,
11545
            textBaseline: 'middle'
11546
        });
11547
    }
11548
}
11549
function pathRadiusLine(scale, radius, circular, labelCount) {
11550
    const { ctx  } = scale;
11551
    if (circular) {
11552
        ctx.arc(scale.xCenter, scale.yCenter, radius, 0, TAU);
11553
    } else {
11554
        let pointPosition = scale.getPointPosition(0, radius);
11555
        ctx.moveTo(pointPosition.x, pointPosition.y);
11556
        for(let i = 1; i < labelCount; i++){
11557
            pointPosition = scale.getPointPosition(i, radius);
11558
            ctx.lineTo(pointPosition.x, pointPosition.y);
11559
        }
11560
    }
11561
}
11562
function drawRadiusLine(scale, gridLineOpts, radius, labelCount, borderOpts) {
11563
    const ctx = scale.ctx;
11564
    const circular = gridLineOpts.circular;
11565
    const { color , lineWidth  } = gridLineOpts;
11566
    if (!circular && !labelCount || !color || !lineWidth || radius < 0) {
11567
        return;
11568
    }
11569
    ctx.save();
11570
    ctx.strokeStyle = color;
11571
    ctx.lineWidth = lineWidth;
11572
    ctx.setLineDash(borderOpts.dash);
11573
    ctx.lineDashOffset = borderOpts.dashOffset;
11574
    ctx.beginPath();
11575
    pathRadiusLine(scale, radius, circular, labelCount);
11576
    ctx.closePath();
11577
    ctx.stroke();
11578
    ctx.restore();
11579
}
11580
function createPointLabelContext(parent, index, label) {
11581
    return createContext(parent, {
11582
        label,
11583
        index,
11584
        type: 'pointLabel'
11585
    });
11586
}
11587
class RadialLinearScale extends LinearScaleBase {
11588
    static id = 'radialLinear';
11589
 static defaults = {
11590
        display: true,
11591
        animate: true,
11592
        position: 'chartArea',
11593
        angleLines: {
11594
            display: true,
11595
            lineWidth: 1,
11596
            borderDash: [],
11597
            borderDashOffset: 0.0
11598
        },
11599
        grid: {
11600
            circular: false
11601
        },
11602
        startAngle: 0,
11603
        ticks: {
11604
            showLabelBackdrop: true,
11605
            callback: Ticks.formatters.numeric
11606
        },
11607
        pointLabels: {
11608
            backdropColor: undefined,
11609
            backdropPadding: 2,
11610
            display: true,
11611
            font: {
11612
                size: 10
11613
            },
11614
            callback (label) {
11615
                return label;
11616
            },
11617
            padding: 5,
11618
            centerPointLabels: false
11619
        }
11620
    };
11621
    static defaultRoutes = {
11622
        'angleLines.color': 'borderColor',
11623
        'pointLabels.color': 'color',
11624
        'ticks.color': 'color'
11625
    };
11626
    static descriptors = {
11627
        angleLines: {
11628
            _fallback: 'grid'
11629
        }
11630
    };
11631
    constructor(cfg){
11632
        super(cfg);
11633
         this.xCenter = undefined;
11634
         this.yCenter = undefined;
11635
         this.drawingArea = undefined;
11636
         this._pointLabels = [];
11637
        this._pointLabelItems = [];
11638
    }
11639
    setDimensions() {
11640
        const padding = this._padding = toPadding(getTickBackdropHeight(this.options) / 2);
11641
        const w = this.width = this.maxWidth - padding.width;
11642
        const h = this.height = this.maxHeight - padding.height;
11643
        this.xCenter = Math.floor(this.left + w / 2 + padding.left);
11644
        this.yCenter = Math.floor(this.top + h / 2 + padding.top);
11645
        this.drawingArea = Math.floor(Math.min(w, h) / 2);
11646
    }
11647
    determineDataLimits() {
11648
        const { min , max  } = this.getMinMax(false);
11649
        this.min = isNumberFinite(min) && !isNaN(min) ? min : 0;
11650
        this.max = isNumberFinite(max) && !isNaN(max) ? max : 0;
11651
        this.handleTickRangeOptions();
11652
    }
11653
 computeTickLimit() {
11654
        return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));
11655
    }
11656
    generateTickLabels(ticks) {
11657
        LinearScaleBase.prototype.generateTickLabels.call(this, ticks);
11658
        this._pointLabels = this.getLabels().map((value, index)=>{
11659
            const label = callback(this.options.pointLabels.callback, [
11660
                value,
11661
                index
11662
            ], this);
11663
            return label || label === 0 ? label : '';
11664
        }).filter((v, i)=>this.chart.getDataVisibility(i));
11665
    }
11666
    fit() {
11667
        const opts = this.options;
11668
        if (opts.display && opts.pointLabels.display) {
11669
            fitWithPointLabels(this);
11670
        } else {
11671
            this.setCenterPoint(0, 0, 0, 0);
11672
        }
11673
    }
11674
    setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) {
11675
        this.xCenter += Math.floor((leftMovement - rightMovement) / 2);
11676
        this.yCenter += Math.floor((topMovement - bottomMovement) / 2);
11677
        this.drawingArea -= Math.min(this.drawingArea / 2, Math.max(leftMovement, rightMovement, topMovement, bottomMovement));
11678
    }
11679
    getIndexAngle(index) {
11680
        const angleMultiplier = TAU / (this._pointLabels.length || 1);
11681
        const startAngle = this.options.startAngle || 0;
11682
        return _normalizeAngle(index * angleMultiplier + toRadians(startAngle));
11683
    }
11684
    getDistanceFromCenterForValue(value) {
11685
        if (isNullOrUndef(value)) {
11686
            return NaN;
11687
        }
11688
        const scalingFactor = this.drawingArea / (this.max - this.min);
11689
        if (this.options.reverse) {
11690
            return (this.max - value) * scalingFactor;
11691
        }
11692
        return (value - this.min) * scalingFactor;
11693
    }
11694
    getValueForDistanceFromCenter(distance) {
11695
        if (isNullOrUndef(distance)) {
11696
            return NaN;
11697
        }
11698
        const scaledDistance = distance / (this.drawingArea / (this.max - this.min));
11699
        return this.options.reverse ? this.max - scaledDistance : this.min + scaledDistance;
11700
    }
11701
    getPointLabelContext(index) {
11702
        const pointLabels = this._pointLabels || [];
11703
        if (index >= 0 && index < pointLabels.length) {
11704
            const pointLabel = pointLabels[index];
11705
            return createPointLabelContext(this.getContext(), index, pointLabel);
11706
        }
11707
    }
11708
    getPointPosition(index, distanceFromCenter, additionalAngle = 0) {
11709
        const angle = this.getIndexAngle(index) - HALF_PI + additionalAngle;
11710
        return {
11711
            x: Math.cos(angle) * distanceFromCenter + this.xCenter,
11712
            y: Math.sin(angle) * distanceFromCenter + this.yCenter,
11713
            angle
11714
        };
11715
    }
11716
    getPointPositionForValue(index, value) {
11717
        return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
11718
    }
11719
    getBasePosition(index) {
11720
        return this.getPointPositionForValue(index || 0, this.getBaseValue());
11721
    }
11722
    getPointLabelPosition(index) {
11723
        const { left , top , right , bottom  } = this._pointLabelItems[index];
11724
        return {
11725
            left,
11726
            top,
11727
            right,
11728
            bottom
11729
        };
11730
    }
11731
 drawBackground() {
11732
        const { backgroundColor , grid: { circular  }  } = this.options;
11733
        if (backgroundColor) {
11734
            const ctx = this.ctx;
11735
            ctx.save();
11736
            ctx.beginPath();
11737
            pathRadiusLine(this, this.getDistanceFromCenterForValue(this._endValue), circular, this._pointLabels.length);
11738
            ctx.closePath();
11739
            ctx.fillStyle = backgroundColor;
11740
            ctx.fill();
11741
            ctx.restore();
11742
        }
11743
    }
11744
 drawGrid() {
11745
        const ctx = this.ctx;
11746
        const opts = this.options;
11747
        const { angleLines , grid , border  } = opts;
11748
        const labelCount = this._pointLabels.length;
11749
        let i, offset, position;
11750
        if (opts.pointLabels.display) {
11751
            drawPointLabels(this, labelCount);
11752
        }
11753
        if (grid.display) {
11754
            this.ticks.forEach((tick, index)=>{
11755
                if (index !== 0 || index === 0 && this.min < 0) {
11756
                    offset = this.getDistanceFromCenterForValue(tick.value);
11757
                    const context = this.getContext(index);
11758
                    const optsAtIndex = grid.setContext(context);
11759
                    const optsAtIndexBorder = border.setContext(context);
11760
                    drawRadiusLine(this, optsAtIndex, offset, labelCount, optsAtIndexBorder);
11761
                }
11762
            });
11763
        }
11764
        if (angleLines.display) {
11765
            ctx.save();
11766
            for(i = labelCount - 1; i >= 0; i--){
11767
                const optsAtIndex = angleLines.setContext(this.getPointLabelContext(i));
11768
                const { color , lineWidth  } = optsAtIndex;
11769
                if (!lineWidth || !color) {
11770
                    continue;
11771
                }
11772
                ctx.lineWidth = lineWidth;
11773
                ctx.strokeStyle = color;
11774
                ctx.setLineDash(optsAtIndex.borderDash);
11775
                ctx.lineDashOffset = optsAtIndex.borderDashOffset;
11776
                offset = this.getDistanceFromCenterForValue(opts.ticks.reverse ? this.min : this.max);
11777
                position = this.getPointPosition(i, offset);
11778
                ctx.beginPath();
11779
                ctx.moveTo(this.xCenter, this.yCenter);
11780
                ctx.lineTo(position.x, position.y);
11781
                ctx.stroke();
11782
            }
11783
            ctx.restore();
11784
        }
11785
    }
11786
 drawBorder() {}
11787
 drawLabels() {
11788
        const ctx = this.ctx;
11789
        const opts = this.options;
11790
        const tickOpts = opts.ticks;
11791
        if (!tickOpts.display) {
11792
            return;
11793
        }
11794
        const startAngle = this.getIndexAngle(0);
11795
        let offset, width;
11796
        ctx.save();
11797
        ctx.translate(this.xCenter, this.yCenter);
11798
        ctx.rotate(startAngle);
11799
        ctx.textAlign = 'center';
11800
        ctx.textBaseline = 'middle';
11801
        this.ticks.forEach((tick, index)=>{
11802
            if (index === 0 && this.min >= 0 && !opts.reverse) {
11803
                return;
11804
            }
11805
            const optsAtIndex = tickOpts.setContext(this.getContext(index));
11806
            const tickFont = toFont(optsAtIndex.font);
11807
            offset = this.getDistanceFromCenterForValue(this.ticks[index].value);
11808
            if (optsAtIndex.showLabelBackdrop) {
11809
                ctx.font = tickFont.string;
11810
                width = ctx.measureText(tick.label).width;
11811
                ctx.fillStyle = optsAtIndex.backdropColor;
11812
                const padding = toPadding(optsAtIndex.backdropPadding);
11813
                ctx.fillRect(-width / 2 - padding.left, -offset - tickFont.size / 2 - padding.top, width + padding.width, tickFont.size + padding.height);
11814
            }
11815
            renderText(ctx, tick.label, 0, -offset, tickFont, {
11816
                color: optsAtIndex.color,
11817
                strokeColor: optsAtIndex.textStrokeColor,
11818
                strokeWidth: optsAtIndex.textStrokeWidth
11819
            });
11820
        });
11821
        ctx.restore();
11822
    }
11823
 drawTitle() {}
11824
}
11825
 
11826
const INTERVALS = {
11827
    millisecond: {
11828
        common: true,
11829
        size: 1,
11830
        steps: 1000
11831
    },
11832
    second: {
11833
        common: true,
11834
        size: 1000,
11835
        steps: 60
11836
    },
11837
    minute: {
11838
        common: true,
11839
        size: 60000,
11840
        steps: 60
11841
    },
11842
    hour: {
11843
        common: true,
11844
        size: 3600000,
11845
        steps: 24
11846
    },
11847
    day: {
11848
        common: true,
11849
        size: 86400000,
11850
        steps: 30
11851
    },
11852
    week: {
11853
        common: false,
11854
        size: 604800000,
11855
        steps: 4
11856
    },
11857
    month: {
11858
        common: true,
11859
        size: 2.628e9,
11860
        steps: 12
11861
    },
11862
    quarter: {
11863
        common: false,
11864
        size: 7.884e9,
11865
        steps: 4
11866
    },
11867
    year: {
11868
        common: true,
11869
        size: 3.154e10
11870
    }
11871
};
11872
 const UNITS =  /* #__PURE__ */ Object.keys(INTERVALS);
11873
 function sorter(a, b) {
11874
    return a - b;
11875
}
11876
 function parse(scale, input) {
11877
    if (isNullOrUndef(input)) {
11878
        return null;
11879
    }
11880
    const adapter = scale._adapter;
11881
    const { parser , round , isoWeekday  } = scale._parseOpts;
11882
    let value = input;
11883
    if (typeof parser === 'function') {
11884
        value = parser(value);
11885
    }
11886
    if (!isNumberFinite(value)) {
11887
        value = typeof parser === 'string' ? adapter.parse(value,  parser) : adapter.parse(value);
11888
    }
11889
    if (value === null) {
11890
        return null;
11891
    }
11892
    if (round) {
11893
        value = round === 'week' && (isNumber(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, 'isoWeek', isoWeekday) : adapter.startOf(value, round);
11894
    }
11895
    return +value;
11896
}
11897
 function determineUnitForAutoTicks(minUnit, min, max, capacity) {
11898
    const ilen = UNITS.length;
11899
    for(let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i){
11900
        const interval = INTERVALS[UNITS[i]];
11901
        const factor = interval.steps ? interval.steps : Number.MAX_SAFE_INTEGER;
11902
        if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
11903
            return UNITS[i];
11904
        }
11905
    }
11906
    return UNITS[ilen - 1];
11907
}
11908
 function determineUnitForFormatting(scale, numTicks, minUnit, min, max) {
11909
    for(let i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--){
11910
        const unit = UNITS[i];
11911
        if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) {
11912
            return unit;
11913
        }
11914
    }
11915
    return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];
11916
}
11917
 function determineMajorUnit(unit) {
11918
    for(let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i){
11919
        if (INTERVALS[UNITS[i]].common) {
11920
            return UNITS[i];
11921
        }
11922
    }
11923
}
11924
 function addTick(ticks, time, timestamps) {
11925
    if (!timestamps) {
11926
        ticks[time] = true;
11927
    } else if (timestamps.length) {
11928
        const { lo , hi  } = _lookup(timestamps, time);
11929
        const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi];
11930
        ticks[timestamp] = true;
11931
    }
11932
}
11933
 function setMajorTicks(scale, ticks, map, majorUnit) {
11934
    const adapter = scale._adapter;
11935
    const first = +adapter.startOf(ticks[0].value, majorUnit);
11936
    const last = ticks[ticks.length - 1].value;
11937
    let major, index;
11938
    for(major = first; major <= last; major = +adapter.add(major, 1, majorUnit)){
11939
        index = map[major];
11940
        if (index >= 0) {
11941
            ticks[index].major = true;
11942
        }
11943
    }
11944
    return ticks;
11945
}
11946
 function ticksFromTimestamps(scale, values, majorUnit) {
11947
    const ticks = [];
11948
     const map = {};
11949
    const ilen = values.length;
11950
    let i, value;
11951
    for(i = 0; i < ilen; ++i){
11952
        value = values[i];
11953
        map[value] = i;
11954
        ticks.push({
11955
            value,
11956
            major: false
11957
        });
11958
    }
11959
    return ilen === 0 || !majorUnit ? ticks : setMajorTicks(scale, ticks, map, majorUnit);
11960
}
11961
class TimeScale extends Scale {
11962
    static id = 'time';
11963
 static defaults = {
11964
 bounds: 'data',
11965
        adapters: {},
11966
        time: {
11967
            parser: false,
11968
            unit: false,
11969
            round: false,
11970
            isoWeekday: false,
11971
            minUnit: 'millisecond',
11972
            displayFormats: {}
11973
        },
11974
        ticks: {
11975
 source: 'auto',
11976
            callback: false,
11977
            major: {
11978
                enabled: false
11979
            }
11980
        }
11981
    };
11982
 constructor(props){
11983
        super(props);
11984
         this._cache = {
11985
            data: [],
11986
            labels: [],
11987
            all: []
11988
        };
11989
         this._unit = 'day';
11990
         this._majorUnit = undefined;
11991
        this._offsets = {};
11992
        this._normalized = false;
11993
        this._parseOpts = undefined;
11994
    }
11995
    init(scaleOpts, opts = {}) {
11996
        const time = scaleOpts.time || (scaleOpts.time = {});
11997
         const adapter = this._adapter = new _adapters._date(scaleOpts.adapters.date);
11998
        adapter.init(opts);
11999
        mergeIf(time.displayFormats, adapter.formats());
12000
        this._parseOpts = {
12001
            parser: time.parser,
12002
            round: time.round,
12003
            isoWeekday: time.isoWeekday
12004
        };
12005
        super.init(scaleOpts);
12006
        this._normalized = opts.normalized;
12007
    }
12008
 parse(raw, index) {
12009
        if (raw === undefined) {
12010
            return null;
12011
        }
12012
        return parse(this, raw);
12013
    }
12014
    beforeLayout() {
12015
        super.beforeLayout();
12016
        this._cache = {
12017
            data: [],
12018
            labels: [],
12019
            all: []
12020
        };
12021
    }
12022
    determineDataLimits() {
12023
        const options = this.options;
12024
        const adapter = this._adapter;
12025
        const unit = options.time.unit || 'day';
12026
        let { min , max , minDefined , maxDefined  } = this.getUserBounds();
12027
 function _applyBounds(bounds) {
12028
            if (!minDefined && !isNaN(bounds.min)) {
12029
                min = Math.min(min, bounds.min);
12030
            }
12031
            if (!maxDefined && !isNaN(bounds.max)) {
12032
                max = Math.max(max, bounds.max);
12033
            }
12034
        }
12035
        if (!minDefined || !maxDefined) {
12036
            _applyBounds(this._getLabelBounds());
12037
            if (options.bounds !== 'ticks' || options.ticks.source !== 'labels') {
12038
                _applyBounds(this.getMinMax(false));
12039
            }
12040
        }
12041
        min = isNumberFinite(min) && !isNaN(min) ? min : +adapter.startOf(Date.now(), unit);
12042
        max = isNumberFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit) + 1;
12043
        this.min = Math.min(min, max - 1);
12044
        this.max = Math.max(min + 1, max);
12045
    }
12046
 _getLabelBounds() {
12047
        const arr = this.getLabelTimestamps();
12048
        let min = Number.POSITIVE_INFINITY;
12049
        let max = Number.NEGATIVE_INFINITY;
12050
        if (arr.length) {
12051
            min = arr[0];
12052
            max = arr[arr.length - 1];
12053
        }
12054
        return {
12055
            min,
12056
            max
12057
        };
12058
    }
12059
 buildTicks() {
12060
        const options = this.options;
12061
        const timeOpts = options.time;
12062
        const tickOpts = options.ticks;
12063
        const timestamps = tickOpts.source === 'labels' ? this.getLabelTimestamps() : this._generate();
12064
        if (options.bounds === 'ticks' && timestamps.length) {
12065
            this.min = this._userMin || timestamps[0];
12066
            this.max = this._userMax || timestamps[timestamps.length - 1];
12067
        }
12068
        const min = this.min;
12069
        const max = this.max;
12070
        const ticks = _filterBetween(timestamps, min, max);
12071
        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));
12072
        this._majorUnit = !tickOpts.major.enabled || this._unit === 'year' ? undefined : determineMajorUnit(this._unit);
12073
        this.initOffsets(timestamps);
12074
        if (options.reverse) {
12075
            ticks.reverse();
12076
        }
12077
        return ticksFromTimestamps(this, ticks, this._majorUnit);
12078
    }
12079
    afterAutoSkip() {
12080
        if (this.options.offsetAfterAutoskip) {
12081
            this.initOffsets(this.ticks.map((tick)=>+tick.value));
12082
        }
12083
    }
12084
 initOffsets(timestamps = []) {
12085
        let start = 0;
12086
        let end = 0;
12087
        let first, last;
12088
        if (this.options.offset && timestamps.length) {
12089
            first = this.getDecimalForValue(timestamps[0]);
12090
            if (timestamps.length === 1) {
12091
                start = 1 - first;
12092
            } else {
12093
                start = (this.getDecimalForValue(timestamps[1]) - first) / 2;
12094
            }
12095
            last = this.getDecimalForValue(timestamps[timestamps.length - 1]);
12096
            if (timestamps.length === 1) {
12097
                end = last;
12098
            } else {
12099
                end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2;
12100
            }
12101
        }
12102
        const limit = timestamps.length < 3 ? 0.5 : 0.25;
12103
        start = _limitValue(start, 0, limit);
12104
        end = _limitValue(end, 0, limit);
12105
        this._offsets = {
12106
            start,
12107
            end,
12108
            factor: 1 / (start + 1 + end)
12109
        };
12110
    }
12111
 _generate() {
12112
        const adapter = this._adapter;
12113
        const min = this.min;
12114
        const max = this.max;
12115
        const options = this.options;
12116
        const timeOpts = options.time;
12117
        const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min));
12118
        const stepSize = valueOrDefault(options.ticks.stepSize, 1);
12119
        const weekday = minor === 'week' ? timeOpts.isoWeekday : false;
12120
        const hasWeekday = isNumber(weekday) || weekday === true;
12121
        const ticks = {};
12122
        let first = min;
12123
        let time, count;
12124
        if (hasWeekday) {
12125
            first = +adapter.startOf(first, 'isoWeek', weekday);
12126
        }
12127
        first = +adapter.startOf(first, hasWeekday ? 'day' : minor);
12128
        if (adapter.diff(max, min, minor) > 100000 * stepSize) {
12129
            throw new Error(min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor);
12130
        }
12131
        const timestamps = options.ticks.source === 'data' && this.getDataTimestamps();
12132
        for(time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++){
12133
            addTick(ticks, time, timestamps);
12134
        }
12135
        if (time === max || options.bounds === 'ticks' || count === 1) {
12136
            addTick(ticks, time, timestamps);
12137
        }
12138
        return Object.keys(ticks).sort(sorter).map((x)=>+x);
12139
    }
12140
 getLabelForValue(value) {
12141
        const adapter = this._adapter;
12142
        const timeOpts = this.options.time;
12143
        if (timeOpts.tooltipFormat) {
12144
            return adapter.format(value, timeOpts.tooltipFormat);
12145
        }
12146
        return adapter.format(value, timeOpts.displayFormats.datetime);
12147
    }
12148
 format(value, format) {
12149
        const options = this.options;
12150
        const formats = options.time.displayFormats;
12151
        const unit = this._unit;
12152
        const fmt = format || formats[unit];
12153
        return this._adapter.format(value, fmt);
12154
    }
12155
 _tickFormatFunction(time, index, ticks, format) {
12156
        const options = this.options;
12157
        const formatter = options.ticks.callback;
12158
        if (formatter) {
12159
            return callback(formatter, [
12160
                time,
12161
                index,
12162
                ticks
12163
            ], this);
12164
        }
12165
        const formats = options.time.displayFormats;
12166
        const unit = this._unit;
12167
        const majorUnit = this._majorUnit;
12168
        const minorFormat = unit && formats[unit];
12169
        const majorFormat = majorUnit && formats[majorUnit];
12170
        const tick = ticks[index];
12171
        const major = majorUnit && majorFormat && tick && tick.major;
12172
        return this._adapter.format(time, format || (major ? majorFormat : minorFormat));
12173
    }
12174
 generateTickLabels(ticks) {
12175
        let i, ilen, tick;
12176
        for(i = 0, ilen = ticks.length; i < ilen; ++i){
12177
            tick = ticks[i];
12178
            tick.label = this._tickFormatFunction(tick.value, i, ticks);
12179
        }
12180
    }
12181
 getDecimalForValue(value) {
12182
        return value === null ? NaN : (value - this.min) / (this.max - this.min);
12183
    }
12184
 getPixelForValue(value) {
12185
        const offsets = this._offsets;
12186
        const pos = this.getDecimalForValue(value);
12187
        return this.getPixelForDecimal((offsets.start + pos) * offsets.factor);
12188
    }
12189
 getValueForPixel(pixel) {
12190
        const offsets = this._offsets;
12191
        const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
12192
        return this.min + pos * (this.max - this.min);
12193
    }
12194
 _getLabelSize(label) {
12195
        const ticksOpts = this.options.ticks;
12196
        const tickLabelWidth = this.ctx.measureText(label).width;
12197
        const angle = toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);
12198
        const cosRotation = Math.cos(angle);
12199
        const sinRotation = Math.sin(angle);
12200
        const tickFontSize = this._resolveTickFontOptions(0).size;
12201
        return {
12202
            w: tickLabelWidth * cosRotation + tickFontSize * sinRotation,
12203
            h: tickLabelWidth * sinRotation + tickFontSize * cosRotation
12204
        };
12205
    }
12206
 _getLabelCapacity(exampleTime) {
12207
        const timeOpts = this.options.time;
12208
        const displayFormats = timeOpts.displayFormats;
12209
        const format = displayFormats[timeOpts.unit] || displayFormats.millisecond;
12210
        const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [
12211
            exampleTime
12212
        ], this._majorUnit), format);
12213
        const size = this._getLabelSize(exampleLabel);
12214
        const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1;
12215
        return capacity > 0 ? capacity : 1;
12216
    }
12217
 getDataTimestamps() {
12218
        let timestamps = this._cache.data || [];
12219
        let i, ilen;
12220
        if (timestamps.length) {
12221
            return timestamps;
12222
        }
12223
        const metas = this.getMatchingVisibleMetas();
12224
        if (this._normalized && metas.length) {
12225
            return this._cache.data = metas[0].controller.getAllParsedValues(this);
12226
        }
12227
        for(i = 0, ilen = metas.length; i < ilen; ++i){
12228
            timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this));
12229
        }
12230
        return this._cache.data = this.normalize(timestamps);
12231
    }
12232
 getLabelTimestamps() {
12233
        const timestamps = this._cache.labels || [];
12234
        let i, ilen;
12235
        if (timestamps.length) {
12236
            return timestamps;
12237
        }
12238
        const labels = this.getLabels();
12239
        for(i = 0, ilen = labels.length; i < ilen; ++i){
12240
            timestamps.push(parse(this, labels[i]));
12241
        }
12242
        return this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps);
12243
    }
12244
 normalize(values) {
12245
        return _arrayUnique(values.sort(sorter));
12246
    }
12247
}
12248
 
12249
function interpolate(table, val, reverse) {
12250
    let lo = 0;
12251
    let hi = table.length - 1;
12252
    let prevSource, nextSource, prevTarget, nextTarget;
12253
    if (reverse) {
12254
        if (val >= table[lo].pos && val <= table[hi].pos) {
12255
            ({ lo , hi  } = _lookupByKey(table, 'pos', val));
12256
        }
12257
        ({ pos: prevSource , time: prevTarget  } = table[lo]);
12258
        ({ pos: nextSource , time: nextTarget  } = table[hi]);
12259
    } else {
12260
        if (val >= table[lo].time && val <= table[hi].time) {
12261
            ({ lo , hi  } = _lookupByKey(table, 'time', val));
12262
        }
12263
        ({ time: prevSource , pos: prevTarget  } = table[lo]);
12264
        ({ time: nextSource , pos: nextTarget  } = table[hi]);
12265
    }
12266
    const span = nextSource - prevSource;
12267
    return span ? prevTarget + (nextTarget - prevTarget) * (val - prevSource) / span : prevTarget;
12268
}
12269
class TimeSeriesScale extends TimeScale {
12270
    static id = 'timeseries';
12271
 static defaults = TimeScale.defaults;
12272
 constructor(props){
12273
        super(props);
12274
         this._table = [];
12275
         this._minPos = undefined;
12276
         this._tableRange = undefined;
12277
    }
12278
 initOffsets() {
12279
        const timestamps = this._getTimestampsForTable();
12280
        const table = this._table = this.buildLookupTable(timestamps);
12281
        this._minPos = interpolate(table, this.min);
12282
        this._tableRange = interpolate(table, this.max) - this._minPos;
12283
        super.initOffsets(timestamps);
12284
    }
12285
 buildLookupTable(timestamps) {
12286
        const { min , max  } = this;
12287
        const items = [];
12288
        const table = [];
12289
        let i, ilen, prev, curr, next;
12290
        for(i = 0, ilen = timestamps.length; i < ilen; ++i){
12291
            curr = timestamps[i];
12292
            if (curr >= min && curr <= max) {
12293
                items.push(curr);
12294
            }
12295
        }
12296
        if (items.length < 2) {
12297
            return [
12298
                {
12299
                    time: min,
12300
                    pos: 0
12301
                },
12302
                {
12303
                    time: max,
12304
                    pos: 1
12305
                }
12306
            ];
12307
        }
12308
        for(i = 0, ilen = items.length; i < ilen; ++i){
12309
            next = items[i + 1];
12310
            prev = items[i - 1];
12311
            curr = items[i];
12312
            if (Math.round((next + prev) / 2) !== curr) {
12313
                table.push({
12314
                    time: curr,
12315
                    pos: i / (ilen - 1)
12316
                });
12317
            }
12318
        }
12319
        return table;
12320
    }
12321
 _generate() {
12322
        const min = this.min;
12323
        const max = this.max;
12324
        let timestamps = super.getDataTimestamps();
12325
        if (!timestamps.includes(min) || !timestamps.length) {
12326
            timestamps.splice(0, 0, min);
12327
        }
12328
        if (!timestamps.includes(max) || timestamps.length === 1) {
12329
            timestamps.push(max);
12330
        }
12331
        return timestamps.sort((a, b)=>a - b);
12332
    }
12333
 _getTimestampsForTable() {
12334
        let timestamps = this._cache.all || [];
12335
        if (timestamps.length) {
12336
            return timestamps;
12337
        }
12338
        const data = this.getDataTimestamps();
12339
        const label = this.getLabelTimestamps();
12340
        if (data.length && label.length) {
12341
            timestamps = this.normalize(data.concat(label));
12342
        } else {
12343
            timestamps = data.length ? data : label;
12344
        }
12345
        timestamps = this._cache.all = timestamps;
12346
        return timestamps;
12347
    }
12348
 getDecimalForValue(value) {
12349
        return (interpolate(this._table, value) - this._minPos) / this._tableRange;
12350
    }
12351
 getValueForPixel(pixel) {
12352
        const offsets = this._offsets;
12353
        const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
12354
        return interpolate(this._table, decimal * this._tableRange + this._minPos, true);
12355
    }
12356
}
12357
 
12358
var scales = /*#__PURE__*/Object.freeze({
12359
__proto__: null,
12360
CategoryScale: CategoryScale,
12361
LinearScale: LinearScale,
12362
LogarithmicScale: LogarithmicScale,
12363
RadialLinearScale: RadialLinearScale,
12364
TimeScale: TimeScale,
12365
TimeSeriesScale: TimeSeriesScale
12366
});
12367
 
12368
const BORDER_COLORS = [
12369
    'rgb(54, 162, 235)',
12370
    'rgb(255, 99, 132)',
12371
    'rgb(255, 159, 64)',
12372
    'rgb(255, 205, 86)',
12373
    'rgb(75, 192, 192)',
12374
    'rgb(153, 102, 255)',
12375
    'rgb(201, 203, 207)' // grey
12376
];
12377
// Border colors with 50% transparency
12378
const BACKGROUND_COLORS = /* #__PURE__ */ BORDER_COLORS.map((color)=>color.replace('rgb(', 'rgba(').replace(')', ', 0.5)'));
12379
function getBorderColor(i) {
12380
    return BORDER_COLORS[i % BORDER_COLORS.length];
12381
}
12382
function getBackgroundColor(i) {
12383
    return BACKGROUND_COLORS[i % BACKGROUND_COLORS.length];
12384
}
12385
function colorizeDefaultDataset(dataset, i) {
12386
    dataset.borderColor = getBorderColor(i);
12387
    dataset.backgroundColor = getBackgroundColor(i);
12388
    return ++i;
12389
}
12390
function colorizeDoughnutDataset(dataset, i) {
12391
    dataset.backgroundColor = dataset.data.map(()=>getBorderColor(i++));
12392
    return i;
12393
}
12394
function colorizePolarAreaDataset(dataset, i) {
12395
    dataset.backgroundColor = dataset.data.map(()=>getBackgroundColor(i++));
12396
    return i;
12397
}
12398
function getColorizer(chart) {
12399
    let i = 0;
12400
    return (dataset, datasetIndex)=>{
12401
        const controller = chart.getDatasetMeta(datasetIndex).controller;
12402
        if (controller instanceof DoughnutController) {
12403
            i = colorizeDoughnutDataset(dataset, i);
12404
        } else if (controller instanceof PolarAreaController) {
12405
            i = colorizePolarAreaDataset(dataset, i);
12406
        } else if (controller) {
12407
            i = colorizeDefaultDataset(dataset, i);
12408
        }
12409
    };
12410
}
12411
function containsColorsDefinitions(descriptors) {
12412
    let k;
12413
    for(k in descriptors){
12414
        if (descriptors[k].borderColor || descriptors[k].backgroundColor) {
12415
            return true;
12416
        }
12417
    }
12418
    return false;
12419
}
12420
function containsColorsDefinition(descriptor) {
12421
    return descriptor && (descriptor.borderColor || descriptor.backgroundColor);
12422
}
12423
var plugin_colors = {
12424
    id: 'colors',
12425
    defaults: {
12426
        enabled: true,
12427
        forceOverride: false
12428
    },
12429
    beforeLayout (chart, _args, options) {
12430
        if (!options.enabled) {
12431
            return;
12432
        }
12433
        const { data: { datasets  } , options: chartOptions  } = chart.config;
12434
        const { elements  } = chartOptions;
12435
        if (!options.forceOverride && (containsColorsDefinitions(datasets) || containsColorsDefinition(chartOptions) || elements && containsColorsDefinitions(elements))) {
12436
            return;
12437
        }
12438
        const colorizer = getColorizer(chart);
12439
        datasets.forEach(colorizer);
12440
    }
12441
};
12442
 
12443
function lttbDecimation(data, start, count, availableWidth, options) {
12444
 const samples = options.samples || availableWidth;
12445
    if (samples >= count) {
12446
        return data.slice(start, start + count);
12447
    }
12448
    const decimated = [];
12449
    const bucketWidth = (count - 2) / (samples - 2);
12450
    let sampledIndex = 0;
12451
    const endIndex = start + count - 1;
12452
    let a = start;
12453
    let i, maxAreaPoint, maxArea, area, nextA;
12454
    decimated[sampledIndex++] = data[a];
12455
    for(i = 0; i < samples - 2; i++){
12456
        let avgX = 0;
12457
        let avgY = 0;
12458
        let j;
12459
        const avgRangeStart = Math.floor((i + 1) * bucketWidth) + 1 + start;
12460
        const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketWidth) + 1, count) + start;
12461
        const avgRangeLength = avgRangeEnd - avgRangeStart;
12462
        for(j = avgRangeStart; j < avgRangeEnd; j++){
12463
            avgX += data[j].x;
12464
            avgY += data[j].y;
12465
        }
12466
        avgX /= avgRangeLength;
12467
        avgY /= avgRangeLength;
12468
        const rangeOffs = Math.floor(i * bucketWidth) + 1 + start;
12469
        const rangeTo = Math.min(Math.floor((i + 1) * bucketWidth) + 1, count) + start;
12470
        const { x: pointAx , y: pointAy  } = data[a];
12471
        maxArea = area = -1;
12472
        for(j = rangeOffs; j < rangeTo; j++){
12473
            area = 0.5 * Math.abs((pointAx - avgX) * (data[j].y - pointAy) - (pointAx - data[j].x) * (avgY - pointAy));
12474
            if (area > maxArea) {
12475
                maxArea = area;
12476
                maxAreaPoint = data[j];
12477
                nextA = j;
12478
            }
12479
        }
12480
        decimated[sampledIndex++] = maxAreaPoint;
12481
        a = nextA;
12482
    }
12483
    decimated[sampledIndex++] = data[endIndex];
12484
    return decimated;
12485
}
12486
function minMaxDecimation(data, start, count, availableWidth) {
12487
    let avgX = 0;
12488
    let countX = 0;
12489
    let i, point, x, y, prevX, minIndex, maxIndex, startIndex, minY, maxY;
12490
    const decimated = [];
12491
    const endIndex = start + count - 1;
12492
    const xMin = data[start].x;
12493
    const xMax = data[endIndex].x;
12494
    const dx = xMax - xMin;
12495
    for(i = start; i < start + count; ++i){
12496
        point = data[i];
12497
        x = (point.x - xMin) / dx * availableWidth;
12498
        y = point.y;
12499
        const truncX = x | 0;
12500
        if (truncX === prevX) {
12501
            if (y < minY) {
12502
                minY = y;
12503
                minIndex = i;
12504
            } else if (y > maxY) {
12505
                maxY = y;
12506
                maxIndex = i;
12507
            }
12508
            avgX = (countX * avgX + point.x) / ++countX;
12509
        } else {
12510
            const lastIndex = i - 1;
12511
            if (!isNullOrUndef(minIndex) && !isNullOrUndef(maxIndex)) {
12512
                const intermediateIndex1 = Math.min(minIndex, maxIndex);
12513
                const intermediateIndex2 = Math.max(minIndex, maxIndex);
12514
                if (intermediateIndex1 !== startIndex && intermediateIndex1 !== lastIndex) {
12515
                    decimated.push({
12516
                        ...data[intermediateIndex1],
12517
                        x: avgX
12518
                    });
12519
                }
12520
                if (intermediateIndex2 !== startIndex && intermediateIndex2 !== lastIndex) {
12521
                    decimated.push({
12522
                        ...data[intermediateIndex2],
12523
                        x: avgX
12524
                    });
12525
                }
12526
            }
12527
            if (i > 0 && lastIndex !== startIndex) {
12528
                decimated.push(data[lastIndex]);
12529
            }
12530
            decimated.push(point);
12531
            prevX = truncX;
12532
            countX = 0;
12533
            minY = maxY = y;
12534
            minIndex = maxIndex = startIndex = i;
12535
        }
12536
    }
12537
    return decimated;
12538
}
12539
function cleanDecimatedDataset(dataset) {
12540
    if (dataset._decimated) {
12541
        const data = dataset._data;
12542
        delete dataset._decimated;
12543
        delete dataset._data;
12544
        Object.defineProperty(dataset, 'data', {
12545
            configurable: true,
12546
            enumerable: true,
12547
            writable: true,
12548
            value: data
12549
        });
12550
    }
12551
}
12552
function cleanDecimatedData(chart) {
12553
    chart.data.datasets.forEach((dataset)=>{
12554
        cleanDecimatedDataset(dataset);
12555
    });
12556
}
12557
function getStartAndCountOfVisiblePointsSimplified(meta, points) {
12558
    const pointCount = points.length;
12559
    let start = 0;
12560
    let count;
12561
    const { iScale  } = meta;
12562
    const { min , max , minDefined , maxDefined  } = iScale.getUserBounds();
12563
    if (minDefined) {
12564
        start = _limitValue(_lookupByKey(points, iScale.axis, min).lo, 0, pointCount - 1);
12565
    }
12566
    if (maxDefined) {
12567
        count = _limitValue(_lookupByKey(points, iScale.axis, max).hi + 1, start, pointCount) - start;
12568
    } else {
12569
        count = pointCount - start;
12570
    }
12571
    return {
12572
        start,
12573
        count
12574
    };
12575
}
12576
var plugin_decimation = {
12577
    id: 'decimation',
12578
    defaults: {
12579
        algorithm: 'min-max',
12580
        enabled: false
12581
    },
12582
    beforeElementsUpdate: (chart, args, options)=>{
12583
        if (!options.enabled) {
12584
            cleanDecimatedData(chart);
12585
            return;
12586
        }
12587
        const availableWidth = chart.width;
12588
        chart.data.datasets.forEach((dataset, datasetIndex)=>{
12589
            const { _data , indexAxis  } = dataset;
12590
            const meta = chart.getDatasetMeta(datasetIndex);
12591
            const data = _data || dataset.data;
12592
            if (resolve([
12593
                indexAxis,
12594
                chart.options.indexAxis
12595
            ]) === 'y') {
12596
                return;
12597
            }
12598
            if (!meta.controller.supportsDecimation) {
12599
                return;
12600
            }
12601
            const xAxis = chart.scales[meta.xAxisID];
12602
            if (xAxis.type !== 'linear' && xAxis.type !== 'time') {
12603
                return;
12604
            }
12605
            if (chart.options.parsing) {
12606
                return;
12607
            }
12608
            let { start , count  } = getStartAndCountOfVisiblePointsSimplified(meta, data);
12609
            const threshold = options.threshold || 4 * availableWidth;
12610
            if (count <= threshold) {
12611
                cleanDecimatedDataset(dataset);
12612
                return;
12613
            }
12614
            if (isNullOrUndef(_data)) {
12615
                dataset._data = data;
12616
                delete dataset.data;
12617
                Object.defineProperty(dataset, 'data', {
12618
                    configurable: true,
12619
                    enumerable: true,
12620
                    get: function() {
12621
                        return this._decimated;
12622
                    },
12623
                    set: function(d) {
12624
                        this._data = d;
12625
                    }
12626
                });
12627
            }
12628
            let decimated;
12629
            switch(options.algorithm){
12630
                case 'lttb':
12631
                    decimated = lttbDecimation(data, start, count, availableWidth, options);
12632
                    break;
12633
                case 'min-max':
12634
                    decimated = minMaxDecimation(data, start, count, availableWidth);
12635
                    break;
12636
                default:
12637
                    throw new Error(`Unsupported decimation algorithm '${options.algorithm}'`);
12638
            }
12639
            dataset._decimated = decimated;
12640
        });
12641
    },
12642
    destroy (chart) {
12643
        cleanDecimatedData(chart);
12644
    }
12645
};
12646
 
12647
function _segments(line, target, property) {
12648
    const segments = line.segments;
12649
    const points = line.points;
12650
    const tpoints = target.points;
12651
    const parts = [];
12652
    for (const segment of segments){
12653
        let { start , end  } = segment;
12654
        end = _findSegmentEnd(start, end, points);
12655
        const bounds = _getBounds(property, points[start], points[end], segment.loop);
12656
        if (!target.segments) {
12657
            parts.push({
12658
                source: segment,
12659
                target: bounds,
12660
                start: points[start],
12661
                end: points[end]
12662
            });
12663
            continue;
12664
        }
12665
        const targetSegments = _boundSegments(target, bounds);
12666
        for (const tgt of targetSegments){
12667
            const subBounds = _getBounds(property, tpoints[tgt.start], tpoints[tgt.end], tgt.loop);
12668
            const fillSources = _boundSegment(segment, points, subBounds);
12669
            for (const fillSource of fillSources){
12670
                parts.push({
12671
                    source: fillSource,
12672
                    target: tgt,
12673
                    start: {
12674
                        [property]: _getEdge(bounds, subBounds, 'start', Math.max)
12675
                    },
12676
                    end: {
12677
                        [property]: _getEdge(bounds, subBounds, 'end', Math.min)
12678
                    }
12679
                });
12680
            }
12681
        }
12682
    }
12683
    return parts;
12684
}
12685
function _getBounds(property, first, last, loop) {
12686
    if (loop) {
12687
        return;
12688
    }
12689
    let start = first[property];
12690
    let end = last[property];
12691
    if (property === 'angle') {
12692
        start = _normalizeAngle(start);
12693
        end = _normalizeAngle(end);
12694
    }
12695
    return {
12696
        property,
12697
        start,
12698
        end
12699
    };
12700
}
12701
function _pointsFromSegments(boundary, line) {
12702
    const { x =null , y =null  } = boundary || {};
12703
    const linePoints = line.points;
12704
    const points = [];
12705
    line.segments.forEach(({ start , end  })=>{
12706
        end = _findSegmentEnd(start, end, linePoints);
12707
        const first = linePoints[start];
12708
        const last = linePoints[end];
12709
        if (y !== null) {
12710
            points.push({
12711
                x: first.x,
12712
                y
12713
            });
12714
            points.push({
12715
                x: last.x,
12716
                y
12717
            });
12718
        } else if (x !== null) {
12719
            points.push({
12720
                x,
12721
                y: first.y
12722
            });
12723
            points.push({
12724
                x,
12725
                y: last.y
12726
            });
12727
        }
12728
    });
12729
    return points;
12730
}
12731
function _findSegmentEnd(start, end, points) {
12732
    for(; end > start; end--){
12733
        const point = points[end];
12734
        if (!isNaN(point.x) && !isNaN(point.y)) {
12735
            break;
12736
        }
12737
    }
12738
    return end;
12739
}
12740
function _getEdge(a, b, prop, fn) {
12741
    if (a && b) {
12742
        return fn(a[prop], b[prop]);
12743
    }
12744
    return a ? a[prop] : b ? b[prop] : 0;
12745
}
12746
 
12747
function _createBoundaryLine(boundary, line) {
12748
    let points = [];
12749
    let _loop = false;
12750
    if (isArray(boundary)) {
12751
        _loop = true;
12752
        points = boundary;
12753
    } else {
12754
        points = _pointsFromSegments(boundary, line);
12755
    }
12756
    return points.length ? new LineElement({
12757
        points,
12758
        options: {
12759
            tension: 0
12760
        },
12761
        _loop,
12762
        _fullLoop: _loop
12763
    }) : null;
12764
}
12765
function _shouldApplyFill(source) {
12766
    return source && source.fill !== false;
12767
}
12768
 
12769
function _resolveTarget(sources, index, propagate) {
12770
    const source = sources[index];
12771
    let fill = source.fill;
12772
    const visited = [
12773
        index
12774
    ];
12775
    let target;
12776
    if (!propagate) {
12777
        return fill;
12778
    }
12779
    while(fill !== false && visited.indexOf(fill) === -1){
12780
        if (!isNumberFinite(fill)) {
12781
            return fill;
12782
        }
12783
        target = sources[fill];
12784
        if (!target) {
12785
            return false;
12786
        }
12787
        if (target.visible) {
12788
            return fill;
12789
        }
12790
        visited.push(fill);
12791
        fill = target.fill;
12792
    }
12793
    return false;
12794
}
12795
 function _decodeFill(line, index, count) {
12796
     const fill = parseFillOption(line);
12797
    if (isObject(fill)) {
12798
        return isNaN(fill.value) ? false : fill;
12799
    }
12800
    let target = parseFloat(fill);
12801
    if (isNumberFinite(target) && Math.floor(target) === target) {
12802
        return decodeTargetIndex(fill[0], index, target, count);
12803
    }
12804
    return [
12805
        'origin',
12806
        'start',
12807
        'end',
12808
        'stack',
12809
        'shape'
12810
    ].indexOf(fill) >= 0 && fill;
12811
}
12812
function decodeTargetIndex(firstCh, index, target, count) {
12813
    if (firstCh === '-' || firstCh === '+') {
12814
        target = index + target;
12815
    }
12816
    if (target === index || target < 0 || target >= count) {
12817
        return false;
12818
    }
12819
    return target;
12820
}
12821
 function _getTargetPixel(fill, scale) {
12822
    let pixel = null;
12823
    if (fill === 'start') {
12824
        pixel = scale.bottom;
12825
    } else if (fill === 'end') {
12826
        pixel = scale.top;
12827
    } else if (isObject(fill)) {
12828
        pixel = scale.getPixelForValue(fill.value);
12829
    } else if (scale.getBasePixel) {
12830
        pixel = scale.getBasePixel();
12831
    }
12832
    return pixel;
12833
}
12834
 function _getTargetValue(fill, scale, startValue) {
12835
    let value;
12836
    if (fill === 'start') {
12837
        value = startValue;
12838
    } else if (fill === 'end') {
12839
        value = scale.options.reverse ? scale.min : scale.max;
12840
    } else if (isObject(fill)) {
12841
        value = fill.value;
12842
    } else {
12843
        value = scale.getBaseValue();
12844
    }
12845
    return value;
12846
}
12847
 function parseFillOption(line) {
12848
    const options = line.options;
12849
    const fillOption = options.fill;
12850
    let fill = valueOrDefault(fillOption && fillOption.target, fillOption);
12851
    if (fill === undefined) {
12852
        fill = !!options.backgroundColor;
12853
    }
12854
    if (fill === false || fill === null) {
12855
        return false;
12856
    }
12857
    if (fill === true) {
12858
        return 'origin';
12859
    }
12860
    return fill;
12861
}
12862
 
12863
function _buildStackLine(source) {
12864
    const { scale , index , line  } = source;
12865
    const points = [];
12866
    const segments = line.segments;
12867
    const sourcePoints = line.points;
12868
    const linesBelow = getLinesBelow(scale, index);
12869
    linesBelow.push(_createBoundaryLine({
12870
        x: null,
12871
        y: scale.bottom
12872
    }, line));
12873
    for(let i = 0; i < segments.length; i++){
12874
        const segment = segments[i];
12875
        for(let j = segment.start; j <= segment.end; j++){
12876
            addPointsBelow(points, sourcePoints[j], linesBelow);
12877
        }
12878
    }
12879
    return new LineElement({
12880
        points,
12881
        options: {}
12882
    });
12883
}
12884
 function getLinesBelow(scale, index) {
12885
    const below = [];
12886
    const metas = scale.getMatchingVisibleMetas('line');
12887
    for(let i = 0; i < metas.length; i++){
12888
        const meta = metas[i];
12889
        if (meta.index === index) {
12890
            break;
12891
        }
12892
        if (!meta.hidden) {
12893
            below.unshift(meta.dataset);
12894
        }
12895
    }
12896
    return below;
12897
}
12898
 function addPointsBelow(points, sourcePoint, linesBelow) {
12899
    const postponed = [];
12900
    for(let j = 0; j < linesBelow.length; j++){
12901
        const line = linesBelow[j];
12902
        const { first , last , point  } = findPoint(line, sourcePoint, 'x');
12903
        if (!point || first && last) {
12904
            continue;
12905
        }
12906
        if (first) {
12907
            postponed.unshift(point);
12908
        } else {
12909
            points.push(point);
12910
            if (!last) {
12911
                break;
12912
            }
12913
        }
12914
    }
12915
    points.push(...postponed);
12916
}
12917
 function findPoint(line, sourcePoint, property) {
12918
    const point = line.interpolate(sourcePoint, property);
12919
    if (!point) {
12920
        return {};
12921
    }
12922
    const pointValue = point[property];
12923
    const segments = line.segments;
12924
    const linePoints = line.points;
12925
    let first = false;
12926
    let last = false;
12927
    for(let i = 0; i < segments.length; i++){
12928
        const segment = segments[i];
12929
        const firstValue = linePoints[segment.start][property];
12930
        const lastValue = linePoints[segment.end][property];
12931
        if (_isBetween(pointValue, firstValue, lastValue)) {
12932
            first = pointValue === firstValue;
12933
            last = pointValue === lastValue;
12934
            break;
12935
        }
12936
    }
12937
    return {
12938
        first,
12939
        last,
12940
        point
12941
    };
12942
}
12943
 
12944
class simpleArc {
12945
    constructor(opts){
12946
        this.x = opts.x;
12947
        this.y = opts.y;
12948
        this.radius = opts.radius;
12949
    }
12950
    pathSegment(ctx, bounds, opts) {
12951
        const { x , y , radius  } = this;
12952
        bounds = bounds || {
12953
            start: 0,
12954
            end: TAU
12955
        };
12956
        ctx.arc(x, y, radius, bounds.end, bounds.start, true);
12957
        return !opts.bounds;
12958
    }
12959
    interpolate(point) {
12960
        const { x , y , radius  } = this;
12961
        const angle = point.angle;
12962
        return {
12963
            x: x + Math.cos(angle) * radius,
12964
            y: y + Math.sin(angle) * radius,
12965
            angle
12966
        };
12967
    }
12968
}
12969
 
12970
function _getTarget(source) {
12971
    const { chart , fill , line  } = source;
12972
    if (isNumberFinite(fill)) {
12973
        return getLineByIndex(chart, fill);
12974
    }
12975
    if (fill === 'stack') {
12976
        return _buildStackLine(source);
12977
    }
12978
    if (fill === 'shape') {
12979
        return true;
12980
    }
12981
    const boundary = computeBoundary(source);
12982
    if (boundary instanceof simpleArc) {
12983
        return boundary;
12984
    }
12985
    return _createBoundaryLine(boundary, line);
12986
}
12987
 function getLineByIndex(chart, index) {
12988
    const meta = chart.getDatasetMeta(index);
12989
    const visible = meta && chart.isDatasetVisible(index);
12990
    return visible ? meta.dataset : null;
12991
}
12992
function computeBoundary(source) {
12993
    const scale = source.scale || {};
12994
    if (scale.getPointPositionForValue) {
12995
        return computeCircularBoundary(source);
12996
    }
12997
    return computeLinearBoundary(source);
12998
}
12999
function computeLinearBoundary(source) {
13000
    const { scale ={} , fill  } = source;
13001
    const pixel = _getTargetPixel(fill, scale);
13002
    if (isNumberFinite(pixel)) {
13003
        const horizontal = scale.isHorizontal();
13004
        return {
13005
            x: horizontal ? pixel : null,
13006
            y: horizontal ? null : pixel
13007
        };
13008
    }
13009
    return null;
13010
}
13011
function computeCircularBoundary(source) {
13012
    const { scale , fill  } = source;
13013
    const options = scale.options;
13014
    const length = scale.getLabels().length;
13015
    const start = options.reverse ? scale.max : scale.min;
13016
    const value = _getTargetValue(fill, scale, start);
13017
    const target = [];
13018
    if (options.grid.circular) {
13019
        const center = scale.getPointPositionForValue(0, start);
13020
        return new simpleArc({
13021
            x: center.x,
13022
            y: center.y,
13023
            radius: scale.getDistanceFromCenterForValue(value)
13024
        });
13025
    }
13026
    for(let i = 0; i < length; ++i){
13027
        target.push(scale.getPointPositionForValue(i, value));
13028
    }
13029
    return target;
13030
}
13031
 
13032
function _drawfill(ctx, source, area) {
13033
    const target = _getTarget(source);
13034
    const { line , scale , axis  } = source;
13035
    const lineOpts = line.options;
13036
    const fillOption = lineOpts.fill;
13037
    const color = lineOpts.backgroundColor;
13038
    const { above =color , below =color  } = fillOption || {};
13039
    if (target && line.points.length) {
13040
        clipArea(ctx, area);
13041
        doFill(ctx, {
13042
            line,
13043
            target,
13044
            above,
13045
            below,
13046
            area,
13047
            scale,
13048
            axis
13049
        });
13050
        unclipArea(ctx);
13051
    }
13052
}
13053
function doFill(ctx, cfg) {
13054
    const { line , target , above , below , area , scale  } = cfg;
13055
    const property = line._loop ? 'angle' : cfg.axis;
13056
    ctx.save();
13057
    if (property === 'x' && below !== above) {
13058
        clipVertical(ctx, target, area.top);
13059
        fill(ctx, {
13060
            line,
13061
            target,
13062
            color: above,
13063
            scale,
13064
            property
13065
        });
13066
        ctx.restore();
13067
        ctx.save();
13068
        clipVertical(ctx, target, area.bottom);
13069
    }
13070
    fill(ctx, {
13071
        line,
13072
        target,
13073
        color: below,
13074
        scale,
13075
        property
13076
    });
13077
    ctx.restore();
13078
}
13079
function clipVertical(ctx, target, clipY) {
13080
    const { segments , points  } = target;
13081
    let first = true;
13082
    let lineLoop = false;
13083
    ctx.beginPath();
13084
    for (const segment of segments){
13085
        const { start , end  } = segment;
13086
        const firstPoint = points[start];
13087
        const lastPoint = points[_findSegmentEnd(start, end, points)];
13088
        if (first) {
13089
            ctx.moveTo(firstPoint.x, firstPoint.y);
13090
            first = false;
13091
        } else {
13092
            ctx.lineTo(firstPoint.x, clipY);
13093
            ctx.lineTo(firstPoint.x, firstPoint.y);
13094
        }
13095
        lineLoop = !!target.pathSegment(ctx, segment, {
13096
            move: lineLoop
13097
        });
13098
        if (lineLoop) {
13099
            ctx.closePath();
13100
        } else {
13101
            ctx.lineTo(lastPoint.x, clipY);
13102
        }
13103
    }
13104
    ctx.lineTo(target.first().x, clipY);
13105
    ctx.closePath();
13106
    ctx.clip();
13107
}
13108
function fill(ctx, cfg) {
13109
    const { line , target , property , color , scale  } = cfg;
13110
    const segments = _segments(line, target, property);
13111
    for (const { source: src , target: tgt , start , end  } of segments){
13112
        const { style: { backgroundColor =color  } = {}  } = src;
13113
        const notShape = target !== true;
13114
        ctx.save();
13115
        ctx.fillStyle = backgroundColor;
13116
        clipBounds(ctx, scale, notShape && _getBounds(property, start, end));
13117
        ctx.beginPath();
13118
        const lineLoop = !!line.pathSegment(ctx, src);
13119
        let loop;
13120
        if (notShape) {
13121
            if (lineLoop) {
13122
                ctx.closePath();
13123
            } else {
13124
                interpolatedLineTo(ctx, target, end, property);
13125
            }
13126
            const targetLoop = !!target.pathSegment(ctx, tgt, {
13127
                move: lineLoop,
13128
                reverse: true
13129
            });
13130
            loop = lineLoop && targetLoop;
13131
            if (!loop) {
13132
                interpolatedLineTo(ctx, target, start, property);
13133
            }
13134
        }
13135
        ctx.closePath();
13136
        ctx.fill(loop ? 'evenodd' : 'nonzero');
13137
        ctx.restore();
13138
    }
13139
}
13140
function clipBounds(ctx, scale, bounds) {
13141
    const { top , bottom  } = scale.chart.chartArea;
13142
    const { property , start , end  } = bounds || {};
13143
    if (property === 'x') {
13144
        ctx.beginPath();
13145
        ctx.rect(start, top, end - start, bottom - top);
13146
        ctx.clip();
13147
    }
13148
}
13149
function interpolatedLineTo(ctx, target, point, property) {
13150
    const interpolatedPoint = target.interpolate(point, property);
13151
    if (interpolatedPoint) {
13152
        ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y);
13153
    }
13154
}
13155
 
13156
var index = {
13157
    id: 'filler',
13158
    afterDatasetsUpdate (chart, _args, options) {
13159
        const count = (chart.data.datasets || []).length;
13160
        const sources = [];
13161
        let meta, i, line, source;
13162
        for(i = 0; i < count; ++i){
13163
            meta = chart.getDatasetMeta(i);
13164
            line = meta.dataset;
13165
            source = null;
13166
            if (line && line.options && line instanceof LineElement) {
13167
                source = {
13168
                    visible: chart.isDatasetVisible(i),
13169
                    index: i,
13170
                    fill: _decodeFill(line, i, count),
13171
                    chart,
13172
                    axis: meta.controller.options.indexAxis,
13173
                    scale: meta.vScale,
13174
                    line
13175
                };
13176
            }
13177
            meta.$filler = source;
13178
            sources.push(source);
13179
        }
13180
        for(i = 0; i < count; ++i){
13181
            source = sources[i];
13182
            if (!source || source.fill === false) {
13183
                continue;
13184
            }
13185
            source.fill = _resolveTarget(sources, i, options.propagate);
13186
        }
13187
    },
13188
    beforeDraw (chart, _args, options) {
13189
        const draw = options.drawTime === 'beforeDraw';
13190
        const metasets = chart.getSortedVisibleDatasetMetas();
13191
        const area = chart.chartArea;
13192
        for(let i = metasets.length - 1; i >= 0; --i){
13193
            const source = metasets[i].$filler;
13194
            if (!source) {
13195
                continue;
13196
            }
13197
            source.line.updateControlPoints(area, source.axis);
13198
            if (draw && source.fill) {
13199
                _drawfill(chart.ctx, source, area);
13200
            }
13201
        }
13202
    },
13203
    beforeDatasetsDraw (chart, _args, options) {
13204
        if (options.drawTime !== 'beforeDatasetsDraw') {
13205
            return;
13206
        }
13207
        const metasets = chart.getSortedVisibleDatasetMetas();
13208
        for(let i = metasets.length - 1; i >= 0; --i){
13209
            const source = metasets[i].$filler;
13210
            if (_shouldApplyFill(source)) {
13211
                _drawfill(chart.ctx, source, chart.chartArea);
13212
            }
13213
        }
13214
    },
13215
    beforeDatasetDraw (chart, args, options) {
13216
        const source = args.meta.$filler;
13217
        if (!_shouldApplyFill(source) || options.drawTime !== 'beforeDatasetDraw') {
13218
            return;
13219
        }
13220
        _drawfill(chart.ctx, source, chart.chartArea);
13221
    },
13222
    defaults: {
13223
        propagate: true,
13224
        drawTime: 'beforeDatasetDraw'
13225
    }
13226
};
13227
 
13228
const getBoxSize = (labelOpts, fontSize)=>{
13229
    let { boxHeight =fontSize , boxWidth =fontSize  } = labelOpts;
13230
    if (labelOpts.usePointStyle) {
13231
        boxHeight = Math.min(boxHeight, fontSize);
13232
        boxWidth = labelOpts.pointStyleWidth || Math.min(boxWidth, fontSize);
13233
    }
13234
    return {
13235
        boxWidth,
13236
        boxHeight,
13237
        itemHeight: Math.max(fontSize, boxHeight)
13238
    };
13239
};
13240
const itemsEqual = (a, b)=>a !== null && b !== null && a.datasetIndex === b.datasetIndex && a.index === b.index;
13241
class Legend extends Element {
13242
 constructor(config){
13243
        super();
13244
        this._added = false;
13245
        this.legendHitBoxes = [];
13246
 this._hoveredItem = null;
13247
        this.doughnutMode = false;
13248
        this.chart = config.chart;
13249
        this.options = config.options;
13250
        this.ctx = config.ctx;
13251
        this.legendItems = undefined;
13252
        this.columnSizes = undefined;
13253
        this.lineWidths = undefined;
13254
        this.maxHeight = undefined;
13255
        this.maxWidth = undefined;
13256
        this.top = undefined;
13257
        this.bottom = undefined;
13258
        this.left = undefined;
13259
        this.right = undefined;
13260
        this.height = undefined;
13261
        this.width = undefined;
13262
        this._margins = undefined;
13263
        this.position = undefined;
13264
        this.weight = undefined;
13265
        this.fullSize = undefined;
13266
    }
13267
    update(maxWidth, maxHeight, margins) {
13268
        this.maxWidth = maxWidth;
13269
        this.maxHeight = maxHeight;
13270
        this._margins = margins;
13271
        this.setDimensions();
13272
        this.buildLabels();
13273
        this.fit();
13274
    }
13275
    setDimensions() {
13276
        if (this.isHorizontal()) {
13277
            this.width = this.maxWidth;
13278
            this.left = this._margins.left;
13279
            this.right = this.width;
13280
        } else {
13281
            this.height = this.maxHeight;
13282
            this.top = this._margins.top;
13283
            this.bottom = this.height;
13284
        }
13285
    }
13286
    buildLabels() {
13287
        const labelOpts = this.options.labels || {};
13288
        let legendItems = callback(labelOpts.generateLabels, [
13289
            this.chart
13290
        ], this) || [];
13291
        if (labelOpts.filter) {
13292
            legendItems = legendItems.filter((item)=>labelOpts.filter(item, this.chart.data));
13293
        }
13294
        if (labelOpts.sort) {
13295
            legendItems = legendItems.sort((a, b)=>labelOpts.sort(a, b, this.chart.data));
13296
        }
13297
        if (this.options.reverse) {
13298
            legendItems.reverse();
13299
        }
13300
        this.legendItems = legendItems;
13301
    }
13302
    fit() {
13303
        const { options , ctx  } = this;
13304
        if (!options.display) {
13305
            this.width = this.height = 0;
13306
            return;
13307
        }
13308
        const labelOpts = options.labels;
13309
        const labelFont = toFont(labelOpts.font);
13310
        const fontSize = labelFont.size;
13311
        const titleHeight = this._computeTitleHeight();
13312
        const { boxWidth , itemHeight  } = getBoxSize(labelOpts, fontSize);
13313
        let width, height;
13314
        ctx.font = labelFont.string;
13315
        if (this.isHorizontal()) {
13316
            width = this.maxWidth;
13317
            height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight) + 10;
13318
        } else {
13319
            height = this.maxHeight;
13320
            width = this._fitCols(titleHeight, labelFont, boxWidth, itemHeight) + 10;
13321
        }
13322
        this.width = Math.min(width, options.maxWidth || this.maxWidth);
13323
        this.height = Math.min(height, options.maxHeight || this.maxHeight);
13324
    }
13325
 _fitRows(titleHeight, fontSize, boxWidth, itemHeight) {
13326
        const { ctx , maxWidth , options: { labels: { padding  }  }  } = this;
13327
        const hitboxes = this.legendHitBoxes = [];
13328
        const lineWidths = this.lineWidths = [
13329
 
13330
        ];
13331
        const lineHeight = itemHeight + padding;
13332
        let totalHeight = titleHeight;
13333
        ctx.textAlign = 'left';
13334
        ctx.textBaseline = 'middle';
13335
        let row = -1;
13336
        let top = -lineHeight;
13337
        this.legendItems.forEach((legendItem, i)=>{
13338
            const itemWidth = boxWidth + fontSize / 2 + ctx.measureText(legendItem.text).width;
13339
            if (i === 0 || lineWidths[lineWidths.length - 1] + itemWidth + 2 * padding > maxWidth) {
13340
                totalHeight += lineHeight;
13341
                lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0;
13342
                top += lineHeight;
13343
                row++;
13344
            }
13345
            hitboxes[i] = {
13346
                left: 0,
13347
                top,
13348
                row,
13349
                width: itemWidth,
13350
                height: itemHeight
13351
            };
13352
            lineWidths[lineWidths.length - 1] += itemWidth + padding;
13353
        });
13354
        return totalHeight;
13355
    }
13356
    _fitCols(titleHeight, labelFont, boxWidth, _itemHeight) {
13357
        const { ctx , maxHeight , options: { labels: { padding  }  }  } = this;
13358
        const hitboxes = this.legendHitBoxes = [];
13359
        const columnSizes = this.columnSizes = [];
13360
        const heightLimit = maxHeight - titleHeight;
13361
        let totalWidth = padding;
13362
        let currentColWidth = 0;
13363
        let currentColHeight = 0;
13364
        let left = 0;
13365
        let col = 0;
13366
        this.legendItems.forEach((legendItem, i)=>{
13367
            const { itemWidth , itemHeight  } = calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight);
13368
            if (i > 0 && currentColHeight + itemHeight + 2 * padding > heightLimit) {
13369
                totalWidth += currentColWidth + padding;
13370
                columnSizes.push({
13371
                    width: currentColWidth,
13372
                    height: currentColHeight
13373
                });
13374
                left += currentColWidth + padding;
13375
                col++;
13376
                currentColWidth = currentColHeight = 0;
13377
            }
13378
            hitboxes[i] = {
13379
                left,
13380
                top: currentColHeight,
13381
                col,
13382
                width: itemWidth,
13383
                height: itemHeight
13384
            };
13385
            currentColWidth = Math.max(currentColWidth, itemWidth);
13386
            currentColHeight += itemHeight + padding;
13387
        });
13388
        totalWidth += currentColWidth;
13389
        columnSizes.push({
13390
            width: currentColWidth,
13391
            height: currentColHeight
13392
        });
13393
        return totalWidth;
13394
    }
13395
    adjustHitBoxes() {
13396
        if (!this.options.display) {
13397
            return;
13398
        }
13399
        const titleHeight = this._computeTitleHeight();
13400
        const { legendHitBoxes: hitboxes , options: { align , labels: { padding  } , rtl  }  } = this;
13401
        const rtlHelper = getRtlAdapter(rtl, this.left, this.width);
13402
        if (this.isHorizontal()) {
13403
            let row = 0;
13404
            let left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);
13405
            for (const hitbox of hitboxes){
13406
                if (row !== hitbox.row) {
13407
                    row = hitbox.row;
13408
                    left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);
13409
                }
13410
                hitbox.top += this.top + titleHeight + padding;
13411
                hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(left), hitbox.width);
13412
                left += hitbox.width + padding;
13413
            }
13414
        } else {
13415
            let col = 0;
13416
            let top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);
13417
            for (const hitbox of hitboxes){
13418
                if (hitbox.col !== col) {
13419
                    col = hitbox.col;
13420
                    top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);
13421
                }
13422
                hitbox.top = top;
13423
                hitbox.left += this.left + padding;
13424
                hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(hitbox.left), hitbox.width);
13425
                top += hitbox.height + padding;
13426
            }
13427
        }
13428
    }
13429
    isHorizontal() {
13430
        return this.options.position === 'top' || this.options.position === 'bottom';
13431
    }
13432
    draw() {
13433
        if (this.options.display) {
13434
            const ctx = this.ctx;
13435
            clipArea(ctx, this);
13436
            this._draw();
13437
            unclipArea(ctx);
13438
        }
13439
    }
13440
 _draw() {
13441
        const { options: opts , columnSizes , lineWidths , ctx  } = this;
13442
        const { align , labels: labelOpts  } = opts;
13443
        const defaultColor = defaults.color;
13444
        const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);
13445
        const labelFont = toFont(labelOpts.font);
13446
        const { padding  } = labelOpts;
13447
        const fontSize = labelFont.size;
13448
        const halfFontSize = fontSize / 2;
13449
        let cursor;
13450
        this.drawTitle();
13451
        ctx.textAlign = rtlHelper.textAlign('left');
13452
        ctx.textBaseline = 'middle';
13453
        ctx.lineWidth = 0.5;
13454
        ctx.font = labelFont.string;
13455
        const { boxWidth , boxHeight , itemHeight  } = getBoxSize(labelOpts, fontSize);
13456
        const drawLegendBox = function(x, y, legendItem) {
13457
            if (isNaN(boxWidth) || boxWidth <= 0 || isNaN(boxHeight) || boxHeight < 0) {
13458
                return;
13459
            }
13460
            ctx.save();
13461
            const lineWidth = valueOrDefault(legendItem.lineWidth, 1);
13462
            ctx.fillStyle = valueOrDefault(legendItem.fillStyle, defaultColor);
13463
            ctx.lineCap = valueOrDefault(legendItem.lineCap, 'butt');
13464
            ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, 0);
13465
            ctx.lineJoin = valueOrDefault(legendItem.lineJoin, 'miter');
13466
            ctx.lineWidth = lineWidth;
13467
            ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, defaultColor);
13468
            ctx.setLineDash(valueOrDefault(legendItem.lineDash, []));
13469
            if (labelOpts.usePointStyle) {
13470
                const drawOptions = {
13471
                    radius: boxHeight * Math.SQRT2 / 2,
13472
                    pointStyle: legendItem.pointStyle,
13473
                    rotation: legendItem.rotation,
13474
                    borderWidth: lineWidth
13475
                };
13476
                const centerX = rtlHelper.xPlus(x, boxWidth / 2);
13477
                const centerY = y + halfFontSize;
13478
                drawPointLegend(ctx, drawOptions, centerX, centerY, labelOpts.pointStyleWidth && boxWidth);
13479
            } else {
13480
                const yBoxTop = y + Math.max((fontSize - boxHeight) / 2, 0);
13481
                const xBoxLeft = rtlHelper.leftForLtr(x, boxWidth);
13482
                const borderRadius = toTRBLCorners(legendItem.borderRadius);
13483
                ctx.beginPath();
13484
                if (Object.values(borderRadius).some((v)=>v !== 0)) {
13485
                    addRoundedRectPath(ctx, {
13486
                        x: xBoxLeft,
13487
                        y: yBoxTop,
13488
                        w: boxWidth,
13489
                        h: boxHeight,
13490
                        radius: borderRadius
13491
                    });
13492
                } else {
13493
                    ctx.rect(xBoxLeft, yBoxTop, boxWidth, boxHeight);
13494
                }
13495
                ctx.fill();
13496
                if (lineWidth !== 0) {
13497
                    ctx.stroke();
13498
                }
13499
            }
13500
            ctx.restore();
13501
        };
13502
        const fillText = function(x, y, legendItem) {
13503
            renderText(ctx, legendItem.text, x, y + itemHeight / 2, labelFont, {
13504
                strikethrough: legendItem.hidden,
13505
                textAlign: rtlHelper.textAlign(legendItem.textAlign)
13506
            });
13507
        };
13508
        const isHorizontal = this.isHorizontal();
13509
        const titleHeight = this._computeTitleHeight();
13510
        if (isHorizontal) {
13511
            cursor = {
13512
                x: _alignStartEnd(align, this.left + padding, this.right - lineWidths[0]),
13513
                y: this.top + padding + titleHeight,
13514
                line: 0
13515
            };
13516
        } else {
13517
            cursor = {
13518
                x: this.left + padding,
13519
                y: _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[0].height),
13520
                line: 0
13521
            };
13522
        }
13523
        overrideTextDirection(this.ctx, opts.textDirection);
13524
        const lineHeight = itemHeight + padding;
13525
        this.legendItems.forEach((legendItem, i)=>{
13526
            ctx.strokeStyle = legendItem.fontColor;
13527
            ctx.fillStyle = legendItem.fontColor;
13528
            const textWidth = ctx.measureText(legendItem.text).width;
13529
            const textAlign = rtlHelper.textAlign(legendItem.textAlign || (legendItem.textAlign = labelOpts.textAlign));
13530
            const width = boxWidth + halfFontSize + textWidth;
13531
            let x = cursor.x;
13532
            let y = cursor.y;
13533
            rtlHelper.setWidth(this.width);
13534
            if (isHorizontal) {
13535
                if (i > 0 && x + width + padding > this.right) {
13536
                    y = cursor.y += lineHeight;
13537
                    cursor.line++;
13538
                    x = cursor.x = _alignStartEnd(align, this.left + padding, this.right - lineWidths[cursor.line]);
13539
                }
13540
            } else if (i > 0 && y + lineHeight > this.bottom) {
13541
                x = cursor.x = x + columnSizes[cursor.line].width + padding;
13542
                cursor.line++;
13543
                y = cursor.y = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[cursor.line].height);
13544
            }
13545
            const realX = rtlHelper.x(x);
13546
            drawLegendBox(realX, y, legendItem);
13547
            x = _textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : this.right, opts.rtl);
13548
            fillText(rtlHelper.x(x), y, legendItem);
13549
            if (isHorizontal) {
13550
                cursor.x += width + padding;
13551
            } else if (typeof legendItem.text !== 'string') {
13552
                const fontLineHeight = labelFont.lineHeight;
13553
                cursor.y += calculateLegendItemHeight(legendItem, fontLineHeight) + padding;
13554
            } else {
13555
                cursor.y += lineHeight;
13556
            }
13557
        });
13558
        restoreTextDirection(this.ctx, opts.textDirection);
13559
    }
13560
 drawTitle() {
13561
        const opts = this.options;
13562
        const titleOpts = opts.title;
13563
        const titleFont = toFont(titleOpts.font);
13564
        const titlePadding = toPadding(titleOpts.padding);
13565
        if (!titleOpts.display) {
13566
            return;
13567
        }
13568
        const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);
13569
        const ctx = this.ctx;
13570
        const position = titleOpts.position;
13571
        const halfFontSize = titleFont.size / 2;
13572
        const topPaddingPlusHalfFontSize = titlePadding.top + halfFontSize;
13573
        let y;
13574
        let left = this.left;
13575
        let maxWidth = this.width;
13576
        if (this.isHorizontal()) {
13577
            maxWidth = Math.max(...this.lineWidths);
13578
            y = this.top + topPaddingPlusHalfFontSize;
13579
            left = _alignStartEnd(opts.align, left, this.right - maxWidth);
13580
        } else {
13581
            const maxHeight = this.columnSizes.reduce((acc, size)=>Math.max(acc, size.height), 0);
13582
            y = topPaddingPlusHalfFontSize + _alignStartEnd(opts.align, this.top, this.bottom - maxHeight - opts.labels.padding - this._computeTitleHeight());
13583
        }
13584
        const x = _alignStartEnd(position, left, left + maxWidth);
13585
        ctx.textAlign = rtlHelper.textAlign(_toLeftRightCenter(position));
13586
        ctx.textBaseline = 'middle';
13587
        ctx.strokeStyle = titleOpts.color;
13588
        ctx.fillStyle = titleOpts.color;
13589
        ctx.font = titleFont.string;
13590
        renderText(ctx, titleOpts.text, x, y, titleFont);
13591
    }
13592
 _computeTitleHeight() {
13593
        const titleOpts = this.options.title;
13594
        const titleFont = toFont(titleOpts.font);
13595
        const titlePadding = toPadding(titleOpts.padding);
13596
        return titleOpts.display ? titleFont.lineHeight + titlePadding.height : 0;
13597
    }
13598
 _getLegendItemAt(x, y) {
13599
        let i, hitBox, lh;
13600
        if (_isBetween(x, this.left, this.right) && _isBetween(y, this.top, this.bottom)) {
13601
            lh = this.legendHitBoxes;
13602
            for(i = 0; i < lh.length; ++i){
13603
                hitBox = lh[i];
13604
                if (_isBetween(x, hitBox.left, hitBox.left + hitBox.width) && _isBetween(y, hitBox.top, hitBox.top + hitBox.height)) {
13605
                    return this.legendItems[i];
13606
                }
13607
            }
13608
        }
13609
        return null;
13610
    }
13611
 handleEvent(e) {
13612
        const opts = this.options;
13613
        if (!isListened(e.type, opts)) {
13614
            return;
13615
        }
13616
        const hoveredItem = this._getLegendItemAt(e.x, e.y);
13617
        if (e.type === 'mousemove' || e.type === 'mouseout') {
13618
            const previous = this._hoveredItem;
13619
            const sameItem = itemsEqual(previous, hoveredItem);
13620
            if (previous && !sameItem) {
13621
                callback(opts.onLeave, [
13622
                    e,
13623
                    previous,
13624
                    this
13625
                ], this);
13626
            }
13627
            this._hoveredItem = hoveredItem;
13628
            if (hoveredItem && !sameItem) {
13629
                callback(opts.onHover, [
13630
                    e,
13631
                    hoveredItem,
13632
                    this
13633
                ], this);
13634
            }
13635
        } else if (hoveredItem) {
13636
            callback(opts.onClick, [
13637
                e,
13638
                hoveredItem,
13639
                this
13640
            ], this);
13641
        }
13642
    }
13643
}
13644
function calculateItemSize(boxWidth, labelFont, ctx, legendItem, _itemHeight) {
13645
    const itemWidth = calculateItemWidth(legendItem, boxWidth, labelFont, ctx);
13646
    const itemHeight = calculateItemHeight(_itemHeight, legendItem, labelFont.lineHeight);
13647
    return {
13648
        itemWidth,
13649
        itemHeight
13650
    };
13651
}
13652
function calculateItemWidth(legendItem, boxWidth, labelFont, ctx) {
13653
    let legendItemText = legendItem.text;
13654
    if (legendItemText && typeof legendItemText !== 'string') {
13655
        legendItemText = legendItemText.reduce((a, b)=>a.length > b.length ? a : b);
13656
    }
13657
    return boxWidth + labelFont.size / 2 + ctx.measureText(legendItemText).width;
13658
}
13659
function calculateItemHeight(_itemHeight, legendItem, fontLineHeight) {
13660
    let itemHeight = _itemHeight;
13661
    if (typeof legendItem.text !== 'string') {
13662
        itemHeight = calculateLegendItemHeight(legendItem, fontLineHeight);
13663
    }
13664
    return itemHeight;
13665
}
13666
function calculateLegendItemHeight(legendItem, fontLineHeight) {
13667
    const labelHeight = legendItem.text ? legendItem.text.length : 0;
13668
    return fontLineHeight * labelHeight;
13669
}
13670
function isListened(type, opts) {
13671
    if ((type === 'mousemove' || type === 'mouseout') && (opts.onHover || opts.onLeave)) {
13672
        return true;
13673
    }
13674
    if (opts.onClick && (type === 'click' || type === 'mouseup')) {
13675
        return true;
13676
    }
13677
    return false;
13678
}
13679
var plugin_legend = {
13680
    id: 'legend',
13681
 _element: Legend,
13682
    start (chart, _args, options) {
13683
        const legend = chart.legend = new Legend({
13684
            ctx: chart.ctx,
13685
            options,
13686
            chart
13687
        });
13688
        layouts.configure(chart, legend, options);
13689
        layouts.addBox(chart, legend);
13690
    },
13691
    stop (chart) {
13692
        layouts.removeBox(chart, chart.legend);
13693
        delete chart.legend;
13694
    },
13695
    beforeUpdate (chart, _args, options) {
13696
        const legend = chart.legend;
13697
        layouts.configure(chart, legend, options);
13698
        legend.options = options;
13699
    },
13700
    afterUpdate (chart) {
13701
        const legend = chart.legend;
13702
        legend.buildLabels();
13703
        legend.adjustHitBoxes();
13704
    },
13705
    afterEvent (chart, args) {
13706
        if (!args.replay) {
13707
            chart.legend.handleEvent(args.event);
13708
        }
13709
    },
13710
    defaults: {
13711
        display: true,
13712
        position: 'top',
13713
        align: 'center',
13714
        fullSize: true,
13715
        reverse: false,
13716
        weight: 1000,
13717
        onClick (e, legendItem, legend) {
13718
            const index = legendItem.datasetIndex;
13719
            const ci = legend.chart;
13720
            if (ci.isDatasetVisible(index)) {
13721
                ci.hide(index);
13722
                legendItem.hidden = true;
13723
            } else {
13724
                ci.show(index);
13725
                legendItem.hidden = false;
13726
            }
13727
        },
13728
        onHover: null,
13729
        onLeave: null,
13730
        labels: {
13731
            color: (ctx)=>ctx.chart.options.color,
13732
            boxWidth: 40,
13733
            padding: 10,
13734
            generateLabels (chart) {
13735
                const datasets = chart.data.datasets;
13736
                const { labels: { usePointStyle , pointStyle , textAlign , color , useBorderRadius , borderRadius  }  } = chart.legend.options;
13737
                return chart._getSortedDatasetMetas().map((meta)=>{
13738
                    const style = meta.controller.getStyle(usePointStyle ? 0 : undefined);
13739
                    const borderWidth = toPadding(style.borderWidth);
13740
                    return {
13741
                        text: datasets[meta.index].label,
13742
                        fillStyle: style.backgroundColor,
13743
                        fontColor: color,
13744
                        hidden: !meta.visible,
13745
                        lineCap: style.borderCapStyle,
13746
                        lineDash: style.borderDash,
13747
                        lineDashOffset: style.borderDashOffset,
13748
                        lineJoin: style.borderJoinStyle,
13749
                        lineWidth: (borderWidth.width + borderWidth.height) / 4,
13750
                        strokeStyle: style.borderColor,
13751
                        pointStyle: pointStyle || style.pointStyle,
13752
                        rotation: style.rotation,
13753
                        textAlign: textAlign || style.textAlign,
13754
                        borderRadius: useBorderRadius && (borderRadius || style.borderRadius),
13755
                        datasetIndex: meta.index
13756
                    };
13757
                }, this);
13758
            }
13759
        },
13760
        title: {
13761
            color: (ctx)=>ctx.chart.options.color,
13762
            display: false,
13763
            position: 'center',
13764
            text: ''
13765
        }
13766
    },
13767
    descriptors: {
13768
        _scriptable: (name)=>!name.startsWith('on'),
13769
        labels: {
13770
            _scriptable: (name)=>![
13771
                    'generateLabels',
13772
                    'filter',
13773
                    'sort'
13774
                ].includes(name)
13775
        }
13776
    }
13777
};
13778
 
13779
class Title extends Element {
13780
 constructor(config){
13781
        super();
13782
        this.chart = config.chart;
13783
        this.options = config.options;
13784
        this.ctx = config.ctx;
13785
        this._padding = undefined;
13786
        this.top = undefined;
13787
        this.bottom = undefined;
13788
        this.left = undefined;
13789
        this.right = undefined;
13790
        this.width = undefined;
13791
        this.height = undefined;
13792
        this.position = undefined;
13793
        this.weight = undefined;
13794
        this.fullSize = undefined;
13795
    }
13796
    update(maxWidth, maxHeight) {
13797
        const opts = this.options;
13798
        this.left = 0;
13799
        this.top = 0;
13800
        if (!opts.display) {
13801
            this.width = this.height = this.right = this.bottom = 0;
13802
            return;
13803
        }
13804
        this.width = this.right = maxWidth;
13805
        this.height = this.bottom = maxHeight;
13806
        const lineCount = isArray(opts.text) ? opts.text.length : 1;
13807
        this._padding = toPadding(opts.padding);
13808
        const textSize = lineCount * toFont(opts.font).lineHeight + this._padding.height;
13809
        if (this.isHorizontal()) {
13810
            this.height = textSize;
13811
        } else {
13812
            this.width = textSize;
13813
        }
13814
    }
13815
    isHorizontal() {
13816
        const pos = this.options.position;
13817
        return pos === 'top' || pos === 'bottom';
13818
    }
13819
    _drawArgs(offset) {
13820
        const { top , left , bottom , right , options  } = this;
13821
        const align = options.align;
13822
        let rotation = 0;
13823
        let maxWidth, titleX, titleY;
13824
        if (this.isHorizontal()) {
13825
            titleX = _alignStartEnd(align, left, right);
13826
            titleY = top + offset;
13827
            maxWidth = right - left;
13828
        } else {
13829
            if (options.position === 'left') {
13830
                titleX = left + offset;
13831
                titleY = _alignStartEnd(align, bottom, top);
13832
                rotation = PI * -0.5;
13833
            } else {
13834
                titleX = right - offset;
13835
                titleY = _alignStartEnd(align, top, bottom);
13836
                rotation = PI * 0.5;
13837
            }
13838
            maxWidth = bottom - top;
13839
        }
13840
        return {
13841
            titleX,
13842
            titleY,
13843
            maxWidth,
13844
            rotation
13845
        };
13846
    }
13847
    draw() {
13848
        const ctx = this.ctx;
13849
        const opts = this.options;
13850
        if (!opts.display) {
13851
            return;
13852
        }
13853
        const fontOpts = toFont(opts.font);
13854
        const lineHeight = fontOpts.lineHeight;
13855
        const offset = lineHeight / 2 + this._padding.top;
13856
        const { titleX , titleY , maxWidth , rotation  } = this._drawArgs(offset);
13857
        renderText(ctx, opts.text, 0, 0, fontOpts, {
13858
            color: opts.color,
13859
            maxWidth,
13860
            rotation,
13861
            textAlign: _toLeftRightCenter(opts.align),
13862
            textBaseline: 'middle',
13863
            translation: [
13864
                titleX,
13865
                titleY
13866
            ]
13867
        });
13868
    }
13869
}
13870
function createTitle(chart, titleOpts) {
13871
    const title = new Title({
13872
        ctx: chart.ctx,
13873
        options: titleOpts,
13874
        chart
13875
    });
13876
    layouts.configure(chart, title, titleOpts);
13877
    layouts.addBox(chart, title);
13878
    chart.titleBlock = title;
13879
}
13880
var plugin_title = {
13881
    id: 'title',
13882
 _element: Title,
13883
    start (chart, _args, options) {
13884
        createTitle(chart, options);
13885
    },
13886
    stop (chart) {
13887
        const titleBlock = chart.titleBlock;
13888
        layouts.removeBox(chart, titleBlock);
13889
        delete chart.titleBlock;
13890
    },
13891
    beforeUpdate (chart, _args, options) {
13892
        const title = chart.titleBlock;
13893
        layouts.configure(chart, title, options);
13894
        title.options = options;
13895
    },
13896
    defaults: {
13897
        align: 'center',
13898
        display: false,
13899
        font: {
13900
            weight: 'bold'
13901
        },
13902
        fullSize: true,
13903
        padding: 10,
13904
        position: 'top',
13905
        text: '',
13906
        weight: 2000
13907
    },
13908
    defaultRoutes: {
13909
        color: 'color'
13910
    },
13911
    descriptors: {
13912
        _scriptable: true,
13913
        _indexable: false
13914
    }
13915
};
13916
 
13917
const map = new WeakMap();
13918
var plugin_subtitle = {
13919
    id: 'subtitle',
13920
    start (chart, _args, options) {
13921
        const title = new Title({
13922
            ctx: chart.ctx,
13923
            options,
13924
            chart
13925
        });
13926
        layouts.configure(chart, title, options);
13927
        layouts.addBox(chart, title);
13928
        map.set(chart, title);
13929
    },
13930
    stop (chart) {
13931
        layouts.removeBox(chart, map.get(chart));
13932
        map.delete(chart);
13933
    },
13934
    beforeUpdate (chart, _args, options) {
13935
        const title = map.get(chart);
13936
        layouts.configure(chart, title, options);
13937
        title.options = options;
13938
    },
13939
    defaults: {
13940
        align: 'center',
13941
        display: false,
13942
        font: {
13943
            weight: 'normal'
13944
        },
13945
        fullSize: true,
13946
        padding: 0,
13947
        position: 'top',
13948
        text: '',
13949
        weight: 1500
13950
    },
13951
    defaultRoutes: {
13952
        color: 'color'
13953
    },
13954
    descriptors: {
13955
        _scriptable: true,
13956
        _indexable: false
13957
    }
13958
};
13959
 
13960
const positioners = {
13961
 average (items) {
13962
        if (!items.length) {
13963
            return false;
13964
        }
13965
        let i, len;
13966
        let xSet = new Set();
13967
        let y = 0;
13968
        let count = 0;
13969
        for(i = 0, len = items.length; i < len; ++i){
13970
            const el = items[i].element;
13971
            if (el && el.hasValue()) {
13972
                const pos = el.tooltipPosition();
13973
                xSet.add(pos.x);
13974
                y += pos.y;
13975
                ++count;
13976
            }
13977
        }
13978
        const xAverage = [
13979
            ...xSet
13980
        ].reduce((a, b)=>a + b) / xSet.size;
13981
        return {
13982
            x: xAverage,
13983
            y: y / count
13984
        };
13985
    },
13986
 nearest (items, eventPosition) {
13987
        if (!items.length) {
13988
            return false;
13989
        }
13990
        let x = eventPosition.x;
13991
        let y = eventPosition.y;
13992
        let minDistance = Number.POSITIVE_INFINITY;
13993
        let i, len, nearestElement;
13994
        for(i = 0, len = items.length; i < len; ++i){
13995
            const el = items[i].element;
13996
            if (el && el.hasValue()) {
13997
                const center = el.getCenterPoint();
13998
                const d = distanceBetweenPoints(eventPosition, center);
13999
                if (d < minDistance) {
14000
                    minDistance = d;
14001
                    nearestElement = el;
14002
                }
14003
            }
14004
        }
14005
        if (nearestElement) {
14006
            const tp = nearestElement.tooltipPosition();
14007
            x = tp.x;
14008
            y = tp.y;
14009
        }
14010
        return {
14011
            x,
14012
            y
14013
        };
14014
    }
14015
};
14016
function pushOrConcat(base, toPush) {
14017
    if (toPush) {
14018
        if (isArray(toPush)) {
14019
            Array.prototype.push.apply(base, toPush);
14020
        } else {
14021
            base.push(toPush);
14022
        }
14023
    }
14024
    return base;
14025
}
14026
 function splitNewlines(str) {
14027
    if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) {
14028
        return str.split('\n');
14029
    }
14030
    return str;
14031
}
14032
 function createTooltipItem(chart, item) {
14033
    const { element , datasetIndex , index  } = item;
14034
    const controller = chart.getDatasetMeta(datasetIndex).controller;
14035
    const { label , value  } = controller.getLabelAndValue(index);
14036
    return {
14037
        chart,
14038
        label,
14039
        parsed: controller.getParsed(index),
14040
        raw: chart.data.datasets[datasetIndex].data[index],
14041
        formattedValue: value,
14042
        dataset: controller.getDataset(),
14043
        dataIndex: index,
14044
        datasetIndex,
14045
        element
14046
    };
14047
}
14048
 function getTooltipSize(tooltip, options) {
14049
    const ctx = tooltip.chart.ctx;
14050
    const { body , footer , title  } = tooltip;
14051
    const { boxWidth , boxHeight  } = options;
14052
    const bodyFont = toFont(options.bodyFont);
14053
    const titleFont = toFont(options.titleFont);
14054
    const footerFont = toFont(options.footerFont);
14055
    const titleLineCount = title.length;
14056
    const footerLineCount = footer.length;
14057
    const bodyLineItemCount = body.length;
14058
    const padding = toPadding(options.padding);
14059
    let height = padding.height;
14060
    let width = 0;
14061
    let combinedBodyLength = body.reduce((count, bodyItem)=>count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length, 0);
14062
    combinedBodyLength += tooltip.beforeBody.length + tooltip.afterBody.length;
14063
    if (titleLineCount) {
14064
        height += titleLineCount * titleFont.lineHeight + (titleLineCount - 1) * options.titleSpacing + options.titleMarginBottom;
14065
    }
14066
    if (combinedBodyLength) {
14067
        const bodyLineHeight = options.displayColors ? Math.max(boxHeight, bodyFont.lineHeight) : bodyFont.lineHeight;
14068
        height += bodyLineItemCount * bodyLineHeight + (combinedBodyLength - bodyLineItemCount) * bodyFont.lineHeight + (combinedBodyLength - 1) * options.bodySpacing;
14069
    }
14070
    if (footerLineCount) {
14071
        height += options.footerMarginTop + footerLineCount * footerFont.lineHeight + (footerLineCount - 1) * options.footerSpacing;
14072
    }
14073
    let widthPadding = 0;
14074
    const maxLineWidth = function(line) {
14075
        width = Math.max(width, ctx.measureText(line).width + widthPadding);
14076
    };
14077
    ctx.save();
14078
    ctx.font = titleFont.string;
14079
    each(tooltip.title, maxLineWidth);
14080
    ctx.font = bodyFont.string;
14081
    each(tooltip.beforeBody.concat(tooltip.afterBody), maxLineWidth);
14082
    widthPadding = options.displayColors ? boxWidth + 2 + options.boxPadding : 0;
14083
    each(body, (bodyItem)=>{
14084
        each(bodyItem.before, maxLineWidth);
14085
        each(bodyItem.lines, maxLineWidth);
14086
        each(bodyItem.after, maxLineWidth);
14087
    });
14088
    widthPadding = 0;
14089
    ctx.font = footerFont.string;
14090
    each(tooltip.footer, maxLineWidth);
14091
    ctx.restore();
14092
    width += padding.width;
14093
    return {
14094
        width,
14095
        height
14096
    };
14097
}
14098
function determineYAlign(chart, size) {
14099
    const { y , height  } = size;
14100
    if (y < height / 2) {
14101
        return 'top';
14102
    } else if (y > chart.height - height / 2) {
14103
        return 'bottom';
14104
    }
14105
    return 'center';
14106
}
14107
function doesNotFitWithAlign(xAlign, chart, options, size) {
14108
    const { x , width  } = size;
14109
    const caret = options.caretSize + options.caretPadding;
14110
    if (xAlign === 'left' && x + width + caret > chart.width) {
14111
        return true;
14112
    }
14113
    if (xAlign === 'right' && x - width - caret < 0) {
14114
        return true;
14115
    }
14116
}
14117
function determineXAlign(chart, options, size, yAlign) {
14118
    const { x , width  } = size;
14119
    const { width: chartWidth , chartArea: { left , right  }  } = chart;
14120
    let xAlign = 'center';
14121
    if (yAlign === 'center') {
14122
        xAlign = x <= (left + right) / 2 ? 'left' : 'right';
14123
    } else if (x <= width / 2) {
14124
        xAlign = 'left';
14125
    } else if (x >= chartWidth - width / 2) {
14126
        xAlign = 'right';
14127
    }
14128
    if (doesNotFitWithAlign(xAlign, chart, options, size)) {
14129
        xAlign = 'center';
14130
    }
14131
    return xAlign;
14132
}
14133
 function determineAlignment(chart, options, size) {
14134
    const yAlign = size.yAlign || options.yAlign || determineYAlign(chart, size);
14135
    return {
14136
        xAlign: size.xAlign || options.xAlign || determineXAlign(chart, options, size, yAlign),
14137
        yAlign
14138
    };
14139
}
14140
function alignX(size, xAlign) {
14141
    let { x , width  } = size;
14142
    if (xAlign === 'right') {
14143
        x -= width;
14144
    } else if (xAlign === 'center') {
14145
        x -= width / 2;
14146
    }
14147
    return x;
14148
}
14149
function alignY(size, yAlign, paddingAndSize) {
14150
    let { y , height  } = size;
14151
    if (yAlign === 'top') {
14152
        y += paddingAndSize;
14153
    } else if (yAlign === 'bottom') {
14154
        y -= height + paddingAndSize;
14155
    } else {
14156
        y -= height / 2;
14157
    }
14158
    return y;
14159
}
14160
 function getBackgroundPoint(options, size, alignment, chart) {
14161
    const { caretSize , caretPadding , cornerRadius  } = options;
14162
    const { xAlign , yAlign  } = alignment;
14163
    const paddingAndSize = caretSize + caretPadding;
14164
    const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(cornerRadius);
14165
    let x = alignX(size, xAlign);
14166
    const y = alignY(size, yAlign, paddingAndSize);
14167
    if (yAlign === 'center') {
14168
        if (xAlign === 'left') {
14169
            x += paddingAndSize;
14170
        } else if (xAlign === 'right') {
14171
            x -= paddingAndSize;
14172
        }
14173
    } else if (xAlign === 'left') {
14174
        x -= Math.max(topLeft, bottomLeft) + caretSize;
14175
    } else if (xAlign === 'right') {
14176
        x += Math.max(topRight, bottomRight) + caretSize;
14177
    }
14178
    return {
14179
        x: _limitValue(x, 0, chart.width - size.width),
14180
        y: _limitValue(y, 0, chart.height - size.height)
14181
    };
14182
}
14183
function getAlignedX(tooltip, align, options) {
14184
    const padding = toPadding(options.padding);
14185
    return align === 'center' ? tooltip.x + tooltip.width / 2 : align === 'right' ? tooltip.x + tooltip.width - padding.right : tooltip.x + padding.left;
14186
}
14187
 function getBeforeAfterBodyLines(callback) {
14188
    return pushOrConcat([], splitNewlines(callback));
14189
}
14190
function createTooltipContext(parent, tooltip, tooltipItems) {
14191
    return createContext(parent, {
14192
        tooltip,
14193
        tooltipItems,
14194
        type: 'tooltip'
14195
    });
14196
}
14197
function overrideCallbacks(callbacks, context) {
14198
    const override = context && context.dataset && context.dataset.tooltip && context.dataset.tooltip.callbacks;
14199
    return override ? callbacks.override(override) : callbacks;
14200
}
14201
const defaultCallbacks = {
14202
    beforeTitle: noop,
14203
    title (tooltipItems) {
14204
        if (tooltipItems.length > 0) {
14205
            const item = tooltipItems[0];
14206
            const labels = item.chart.data.labels;
14207
            const labelCount = labels ? labels.length : 0;
14208
            if (this && this.options && this.options.mode === 'dataset') {
14209
                return item.dataset.label || '';
14210
            } else if (item.label) {
14211
                return item.label;
14212
            } else if (labelCount > 0 && item.dataIndex < labelCount) {
14213
                return labels[item.dataIndex];
14214
            }
14215
        }
14216
        return '';
14217
    },
14218
    afterTitle: noop,
14219
    beforeBody: noop,
14220
    beforeLabel: noop,
14221
    label (tooltipItem) {
14222
        if (this && this.options && this.options.mode === 'dataset') {
14223
            return tooltipItem.label + ': ' + tooltipItem.formattedValue || tooltipItem.formattedValue;
14224
        }
14225
        let label = tooltipItem.dataset.label || '';
14226
        if (label) {
14227
            label += ': ';
14228
        }
14229
        const value = tooltipItem.formattedValue;
14230
        if (!isNullOrUndef(value)) {
14231
            label += value;
14232
        }
14233
        return label;
14234
    },
14235
    labelColor (tooltipItem) {
14236
        const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);
14237
        const options = meta.controller.getStyle(tooltipItem.dataIndex);
14238
        return {
14239
            borderColor: options.borderColor,
14240
            backgroundColor: options.backgroundColor,
14241
            borderWidth: options.borderWidth,
14242
            borderDash: options.borderDash,
14243
            borderDashOffset: options.borderDashOffset,
14244
            borderRadius: 0
14245
        };
14246
    },
14247
    labelTextColor () {
14248
        return this.options.bodyColor;
14249
    },
14250
    labelPointStyle (tooltipItem) {
14251
        const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);
14252
        const options = meta.controller.getStyle(tooltipItem.dataIndex);
14253
        return {
14254
            pointStyle: options.pointStyle,
14255
            rotation: options.rotation
14256
        };
14257
    },
14258
    afterLabel: noop,
14259
    afterBody: noop,
14260
    beforeFooter: noop,
14261
    footer: noop,
14262
    afterFooter: noop
14263
};
14264
 function invokeCallbackWithFallback(callbacks, name, ctx, arg) {
14265
    const result = callbacks[name].call(ctx, arg);
14266
    if (typeof result === 'undefined') {
14267
        return defaultCallbacks[name].call(ctx, arg);
14268
    }
14269
    return result;
14270
}
14271
class Tooltip extends Element {
14272
 static positioners = positioners;
14273
    constructor(config){
14274
        super();
14275
        this.opacity = 0;
14276
        this._active = [];
14277
        this._eventPosition = undefined;
14278
        this._size = undefined;
14279
        this._cachedAnimations = undefined;
14280
        this._tooltipItems = [];
14281
        this.$animations = undefined;
14282
        this.$context = undefined;
14283
        this.chart = config.chart;
14284
        this.options = config.options;
14285
        this.dataPoints = undefined;
14286
        this.title = undefined;
14287
        this.beforeBody = undefined;
14288
        this.body = undefined;
14289
        this.afterBody = undefined;
14290
        this.footer = undefined;
14291
        this.xAlign = undefined;
14292
        this.yAlign = undefined;
14293
        this.x = undefined;
14294
        this.y = undefined;
14295
        this.height = undefined;
14296
        this.width = undefined;
14297
        this.caretX = undefined;
14298
        this.caretY = undefined;
14299
        this.labelColors = undefined;
14300
        this.labelPointStyles = undefined;
14301
        this.labelTextColors = undefined;
14302
    }
14303
    initialize(options) {
14304
        this.options = options;
14305
        this._cachedAnimations = undefined;
14306
        this.$context = undefined;
14307
    }
14308
 _resolveAnimations() {
14309
        const cached = this._cachedAnimations;
14310
        if (cached) {
14311
            return cached;
14312
        }
14313
        const chart = this.chart;
14314
        const options = this.options.setContext(this.getContext());
14315
        const opts = options.enabled && chart.options.animation && options.animations;
14316
        const animations = new Animations(this.chart, opts);
14317
        if (opts._cacheable) {
14318
            this._cachedAnimations = Object.freeze(animations);
14319
        }
14320
        return animations;
14321
    }
14322
 getContext() {
14323
        return this.$context || (this.$context = createTooltipContext(this.chart.getContext(), this, this._tooltipItems));
14324
    }
14325
    getTitle(context, options) {
14326
        const { callbacks  } = options;
14327
        const beforeTitle = invokeCallbackWithFallback(callbacks, 'beforeTitle', this, context);
14328
        const title = invokeCallbackWithFallback(callbacks, 'title', this, context);
14329
        const afterTitle = invokeCallbackWithFallback(callbacks, 'afterTitle', this, context);
14330
        let lines = [];
14331
        lines = pushOrConcat(lines, splitNewlines(beforeTitle));
14332
        lines = pushOrConcat(lines, splitNewlines(title));
14333
        lines = pushOrConcat(lines, splitNewlines(afterTitle));
14334
        return lines;
14335
    }
14336
    getBeforeBody(tooltipItems, options) {
14337
        return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'beforeBody', this, tooltipItems));
14338
    }
14339
    getBody(tooltipItems, options) {
14340
        const { callbacks  } = options;
14341
        const bodyItems = [];
14342
        each(tooltipItems, (context)=>{
14343
            const bodyItem = {
14344
                before: [],
14345
                lines: [],
14346
                after: []
14347
            };
14348
            const scoped = overrideCallbacks(callbacks, context);
14349
            pushOrConcat(bodyItem.before, splitNewlines(invokeCallbackWithFallback(scoped, 'beforeLabel', this, context)));
14350
            pushOrConcat(bodyItem.lines, invokeCallbackWithFallback(scoped, 'label', this, context));
14351
            pushOrConcat(bodyItem.after, splitNewlines(invokeCallbackWithFallback(scoped, 'afterLabel', this, context)));
14352
            bodyItems.push(bodyItem);
14353
        });
14354
        return bodyItems;
14355
    }
14356
    getAfterBody(tooltipItems, options) {
14357
        return getBeforeAfterBodyLines(invokeCallbackWithFallback(options.callbacks, 'afterBody', this, tooltipItems));
14358
    }
14359
    getFooter(tooltipItems, options) {
14360
        const { callbacks  } = options;
14361
        const beforeFooter = invokeCallbackWithFallback(callbacks, 'beforeFooter', this, tooltipItems);
14362
        const footer = invokeCallbackWithFallback(callbacks, 'footer', this, tooltipItems);
14363
        const afterFooter = invokeCallbackWithFallback(callbacks, 'afterFooter', this, tooltipItems);
14364
        let lines = [];
14365
        lines = pushOrConcat(lines, splitNewlines(beforeFooter));
14366
        lines = pushOrConcat(lines, splitNewlines(footer));
14367
        lines = pushOrConcat(lines, splitNewlines(afterFooter));
14368
        return lines;
14369
    }
14370
 _createItems(options) {
14371
        const active = this._active;
14372
        const data = this.chart.data;
14373
        const labelColors = [];
14374
        const labelPointStyles = [];
14375
        const labelTextColors = [];
14376
        let tooltipItems = [];
14377
        let i, len;
14378
        for(i = 0, len = active.length; i < len; ++i){
14379
            tooltipItems.push(createTooltipItem(this.chart, active[i]));
14380
        }
14381
        if (options.filter) {
14382
            tooltipItems = tooltipItems.filter((element, index, array)=>options.filter(element, index, array, data));
14383
        }
14384
        if (options.itemSort) {
14385
            tooltipItems = tooltipItems.sort((a, b)=>options.itemSort(a, b, data));
14386
        }
14387
        each(tooltipItems, (context)=>{
14388
            const scoped = overrideCallbacks(options.callbacks, context);
14389
            labelColors.push(invokeCallbackWithFallback(scoped, 'labelColor', this, context));
14390
            labelPointStyles.push(invokeCallbackWithFallback(scoped, 'labelPointStyle', this, context));
14391
            labelTextColors.push(invokeCallbackWithFallback(scoped, 'labelTextColor', this, context));
14392
        });
14393
        this.labelColors = labelColors;
14394
        this.labelPointStyles = labelPointStyles;
14395
        this.labelTextColors = labelTextColors;
14396
        this.dataPoints = tooltipItems;
14397
        return tooltipItems;
14398
    }
14399
    update(changed, replay) {
14400
        const options = this.options.setContext(this.getContext());
14401
        const active = this._active;
14402
        let properties;
14403
        let tooltipItems = [];
14404
        if (!active.length) {
14405
            if (this.opacity !== 0) {
14406
                properties = {
14407
                    opacity: 0
14408
                };
14409
            }
14410
        } else {
14411
            const position = positioners[options.position].call(this, active, this._eventPosition);
14412
            tooltipItems = this._createItems(options);
14413
            this.title = this.getTitle(tooltipItems, options);
14414
            this.beforeBody = this.getBeforeBody(tooltipItems, options);
14415
            this.body = this.getBody(tooltipItems, options);
14416
            this.afterBody = this.getAfterBody(tooltipItems, options);
14417
            this.footer = this.getFooter(tooltipItems, options);
14418
            const size = this._size = getTooltipSize(this, options);
14419
            const positionAndSize = Object.assign({}, position, size);
14420
            const alignment = determineAlignment(this.chart, options, positionAndSize);
14421
            const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, this.chart);
14422
            this.xAlign = alignment.xAlign;
14423
            this.yAlign = alignment.yAlign;
14424
            properties = {
14425
                opacity: 1,
14426
                x: backgroundPoint.x,
14427
                y: backgroundPoint.y,
14428
                width: size.width,
14429
                height: size.height,
14430
                caretX: position.x,
14431
                caretY: position.y
14432
            };
14433
        }
14434
        this._tooltipItems = tooltipItems;
14435
        this.$context = undefined;
14436
        if (properties) {
14437
            this._resolveAnimations().update(this, properties);
14438
        }
14439
        if (changed && options.external) {
14440
            options.external.call(this, {
14441
                chart: this.chart,
14442
                tooltip: this,
14443
                replay
14444
            });
14445
        }
14446
    }
14447
    drawCaret(tooltipPoint, ctx, size, options) {
14448
        const caretPosition = this.getCaretPosition(tooltipPoint, size, options);
14449
        ctx.lineTo(caretPosition.x1, caretPosition.y1);
14450
        ctx.lineTo(caretPosition.x2, caretPosition.y2);
14451
        ctx.lineTo(caretPosition.x3, caretPosition.y3);
14452
    }
14453
    getCaretPosition(tooltipPoint, size, options) {
14454
        const { xAlign , yAlign  } = this;
14455
        const { caretSize , cornerRadius  } = options;
14456
        const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(cornerRadius);
14457
        const { x: ptX , y: ptY  } = tooltipPoint;
14458
        const { width , height  } = size;
14459
        let x1, x2, x3, y1, y2, y3;
14460
        if (yAlign === 'center') {
14461
            y2 = ptY + height / 2;
14462
            if (xAlign === 'left') {
14463
                x1 = ptX;
14464
                x2 = x1 - caretSize;
14465
                y1 = y2 + caretSize;
14466
                y3 = y2 - caretSize;
14467
            } else {
14468
                x1 = ptX + width;
14469
                x2 = x1 + caretSize;
14470
                y1 = y2 - caretSize;
14471
                y3 = y2 + caretSize;
14472
            }
14473
            x3 = x1;
14474
        } else {
14475
            if (xAlign === 'left') {
14476
                x2 = ptX + Math.max(topLeft, bottomLeft) + caretSize;
14477
            } else if (xAlign === 'right') {
14478
                x2 = ptX + width - Math.max(topRight, bottomRight) - caretSize;
14479
            } else {
14480
                x2 = this.caretX;
14481
            }
14482
            if (yAlign === 'top') {
14483
                y1 = ptY;
14484
                y2 = y1 - caretSize;
14485
                x1 = x2 - caretSize;
14486
                x3 = x2 + caretSize;
14487
            } else {
14488
                y1 = ptY + height;
14489
                y2 = y1 + caretSize;
14490
                x1 = x2 + caretSize;
14491
                x3 = x2 - caretSize;
14492
            }
14493
            y3 = y1;
14494
        }
14495
        return {
14496
            x1,
14497
            x2,
14498
            x3,
14499
            y1,
14500
            y2,
14501
            y3
14502
        };
14503
    }
14504
    drawTitle(pt, ctx, options) {
14505
        const title = this.title;
14506
        const length = title.length;
14507
        let titleFont, titleSpacing, i;
14508
        if (length) {
14509
            const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
14510
            pt.x = getAlignedX(this, options.titleAlign, options);
14511
            ctx.textAlign = rtlHelper.textAlign(options.titleAlign);
14512
            ctx.textBaseline = 'middle';
14513
            titleFont = toFont(options.titleFont);
14514
            titleSpacing = options.titleSpacing;
14515
            ctx.fillStyle = options.titleColor;
14516
            ctx.font = titleFont.string;
14517
            for(i = 0; i < length; ++i){
14518
                ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFont.lineHeight / 2);
14519
                pt.y += titleFont.lineHeight + titleSpacing;
14520
                if (i + 1 === length) {
14521
                    pt.y += options.titleMarginBottom - titleSpacing;
14522
                }
14523
            }
14524
        }
14525
    }
14526
 _drawColorBox(ctx, pt, i, rtlHelper, options) {
14527
        const labelColor = this.labelColors[i];
14528
        const labelPointStyle = this.labelPointStyles[i];
14529
        const { boxHeight , boxWidth  } = options;
14530
        const bodyFont = toFont(options.bodyFont);
14531
        const colorX = getAlignedX(this, 'left', options);
14532
        const rtlColorX = rtlHelper.x(colorX);
14533
        const yOffSet = boxHeight < bodyFont.lineHeight ? (bodyFont.lineHeight - boxHeight) / 2 : 0;
14534
        const colorY = pt.y + yOffSet;
14535
        if (options.usePointStyle) {
14536
            const drawOptions = {
14537
                radius: Math.min(boxWidth, boxHeight) / 2,
14538
                pointStyle: labelPointStyle.pointStyle,
14539
                rotation: labelPointStyle.rotation,
14540
                borderWidth: 1
14541
            };
14542
            const centerX = rtlHelper.leftForLtr(rtlColorX, boxWidth) + boxWidth / 2;
14543
            const centerY = colorY + boxHeight / 2;
14544
            ctx.strokeStyle = options.multiKeyBackground;
14545
            ctx.fillStyle = options.multiKeyBackground;
14546
            drawPoint(ctx, drawOptions, centerX, centerY);
14547
            ctx.strokeStyle = labelColor.borderColor;
14548
            ctx.fillStyle = labelColor.backgroundColor;
14549
            drawPoint(ctx, drawOptions, centerX, centerY);
14550
        } else {
14551
            ctx.lineWidth = isObject(labelColor.borderWidth) ? Math.max(...Object.values(labelColor.borderWidth)) : labelColor.borderWidth || 1;
14552
            ctx.strokeStyle = labelColor.borderColor;
14553
            ctx.setLineDash(labelColor.borderDash || []);
14554
            ctx.lineDashOffset = labelColor.borderDashOffset || 0;
14555
            const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth);
14556
            const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - 2);
14557
            const borderRadius = toTRBLCorners(labelColor.borderRadius);
14558
            if (Object.values(borderRadius).some((v)=>v !== 0)) {
14559
                ctx.beginPath();
14560
                ctx.fillStyle = options.multiKeyBackground;
14561
                addRoundedRectPath(ctx, {
14562
                    x: outerX,
14563
                    y: colorY,
14564
                    w: boxWidth,
14565
                    h: boxHeight,
14566
                    radius: borderRadius
14567
                });
14568
                ctx.fill();
14569
                ctx.stroke();
14570
                ctx.fillStyle = labelColor.backgroundColor;
14571
                ctx.beginPath();
14572
                addRoundedRectPath(ctx, {
14573
                    x: innerX,
14574
                    y: colorY + 1,
14575
                    w: boxWidth - 2,
14576
                    h: boxHeight - 2,
14577
                    radius: borderRadius
14578
                });
14579
                ctx.fill();
14580
            } else {
14581
                ctx.fillStyle = options.multiKeyBackground;
14582
                ctx.fillRect(outerX, colorY, boxWidth, boxHeight);
14583
                ctx.strokeRect(outerX, colorY, boxWidth, boxHeight);
14584
                ctx.fillStyle = labelColor.backgroundColor;
14585
                ctx.fillRect(innerX, colorY + 1, boxWidth - 2, boxHeight - 2);
14586
            }
14587
        }
14588
        ctx.fillStyle = this.labelTextColors[i];
14589
    }
14590
    drawBody(pt, ctx, options) {
14591
        const { body  } = this;
14592
        const { bodySpacing , bodyAlign , displayColors , boxHeight , boxWidth , boxPadding  } = options;
14593
        const bodyFont = toFont(options.bodyFont);
14594
        let bodyLineHeight = bodyFont.lineHeight;
14595
        let xLinePadding = 0;
14596
        const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
14597
        const fillLineOfText = function(line) {
14598
            ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyLineHeight / 2);
14599
            pt.y += bodyLineHeight + bodySpacing;
14600
        };
14601
        const bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign);
14602
        let bodyItem, textColor, lines, i, j, ilen, jlen;
14603
        ctx.textAlign = bodyAlign;
14604
        ctx.textBaseline = 'middle';
14605
        ctx.font = bodyFont.string;
14606
        pt.x = getAlignedX(this, bodyAlignForCalculation, options);
14607
        ctx.fillStyle = options.bodyColor;
14608
        each(this.beforeBody, fillLineOfText);
14609
        xLinePadding = displayColors && bodyAlignForCalculation !== 'right' ? bodyAlign === 'center' ? boxWidth / 2 + boxPadding : boxWidth + 2 + boxPadding : 0;
14610
        for(i = 0, ilen = body.length; i < ilen; ++i){
14611
            bodyItem = body[i];
14612
            textColor = this.labelTextColors[i];
14613
            ctx.fillStyle = textColor;
14614
            each(bodyItem.before, fillLineOfText);
14615
            lines = bodyItem.lines;
14616
            if (displayColors && lines.length) {
14617
                this._drawColorBox(ctx, pt, i, rtlHelper, options);
14618
                bodyLineHeight = Math.max(bodyFont.lineHeight, boxHeight);
14619
            }
14620
            for(j = 0, jlen = lines.length; j < jlen; ++j){
14621
                fillLineOfText(lines[j]);
14622
                bodyLineHeight = bodyFont.lineHeight;
14623
            }
14624
            each(bodyItem.after, fillLineOfText);
14625
        }
14626
        xLinePadding = 0;
14627
        bodyLineHeight = bodyFont.lineHeight;
14628
        each(this.afterBody, fillLineOfText);
14629
        pt.y -= bodySpacing;
14630
    }
14631
    drawFooter(pt, ctx, options) {
14632
        const footer = this.footer;
14633
        const length = footer.length;
14634
        let footerFont, i;
14635
        if (length) {
14636
            const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
14637
            pt.x = getAlignedX(this, options.footerAlign, options);
14638
            pt.y += options.footerMarginTop;
14639
            ctx.textAlign = rtlHelper.textAlign(options.footerAlign);
14640
            ctx.textBaseline = 'middle';
14641
            footerFont = toFont(options.footerFont);
14642
            ctx.fillStyle = options.footerColor;
14643
            ctx.font = footerFont.string;
14644
            for(i = 0; i < length; ++i){
14645
                ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFont.lineHeight / 2);
14646
                pt.y += footerFont.lineHeight + options.footerSpacing;
14647
            }
14648
        }
14649
    }
14650
    drawBackground(pt, ctx, tooltipSize, options) {
14651
        const { xAlign , yAlign  } = this;
14652
        const { x , y  } = pt;
14653
        const { width , height  } = tooltipSize;
14654
        const { topLeft , topRight , bottomLeft , bottomRight  } = toTRBLCorners(options.cornerRadius);
14655
        ctx.fillStyle = options.backgroundColor;
14656
        ctx.strokeStyle = options.borderColor;
14657
        ctx.lineWidth = options.borderWidth;
14658
        ctx.beginPath();
14659
        ctx.moveTo(x + topLeft, y);
14660
        if (yAlign === 'top') {
14661
            this.drawCaret(pt, ctx, tooltipSize, options);
14662
        }
14663
        ctx.lineTo(x + width - topRight, y);
14664
        ctx.quadraticCurveTo(x + width, y, x + width, y + topRight);
14665
        if (yAlign === 'center' && xAlign === 'right') {
14666
            this.drawCaret(pt, ctx, tooltipSize, options);
14667
        }
14668
        ctx.lineTo(x + width, y + height - bottomRight);
14669
        ctx.quadraticCurveTo(x + width, y + height, x + width - bottomRight, y + height);
14670
        if (yAlign === 'bottom') {
14671
            this.drawCaret(pt, ctx, tooltipSize, options);
14672
        }
14673
        ctx.lineTo(x + bottomLeft, y + height);
14674
        ctx.quadraticCurveTo(x, y + height, x, y + height - bottomLeft);
14675
        if (yAlign === 'center' && xAlign === 'left') {
14676
            this.drawCaret(pt, ctx, tooltipSize, options);
14677
        }
14678
        ctx.lineTo(x, y + topLeft);
14679
        ctx.quadraticCurveTo(x, y, x + topLeft, y);
14680
        ctx.closePath();
14681
        ctx.fill();
14682
        if (options.borderWidth > 0) {
14683
            ctx.stroke();
14684
        }
14685
    }
14686
 _updateAnimationTarget(options) {
14687
        const chart = this.chart;
14688
        const anims = this.$animations;
14689
        const animX = anims && anims.x;
14690
        const animY = anims && anims.y;
14691
        if (animX || animY) {
14692
            const position = positioners[options.position].call(this, this._active, this._eventPosition);
14693
            if (!position) {
14694
                return;
14695
            }
14696
            const size = this._size = getTooltipSize(this, options);
14697
            const positionAndSize = Object.assign({}, position, this._size);
14698
            const alignment = determineAlignment(chart, options, positionAndSize);
14699
            const point = getBackgroundPoint(options, positionAndSize, alignment, chart);
14700
            if (animX._to !== point.x || animY._to !== point.y) {
14701
                this.xAlign = alignment.xAlign;
14702
                this.yAlign = alignment.yAlign;
14703
                this.width = size.width;
14704
                this.height = size.height;
14705
                this.caretX = position.x;
14706
                this.caretY = position.y;
14707
                this._resolveAnimations().update(this, point);
14708
            }
14709
        }
14710
    }
14711
 _willRender() {
14712
        return !!this.opacity;
14713
    }
14714
    draw(ctx) {
14715
        const options = this.options.setContext(this.getContext());
14716
        let opacity = this.opacity;
14717
        if (!opacity) {
14718
            return;
14719
        }
14720
        this._updateAnimationTarget(options);
14721
        const tooltipSize = {
14722
            width: this.width,
14723
            height: this.height
14724
        };
14725
        const pt = {
14726
            x: this.x,
14727
            y: this.y
14728
        };
14729
        opacity = Math.abs(opacity) < 1e-3 ? 0 : opacity;
14730
        const padding = toPadding(options.padding);
14731
        const hasTooltipContent = this.title.length || this.beforeBody.length || this.body.length || this.afterBody.length || this.footer.length;
14732
        if (options.enabled && hasTooltipContent) {
14733
            ctx.save();
14734
            ctx.globalAlpha = opacity;
14735
            this.drawBackground(pt, ctx, tooltipSize, options);
14736
            overrideTextDirection(ctx, options.textDirection);
14737
            pt.y += padding.top;
14738
            this.drawTitle(pt, ctx, options);
14739
            this.drawBody(pt, ctx, options);
14740
            this.drawFooter(pt, ctx, options);
14741
            restoreTextDirection(ctx, options.textDirection);
14742
            ctx.restore();
14743
        }
14744
    }
14745
 getActiveElements() {
14746
        return this._active || [];
14747
    }
14748
 setActiveElements(activeElements, eventPosition) {
14749
        const lastActive = this._active;
14750
        const active = activeElements.map(({ datasetIndex , index  })=>{
14751
            const meta = this.chart.getDatasetMeta(datasetIndex);
14752
            if (!meta) {
14753
                throw new Error('Cannot find a dataset at index ' + datasetIndex);
14754
            }
14755
            return {
14756
                datasetIndex,
14757
                element: meta.data[index],
14758
                index
14759
            };
14760
        });
14761
        const changed = !_elementsEqual(lastActive, active);
14762
        const positionChanged = this._positionChanged(active, eventPosition);
14763
        if (changed || positionChanged) {
14764
            this._active = active;
14765
            this._eventPosition = eventPosition;
14766
            this._ignoreReplayEvents = true;
14767
            this.update(true);
14768
        }
14769
    }
14770
 handleEvent(e, replay, inChartArea = true) {
14771
        if (replay && this._ignoreReplayEvents) {
14772
            return false;
14773
        }
14774
        this._ignoreReplayEvents = false;
14775
        const options = this.options;
14776
        const lastActive = this._active || [];
14777
        const active = this._getActiveElements(e, lastActive, replay, inChartArea);
14778
        const positionChanged = this._positionChanged(active, e);
14779
        const changed = replay || !_elementsEqual(active, lastActive) || positionChanged;
14780
        if (changed) {
14781
            this._active = active;
14782
            if (options.enabled || options.external) {
14783
                this._eventPosition = {
14784
                    x: e.x,
14785
                    y: e.y
14786
                };
14787
                this.update(true, replay);
14788
            }
14789
        }
14790
        return changed;
14791
    }
14792
 _getActiveElements(e, lastActive, replay, inChartArea) {
14793
        const options = this.options;
14794
        if (e.type === 'mouseout') {
14795
            return [];
14796
        }
14797
        if (!inChartArea) {
14798
            return lastActive.filter((i)=>this.chart.data.datasets[i.datasetIndex] && this.chart.getDatasetMeta(i.datasetIndex).controller.getParsed(i.index) !== undefined);
14799
        }
14800
        const active = this.chart.getElementsAtEventForMode(e, options.mode, options, replay);
14801
        if (options.reverse) {
14802
            active.reverse();
14803
        }
14804
        return active;
14805
    }
14806
 _positionChanged(active, e) {
14807
        const { caretX , caretY , options  } = this;
14808
        const position = positioners[options.position].call(this, active, e);
14809
        return position !== false && (caretX !== position.x || caretY !== position.y);
14810
    }
14811
}
14812
var plugin_tooltip = {
14813
    id: 'tooltip',
14814
    _element: Tooltip,
14815
    positioners,
14816
    afterInit (chart, _args, options) {
14817
        if (options) {
14818
            chart.tooltip = new Tooltip({
14819
                chart,
14820
                options
14821
            });
14822
        }
14823
    },
14824
    beforeUpdate (chart, _args, options) {
14825
        if (chart.tooltip) {
14826
            chart.tooltip.initialize(options);
14827
        }
14828
    },
14829
    reset (chart, _args, options) {
14830
        if (chart.tooltip) {
14831
            chart.tooltip.initialize(options);
14832
        }
14833
    },
14834
    afterDraw (chart) {
14835
        const tooltip = chart.tooltip;
14836
        if (tooltip && tooltip._willRender()) {
14837
            const args = {
14838
                tooltip
14839
            };
14840
            if (chart.notifyPlugins('beforeTooltipDraw', {
14841
                ...args,
14842
                cancelable: true
14843
            }) === false) {
14844
                return;
14845
            }
14846
            tooltip.draw(chart.ctx);
14847
            chart.notifyPlugins('afterTooltipDraw', args);
14848
        }
14849
    },
14850
    afterEvent (chart, args) {
14851
        if (chart.tooltip) {
14852
            const useFinalPosition = args.replay;
14853
            if (chart.tooltip.handleEvent(args.event, useFinalPosition, args.inChartArea)) {
14854
                args.changed = true;
14855
            }
14856
        }
14857
    },
14858
    defaults: {
14859
        enabled: true,
14860
        external: null,
14861
        position: 'average',
14862
        backgroundColor: 'rgba(0,0,0,0.8)',
14863
        titleColor: '#fff',
14864
        titleFont: {
14865
            weight: 'bold'
14866
        },
14867
        titleSpacing: 2,
14868
        titleMarginBottom: 6,
14869
        titleAlign: 'left',
14870
        bodyColor: '#fff',
14871
        bodySpacing: 2,
14872
        bodyFont: {},
14873
        bodyAlign: 'left',
14874
        footerColor: '#fff',
14875
        footerSpacing: 2,
14876
        footerMarginTop: 6,
14877
        footerFont: {
14878
            weight: 'bold'
14879
        },
14880
        footerAlign: 'left',
14881
        padding: 6,
14882
        caretPadding: 2,
14883
        caretSize: 5,
14884
        cornerRadius: 6,
14885
        boxHeight: (ctx, opts)=>opts.bodyFont.size,
14886
        boxWidth: (ctx, opts)=>opts.bodyFont.size,
14887
        multiKeyBackground: '#fff',
14888
        displayColors: true,
14889
        boxPadding: 0,
14890
        borderColor: 'rgba(0,0,0,0)',
14891
        borderWidth: 0,
14892
        animation: {
14893
            duration: 400,
14894
            easing: 'easeOutQuart'
14895
        },
14896
        animations: {
14897
            numbers: {
14898
                type: 'number',
14899
                properties: [
14900
                    'x',
14901
                    'y',
14902
                    'width',
14903
                    'height',
14904
                    'caretX',
14905
                    'caretY'
14906
                ]
14907
            },
14908
            opacity: {
14909
                easing: 'linear',
14910
                duration: 200
14911
            }
14912
        },
14913
        callbacks: defaultCallbacks
14914
    },
14915
    defaultRoutes: {
14916
        bodyFont: 'font',
14917
        footerFont: 'font',
14918
        titleFont: 'font'
14919
    },
14920
    descriptors: {
14921
        _scriptable: (name)=>name !== 'filter' && name !== 'itemSort' && name !== 'external',
14922
        _indexable: false,
14923
        callbacks: {
14924
            _scriptable: false,
14925
            _indexable: false
14926
        },
14927
        animation: {
14928
            _fallback: false
14929
        },
14930
        animations: {
14931
            _fallback: 'animation'
14932
        }
14933
    },
14934
    additionalOptionScopes: [
14935
        'interaction'
14936
    ]
14937
};
14938
 
14939
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
14940
// Register built-ins
14941
Chart.register(controllers, scales, elements, plugins);
14942
Chart.helpers = {
14943
    ...helpers
14944
};
14945
Chart._adapters = _adapters;
14946
Chart.Animation = Animation;
14947
Chart.Animations = Animations;
14948
Chart.animator = animator;
14949
Chart.controllers = registry.controllers.items;
14950
Chart.DatasetController = DatasetController;
14951
Chart.Element = Element;
14952
Chart.elements = elements;
14953
Chart.Interaction = Interaction;
14954
Chart.layouts = layouts;
14955
Chart.platforms = platforms;
14956
Chart.Scale = Scale;
14957
Chart.Ticks = Ticks;
14958
// Compatibility with ESM extensions
14959
Object.assign(Chart, controllers, scales, elements, plugins, platforms);
14960
Chart.Chart = Chart;
14961
if (typeof window !== 'undefined') {
14962
    window.Chart = Chart;
14963
}
14964
 
14965
return Chart;
14966
 
14967
}));