MDL-80634 libraries: upgrade to version 4.4.2 of ChartJS.
[moodle.git] / lib / amd / src / chartjs-lazy.js
blob68760fb3298f4f30c790aed6c62b1daeac01f7ed
1 /*!
2  * Chart.js v4.4.2
3  * https://www.chartjs.org
4  * (c) 2023 Chart.js Contributors
5  * Released under the MIT License
6  */
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  */
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';
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 });
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';
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;
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]';
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);
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;
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;
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;
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     }
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     }
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;
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;
195 function isValidKey(key) {
196     return [
197         '__proto__',
198         'prototype',
199         'constructor'
200     ].indexOf(key) === -1;
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     }
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;
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     });
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     }
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     }
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
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;
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     };
307 function resolveObjectKey(obj, key) {
308     const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));
309     return resolver(obj);
312  * @private
313  */ function _capitalize(str) {
314     return str.charAt(0).toUpperCase() + str.slice(1);
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;
331  * @param e - The event
332  * @private
333  */ function _isClickEvent(e) {
334     return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu';
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;
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;
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;
382 function isNumber(n) {
383     return !isNaN(parseFloat(n)) && isFinite(n);
385 function almostWhole(x, epsilon) {
386     const rounded = Math.round(x);
387     return rounded - epsilon <= x && rounded + epsilon >= x;
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     }
401 function toRadians(degrees) {
402     return degrees * (PI / 180);
404 function toDegrees(radians) {
405     return radians * (180 / PI);
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;
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     };
439 function distanceBetweenPoints(pt1, pt2) {
440     return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
443  * Shortest distance between angles, in either direction.
444  * @private
445  */ function _angleDiff(a, b) {
446     return (a - b + PITAU) % TAU - PI;
449  * Normalize angle to be between 0 and 2*PI
450  * @private
451  */ function _normalizeAngle(a) {
452     return (a % TAU + TAU) % TAU;
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;
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));
476  * @param {number} value
477  * @private
478  */ function _int16Range(value) {
479     return _limitValue(value, -32768, 32767);
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;
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     };
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);
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);
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;
544 const arrayEvents = [
545     'push',
546     'pop',
547     'shift',
548     'splice',
549     'unshift'
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     });
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;
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);
611 function fontString(pixelSize, fontStyle, fontFamily) {
612     return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
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 }();
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     };
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     };
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';
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;
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;
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     };
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;
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     }
857 var animator = /* #__PURE__ */ new Animator();
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;
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);
872 function n2b(v) {
873   return lim(round(v * 255), 0, 255);
875 function b2n(v) {
876   return lim(round(v / 2.55) / 100, 0, 1);
878 function n2p(v) {
879   return lim(round(v * 100), 0, 100);
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;
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;
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)];
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)];
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;
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;
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];
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);
973 function hsl2rgb(h, s, l) {
974   return calln(hsl2rgbn, h, s, l);
976 function hwb2rgb(h, w, b) {
977   return calln(hwb2rgbn, h, w, b);
979 function hsv2rgb(h, s, v) {
980   return calln(hsv2rgbn, h, s, v);
982 function hue(h) {
983   return (h % 360 + 360) % 360;
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   };
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];
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}%)`;
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'
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'
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;
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   };
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   };
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   );
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   };
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   }
1296 function clone(v, proto) {
1297   return v ? Object.assign(proto || {}, v) : v;
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;
1314 function functionParse(str) {
1315   if (str.charAt(0) === 'r') {
1316     return rgbParse(str);
1317   }
1318   return hueParse(str);
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   }
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;
1440 function color(value) {
1441     return isPatternOrGradient(value) ? value : new Color(value);
1443 function getHoverColor(value) {
1444     return isPatternOrGradient(value) ? value : new Color(value).saturate(0.5).darken(0.1).hexString();
1447 const numbers = [
1448     'x',
1449     'y',
1450     'borderWidth',
1451     'radius',
1452     'tension'
1454 const colors = [
1455     'color',
1456     'borderColor',
1457     'backgroundColor'
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     });
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     });
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;
1548 function formatNumber(num, locale, options) {
1549     return getNumberFormat(locale, options).format(num);
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     }
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;
1605  var Ticks = {
1606     formatters
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     });
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;
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);
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     }
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
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';
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;
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;
1839 const getComputedStyle = (element)=>element.ownerDocument.defaultView.getComputedStyle(element, null);
1840 function getStyle(el, property) {
1841     return getComputedStyle(el).getPropertyValue(property);
1843 const positions = [
1844     'top',
1845     'right',
1846     'bottom',
1847     'left'
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;
1860 const useOffsetPos = (x, y, target)=>(x > 0 || y > 0) && (!target || !target.shadowRoot);
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     };
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     };
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     };
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     };
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;
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 }();
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;
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;
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;
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;
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;
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();
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);
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     }
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;
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();
2277 function unclipArea(ctx) {
2278     ctx.restore();
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);
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);
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     }
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     }
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;
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();
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);
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     });
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     });
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     };
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;
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;
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;
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;
2614 function resolveFallback(fallback, prop, value) {
2615     return isFunction(fallback) ? fallback(prop, value) : fallback;
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;
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));
2660 function addScopesFromKey(set, allScopes, key, fallback, item) {
2661     while(key){
2662         key = addScopes(set, allScopes, key, fallback, item);
2663     }
2664     return key;
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 || {};
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     }
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     }
2698 function getKeysFromAllScopes(target) {
2699     let keys = target._keys;
2700     if (!keys) {
2701         keys = target._keys = resolveKeysFromAllScopes(target._scopes);
2702     }
2703     return keys;
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);
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;
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     };
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     }
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     }
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);
2842 function capControlPoint(pt, min, max) {
2843     return Math.max(Math.min(pt, max), min);
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     }
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     }
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;
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
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     };
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     };
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);
2998 const LINE_HEIGHT = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/;
2999 const FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;
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;
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;
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     });
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     ]);
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;
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;
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     }
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     };
3155 function createContext(parentContext, context) {
3156     return Object.assign(Object.create(parentContext), context);
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     };
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     };
3198 function getRtlAdapter(rtl, rectX, width) {
3199     return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter();
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     }
3213 function restoreTextDirection(ctx, original) {
3214     if (original !== undefined) {
3215         delete ctx.prevTextDirection;
3216         ctx.canvas.style.setProperty('direction', original[0], original[1]);
3217     }
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     };
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     };
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     };
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;
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;
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     };
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;
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);
3418  function splitByStyles(line, segments, points, segmentOptions) {
3419     if (!segmentOptions || !segmentOptions.setContext || !points) {
3420         return segments;
3421     }
3422     return doSplitByStyles(line, segments, points, segmentOptions);
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;
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     };
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);
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
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     };
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     }
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     };
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;
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;
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;
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);
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;
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     }
3852 const STATIC_POSITIONS = [
3853     'left',
3854     'top',
3855     'right',
3856     'bottom'
3858 function filterByPosition(array, position) {
3859     return array.filter((v)=>v.pos === position);
3861 function filterDynamicPositionByAxis(array, axis) {
3862     return array.filter((v)=>STATIC_POSITIONS.indexOf(v.pos) === -1 && v.box.axis === axis);
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     });
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;
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;
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;
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     };
3944 function getCombinedMax(maxPadding, chartArea, a, b) {
3945     return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);
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);
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     };
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');
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     ]);
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;
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;
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;
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     }
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     }
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     }
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'
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;
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     }
4274 function removeListener(chart, type, listener) {
4275     if (chart && chart.canvas) {
4276         chart.canvas.removeEventListener(type, listener, eventListenerOptions);
4277     }
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     };
4290 function nodeListContains(nodeList, canvas) {
4291     for (const node of nodeList){
4292         if (node === canvas || node.contains(canvas)) {
4293             return true;
4294         }
4295     }
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;
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;
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     });
4347 function listenDevicePixelRatioChanges(chart, resize) {
4348     if (!drpListeningCharts.size) {
4349         window.addEventListener('resize', onWindowResize);
4350     }
4351     drpListeningCharts.set(chart, resize);
4353 function unlistenDevicePixelRatioChanges(chart) {
4354     drpListeningCharts.delete(chart);
4355     if (!drpListeningCharts.size) {
4356         window.removeEventListener('resize', onWindowResize);
4357     }
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;
4385 function releaseObserver(chart, type, observer) {
4386     if (observer) {
4387         observer.disconnect();
4388     }
4389     if (type === 'resize') {
4390         unlistenDevicePixelRatioChanges(chart);
4391     }
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;
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     }
4475 function _detectPlatform(canvas) {
4476     if (!_isDomSupported() || typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
4477         return BasicPlatform;
4478     }
4479     return DomPlatform;
4482 var platforms = /*#__PURE__*/Object.freeze({
4483 __proto__: null,
4484 BasePlatform: BasePlatform,
4485 BasicPlatform: BasicPlatform,
4486 DomPlatform: DomPlatform,
4487 _detectPlatform: _detectPlatform
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     }
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     }
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     }
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);
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;
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     };
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     };
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     };
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;
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;
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;
4811 function isStacked(scale, meta) {
4812     const stacked = scale && scale.options.stacked;
4813     return stacked || stacked === undefined && meta.stack !== undefined;
4815 function getStackKey(indexScale, valueScale, meta) {
4816     return `${indexScale.id}.${valueScale.id}.${meta.stack || meta.type}`;
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     };
4825 function getOrCreateStack(stacks, stackKey, indexValue) {
4826     const subStack = stacks[stackKey] || (stacks[stackKey] = {});
4827     return subStack[indexValue] || (subStack[indexValue] = {});
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;
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     }
4859 function getFirstScaleId(chart, axis) {
4860     const scales = chart.scales;
4861     return Object.keys(scales).filter((key)=>scales[key].axis === axis).shift();
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     });
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     });
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     }
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     }
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     }
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;
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));
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);
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;
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     }
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     }
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;
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;
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;
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     });
5695  function getTickMarkLength(options) {
5696     return options.drawTicks ? options.tickLength : 0;
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;
5707 function createScaleContext(parent, scale) {
5708     return createContext(parent, {
5709         scale,
5710         type: 'scale'
5711     });
5713 function createTickContext(parent, index, tick) {
5714     return createContext(parent, {
5715         tick,
5716         index,
5717         type: 'tick'
5718     });
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;
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     };
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     }
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     }
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     }
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     });
6995 function isIChartComponent(proto) {
6996     return 'id' in proto && 'defaults' in proto;
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     }
7091 var registry = /* #__PURE__ */ new Registry();
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     }
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     };
7174 function getOpts(options, all) {
7175     if (!all && options === false) {
7176         return null;
7177     }
7178     if (options === true) {
7179         return {};
7180     }
7181     return options;
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;
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     });
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';
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;
7231 function getDefaultScaleIDFromAxis(axis, indexAxis) {
7232     return axis === indexAxis ? '_index_' : '_value_';
7234 function idMatchesAxis(id) {
7235     if (id === 'x' || id === 'y' || id === 'r') {
7236         return id;
7237     }
7239 function axisFromPosition(position) {
7240     if (position === 'top' || position === 'bottom') {
7241         return 'x';
7242     }
7243     if (position === 'left' || position === 'right') {
7244         return 'y';
7245     }
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.`);
7259 function getAxisFromDataset(id, axis, dataset) {
7260     if (dataset[axis + 'AxisID'] === id) {
7261         return {
7262             axis
7263         };
7264     }
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 {};
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;
7329 function initOptions(config) {
7330     const options = config.options || (config.options = {});
7331     options.plugins = valueOrDefault(options.plugins, {});
7332     options.scales = mergeScaleConfig(config, options);
7334 function initData(data) {
7335     data = data || {};
7336     data.datasets = data.datasets || [];
7337     data.labels = data.labels || [];
7338     return data;
7340 function initConfig(config) {
7341     config = config || {};
7342     config.data = initData(config.data);
7343     initOptions(config);
7344     return config;
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;
7357 const addIfFound = (set, obj, key)=>{
7358     const opts = resolveObjectKey(obj, key);
7359     if (opts !== undefined) {
7360         set.add(opts);
7361     }
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     }
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;
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;
7549 var version = "4.4.2";
7551 const KNOWN_POSITIONS = [
7552     'top',
7553     'bottom',
7554     'left',
7555     'right',
7556     'chartArea'
7558 function positionIsHorizontal(position, axis) {
7559     return position === 'top' || position === 'bottom' || KNOWN_POSITIONS.indexOf(position) === -1 && axis === 'x';
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     };
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);
7574 function onAnimationProgress(context) {
7575     const chart = context.chart;
7576     const animationOptions = chart.options.animation;
7577     callback(animationOptions && animationOptions.onProgress, [
7578         context
7579     ], chart);
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;
7592 const instances = {};
7593 const getChart = (key)=>{
7594     const canvas = getCanvas(key);
7595     return Object.values(instances).filter((c)=>c.canvas === canvas).pop();
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     }
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;
7619 function getSizeForArea(scale, chartArea, field) {
7620     return scale.options.clip ? scale[field] : chartArea[field];
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;
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     }
8461 function invalidatePlugins() {
8462     return each(Chart.instances, (chart)=>chart._plugins.invalidate());
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.');
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     }
8518 var _adapters = {
8519     _date: DateAdapterBase
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;
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;
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     };
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     };
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     };
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;
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;
8638 function isFloatBar(custom) {
8639     return custom && custom.barStart !== undefined && custom.barEnd !== undefined;
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);
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     };
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;
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;
8713 function swap(orig, v1, v2) {
8714     return orig === v1 ? v2 : orig === v2 ? v1 : orig;
8716 function startEnd(v, start, end) {
8717     return v === 'start' ? start : v === 'end' ? end : v;
8719 function setInflateAmount(properties, { inflateAmount  }, ratio) {
8720     properties.inflateAmount = inflateAmount === 'auto' ? ratio === 1 ? 0.33 : 0 : inflateAmount;
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     }
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     }
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     };
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     }
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     }
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     }
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     };
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     }
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     }
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
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();
9941 function toRadiusCorners(value) {
9942     return _readValueToProps(value, [
9943         'outerStart',
9944         'outerEnd',
9945         'innerStart',
9946         'innerEnd'
9947     ]);
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     };
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     };
9982  * Path the arc, respecting border radius by separating into left and right halves.
9984  *   Start      End
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();
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;
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     }
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     }
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);
10236 function lineTo(ctx, previous, target) {
10237     ctx.lineTo(target.x, target.y);
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;
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     };
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;
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();
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;
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;
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);
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     }
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     }
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     }
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;
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     }
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     };
10630 function skipOrLimit(skip, value, min, max) {
10631     return skip ? 0 : _limitValue(value, min, max);
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     };
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     };
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     };
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));
10695 function hasRadius(radius) {
10696     return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;
10698  function addNormalRectPath(ctx, rect) {
10699     ctx.rect(rect.x, rect.y, rect.w, rect.h);
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     };
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     }
10784 var elements = /*#__PURE__*/Object.freeze({
10785 __proto__: null,
10786 ArcElement: ArcElement,
10787 BarElement: BarElement,
10788 LineElement: LineElement,
10789 PointElement: PointElement
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;
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;
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;
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     }
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;
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);
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     }
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     }
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;
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;
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));
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;
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     }
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;
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     };
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     };
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);
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     }
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     };
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;
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;
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';
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;
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;
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     }
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     }
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     }
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();
11580 function createPointLabelContext(parent, index, label) {
11581     return createContext(parent, {
11582         label,
11583         index,
11584         type: 'pointLabel'
11585     });
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() {}
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     }
11872  const UNITS =  /* #__PURE__ */ Object.keys(INTERVALS);
11873  function sorter(a, b) {
11874     return a - b;
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;
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];
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];
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     }
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     }
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;
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);
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     }
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;
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     }
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
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
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];
12382 function getBackgroundColor(i) {
12383     return BACKGROUND_COLORS[i % BACKGROUND_COLORS.length];
12385 function colorizeDefaultDataset(dataset, i) {
12386     dataset.borderColor = getBorderColor(i);
12387     dataset.backgroundColor = getBackgroundColor(i);
12388     return ++i;
12390 function colorizeDoughnutDataset(dataset, i) {
12391     dataset.backgroundColor = dataset.data.map(()=>getBorderColor(i++));
12392     return i;
12394 function colorizePolarAreaDataset(dataset, i) {
12395     dataset.backgroundColor = dataset.data.map(()=>getBackgroundColor(i++));
12396     return i;
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     };
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;
12420 function containsColorsDefinition(descriptor) {
12421     return descriptor && (descriptor.borderColor || descriptor.backgroundColor);
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     }
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;
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;
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     }
12552 function cleanDecimatedData(chart) {
12553     chart.data.datasets.forEach((dataset)=>{
12554         cleanDecimatedDataset(dataset);
12555     });
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     };
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     }
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;
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     };
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;
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;
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;
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;
12765 function _shouldApplyFill(source) {
12766     return source && source.fill !== false;
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;
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;
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;
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;
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;
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;
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     });
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;
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);
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     };
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     }
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);
12987  function getLineByIndex(chart, index) {
12988     const meta = chart.getDatasetMeta(index);
12989     const visible = meta && chart.isDatasetVisible(index);
12990     return visible ? meta.dataset : null;
12992 function computeBoundary(source) {
12993     const scale = source.scale || {};
12994     if (scale.getPointPositionForValue) {
12995         return computeCircularBoundary(source);
12996     }
12997     return computeLinearBoundary(source);
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;
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;
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     }
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();
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();
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     }
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     }
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     }
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     }
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     };
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             0
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     }
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     };
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;
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;
13666 function calculateLegendItemHeight(legendItem, fontLineHeight) {
13667     const labelHeight = legendItem.text ? legendItem.text.length : 0;
13668     return fontLineHeight * labelHeight;
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;
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     }
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     }
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;
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     }
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     }
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     }
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;
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;
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     };
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     };
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';
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     }
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;
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     };
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;
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;
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     };
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;
14187  function getBeforeAfterBodyLines(callback) {
14188     return pushOrConcat([], splitNewlines(callback));
14190 function createTooltipContext(parent, tooltip, tooltipItems) {
14191     return createContext(parent, {
14192         tooltip,
14193         tooltipItems,
14194         type: 'tooltip'
14195     });
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;
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
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;
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     }
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     ]
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
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;
14965 return Chart;
14967 }));