You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2736 lines
89 KiB

1 year ago
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
  1. /*!
  2. * Chart.js v4.2.1
  3. * https://www.chartjs.org
  4. * (c) 2023 Chart.js Contributors
  5. * Released under the MIT License
  6. */
  7. 'use strict';
  8. var color$1 = require('@kurkle/color');
  9. /**
  10. * @namespace Chart.helpers
  11. */ /**
  12. * An empty function that can be used, for example, for optional callback.
  13. */ function noop() {
  14. /* noop */ }
  15. /**
  16. * Returns a unique id, sequentially generated from a global variable.
  17. */ const uid = (()=>{
  18. let id = 0;
  19. return ()=>id++;
  20. })();
  21. /**
  22. * Returns true if `value` is neither null nor undefined, else returns false.
  23. * @param value - The value to test.
  24. * @since 2.7.0
  25. */ function isNullOrUndef(value) {
  26. return value === null || typeof value === 'undefined';
  27. }
  28. /**
  29. * Returns true if `value` is an array (including typed arrays), else returns false.
  30. * @param value - The value to test.
  31. * @function
  32. */ function isArray(value) {
  33. if (Array.isArray && Array.isArray(value)) {
  34. return true;
  35. }
  36. const type = Object.prototype.toString.call(value);
  37. if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') {
  38. return true;
  39. }
  40. return false;
  41. }
  42. /**
  43. * Returns true if `value` is an object (excluding null), else returns false.
  44. * @param value - The value to test.
  45. * @since 2.7.0
  46. */ function isObject(value) {
  47. return value !== null && Object.prototype.toString.call(value) === '[object Object]';
  48. }
  49. /**
  50. * Returns true if `value` is a finite number, else returns false
  51. * @param value - The value to test.
  52. */ function isNumberFinite(value) {
  53. return (typeof value === 'number' || value instanceof Number) && isFinite(+value);
  54. }
  55. /**
  56. * Returns `value` if finite, else returns `defaultValue`.
  57. * @param value - The value to return if defined.
  58. * @param defaultValue - The value to return if `value` is not finite.
  59. */ function finiteOrDefault(value, defaultValue) {
  60. return isNumberFinite(value) ? value : defaultValue;
  61. }
  62. /**
  63. * Returns `value` if defined, else returns `defaultValue`.
  64. * @param value - The value to return if defined.
  65. * @param defaultValue - The value to return if `value` is undefined.
  66. */ function valueOrDefault(value, defaultValue) {
  67. return typeof value === 'undefined' ? defaultValue : value;
  68. }
  69. const toPercentage = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 : +value / dimension;
  70. const toDimension = (value, dimension)=>typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 * dimension : +value;
  71. /**
  72. * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
  73. * value returned by `fn`. If `fn` is not a function, this method returns undefined.
  74. * @param fn - The function to call.
  75. * @param args - The arguments with which `fn` should be called.
  76. * @param [thisArg] - The value of `this` provided for the call to `fn`.
  77. */ function callback(fn, args, thisArg) {
  78. if (fn && typeof fn.call === 'function') {
  79. return fn.apply(thisArg, args);
  80. }
  81. }
  82. function each(loopable, fn, thisArg, reverse) {
  83. let i, len, keys;
  84. if (isArray(loopable)) {
  85. len = loopable.length;
  86. if (reverse) {
  87. for(i = len - 1; i >= 0; i--){
  88. fn.call(thisArg, loopable[i], i);
  89. }
  90. } else {
  91. for(i = 0; i < len; i++){
  92. fn.call(thisArg, loopable[i], i);
  93. }
  94. }
  95. } else if (isObject(loopable)) {
  96. keys = Object.keys(loopable);
  97. len = keys.length;
  98. for(i = 0; i < len; i++){
  99. fn.call(thisArg, loopable[keys[i]], keys[i]);
  100. }
  101. }
  102. }
  103. /**
  104. * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
  105. * @param a0 - The array to compare
  106. * @param a1 - The array to compare
  107. * @private
  108. */ function _elementsEqual(a0, a1) {
  109. let i, ilen, v0, v1;
  110. if (!a0 || !a1 || a0.length !== a1.length) {
  111. return false;
  112. }
  113. for(i = 0, ilen = a0.length; i < ilen; ++i){
  114. v0 = a0[i];
  115. v1 = a1[i];
  116. if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {
  117. return false;
  118. }
  119. }
  120. return true;
  121. }
  122. /**
  123. * Returns a deep copy of `source` without keeping references on objects and arrays.
  124. * @param source - The value to clone.
  125. */ function clone(source) {
  126. if (isArray(source)) {
  127. return source.map(clone);
  128. }
  129. if (isObject(source)) {
  130. const target = Object.create(null);
  131. const keys = Object.keys(source);
  132. const klen = keys.length;
  133. let k = 0;
  134. for(; k < klen; ++k){
  135. target[keys[k]] = clone(source[keys[k]]);
  136. }
  137. return target;
  138. }
  139. return source;
  140. }
  141. function isValidKey(key) {
  142. return [
  143. '__proto__',
  144. 'prototype',
  145. 'constructor'
  146. ].indexOf(key) === -1;
  147. }
  148. /**
  149. * The default merger when Chart.helpers.merge is called without merger option.
  150. * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.
  151. * @private
  152. */ function _merger(key, target, source, options) {
  153. if (!isValidKey(key)) {
  154. return;
  155. }
  156. const tval = target[key];
  157. const sval = source[key];
  158. if (isObject(tval) && isObject(sval)) {
  159. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  160. merge(tval, sval, options);
  161. } else {
  162. target[key] = clone(sval);
  163. }
  164. }
  165. function merge(target, source, options) {
  166. const sources = isArray(source) ? source : [
  167. source
  168. ];
  169. const ilen = sources.length;
  170. if (!isObject(target)) {
  171. return target;
  172. }
  173. options = options || {};
  174. const merger = options.merger || _merger;
  175. let current;
  176. for(let i = 0; i < ilen; ++i){
  177. current = sources[i];
  178. if (!isObject(current)) {
  179. continue;
  180. }
  181. const keys = Object.keys(current);
  182. for(let k = 0, klen = keys.length; k < klen; ++k){
  183. merger(keys[k], target, current, options);
  184. }
  185. }
  186. return target;
  187. }
  188. function mergeIf(target, source) {
  189. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  190. return merge(target, source, {
  191. merger: _mergerIf
  192. });
  193. }
  194. /**
  195. * Merges source[key] in target[key] only if target[key] is undefined.
  196. * @private
  197. */ function _mergerIf(key, target, source) {
  198. if (!isValidKey(key)) {
  199. return;
  200. }
  201. const tval = target[key];
  202. const sval = source[key];
  203. if (isObject(tval) && isObject(sval)) {
  204. mergeIf(tval, sval);
  205. } else if (!Object.prototype.hasOwnProperty.call(target, key)) {
  206. target[key] = clone(sval);
  207. }
  208. }
  209. /**
  210. * @private
  211. */ function _deprecated(scope, value, previous, current) {
  212. if (value !== undefined) {
  213. console.warn(scope + ': "' + previous + '" is deprecated. Please use "' + current + '" instead');
  214. }
  215. }
  216. // resolveObjectKey resolver cache
  217. const keyResolvers = {
  218. // Chart.helpers.core resolveObjectKey should resolve empty key to root object
  219. '': (v)=>v,
  220. // default resolvers
  221. x: (o)=>o.x,
  222. y: (o)=>o.y
  223. };
  224. /**
  225. * @private
  226. */ function _splitKey(key) {
  227. const parts = key.split('.');
  228. const keys = [];
  229. let tmp = '';
  230. for (const part of parts){
  231. tmp += part;
  232. if (tmp.endsWith('\\')) {
  233. tmp = tmp.slice(0, -1) + '.';
  234. } else {
  235. keys.push(tmp);
  236. tmp = '';
  237. }
  238. }
  239. return keys;
  240. }
  241. function _getKeyResolver(key) {
  242. const keys = _splitKey(key);
  243. return (obj)=>{
  244. for (const k of keys){
  245. if (k === '') {
  246. break;
  247. }
  248. obj = obj && obj[k];
  249. }
  250. return obj;
  251. };
  252. }
  253. function resolveObjectKey(obj, key) {
  254. const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));
  255. return resolver(obj);
  256. }
  257. /**
  258. * @private
  259. */ function _capitalize(str) {
  260. return str.charAt(0).toUpperCase() + str.slice(1);
  261. }
  262. const defined = (value)=>typeof value !== 'undefined';
  263. const isFunction = (value)=>typeof value === 'function';
  264. // Adapted from https://stackoverflow.com/questions/31128855/comparing-ecma6-sets-for-equality#31129384
  265. const setsEqual = (a, b)=>{
  266. if (a.size !== b.size) {
  267. return false;
  268. }
  269. for (const item of a){
  270. if (!b.has(item)) {
  271. return false;
  272. }
  273. }
  274. return true;
  275. };
  276. /**
  277. * @param e - The event
  278. * @private
  279. */ function _isClickEvent(e) {
  280. return e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu';
  281. }
  282. /**
  283. * @alias Chart.helpers.math
  284. * @namespace
  285. */ const PI = Math.PI;
  286. const TAU = 2 * PI;
  287. const PITAU = TAU + PI;
  288. const INFINITY = Number.POSITIVE_INFINITY;
  289. const RAD_PER_DEG = PI / 180;
  290. const HALF_PI = PI / 2;
  291. const QUARTER_PI = PI / 4;
  292. const TWO_THIRDS_PI = PI * 2 / 3;
  293. const log10 = Math.log10;
  294. const sign = Math.sign;
  295. function almostEquals(x, y, epsilon) {
  296. return Math.abs(x - y) < epsilon;
  297. }
  298. /**
  299. * Implementation of the nice number algorithm used in determining where axis labels will go
  300. */ function niceNum(range) {
  301. const roundedRange = Math.round(range);
  302. range = almostEquals(range, roundedRange, range / 1000) ? roundedRange : range;
  303. const niceRange = Math.pow(10, Math.floor(log10(range)));
  304. const fraction = range / niceRange;
  305. const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;
  306. return niceFraction * niceRange;
  307. }
  308. /**
  309. * Returns an array of factors sorted from 1 to sqrt(value)
  310. * @private
  311. */ function _factorize(value) {
  312. const result = [];
  313. const sqrt = Math.sqrt(value);
  314. let i;
  315. for(i = 1; i < sqrt; i++){
  316. if (value % i === 0) {
  317. result.push(i);
  318. result.push(value / i);
  319. }
  320. }
  321. if (sqrt === (sqrt | 0)) {
  322. result.push(sqrt);
  323. }
  324. result.sort((a, b)=>a - b).pop();
  325. return result;
  326. }
  327. function isNumber(n) {
  328. return !isNaN(parseFloat(n)) && isFinite(n);
  329. }
  330. function almostWhole(x, epsilon) {
  331. const rounded = Math.round(x);
  332. return rounded - epsilon <= x && rounded + epsilon >= x;
  333. }
  334. /**
  335. * @private
  336. */ function _setMinAndMaxByKey(array, target, property) {
  337. let i, ilen, value;
  338. for(i = 0, ilen = array.length; i < ilen; i++){
  339. value = array[i][property];
  340. if (!isNaN(value)) {
  341. target.min = Math.min(target.min, value);
  342. target.max = Math.max(target.max, value);
  343. }
  344. }
  345. }
  346. function toRadians(degrees) {
  347. return degrees * (PI / 180);
  348. }
  349. function toDegrees(radians) {
  350. return radians * (180 / PI);
  351. }
  352. /**
  353. * Returns the number of decimal places
  354. * i.e. the number of digits after the decimal point, of the value of this Number.
  355. * @param x - A number.
  356. * @returns The number of decimal places.
  357. * @private
  358. */ function _decimalPlaces(x) {
  359. if (!isNumberFinite(x)) {
  360. return;
  361. }
  362. let e = 1;
  363. let p = 0;
  364. while(Math.round(x * e) / e !== x){
  365. e *= 10;
  366. p++;
  367. }
  368. return p;
  369. }
  370. // Gets the angle from vertical upright to the point about a centre.
  371. function getAngleFromPoint(centrePoint, anglePoint) {
  372. const distanceFromXCenter = anglePoint.x - centrePoint.x;
  373. const distanceFromYCenter = anglePoint.y - centrePoint.y;
  374. const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
  375. let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
  376. if (angle < -0.5 * PI) {
  377. angle += TAU; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
  378. }
  379. return {
  380. angle,
  381. distance: radialDistanceFromCenter
  382. };
  383. }
  384. function distanceBetweenPoints(pt1, pt2) {
  385. return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
  386. }
  387. /**
  388. * Shortest distance between angles, in either direction.
  389. * @private
  390. */ function _angleDiff(a, b) {
  391. return (a - b + PITAU) % TAU - PI;
  392. }
  393. /**
  394. * Normalize angle to be between 0 and 2*PI
  395. * @private
  396. */ function _normalizeAngle(a) {
  397. return (a % TAU + TAU) % TAU;
  398. }
  399. /**
  400. * @private
  401. */ function _angleBetween(angle, start, end, sameAngleIsFullCircle) {
  402. const a = _normalizeAngle(angle);
  403. const s = _normalizeAngle(start);
  404. const e = _normalizeAngle(end);
  405. const angleToStart = _normalizeAngle(s - a);
  406. const angleToEnd = _normalizeAngle(e - a);
  407. const startToAngle = _normalizeAngle(a - s);
  408. const endToAngle = _normalizeAngle(a - e);
  409. return a === s || a === e || sameAngleIsFullCircle && s === e || angleToStart > angleToEnd && startToAngle < endToAngle;
  410. }
  411. /**
  412. * Limit `value` between `min` and `max`
  413. * @param value
  414. * @param min
  415. * @param max
  416. * @private
  417. */ function _limitValue(value, min, max) {
  418. return Math.max(min, Math.min(max, value));
  419. }
  420. /**
  421. * @param {number} value
  422. * @private
  423. */ function _int16Range(value) {
  424. return _limitValue(value, -32768, 32767);
  425. }
  426. /**
  427. * @param value
  428. * @param start
  429. * @param end
  430. * @param [epsilon]
  431. * @private
  432. */ function _isBetween(value, start, end, epsilon = 1e-6) {
  433. return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;
  434. }
  435. function _lookup(table, value, cmp) {
  436. cmp = cmp || ((index)=>table[index] < value);
  437. let hi = table.length - 1;
  438. let lo = 0;
  439. let mid;
  440. while(hi - lo > 1){
  441. mid = lo + hi >> 1;
  442. if (cmp(mid)) {
  443. lo = mid;
  444. } else {
  445. hi = mid;
  446. }
  447. }
  448. return {
  449. lo,
  450. hi
  451. };
  452. }
  453. /**
  454. * Binary search
  455. * @param table - the table search. must be sorted!
  456. * @param key - property name for the value in each entry
  457. * @param value - value to find
  458. * @param last - lookup last index
  459. * @private
  460. */ const _lookupByKey = (table, key, value, last)=>_lookup(table, value, last ? (index)=>{
  461. const ti = table[index][key];
  462. return ti < value || ti === value && table[index + 1][key] === value;
  463. } : (index)=>table[index][key] < value);
  464. /**
  465. * Reverse binary search
  466. * @param table - the table search. must be sorted!
  467. * @param key - property name for the value in each entry
  468. * @param value - value to find
  469. * @private
  470. */ const _rlookupByKey = (table, key, value)=>_lookup(table, value, (index)=>table[index][key] >= value);
  471. /**
  472. * Return subset of `values` between `min` and `max` inclusive.
  473. * Values are assumed to be in sorted order.
  474. * @param values - sorted array of values
  475. * @param min - min value
  476. * @param max - max value
  477. */ function _filterBetween(values, min, max) {
  478. let start = 0;
  479. let end = values.length;
  480. while(start < end && values[start] < min){
  481. start++;
  482. }
  483. while(end > start && values[end - 1] > max){
  484. end--;
  485. }
  486. return start > 0 || end < values.length ? values.slice(start, end) : values;
  487. }
  488. const arrayEvents = [
  489. 'push',
  490. 'pop',
  491. 'shift',
  492. 'splice',
  493. 'unshift'
  494. ];
  495. function listenArrayEvents(array, listener) {
  496. if (array._chartjs) {
  497. array._chartjs.listeners.push(listener);
  498. return;
  499. }
  500. Object.defineProperty(array, '_chartjs', {
  501. configurable: true,
  502. enumerable: false,
  503. value: {
  504. listeners: [
  505. listener
  506. ]
  507. }
  508. });
  509. arrayEvents.forEach((key)=>{
  510. const method = '_onData' + _capitalize(key);
  511. const base = array[key];
  512. Object.defineProperty(array, key, {
  513. configurable: true,
  514. enumerable: false,
  515. value (...args) {
  516. const res = base.apply(this, args);
  517. array._chartjs.listeners.forEach((object)=>{
  518. if (typeof object[method] === 'function') {
  519. object[method](...args);
  520. }
  521. });
  522. return res;
  523. }
  524. });
  525. });
  526. }
  527. function unlistenArrayEvents(array, listener) {
  528. const stub = array._chartjs;
  529. if (!stub) {
  530. return;
  531. }
  532. const listeners = stub.listeners;
  533. const index = listeners.indexOf(listener);
  534. if (index !== -1) {
  535. listeners.splice(index, 1);
  536. }
  537. if (listeners.length > 0) {
  538. return;
  539. }
  540. arrayEvents.forEach((key)=>{
  541. delete array[key];
  542. });
  543. delete array._chartjs;
  544. }
  545. /**
  546. * @param items
  547. */ function _arrayUnique(items) {
  548. const set = new Set();
  549. let i, ilen;
  550. for(i = 0, ilen = items.length; i < ilen; ++i){
  551. set.add(items[i]);
  552. }
  553. if (set.size === ilen) {
  554. return items;
  555. }
  556. return Array.from(set);
  557. }
  558. function fontString(pixelSize, fontStyle, fontFamily) {
  559. return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
  560. }
  561. /**
  562. * Request animation polyfill
  563. */ const requestAnimFrame = function() {
  564. if (typeof window === 'undefined') {
  565. return function(callback) {
  566. return callback();
  567. };
  568. }
  569. return window.requestAnimationFrame;
  570. }();
  571. /**
  572. * Throttles calling `fn` once per animation frame
  573. * Latest arguments are used on the actual call
  574. */ function throttled(fn, thisArg) {
  575. let argsToUse = [];
  576. let ticking = false;
  577. return function(...args) {
  578. // Save the args for use later
  579. argsToUse = args;
  580. if (!ticking) {
  581. ticking = true;
  582. requestAnimFrame.call(window, ()=>{
  583. ticking = false;
  584. fn.apply(thisArg, argsToUse);
  585. });
  586. }
  587. };
  588. }
  589. /**
  590. * Debounces calling `fn` for `delay` ms
  591. */ function debounce(fn, delay) {
  592. let timeout;
  593. return function(...args) {
  594. if (delay) {
  595. clearTimeout(timeout);
  596. timeout = setTimeout(fn, delay, args);
  597. } else {
  598. fn.apply(this, args);
  599. }
  600. return delay;
  601. };
  602. }
  603. /**
  604. * Converts 'start' to 'left', 'end' to 'right' and others to 'center'
  605. * @private
  606. */ const _toLeftRightCenter = (align)=>align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';
  607. /**
  608. * Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center`
  609. * @private
  610. */ const _alignStartEnd = (align, start, end)=>align === 'start' ? start : align === 'end' ? end : (start + end) / 2;
  611. /**
  612. * Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left`
  613. * @private
  614. */ const _textX = (align, left, right, rtl)=>{
  615. const check = rtl ? 'left' : 'right';
  616. return align === check ? right : align === 'center' ? (left + right) / 2 : left;
  617. };
  618. /**
  619. * Return start and count of visible points.
  620. * @private
  621. */ function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {
  622. const pointCount = points.length;
  623. let start = 0;
  624. let count = pointCount;
  625. if (meta._sorted) {
  626. const { iScale , _parsed } = meta;
  627. const axis = iScale.axis;
  628. const { min , max , minDefined , maxDefined } = iScale.getUserBounds();
  629. if (minDefined) {
  630. start = _limitValue(Math.min(// @ts-expect-error Need to type _parsed
  631. _lookupByKey(_parsed, iScale.axis, min).lo, // @ts-expect-error Need to fix types on _lookupByKey
  632. animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo), 0, pointCount - 1);
  633. }
  634. if (maxDefined) {
  635. count = _limitValue(Math.max(// @ts-expect-error Need to type _parsed
  636. _lookupByKey(_parsed, iScale.axis, max, true).hi + 1, // @ts-expect-error Need to fix types on _lookupByKey
  637. animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1), start, pointCount) - start;
  638. } else {
  639. count = pointCount - start;
  640. }
  641. }
  642. return {
  643. start,
  644. count
  645. };
  646. }
  647. /**
  648. * Checks if the scale ranges have changed.
  649. * @param {object} meta - dataset meta.
  650. * @returns {boolean}
  651. * @private
  652. */ function _scaleRangesChanged(meta) {
  653. const { xScale , yScale , _scaleRanges } = meta;
  654. const newRanges = {
  655. xmin: xScale.min,
  656. xmax: xScale.max,
  657. ymin: yScale.min,
  658. ymax: yScale.max
  659. };
  660. if (!_scaleRanges) {
  661. meta._scaleRanges = newRanges;
  662. return true;
  663. }
  664. const changed = _scaleRanges.xmin !== xScale.min || _scaleRanges.xmax !== xScale.max || _scaleRanges.ymin !== yScale.min || _scaleRanges.ymax !== yScale.max;
  665. Object.assign(_scaleRanges, newRanges);
  666. return changed;
  667. }
  668. const atEdge = (t)=>t === 0 || t === 1;
  669. const elasticIn = (t, s, p)=>-(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));
  670. const elasticOut = (t, s, p)=>Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;
  671. /**
  672. * Easing functions adapted from Robert Penner's easing equations.
  673. * @namespace Chart.helpers.easing.effects
  674. * @see http://www.robertpenner.com/easing/
  675. */ const effects = {
  676. linear: (t)=>t,
  677. easeInQuad: (t)=>t * t,
  678. easeOutQuad: (t)=>-t * (t - 2),
  679. easeInOutQuad: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1),
  680. easeInCubic: (t)=>t * t * t,
  681. easeOutCubic: (t)=>(t -= 1) * t * t + 1,
  682. easeInOutCubic: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2),
  683. easeInQuart: (t)=>t * t * t * t,
  684. easeOutQuart: (t)=>-((t -= 1) * t * t * t - 1),
  685. easeInOutQuart: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t : -0.5 * ((t -= 2) * t * t * t - 2),
  686. easeInQuint: (t)=>t * t * t * t * t,
  687. easeOutQuint: (t)=>(t -= 1) * t * t * t * t + 1,
  688. easeInOutQuint: (t)=>(t /= 0.5) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2),
  689. easeInSine: (t)=>-Math.cos(t * HALF_PI) + 1,
  690. easeOutSine: (t)=>Math.sin(t * HALF_PI),
  691. easeInOutSine: (t)=>-0.5 * (Math.cos(PI * t) - 1),
  692. easeInExpo: (t)=>t === 0 ? 0 : Math.pow(2, 10 * (t - 1)),
  693. easeOutExpo: (t)=>t === 1 ? 1 : -Math.pow(2, -10 * t) + 1,
  694. 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),
  695. easeInCirc: (t)=>t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1),
  696. easeOutCirc: (t)=>Math.sqrt(1 - (t -= 1) * t),
  697. easeInOutCirc: (t)=>(t /= 0.5) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1),
  698. easeInElastic: (t)=>atEdge(t) ? t : elasticIn(t, 0.075, 0.3),
  699. easeOutElastic: (t)=>atEdge(t) ? t : elasticOut(t, 0.075, 0.3),
  700. easeInOutElastic (t) {
  701. const s = 0.1125;
  702. const p = 0.45;
  703. return atEdge(t) ? t : t < 0.5 ? 0.5 * elasticIn(t * 2, s, p) : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p);
  704. },
  705. easeInBack (t) {
  706. const s = 1.70158;
  707. return t * t * ((s + 1) * t - s);
  708. },
  709. easeOutBack (t) {
  710. const s = 1.70158;
  711. return (t -= 1) * t * ((s + 1) * t + s) + 1;
  712. },
  713. easeInOutBack (t) {
  714. let s = 1.70158;
  715. if ((t /= 0.5) < 1) {
  716. return 0.5 * (t * t * (((s *= 1.525) + 1) * t - s));
  717. }
  718. return 0.5 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2);
  719. },
  720. easeInBounce: (t)=>1 - effects.easeOutBounce(1 - t),
  721. easeOutBounce (t) {
  722. const m = 7.5625;
  723. const d = 2.75;
  724. if (t < 1 / d) {
  725. return m * t * t;
  726. }
  727. if (t < 2 / d) {
  728. return m * (t -= 1.5 / d) * t + 0.75;
  729. }
  730. if (t < 2.5 / d) {
  731. return m * (t -= 2.25 / d) * t + 0.9375;
  732. }
  733. return m * (t -= 2.625 / d) * t + 0.984375;
  734. },
  735. easeInOutBounce: (t)=>t < 0.5 ? effects.easeInBounce(t * 2) * 0.5 : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5
  736. };
  737. function isPatternOrGradient(value) {
  738. if (value && typeof value === 'object') {
  739. const type = value.toString();
  740. return type === '[object CanvasPattern]' || type === '[object CanvasGradient]';
  741. }
  742. return false;
  743. }
  744. function color(value) {
  745. return isPatternOrGradient(value) ? value : new color$1.Color(value);
  746. }
  747. function getHoverColor(value) {
  748. return isPatternOrGradient(value) ? value : new color$1.Color(value).saturate(0.5).darken(0.1).hexString();
  749. }
  750. const numbers = [
  751. 'x',
  752. 'y',
  753. 'borderWidth',
  754. 'radius',
  755. 'tension'
  756. ];
  757. const colors = [
  758. 'color',
  759. 'borderColor',
  760. 'backgroundColor'
  761. ];
  762. function applyAnimationsDefaults(defaults) {
  763. defaults.set('animation', {
  764. delay: undefined,
  765. duration: 1000,
  766. easing: 'easeOutQuart',
  767. fn: undefined,
  768. from: undefined,
  769. loop: undefined,
  770. to: undefined,
  771. type: undefined
  772. });
  773. defaults.describe('animation', {
  774. _fallback: false,
  775. _indexable: false,
  776. _scriptable: (name)=>name !== 'onProgress' && name !== 'onComplete' && name !== 'fn'
  777. });
  778. defaults.set('animations', {
  779. colors: {
  780. type: 'color',
  781. properties: colors
  782. },
  783. numbers: {
  784. type: 'number',
  785. properties: numbers
  786. }
  787. });
  788. defaults.describe('animations', {
  789. _fallback: 'animation'
  790. });
  791. defaults.set('transitions', {
  792. active: {
  793. animation: {
  794. duration: 400
  795. }
  796. },
  797. resize: {
  798. animation: {
  799. duration: 0
  800. }
  801. },
  802. show: {
  803. animations: {
  804. colors: {
  805. from: 'transparent'
  806. },
  807. visible: {
  808. type: 'boolean',
  809. duration: 0
  810. }
  811. }
  812. },
  813. hide: {
  814. animations: {
  815. colors: {
  816. to: 'transparent'
  817. },
  818. visible: {
  819. type: 'boolean',
  820. easing: 'linear',
  821. fn: (v)=>v | 0
  822. }
  823. }
  824. }
  825. });
  826. }
  827. function applyLayoutsDefaults(defaults) {
  828. defaults.set('layout', {
  829. autoPadding: true,
  830. padding: {
  831. top: 0,
  832. right: 0,
  833. bottom: 0,
  834. left: 0
  835. }
  836. });
  837. }
  838. const intlCache = new Map();
  839. function getNumberFormat(locale, options) {
  840. options = options || {};
  841. const cacheKey = locale + JSON.stringify(options);
  842. let formatter = intlCache.get(cacheKey);
  843. if (!formatter) {
  844. formatter = new Intl.NumberFormat(locale, options);
  845. intlCache.set(cacheKey, formatter);
  846. }
  847. return formatter;
  848. }
  849. function formatNumber(num, locale, options) {
  850. return getNumberFormat(locale, options).format(num);
  851. }
  852. const formatters = {
  853. values (value) {
  854. return isArray(value) ? value : '' + value;
  855. },
  856. numeric (tickValue, index, ticks) {
  857. if (tickValue === 0) {
  858. return '0';
  859. }
  860. const locale = this.chart.options.locale;
  861. let notation;
  862. let delta = tickValue;
  863. if (ticks.length > 1) {
  864. const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));
  865. if (maxTick < 1e-4 || maxTick > 1e+15) {
  866. notation = 'scientific';
  867. }
  868. delta = calculateDelta(tickValue, ticks);
  869. }
  870. const logDelta = log10(Math.abs(delta));
  871. const numDecimal = Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0);
  872. const options = {
  873. notation,
  874. minimumFractionDigits: numDecimal,
  875. maximumFractionDigits: numDecimal
  876. };
  877. Object.assign(options, this.options.ticks.format);
  878. return formatNumber(tickValue, locale, options);
  879. },
  880. logarithmic (tickValue, index, ticks) {
  881. if (tickValue === 0) {
  882. return '0';
  883. }
  884. const remain = ticks[index].significand || tickValue / Math.pow(10, Math.floor(log10(tickValue)));
  885. if ([
  886. 1,
  887. 2,
  888. 3,
  889. 5,
  890. 10,
  891. 15
  892. ].includes(remain) || index > 0.8 * ticks.length) {
  893. return formatters.numeric.call(this, tickValue, index, ticks);
  894. }
  895. return '';
  896. }
  897. };
  898. function calculateDelta(tickValue, ticks) {
  899. let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;
  900. if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) {
  901. delta = tickValue - Math.floor(tickValue);
  902. }
  903. return delta;
  904. }
  905. var Ticks = {
  906. formatters
  907. };
  908. function applyScaleDefaults(defaults) {
  909. defaults.set('scale', {
  910. display: true,
  911. offset: false,
  912. reverse: false,
  913. beginAtZero: false,
  914. bounds: 'ticks',
  915. grace: 0,
  916. grid: {
  917. display: true,
  918. lineWidth: 1,
  919. drawOnChartArea: true,
  920. drawTicks: true,
  921. tickLength: 8,
  922. tickWidth: (_ctx, options)=>options.lineWidth,
  923. tickColor: (_ctx, options)=>options.color,
  924. offset: false
  925. },
  926. border: {
  927. display: true,
  928. dash: [],
  929. dashOffset: 0.0,
  930. width: 1
  931. },
  932. title: {
  933. display: false,
  934. text: '',
  935. padding: {
  936. top: 4,
  937. bottom: 4
  938. }
  939. },
  940. ticks: {
  941. minRotation: 0,
  942. maxRotation: 50,
  943. mirror: false,
  944. textStrokeWidth: 0,
  945. textStrokeColor: '',
  946. padding: 3,
  947. display: true,
  948. autoSkip: true,
  949. autoSkipPadding: 3,
  950. labelOffset: 0,
  951. callback: Ticks.formatters.values,
  952. minor: {},
  953. major: {},
  954. align: 'center',
  955. crossAlign: 'near',
  956. showLabelBackdrop: false,
  957. backdropColor: 'rgba(255, 255, 255, 0.75)',
  958. backdropPadding: 2
  959. }
  960. });
  961. defaults.route('scale.ticks', 'color', '', 'color');
  962. defaults.route('scale.grid', 'color', '', 'borderColor');
  963. defaults.route('scale.border', 'color', '', 'borderColor');
  964. defaults.route('scale.title', 'color', '', 'color');
  965. defaults.describe('scale', {
  966. _fallback: false,
  967. _scriptable: (name)=>!name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser',
  968. _indexable: (name)=>name !== 'borderDash' && name !== 'tickBorderDash' && name !== 'dash'
  969. });
  970. defaults.describe('scales', {
  971. _fallback: 'scale'
  972. });
  973. defaults.describe('scale.ticks', {
  974. _scriptable: (name)=>name !== 'backdropPadding' && name !== 'callback',
  975. _indexable: (name)=>name !== 'backdropPadding'
  976. });
  977. }
  978. const overrides = Object.create(null);
  979. const descriptors = Object.create(null);
  980. function getScope$1(node, key) {
  981. if (!key) {
  982. return node;
  983. }
  984. const keys = key.split('.');
  985. for(let i = 0, n = keys.length; i < n; ++i){
  986. const k = keys[i];
  987. node = node[k] || (node[k] = Object.create(null));
  988. }
  989. return node;
  990. }
  991. function set(root, scope, values) {
  992. if (typeof scope === 'string') {
  993. return merge(getScope$1(root, scope), values);
  994. }
  995. return merge(getScope$1(root, ''), scope);
  996. }
  997. class Defaults {
  998. constructor(_descriptors, _appliers){
  999. this.animation = undefined;
  1000. this.backgroundColor = 'rgba(0,0,0,0.1)';
  1001. this.borderColor = 'rgba(0,0,0,0.1)';
  1002. this.color = '#666';
  1003. this.datasets = {};
  1004. this.devicePixelRatio = (context)=>context.chart.platform.getDevicePixelRatio();
  1005. this.elements = {};
  1006. this.events = [
  1007. 'mousemove',
  1008. 'mouseout',
  1009. 'click',
  1010. 'touchstart',
  1011. 'touchmove'
  1012. ];
  1013. this.font = {
  1014. family: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
  1015. size: 12,
  1016. style: 'normal',
  1017. lineHeight: 1.2,
  1018. weight: null
  1019. };
  1020. this.hover = {};
  1021. this.hoverBackgroundColor = (ctx, options)=>getHoverColor(options.backgroundColor);
  1022. this.hoverBorderColor = (ctx, options)=>getHoverColor(options.borderColor);
  1023. this.hoverColor = (ctx, options)=>getHoverColor(options.color);
  1024. this.indexAxis = 'x';
  1025. this.interaction = {
  1026. mode: 'nearest',
  1027. intersect: true,
  1028. includeInvisible: false
  1029. };
  1030. this.maintainAspectRatio = true;
  1031. this.onHover = null;
  1032. this.onClick = null;
  1033. this.parsing = true;
  1034. this.plugins = {};
  1035. this.responsive = true;
  1036. this.scale = undefined;
  1037. this.scales = {};
  1038. this.showLine = true;
  1039. this.drawActiveElementsOnTop = true;
  1040. this.describe(_descriptors);
  1041. this.apply(_appliers);
  1042. }
  1043. set(scope, values) {
  1044. return set(this, scope, values);
  1045. }
  1046. get(scope) {
  1047. return getScope$1(this, scope);
  1048. }
  1049. describe(scope, values) {
  1050. return set(descriptors, scope, values);
  1051. }
  1052. override(scope, values) {
  1053. return set(overrides, scope, values);
  1054. }
  1055. route(scope, name, targetScope, targetName) {
  1056. const scopeObject = getScope$1(this, scope);
  1057. const targetScopeObject = getScope$1(this, targetScope);
  1058. const privateName = '_' + name;
  1059. Object.defineProperties(scopeObject, {
  1060. [privateName]: {
  1061. value: scopeObject[name],
  1062. writable: true
  1063. },
  1064. [name]: {
  1065. enumerable: true,
  1066. get () {
  1067. const local = this[privateName];
  1068. const target = targetScopeObject[targetName];
  1069. if (isObject(local)) {
  1070. return Object.assign({}, target, local);
  1071. }
  1072. return valueOrDefault(local, target);
  1073. },
  1074. set (value) {
  1075. this[privateName] = value;
  1076. }
  1077. }
  1078. });
  1079. }
  1080. apply(appliers) {
  1081. appliers.forEach((apply)=>apply(this));
  1082. }
  1083. }
  1084. var defaults = /* #__PURE__ */ new Defaults({
  1085. _scriptable: (name)=>!name.startsWith('on'),
  1086. _indexable: (name)=>name !== 'events',
  1087. hover: {
  1088. _fallback: 'interaction'
  1089. },
  1090. interaction: {
  1091. _scriptable: false,
  1092. _indexable: false
  1093. }
  1094. }, [
  1095. applyAnimationsDefaults,
  1096. applyLayoutsDefaults,
  1097. applyScaleDefaults
  1098. ]);
  1099. function toFontString(font) {
  1100. if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {
  1101. return null;
  1102. }
  1103. return (font.style ? font.style + ' ' : '') + (font.weight ? font.weight + ' ' : '') + font.size + 'px ' + font.family;
  1104. }
  1105. function _measureText(ctx, data, gc, longest, string) {
  1106. let textWidth = data[string];
  1107. if (!textWidth) {
  1108. textWidth = data[string] = ctx.measureText(string).width;
  1109. gc.push(string);
  1110. }
  1111. if (textWidth > longest) {
  1112. longest = textWidth;
  1113. }
  1114. return longest;
  1115. }
  1116. function _longestText(ctx, font, arrayOfThings, cache) {
  1117. cache = cache || {};
  1118. let data = cache.data = cache.data || {};
  1119. let gc = cache.garbageCollect = cache.garbageCollect || [];
  1120. if (cache.font !== font) {
  1121. data = cache.data = {};
  1122. gc = cache.garbageCollect = [];
  1123. cache.font = font;
  1124. }
  1125. ctx.save();
  1126. ctx.font = font;
  1127. let longest = 0;
  1128. const ilen = arrayOfThings.length;
  1129. let i, j, jlen, thing, nestedThing;
  1130. for(i = 0; i < ilen; i++){
  1131. thing = arrayOfThings[i];
  1132. if (thing !== undefined && thing !== null && isArray(thing) !== true) {
  1133. longest = _measureText(ctx, data, gc, longest, thing);
  1134. } else if (isArray(thing)) {
  1135. for(j = 0, jlen = thing.length; j < jlen; j++){
  1136. nestedThing = thing[j];
  1137. if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) {
  1138. longest = _measureText(ctx, data, gc, longest, nestedThing);
  1139. }
  1140. }
  1141. }
  1142. }
  1143. ctx.restore();
  1144. const gcLen = gc.length / 2;
  1145. if (gcLen > arrayOfThings.length) {
  1146. for(i = 0; i < gcLen; i++){
  1147. delete data[gc[i]];
  1148. }
  1149. gc.splice(0, gcLen);
  1150. }
  1151. return longest;
  1152. }
  1153. function _alignPixel(chart, pixel, width) {
  1154. const devicePixelRatio = chart.currentDevicePixelRatio;
  1155. const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;
  1156. return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
  1157. }
  1158. function clearCanvas(canvas, ctx) {
  1159. ctx = ctx || canvas.getContext('2d');
  1160. ctx.save();
  1161. ctx.resetTransform();
  1162. ctx.clearRect(0, 0, canvas.width, canvas.height);
  1163. ctx.restore();
  1164. }
  1165. function drawPoint(ctx, options, x, y) {
  1166. drawPointLegend(ctx, options, x, y, null);
  1167. }
  1168. function drawPointLegend(ctx, options, x, y, w) {
  1169. let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW;
  1170. const style = options.pointStyle;
  1171. const rotation = options.rotation;
  1172. const radius = options.radius;
  1173. let rad = (rotation || 0) * RAD_PER_DEG;
  1174. if (style && typeof style === 'object') {
  1175. type = style.toString();
  1176. if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
  1177. ctx.save();
  1178. ctx.translate(x, y);
  1179. ctx.rotate(rad);
  1180. ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);
  1181. ctx.restore();
  1182. return;
  1183. }
  1184. }
  1185. if (isNaN(radius) || radius <= 0) {
  1186. return;
  1187. }
  1188. ctx.beginPath();
  1189. switch(style){
  1190. default:
  1191. if (w) {
  1192. ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);
  1193. } else {
  1194. ctx.arc(x, y, radius, 0, TAU);
  1195. }
  1196. ctx.closePath();
  1197. break;
  1198. case 'triangle':
  1199. width = w ? w / 2 : radius;
  1200. ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
  1201. rad += TWO_THIRDS_PI;
  1202. ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
  1203. rad += TWO_THIRDS_PI;
  1204. ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
  1205. ctx.closePath();
  1206. break;
  1207. case 'rectRounded':
  1208. cornerRadius = radius * 0.516;
  1209. size = radius - cornerRadius;
  1210. xOffset = Math.cos(rad + QUARTER_PI) * size;
  1211. xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
  1212. yOffset = Math.sin(rad + QUARTER_PI) * size;
  1213. yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
  1214. ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
  1215. ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);
  1216. ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);
  1217. ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
  1218. ctx.closePath();
  1219. break;
  1220. case 'rect':
  1221. if (!rotation) {
  1222. size = Math.SQRT1_2 * radius;
  1223. width = w ? w / 2 : size;
  1224. ctx.rect(x - width, y - size, 2 * width, 2 * size);
  1225. break;
  1226. }
  1227. rad += QUARTER_PI;
  1228. case 'rectRot':
  1229. xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
  1230. xOffset = Math.cos(rad) * radius;
  1231. yOffset = Math.sin(rad) * radius;
  1232. yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
  1233. ctx.moveTo(x - xOffsetW, y - yOffset);
  1234. ctx.lineTo(x + yOffsetW, y - xOffset);
  1235. ctx.lineTo(x + xOffsetW, y + yOffset);
  1236. ctx.lineTo(x - yOffsetW, y + xOffset);
  1237. ctx.closePath();
  1238. break;
  1239. case 'crossRot':
  1240. rad += QUARTER_PI;
  1241. case 'cross':
  1242. xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
  1243. xOffset = Math.cos(rad) * radius;
  1244. yOffset = Math.sin(rad) * radius;
  1245. yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
  1246. ctx.moveTo(x - xOffsetW, y - yOffset);
  1247. ctx.lineTo(x + xOffsetW, y + yOffset);
  1248. ctx.moveTo(x + yOffsetW, y - xOffset);
  1249. ctx.lineTo(x - yOffsetW, y + xOffset);
  1250. break;
  1251. case 'star':
  1252. xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
  1253. xOffset = Math.cos(rad) * radius;
  1254. yOffset = Math.sin(rad) * radius;
  1255. yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
  1256. ctx.moveTo(x - xOffsetW, y - yOffset);
  1257. ctx.lineTo(x + xOffsetW, y + yOffset);
  1258. ctx.moveTo(x + yOffsetW, y - xOffset);
  1259. ctx.lineTo(x - yOffsetW, y + xOffset);
  1260. rad += QUARTER_PI;
  1261. xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
  1262. xOffset = Math.cos(rad) * radius;
  1263. yOffset = Math.sin(rad) * radius;
  1264. yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
  1265. ctx.moveTo(x - xOffsetW, y - yOffset);
  1266. ctx.lineTo(x + xOffsetW, y + yOffset);
  1267. ctx.moveTo(x + yOffsetW, y - xOffset);
  1268. ctx.lineTo(x - yOffsetW, y + xOffset);
  1269. break;
  1270. case 'line':
  1271. xOffset = w ? w / 2 : Math.cos(rad) * radius;
  1272. yOffset = Math.sin(rad) * radius;
  1273. ctx.moveTo(x - xOffset, y - yOffset);
  1274. ctx.lineTo(x + xOffset, y + yOffset);
  1275. break;
  1276. case 'dash':
  1277. ctx.moveTo(x, y);
  1278. ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);
  1279. break;
  1280. case false:
  1281. ctx.closePath();
  1282. break;
  1283. }
  1284. ctx.fill();
  1285. if (options.borderWidth > 0) {
  1286. ctx.stroke();
  1287. }
  1288. }
  1289. function _isPointInArea(point, area, margin) {
  1290. margin = margin || 0.5;
  1291. return !area || point && point.x > area.left - margin && point.x < area.right + margin && point.y > area.top - margin && point.y < area.bottom + margin;
  1292. }
  1293. function clipArea(ctx, area) {
  1294. ctx.save();
  1295. ctx.beginPath();
  1296. ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
  1297. ctx.clip();
  1298. }
  1299. function unclipArea(ctx) {
  1300. ctx.restore();
  1301. }
  1302. function _steppedLineTo(ctx, previous, target, flip, mode) {
  1303. if (!previous) {
  1304. return ctx.lineTo(target.x, target.y);
  1305. }
  1306. if (mode === 'middle') {
  1307. const midpoint = (previous.x + target.x) / 2.0;
  1308. ctx.lineTo(midpoint, previous.y);
  1309. ctx.lineTo(midpoint, target.y);
  1310. } else if (mode === 'after' !== !!flip) {
  1311. ctx.lineTo(previous.x, target.y);
  1312. } else {
  1313. ctx.lineTo(target.x, previous.y);
  1314. }
  1315. ctx.lineTo(target.x, target.y);
  1316. }
  1317. function _bezierCurveTo(ctx, previous, target, flip) {
  1318. if (!previous) {
  1319. return ctx.lineTo(target.x, target.y);
  1320. }
  1321. 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);
  1322. }
  1323. function renderText(ctx, text, x, y, font, opts = {}) {
  1324. const lines = isArray(text) ? text : [
  1325. text
  1326. ];
  1327. const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';
  1328. let i, line;
  1329. ctx.save();
  1330. ctx.font = font.string;
  1331. setRenderOpts(ctx, opts);
  1332. for(i = 0; i < lines.length; ++i){
  1333. line = lines[i];
  1334. if (opts.backdrop) {
  1335. drawBackdrop(ctx, opts.backdrop);
  1336. }
  1337. if (stroke) {
  1338. if (opts.strokeColor) {
  1339. ctx.strokeStyle = opts.strokeColor;
  1340. }
  1341. if (!isNullOrUndef(opts.strokeWidth)) {
  1342. ctx.lineWidth = opts.strokeWidth;
  1343. }
  1344. ctx.strokeText(line, x, y, opts.maxWidth);
  1345. }
  1346. ctx.fillText(line, x, y, opts.maxWidth);
  1347. decorateText(ctx, x, y, line, opts);
  1348. y += font.lineHeight;
  1349. }
  1350. ctx.restore();
  1351. }
  1352. function setRenderOpts(ctx, opts) {
  1353. if (opts.translation) {
  1354. ctx.translate(opts.translation[0], opts.translation[1]);
  1355. }
  1356. if (!isNullOrUndef(opts.rotation)) {
  1357. ctx.rotate(opts.rotation);
  1358. }
  1359. if (opts.color) {
  1360. ctx.fillStyle = opts.color;
  1361. }
  1362. if (opts.textAlign) {
  1363. ctx.textAlign = opts.textAlign;
  1364. }
  1365. if (opts.textBaseline) {
  1366. ctx.textBaseline = opts.textBaseline;
  1367. }
  1368. }
  1369. function decorateText(ctx, x, y, line, opts) {
  1370. if (opts.strikethrough || opts.underline) {
  1371. const metrics = ctx.measureText(line);
  1372. const left = x - metrics.actualBoundingBoxLeft;
  1373. const right = x + metrics.actualBoundingBoxRight;
  1374. const top = y - metrics.actualBoundingBoxAscent;
  1375. const bottom = y + metrics.actualBoundingBoxDescent;
  1376. const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;
  1377. ctx.strokeStyle = ctx.fillStyle;
  1378. ctx.beginPath();
  1379. ctx.lineWidth = opts.decorationWidth || 2;
  1380. ctx.moveTo(left, yDecoration);
  1381. ctx.lineTo(right, yDecoration);
  1382. ctx.stroke();
  1383. }
  1384. }
  1385. function drawBackdrop(ctx, opts) {
  1386. const oldColor = ctx.fillStyle;
  1387. ctx.fillStyle = opts.color;
  1388. ctx.fillRect(opts.left, opts.top, opts.width, opts.height);
  1389. ctx.fillStyle = oldColor;
  1390. }
  1391. function addRoundedRectPath(ctx, rect) {
  1392. const { x , y , w , h , radius } = rect;
  1393. ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, -HALF_PI, PI, true);
  1394. ctx.lineTo(x, y + h - radius.bottomLeft);
  1395. ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true);
  1396. ctx.lineTo(x + w - radius.bottomRight, y + h);
  1397. ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true);
  1398. ctx.lineTo(x + w, y + radius.topRight);
  1399. ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true);
  1400. ctx.lineTo(x + radius.topLeft, y);
  1401. }
  1402. const LINE_HEIGHT = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/;
  1403. const FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;
  1404. /**
  1405. * @alias Chart.helpers.options
  1406. * @namespace
  1407. */ /**
  1408. * Converts the given line height `value` in pixels for a specific font `size`.
  1409. * @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
  1410. * @param size - The font size (in pixels) used to resolve relative `value`.
  1411. * @returns The effective line height in pixels (size * 1.2 if value is invalid).
  1412. * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
  1413. * @since 2.7.0
  1414. */ function toLineHeight(value, size) {
  1415. const matches = ('' + value).match(LINE_HEIGHT);
  1416. if (!matches || matches[1] === 'normal') {
  1417. return size * 1.2;
  1418. }
  1419. value = +matches[2];
  1420. switch(matches[3]){
  1421. case 'px':
  1422. return value;
  1423. case '%':
  1424. value /= 100;
  1425. break;
  1426. }
  1427. return size * value;
  1428. }
  1429. const numberOrZero = (v)=>+v || 0;
  1430. function _readValueToProps(value, props) {
  1431. const ret = {};
  1432. const objProps = isObject(props);
  1433. const keys = objProps ? Object.keys(props) : props;
  1434. const read = isObject(value) ? objProps ? (prop)=>valueOrDefault(value[prop], value[props[prop]]) : (prop)=>value[prop] : ()=>value;
  1435. for (const prop of keys){
  1436. ret[prop] = numberOrZero(read(prop));
  1437. }
  1438. return ret;
  1439. }
  1440. /**
  1441. * Converts the given value into a TRBL object.
  1442. * @param value - If a number, set the value to all TRBL component,
  1443. * else, if an object, use defined properties and sets undefined ones to 0.
  1444. * x / y are shorthands for same value for left/right and top/bottom.
  1445. * @returns The padding values (top, right, bottom, left)
  1446. * @since 3.0.0
  1447. */ function toTRBL(value) {
  1448. return _readValueToProps(value, {
  1449. top: 'y',
  1450. right: 'x',
  1451. bottom: 'y',
  1452. left: 'x'
  1453. });
  1454. }
  1455. /**
  1456. * Converts the given value into a TRBL corners object (similar with css border-radius).
  1457. * @param value - If a number, set the value to all TRBL corner components,
  1458. * else, if an object, use defined properties and sets undefined ones to 0.
  1459. * @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight)
  1460. * @since 3.0.0
  1461. */ function toTRBLCorners(value) {
  1462. return _readValueToProps(value, [
  1463. 'topLeft',
  1464. 'topRight',
  1465. 'bottomLeft',
  1466. 'bottomRight'
  1467. ]);
  1468. }
  1469. /**
  1470. * Converts the given value into a padding object with pre-computed width/height.
  1471. * @param value - If a number, set the value to all TRBL component,
  1472. * else, if an object, use defined properties and sets undefined ones to 0.
  1473. * x / y are shorthands for same value for left/right and top/bottom.
  1474. * @returns The padding values (top, right, bottom, left, width, height)
  1475. * @since 2.7.0
  1476. */ function toPadding(value) {
  1477. const obj = toTRBL(value);
  1478. obj.width = obj.left + obj.right;
  1479. obj.height = obj.top + obj.bottom;
  1480. return obj;
  1481. }
  1482. /**
  1483. * Parses font options and returns the font object.
  1484. * @param options - A object that contains font options to be parsed.
  1485. * @param fallback - A object that contains fallback font options.
  1486. * @return The font object.
  1487. * @private
  1488. */ function toFont(options, fallback) {
  1489. options = options || {};
  1490. fallback = fallback || defaults.font;
  1491. let size = valueOrDefault(options.size, fallback.size);
  1492. if (typeof size === 'string') {
  1493. size = parseInt(size, 10);
  1494. }
  1495. let style = valueOrDefault(options.style, fallback.style);
  1496. if (style && !('' + style).match(FONT_STYLE)) {
  1497. console.warn('Invalid font style specified: "' + style + '"');
  1498. style = undefined;
  1499. }
  1500. const font = {
  1501. family: valueOrDefault(options.family, fallback.family),
  1502. lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size),
  1503. size,
  1504. style,
  1505. weight: valueOrDefault(options.weight, fallback.weight),
  1506. string: ''
  1507. };
  1508. font.string = toFontString(font);
  1509. return font;
  1510. }
  1511. /**
  1512. * Evaluates the given `inputs` sequentially and returns the first defined value.
  1513. * @param inputs - An array of values, falling back to the last value.
  1514. * @param context - If defined and the current value is a function, the value
  1515. * is called with `context` as first argument and the result becomes the new input.
  1516. * @param index - If defined and the current value is an array, the value
  1517. * at `index` become the new input.
  1518. * @param info - object to return information about resolution in
  1519. * @param info.cacheable - Will be set to `false` if option is not cacheable.
  1520. * @since 2.7.0
  1521. */ function resolve(inputs, context, index, info) {
  1522. let cacheable = true;
  1523. let i, ilen, value;
  1524. for(i = 0, ilen = inputs.length; i < ilen; ++i){
  1525. value = inputs[i];
  1526. if (value === undefined) {
  1527. continue;
  1528. }
  1529. if (context !== undefined && typeof value === 'function') {
  1530. value = value(context);
  1531. cacheable = false;
  1532. }
  1533. if (index !== undefined && isArray(value)) {
  1534. value = value[index % value.length];
  1535. cacheable = false;
  1536. }
  1537. if (value !== undefined) {
  1538. if (info && !cacheable) {
  1539. info.cacheable = false;
  1540. }
  1541. return value;
  1542. }
  1543. }
  1544. }
  1545. /**
  1546. * @param minmax
  1547. * @param grace
  1548. * @param beginAtZero
  1549. * @private
  1550. */ function _addGrace(minmax, grace, beginAtZero) {
  1551. const { min , max } = minmax;
  1552. const change = toDimension(grace, (max - min) / 2);
  1553. const keepZero = (value, add)=>beginAtZero && value === 0 ? 0 : value + add;
  1554. return {
  1555. min: keepZero(min, -Math.abs(change)),
  1556. max: keepZero(max, change)
  1557. };
  1558. }
  1559. function createContext(parentContext, context) {
  1560. return Object.assign(Object.create(parentContext), context);
  1561. }
  1562. function _createResolver(scopes, prefixes = [
  1563. ''
  1564. ], rootScopes = scopes, fallback, getTarget = ()=>scopes[0]) {
  1565. if (!defined(fallback)) {
  1566. fallback = _resolve('_fallback', scopes);
  1567. }
  1568. const cache = {
  1569. [Symbol.toStringTag]: 'Object',
  1570. _cacheable: true,
  1571. _scopes: scopes,
  1572. _rootScopes: rootScopes,
  1573. _fallback: fallback,
  1574. _getTarget: getTarget,
  1575. override: (scope)=>_createResolver([
  1576. scope,
  1577. ...scopes
  1578. ], prefixes, rootScopes, fallback)
  1579. };
  1580. return new Proxy(cache, {
  1581. deleteProperty (target, prop) {
  1582. delete target[prop];
  1583. delete target._keys;
  1584. delete scopes[0][prop];
  1585. return true;
  1586. },
  1587. get (target, prop) {
  1588. return _cached(target, prop, ()=>_resolveWithPrefixes(prop, prefixes, scopes, target));
  1589. },
  1590. getOwnPropertyDescriptor (target, prop) {
  1591. return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop);
  1592. },
  1593. getPrototypeOf () {
  1594. return Reflect.getPrototypeOf(scopes[0]);
  1595. },
  1596. has (target, prop) {
  1597. return getKeysFromAllScopes(target).includes(prop);
  1598. },
  1599. ownKeys (target) {
  1600. return getKeysFromAllScopes(target);
  1601. },
  1602. set (target, prop, value) {
  1603. const storage = target._storage || (target._storage = getTarget());
  1604. target[prop] = storage[prop] = value;
  1605. delete target._keys;
  1606. return true;
  1607. }
  1608. });
  1609. }
  1610. function _attachContext(proxy, context, subProxy, descriptorDefaults) {
  1611. const cache = {
  1612. _cacheable: false,
  1613. _proxy: proxy,
  1614. _context: context,
  1615. _subProxy: subProxy,
  1616. _stack: new Set(),
  1617. _descriptors: _descriptors(proxy, descriptorDefaults),
  1618. setContext: (ctx)=>_attachContext(proxy, ctx, subProxy, descriptorDefaults),
  1619. override: (scope)=>_attachContext(proxy.override(scope), context, subProxy, descriptorDefaults)
  1620. };
  1621. return new Proxy(cache, {
  1622. deleteProperty (target, prop) {
  1623. delete target[prop];
  1624. delete proxy[prop];
  1625. return true;
  1626. },
  1627. get (target, prop, receiver) {
  1628. return _cached(target, prop, ()=>_resolveWithContext(target, prop, receiver));
  1629. },
  1630. getOwnPropertyDescriptor (target, prop) {
  1631. return target._descriptors.allKeys ? Reflect.has(proxy, prop) ? {
  1632. enumerable: true,
  1633. configurable: true
  1634. } : undefined : Reflect.getOwnPropertyDescriptor(proxy, prop);
  1635. },
  1636. getPrototypeOf () {
  1637. return Reflect.getPrototypeOf(proxy);
  1638. },
  1639. has (target, prop) {
  1640. return Reflect.has(proxy, prop);
  1641. },
  1642. ownKeys () {
  1643. return Reflect.ownKeys(proxy);
  1644. },
  1645. set (target, prop, value) {
  1646. proxy[prop] = value;
  1647. delete target[prop];
  1648. return true;
  1649. }
  1650. });
  1651. }
  1652. function _descriptors(proxy, defaults = {
  1653. scriptable: true,
  1654. indexable: true
  1655. }) {
  1656. const { _scriptable =defaults.scriptable , _indexable =defaults.indexable , _allKeys =defaults.allKeys } = proxy;
  1657. return {
  1658. allKeys: _allKeys,
  1659. scriptable: _scriptable,
  1660. indexable: _indexable,
  1661. isScriptable: isFunction(_scriptable) ? _scriptable : ()=>_scriptable,
  1662. isIndexable: isFunction(_indexable) ? _indexable : ()=>_indexable
  1663. };
  1664. }
  1665. const readKey = (prefix, name)=>prefix ? prefix + _capitalize(name) : name;
  1666. const needsSubResolver = (prop, value)=>isObject(value) && prop !== 'adapters' && (Object.getPrototypeOf(value) === null || value.constructor === Object);
  1667. function _cached(target, prop, resolve) {
  1668. if (Object.prototype.hasOwnProperty.call(target, prop)) {
  1669. return target[prop];
  1670. }
  1671. const value = resolve();
  1672. target[prop] = value;
  1673. return value;
  1674. }
  1675. function _resolveWithContext(target, prop, receiver) {
  1676. const { _proxy , _context , _subProxy , _descriptors: descriptors } = target;
  1677. let value = _proxy[prop];
  1678. if (isFunction(value) && descriptors.isScriptable(prop)) {
  1679. value = _resolveScriptable(prop, value, target, receiver);
  1680. }
  1681. if (isArray(value) && value.length) {
  1682. value = _resolveArray(prop, value, target, descriptors.isIndexable);
  1683. }
  1684. if (needsSubResolver(prop, value)) {
  1685. value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors);
  1686. }
  1687. return value;
  1688. }
  1689. function _resolveScriptable(prop, value, target, receiver) {
  1690. const { _proxy , _context , _subProxy , _stack } = target;
  1691. if (_stack.has(prop)) {
  1692. throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop);
  1693. }
  1694. _stack.add(prop);
  1695. value = value(_context, _subProxy || receiver);
  1696. _stack.delete(prop);
  1697. if (needsSubResolver(prop, value)) {
  1698. value = createSubResolver(_proxy._scopes, _proxy, prop, value);
  1699. }
  1700. return value;
  1701. }
  1702. function _resolveArray(prop, value, target, isIndexable) {
  1703. const { _proxy , _context , _subProxy , _descriptors: descriptors } = target;
  1704. if (defined(_context.index) && isIndexable(prop)) {
  1705. value = value[_context.index % value.length];
  1706. } else if (isObject(value[0])) {
  1707. const arr = value;
  1708. const scopes = _proxy._scopes.filter((s)=>s !== arr);
  1709. value = [];
  1710. for (const item of arr){
  1711. const resolver = createSubResolver(scopes, _proxy, prop, item);
  1712. value.push(_attachContext(resolver, _context, _subProxy && _subProxy[prop], descriptors));
  1713. }
  1714. }
  1715. return value;
  1716. }
  1717. function resolveFallback(fallback, prop, value) {
  1718. return isFunction(fallback) ? fallback(prop, value) : fallback;
  1719. }
  1720. const getScope = (key, parent)=>key === true ? parent : typeof key === 'string' ? resolveObjectKey(parent, key) : undefined;
  1721. function addScopes(set, parentScopes, key, parentFallback, value) {
  1722. for (const parent of parentScopes){
  1723. const scope = getScope(key, parent);
  1724. if (scope) {
  1725. set.add(scope);
  1726. const fallback = resolveFallback(scope._fallback, key, value);
  1727. if (defined(fallback) && fallback !== key && fallback !== parentFallback) {
  1728. return fallback;
  1729. }
  1730. } else if (scope === false && defined(parentFallback) && key !== parentFallback) {
  1731. return null;
  1732. }
  1733. }
  1734. return false;
  1735. }
  1736. function createSubResolver(parentScopes, resolver, prop, value) {
  1737. const rootScopes = resolver._rootScopes;
  1738. const fallback = resolveFallback(resolver._fallback, prop, value);
  1739. const allScopes = [
  1740. ...parentScopes,
  1741. ...rootScopes
  1742. ];
  1743. const set = new Set();
  1744. set.add(value);
  1745. let key = addScopesFromKey(set, allScopes, prop, fallback || prop, value);
  1746. if (key === null) {
  1747. return false;
  1748. }
  1749. if (defined(fallback) && fallback !== prop) {
  1750. key = addScopesFromKey(set, allScopes, fallback, key, value);
  1751. if (key === null) {
  1752. return false;
  1753. }
  1754. }
  1755. return _createResolver(Array.from(set), [
  1756. ''
  1757. ], rootScopes, fallback, ()=>subGetTarget(resolver, prop, value));
  1758. }
  1759. function addScopesFromKey(set, allScopes, key, fallback, item) {
  1760. while(key){
  1761. key = addScopes(set, allScopes, key, fallback, item);
  1762. }
  1763. return key;
  1764. }
  1765. function subGetTarget(resolver, prop, value) {
  1766. const parent = resolver._getTarget();
  1767. if (!(prop in parent)) {
  1768. parent[prop] = {};
  1769. }
  1770. const target = parent[prop];
  1771. if (isArray(target) && isObject(value)) {
  1772. return value;
  1773. }
  1774. return target || {};
  1775. }
  1776. function _resolveWithPrefixes(prop, prefixes, scopes, proxy) {
  1777. let value;
  1778. for (const prefix of prefixes){
  1779. value = _resolve(readKey(prefix, prop), scopes);
  1780. if (defined(value)) {
  1781. return needsSubResolver(prop, value) ? createSubResolver(scopes, proxy, prop, value) : value;
  1782. }
  1783. }
  1784. }
  1785. function _resolve(key, scopes) {
  1786. for (const scope of scopes){
  1787. if (!scope) {
  1788. continue;
  1789. }
  1790. const value = scope[key];
  1791. if (defined(value)) {
  1792. return value;
  1793. }
  1794. }
  1795. }
  1796. function getKeysFromAllScopes(target) {
  1797. let keys = target._keys;
  1798. if (!keys) {
  1799. keys = target._keys = resolveKeysFromAllScopes(target._scopes);
  1800. }
  1801. return keys;
  1802. }
  1803. function resolveKeysFromAllScopes(scopes) {
  1804. const set = new Set();
  1805. for (const scope of scopes){
  1806. for (const key of Object.keys(scope).filter((k)=>!k.startsWith('_'))){
  1807. set.add(key);
  1808. }
  1809. }
  1810. return Array.from(set);
  1811. }
  1812. function _parseObjectDataRadialScale(meta, data, start, count) {
  1813. const { iScale } = meta;
  1814. const { key ='r' } = this._parsing;
  1815. const parsed = new Array(count);
  1816. let i, ilen, index, item;
  1817. for(i = 0, ilen = count; i < ilen; ++i){
  1818. index = i + start;
  1819. item = data[index];
  1820. parsed[i] = {
  1821. r: iScale.parse(resolveObjectKey(item, key), index)
  1822. };
  1823. }
  1824. return parsed;
  1825. }
  1826. const EPSILON = Number.EPSILON || 1e-14;
  1827. const getPoint = (points, i)=>i < points.length && !points[i].skip && points[i];
  1828. const getValueAxis = (indexAxis)=>indexAxis === 'x' ? 'y' : 'x';
  1829. function splineCurve(firstPoint, middlePoint, afterPoint, t) {
  1830. // Props to Rob Spencer at scaled innovation for his post on splining between points
  1831. // http://scaledinnovation.com/analytics/splines/aboutSplines.html
  1832. // This function must also respect "skipped" points
  1833. const previous = firstPoint.skip ? middlePoint : firstPoint;
  1834. const current = middlePoint;
  1835. const next = afterPoint.skip ? middlePoint : afterPoint;
  1836. const d01 = distanceBetweenPoints(current, previous);
  1837. const d12 = distanceBetweenPoints(next, current);
  1838. let s01 = d01 / (d01 + d12);
  1839. let s12 = d12 / (d01 + d12);
  1840. // If all points are the same, s01 & s02 will be inf
  1841. s01 = isNaN(s01) ? 0 : s01;
  1842. s12 = isNaN(s12) ? 0 : s12;
  1843. const fa = t * s01; // scaling factor for triangle Ta
  1844. const fb = t * s12;
  1845. return {
  1846. previous: {
  1847. x: current.x - fa * (next.x - previous.x),
  1848. y: current.y - fa * (next.y - previous.y)
  1849. },
  1850. next: {
  1851. x: current.x + fb * (next.x - previous.x),
  1852. y: current.y + fb * (next.y - previous.y)
  1853. }
  1854. };
  1855. }
  1856. /**
  1857. * Adjust tangents to ensure monotonic properties
  1858. */ function monotoneAdjust(points, deltaK, mK) {
  1859. const pointsLen = points.length;
  1860. let alphaK, betaK, tauK, squaredMagnitude, pointCurrent;
  1861. let pointAfter = getPoint(points, 0);
  1862. for(let i = 0; i < pointsLen - 1; ++i){
  1863. pointCurrent = pointAfter;
  1864. pointAfter = getPoint(points, i + 1);
  1865. if (!pointCurrent || !pointAfter) {
  1866. continue;
  1867. }
  1868. if (almostEquals(deltaK[i], 0, EPSILON)) {
  1869. mK[i] = mK[i + 1] = 0;
  1870. continue;
  1871. }
  1872. alphaK = mK[i] / deltaK[i];
  1873. betaK = mK[i + 1] / deltaK[i];
  1874. squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
  1875. if (squaredMagnitude <= 9) {
  1876. continue;
  1877. }
  1878. tauK = 3 / Math.sqrt(squaredMagnitude);
  1879. mK[i] = alphaK * tauK * deltaK[i];
  1880. mK[i + 1] = betaK * tauK * deltaK[i];
  1881. }
  1882. }
  1883. function monotoneCompute(points, mK, indexAxis = 'x') {
  1884. const valueAxis = getValueAxis(indexAxis);
  1885. const pointsLen = points.length;
  1886. let delta, pointBefore, pointCurrent;
  1887. let pointAfter = getPoint(points, 0);
  1888. for(let i = 0; i < pointsLen; ++i){
  1889. pointBefore = pointCurrent;
  1890. pointCurrent = pointAfter;
  1891. pointAfter = getPoint(points, i + 1);
  1892. if (!pointCurrent) {
  1893. continue;
  1894. }
  1895. const iPixel = pointCurrent[indexAxis];
  1896. const vPixel = pointCurrent[valueAxis];
  1897. if (pointBefore) {
  1898. delta = (iPixel - pointBefore[indexAxis]) / 3;
  1899. pointCurrent[`cp1${indexAxis}`] = iPixel - delta;
  1900. pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i];
  1901. }
  1902. if (pointAfter) {
  1903. delta = (pointAfter[indexAxis] - iPixel) / 3;
  1904. pointCurrent[`cp2${indexAxis}`] = iPixel + delta;
  1905. pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i];
  1906. }
  1907. }
  1908. }
  1909. /**
  1910. * This function calculates Bézier control points in a similar way than |splineCurve|,
  1911. * but preserves monotonicity of the provided data and ensures no local extremums are added
  1912. * between the dataset discrete points due to the interpolation.
  1913. * See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
  1914. */ function splineCurveMonotone(points, indexAxis = 'x') {
  1915. const valueAxis = getValueAxis(indexAxis);
  1916. const pointsLen = points.length;
  1917. const deltaK = Array(pointsLen).fill(0);
  1918. const mK = Array(pointsLen);
  1919. // Calculate slopes (deltaK) and initialize tangents (mK)
  1920. let i, pointBefore, pointCurrent;
  1921. let pointAfter = getPoint(points, 0);
  1922. for(i = 0; i < pointsLen; ++i){
  1923. pointBefore = pointCurrent;
  1924. pointCurrent = pointAfter;
  1925. pointAfter = getPoint(points, i + 1);
  1926. if (!pointCurrent) {
  1927. continue;
  1928. }
  1929. if (pointAfter) {
  1930. const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis];
  1931. // In the case of two points that appear at the same x pixel, slopeDeltaX is 0
  1932. deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0;
  1933. }
  1934. mK[i] = !pointBefore ? deltaK[i] : !pointAfter ? deltaK[i - 1] : sign(deltaK[i - 1]) !== sign(deltaK[i]) ? 0 : (deltaK[i - 1] + deltaK[i]) / 2;
  1935. }
  1936. monotoneAdjust(points, deltaK, mK);
  1937. monotoneCompute(points, mK, indexAxis);
  1938. }
  1939. function capControlPoint(pt, min, max) {
  1940. return Math.max(Math.min(pt, max), min);
  1941. }
  1942. function capBezierPoints(points, area) {
  1943. let i, ilen, point, inArea, inAreaPrev;
  1944. let inAreaNext = _isPointInArea(points[0], area);
  1945. for(i = 0, ilen = points.length; i < ilen; ++i){
  1946. inAreaPrev = inArea;
  1947. inArea = inAreaNext;
  1948. inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area);
  1949. if (!inArea) {
  1950. continue;
  1951. }
  1952. point = points[i];
  1953. if (inAreaPrev) {
  1954. point.cp1x = capControlPoint(point.cp1x, area.left, area.right);
  1955. point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom);
  1956. }
  1957. if (inAreaNext) {
  1958. point.cp2x = capControlPoint(point.cp2x, area.left, area.right);
  1959. point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom);
  1960. }
  1961. }
  1962. }
  1963. /**
  1964. * @private
  1965. */ function _updateBezierControlPoints(points, options, area, loop, indexAxis) {
  1966. let i, ilen, point, controlPoints;
  1967. // Only consider points that are drawn in case the spanGaps option is used
  1968. if (options.spanGaps) {
  1969. points = points.filter((pt)=>!pt.skip);
  1970. }
  1971. if (options.cubicInterpolationMode === 'monotone') {
  1972. splineCurveMonotone(points, indexAxis);
  1973. } else {
  1974. let prev = loop ? points[points.length - 1] : points[0];
  1975. for(i = 0, ilen = points.length; i < ilen; ++i){
  1976. point = points[i];
  1977. controlPoints = splineCurve(prev, point, points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen], options.tension);
  1978. point.cp1x = controlPoints.previous.x;
  1979. point.cp1y = controlPoints.previous.y;
  1980. point.cp2x = controlPoints.next.x;
  1981. point.cp2y = controlPoints.next.y;
  1982. prev = point;
  1983. }
  1984. }
  1985. if (options.capBezierPoints) {
  1986. capBezierPoints(points, area);
  1987. }
  1988. }
  1989. /**
  1990. * Note: typedefs are auto-exported, so use a made-up `dom` namespace where
  1991. * necessary to avoid duplicates with `export * from './helpers`; see
  1992. * https://github.com/microsoft/TypeScript/issues/46011
  1993. * @typedef { import('../core/core.controller.js').default } dom.Chart
  1994. * @typedef { import('../../types').ChartEvent } ChartEvent
  1995. */ /**
  1996. * @private
  1997. */ function _isDomSupported() {
  1998. return typeof window !== 'undefined' && typeof document !== 'undefined';
  1999. }
  2000. /**
  2001. * @private
  2002. */ function _getParentNode(domNode) {
  2003. let parent = domNode.parentNode;
  2004. if (parent && parent.toString() === '[object ShadowRoot]') {
  2005. parent = parent.host;
  2006. }
  2007. return parent;
  2008. }
  2009. /**
  2010. * convert max-width/max-height values that may be percentages into a number
  2011. * @private
  2012. */ function parseMaxStyle(styleValue, node, parentProperty) {
  2013. let valueInPixels;
  2014. if (typeof styleValue === 'string') {
  2015. valueInPixels = parseInt(styleValue, 10);
  2016. if (styleValue.indexOf('%') !== -1) {
  2017. // percentage * size in dimension
  2018. valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
  2019. }
  2020. } else {
  2021. valueInPixels = styleValue;
  2022. }
  2023. return valueInPixels;
  2024. }
  2025. const getComputedStyle = (element)=>element.ownerDocument.defaultView.getComputedStyle(element, null);
  2026. function getStyle(el, property) {
  2027. return getComputedStyle(el).getPropertyValue(property);
  2028. }
  2029. const positions = [
  2030. 'top',
  2031. 'right',
  2032. 'bottom',
  2033. 'left'
  2034. ];
  2035. function getPositionedStyle(styles, style, suffix) {
  2036. const result = {};
  2037. suffix = suffix ? '-' + suffix : '';
  2038. for(let i = 0; i < 4; i++){
  2039. const pos = positions[i];
  2040. result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0;
  2041. }
  2042. result.width = result.left + result.right;
  2043. result.height = result.top + result.bottom;
  2044. return result;
  2045. }
  2046. const useOffsetPos = (x, y, target)=>(x > 0 || y > 0) && (!target || !target.shadowRoot);
  2047. /**
  2048. * @param e
  2049. * @param canvas
  2050. * @returns Canvas position
  2051. */ function getCanvasPosition(e, canvas) {
  2052. const touches = e.touches;
  2053. const source = touches && touches.length ? touches[0] : e;
  2054. const { offsetX , offsetY } = source;
  2055. let box = false;
  2056. let x, y;
  2057. if (useOffsetPos(offsetX, offsetY, e.target)) {
  2058. x = offsetX;
  2059. y = offsetY;
  2060. } else {
  2061. const rect = canvas.getBoundingClientRect();
  2062. x = source.clientX - rect.left;
  2063. y = source.clientY - rect.top;
  2064. box = true;
  2065. }
  2066. return {
  2067. x,
  2068. y,
  2069. box
  2070. };
  2071. }
  2072. /**
  2073. * Gets an event's x, y coordinates, relative to the chart area
  2074. * @param event
  2075. * @param chart
  2076. * @returns x and y coordinates of the event
  2077. */ function getRelativePosition(event, chart) {
  2078. if ('native' in event) {
  2079. return event;
  2080. }
  2081. const { canvas , currentDevicePixelRatio } = chart;
  2082. const style = getComputedStyle(canvas);
  2083. const borderBox = style.boxSizing === 'border-box';
  2084. const paddings = getPositionedStyle(style, 'padding');
  2085. const borders = getPositionedStyle(style, 'border', 'width');
  2086. const { x , y , box } = getCanvasPosition(event, canvas);
  2087. const xOffset = paddings.left + (box && borders.left);
  2088. const yOffset = paddings.top + (box && borders.top);
  2089. let { width , height } = chart;
  2090. if (borderBox) {
  2091. width -= paddings.width + borders.width;
  2092. height -= paddings.height + borders.height;
  2093. }
  2094. return {
  2095. x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),
  2096. y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)
  2097. };
  2098. }
  2099. function getContainerSize(canvas, width, height) {
  2100. let maxWidth, maxHeight;
  2101. if (width === undefined || height === undefined) {
  2102. const container = _getParentNode(canvas);
  2103. if (!container) {
  2104. width = canvas.clientWidth;
  2105. height = canvas.clientHeight;
  2106. } else {
  2107. const rect = container.getBoundingClientRect(); // this is the border box of the container
  2108. const containerStyle = getComputedStyle(container);
  2109. const containerBorder = getPositionedStyle(containerStyle, 'border', 'width');
  2110. const containerPadding = getPositionedStyle(containerStyle, 'padding');
  2111. width = rect.width - containerPadding.width - containerBorder.width;
  2112. height = rect.height - containerPadding.height - containerBorder.height;
  2113. maxWidth = parseMaxStyle(containerStyle.maxWidth, container, 'clientWidth');
  2114. maxHeight = parseMaxStyle(containerStyle.maxHeight, container, 'clientHeight');
  2115. }
  2116. }
  2117. return {
  2118. width,
  2119. height,
  2120. maxWidth: maxWidth || INFINITY,
  2121. maxHeight: maxHeight || INFINITY
  2122. };
  2123. }
  2124. const round1 = (v)=>Math.round(v * 10) / 10;
  2125. // eslint-disable-next-line complexity
  2126. function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) {
  2127. const style = getComputedStyle(canvas);
  2128. const margins = getPositionedStyle(style, 'margin');
  2129. const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY;
  2130. const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY;
  2131. const containerSize = getContainerSize(canvas, bbWidth, bbHeight);
  2132. let { width , height } = containerSize;
  2133. if (style.boxSizing === 'content-box') {
  2134. const borders = getPositionedStyle(style, 'border', 'width');
  2135. const paddings = getPositionedStyle(style, 'padding');
  2136. width -= paddings.width + borders.width;
  2137. height -= paddings.height + borders.height;
  2138. }
  2139. width = Math.max(0, width - margins.width);
  2140. height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height);
  2141. width = round1(Math.min(width, maxWidth, containerSize.maxWidth));
  2142. height = round1(Math.min(height, maxHeight, containerSize.maxHeight));
  2143. if (width && !height) {
  2144. // https://github.com/chartjs/Chart.js/issues/4659
  2145. // If the canvas has width, but no height, default to aspectRatio of 2 (canvas default)
  2146. height = round1(width / 2);
  2147. }
  2148. const maintainHeight = bbWidth !== undefined || bbHeight !== undefined;
  2149. if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) {
  2150. height = containerSize.height;
  2151. width = round1(Math.floor(height * aspectRatio));
  2152. }
  2153. return {
  2154. width,
  2155. height
  2156. };
  2157. }
  2158. /**
  2159. * @param chart
  2160. * @param forceRatio
  2161. * @param forceStyle
  2162. * @returns True if the canvas context size or transformation has changed.
  2163. */ function retinaScale(chart, forceRatio, forceStyle) {
  2164. const pixelRatio = forceRatio || 1;
  2165. const deviceHeight = Math.floor(chart.height * pixelRatio);
  2166. const deviceWidth = Math.floor(chart.width * pixelRatio);
  2167. chart.height = Math.floor(chart.height);
  2168. chart.width = Math.floor(chart.width);
  2169. const canvas = chart.canvas;
  2170. // If no style has been set on the canvas, the render size is used as display size,
  2171. // making the chart visually bigger, so let's enforce it to the "correct" values.
  2172. // See https://github.com/chartjs/Chart.js/issues/3575
  2173. if (canvas.style && (forceStyle || !canvas.style.height && !canvas.style.width)) {
  2174. canvas.style.height = `${chart.height}px`;
  2175. canvas.style.width = `${chart.width}px`;
  2176. }
  2177. if (chart.currentDevicePixelRatio !== pixelRatio || canvas.height !== deviceHeight || canvas.width !== deviceWidth) {
  2178. chart.currentDevicePixelRatio = pixelRatio;
  2179. canvas.height = deviceHeight;
  2180. canvas.width = deviceWidth;
  2181. chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
  2182. return true;
  2183. }
  2184. return false;
  2185. }
  2186. /**
  2187. * Detects support for options object argument in addEventListener.
  2188. * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
  2189. * @private
  2190. */ const supportsEventListenerOptions = function() {
  2191. let passiveSupported = false;
  2192. try {
  2193. const options = {
  2194. get passive () {
  2195. passiveSupported = true;
  2196. return false;
  2197. }
  2198. };
  2199. window.addEventListener('test', null, options);
  2200. window.removeEventListener('test', null, options);
  2201. } catch (e) {
  2202. // continue regardless of error
  2203. }
  2204. return passiveSupported;
  2205. }();
  2206. /**
  2207. * The "used" size is the final value of a dimension property after all calculations have
  2208. * been performed. This method uses the computed style of `element` but returns undefined
  2209. * if the computed style is not expressed in pixels. That can happen in some cases where
  2210. * `element` has a size relative to its parent and this last one is not yet displayed,
  2211. * for example because of `display: none` on a parent node.
  2212. * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
  2213. * @returns Size in pixels or undefined if unknown.
  2214. */ function readUsedSize(element, property) {
  2215. const value = getStyle(element, property);
  2216. const matches = value && value.match(/^(\d+)(\.\d+)?px$/);
  2217. return matches ? +matches[1] : undefined;
  2218. }
  2219. /**
  2220. * @private
  2221. */ function _pointInLine(p1, p2, t, mode) {
  2222. return {
  2223. x: p1.x + t * (p2.x - p1.x),
  2224. y: p1.y + t * (p2.y - p1.y)
  2225. };
  2226. }
  2227. /**
  2228. * @private
  2229. */ function _steppedInterpolation(p1, p2, t, mode) {
  2230. return {
  2231. x: p1.x + t * (p2.x - p1.x),
  2232. y: mode === 'middle' ? t < 0.5 ? p1.y : p2.y : mode === 'after' ? t < 1 ? p1.y : p2.y : t > 0 ? p2.y : p1.y
  2233. };
  2234. }
  2235. /**
  2236. * @private
  2237. */ function _bezierInterpolation(p1, p2, t, mode) {
  2238. const cp1 = {
  2239. x: p1.cp2x,
  2240. y: p1.cp2y
  2241. };
  2242. const cp2 = {
  2243. x: p2.cp1x,
  2244. y: p2.cp1y
  2245. };
  2246. const a = _pointInLine(p1, cp1, t);
  2247. const b = _pointInLine(cp1, cp2, t);
  2248. const c = _pointInLine(cp2, p2, t);
  2249. const d = _pointInLine(a, b, t);
  2250. const e = _pointInLine(b, c, t);
  2251. return _pointInLine(d, e, t);
  2252. }
  2253. const getRightToLeftAdapter = function(rectX, width) {
  2254. return {
  2255. x (x) {
  2256. return rectX + rectX + width - x;
  2257. },
  2258. setWidth (w) {
  2259. width = w;
  2260. },
  2261. textAlign (align) {
  2262. if (align === 'center') {
  2263. return align;
  2264. }
  2265. return align === 'right' ? 'left' : 'right';
  2266. },
  2267. xPlus (x, value) {
  2268. return x - value;
  2269. },
  2270. leftForLtr (x, itemWidth) {
  2271. return x - itemWidth;
  2272. }
  2273. };
  2274. };
  2275. const getLeftToRightAdapter = function() {
  2276. return {
  2277. x (x) {
  2278. return x;
  2279. },
  2280. setWidth (w) {},
  2281. textAlign (align) {
  2282. return align;
  2283. },
  2284. xPlus (x, value) {
  2285. return x + value;
  2286. },
  2287. leftForLtr (x, _itemWidth) {
  2288. return x;
  2289. }
  2290. };
  2291. };
  2292. function getRtlAdapter(rtl, rectX, width) {
  2293. return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter();
  2294. }
  2295. function overrideTextDirection(ctx, direction) {
  2296. let style, original;
  2297. if (direction === 'ltr' || direction === 'rtl') {
  2298. style = ctx.canvas.style;
  2299. original = [
  2300. style.getPropertyValue('direction'),
  2301. style.getPropertyPriority('direction')
  2302. ];
  2303. style.setProperty('direction', direction, 'important');
  2304. ctx.prevTextDirection = original;
  2305. }
  2306. }
  2307. function restoreTextDirection(ctx, original) {
  2308. if (original !== undefined) {
  2309. delete ctx.prevTextDirection;
  2310. ctx.canvas.style.setProperty('direction', original[0], original[1]);
  2311. }
  2312. }
  2313. function propertyFn(property) {
  2314. if (property === 'angle') {
  2315. return {
  2316. between: _angleBetween,
  2317. compare: _angleDiff,
  2318. normalize: _normalizeAngle
  2319. };
  2320. }
  2321. return {
  2322. between: _isBetween,
  2323. compare: (a, b)=>a - b,
  2324. normalize: (x)=>x
  2325. };
  2326. }
  2327. function normalizeSegment({ start , end , count , loop , style }) {
  2328. return {
  2329. start: start % count,
  2330. end: end % count,
  2331. loop: loop && (end - start + 1) % count === 0,
  2332. style
  2333. };
  2334. }
  2335. function getSegment(segment, points, bounds) {
  2336. const { property , start: startBound , end: endBound } = bounds;
  2337. const { between , normalize } = propertyFn(property);
  2338. const count = points.length;
  2339. let { start , end , loop } = segment;
  2340. let i, ilen;
  2341. if (loop) {
  2342. start += count;
  2343. end += count;
  2344. for(i = 0, ilen = count; i < ilen; ++i){
  2345. if (!between(normalize(points[start % count][property]), startBound, endBound)) {
  2346. break;
  2347. }
  2348. start--;
  2349. end--;
  2350. }
  2351. start %= count;
  2352. end %= count;
  2353. }
  2354. if (end < start) {
  2355. end += count;
  2356. }
  2357. return {
  2358. start,
  2359. end,
  2360. loop,
  2361. style: segment.style
  2362. };
  2363. }
  2364. function _boundSegment(segment, points, bounds) {
  2365. if (!bounds) {
  2366. return [
  2367. segment
  2368. ];
  2369. }
  2370. const { property , start: startBound , end: endBound } = bounds;
  2371. const count = points.length;
  2372. const { compare , between , normalize } = propertyFn(property);
  2373. const { start , end , loop , style } = getSegment(segment, points, bounds);
  2374. const result = [];
  2375. let inside = false;
  2376. let subStart = null;
  2377. let value, point, prevValue;
  2378. const startIsBefore = ()=>between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0;
  2379. const endIsBefore = ()=>compare(endBound, value) === 0 || between(endBound, prevValue, value);
  2380. const shouldStart = ()=>inside || startIsBefore();
  2381. const shouldStop = ()=>!inside || endIsBefore();
  2382. for(let i = start, prev = start; i <= end; ++i){
  2383. point = points[i % count];
  2384. if (point.skip) {
  2385. continue;
  2386. }
  2387. value = normalize(point[property]);
  2388. if (value === prevValue) {
  2389. continue;
  2390. }
  2391. inside = between(value, startBound, endBound);
  2392. if (subStart === null && shouldStart()) {
  2393. subStart = compare(value, startBound) === 0 ? i : prev;
  2394. }
  2395. if (subStart !== null && shouldStop()) {
  2396. result.push(normalizeSegment({
  2397. start: subStart,
  2398. end: i,
  2399. loop,
  2400. count,
  2401. style
  2402. }));
  2403. subStart = null;
  2404. }
  2405. prev = i;
  2406. prevValue = value;
  2407. }
  2408. if (subStart !== null) {
  2409. result.push(normalizeSegment({
  2410. start: subStart,
  2411. end,
  2412. loop,
  2413. count,
  2414. style
  2415. }));
  2416. }
  2417. return result;
  2418. }
  2419. function _boundSegments(line, bounds) {
  2420. const result = [];
  2421. const segments = line.segments;
  2422. for(let i = 0; i < segments.length; i++){
  2423. const sub = _boundSegment(segments[i], line.points, bounds);
  2424. if (sub.length) {
  2425. result.push(...sub);
  2426. }
  2427. }
  2428. return result;
  2429. }
  2430. function findStartAndEnd(points, count, loop, spanGaps) {
  2431. let start = 0;
  2432. let end = count - 1;
  2433. if (loop && !spanGaps) {
  2434. while(start < count && !points[start].skip){
  2435. start++;
  2436. }
  2437. }
  2438. while(start < count && points[start].skip){
  2439. start++;
  2440. }
  2441. start %= count;
  2442. if (loop) {
  2443. end += start;
  2444. }
  2445. while(end > start && points[end % count].skip){
  2446. end--;
  2447. }
  2448. end %= count;
  2449. return {
  2450. start,
  2451. end
  2452. };
  2453. }
  2454. function solidSegments(points, start, max, loop) {
  2455. const count = points.length;
  2456. const result = [];
  2457. let last = start;
  2458. let prev = points[start];
  2459. let end;
  2460. for(end = start + 1; end <= max; ++end){
  2461. const cur = points[end % count];
  2462. if (cur.skip || cur.stop) {
  2463. if (!prev.skip) {
  2464. loop = false;
  2465. result.push({
  2466. start: start % count,
  2467. end: (end - 1) % count,
  2468. loop
  2469. });
  2470. start = last = cur.stop ? end : null;
  2471. }
  2472. } else {
  2473. last = end;
  2474. if (prev.skip) {
  2475. start = end;
  2476. }
  2477. }
  2478. prev = cur;
  2479. }
  2480. if (last !== null) {
  2481. result.push({
  2482. start: start % count,
  2483. end: last % count,
  2484. loop
  2485. });
  2486. }
  2487. return result;
  2488. }
  2489. function _computeSegments(line, segmentOptions) {
  2490. const points = line.points;
  2491. const spanGaps = line.options.spanGaps;
  2492. const count = points.length;
  2493. if (!count) {
  2494. return [];
  2495. }
  2496. const loop = !!line._loop;
  2497. const { start , end } = findStartAndEnd(points, count, loop, spanGaps);
  2498. if (spanGaps === true) {
  2499. return splitByStyles(line, [
  2500. {
  2501. start,
  2502. end,
  2503. loop
  2504. }
  2505. ], points, segmentOptions);
  2506. }
  2507. const max = end < start ? end + count : end;
  2508. const completeLoop = !!line._fullLoop && start === 0 && end === count - 1;
  2509. return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions);
  2510. }
  2511. function splitByStyles(line, segments, points, segmentOptions) {
  2512. if (!segmentOptions || !segmentOptions.setContext || !points) {
  2513. return segments;
  2514. }
  2515. return doSplitByStyles(line, segments, points, segmentOptions);
  2516. }
  2517. function doSplitByStyles(line, segments, points, segmentOptions) {
  2518. const chartContext = line._chart.getContext();
  2519. const baseStyle = readStyle(line.options);
  2520. const { _datasetIndex: datasetIndex , options: { spanGaps } } = line;
  2521. const count = points.length;
  2522. const result = [];
  2523. let prevStyle = baseStyle;
  2524. let start = segments[0].start;
  2525. let i = start;
  2526. function addStyle(s, e, l, st) {
  2527. const dir = spanGaps ? -1 : 1;
  2528. if (s === e) {
  2529. return;
  2530. }
  2531. s += count;
  2532. while(points[s % count].skip){
  2533. s -= dir;
  2534. }
  2535. while(points[e % count].skip){
  2536. e += dir;
  2537. }
  2538. if (s % count !== e % count) {
  2539. result.push({
  2540. start: s % count,
  2541. end: e % count,
  2542. loop: l,
  2543. style: st
  2544. });
  2545. prevStyle = st;
  2546. start = e % count;
  2547. }
  2548. }
  2549. for (const segment of segments){
  2550. start = spanGaps ? start : segment.start;
  2551. let prev = points[start % count];
  2552. let style;
  2553. for(i = start + 1; i <= segment.end; i++){
  2554. const pt = points[i % count];
  2555. style = readStyle(segmentOptions.setContext(createContext(chartContext, {
  2556. type: 'segment',
  2557. p0: prev,
  2558. p1: pt,
  2559. p0DataIndex: (i - 1) % count,
  2560. p1DataIndex: i % count,
  2561. datasetIndex
  2562. })));
  2563. if (styleChanged(style, prevStyle)) {
  2564. addStyle(start, i - 1, segment.loop, prevStyle);
  2565. }
  2566. prev = pt;
  2567. prevStyle = style;
  2568. }
  2569. if (start < i - 1) {
  2570. addStyle(start, i - 1, segment.loop, prevStyle);
  2571. }
  2572. }
  2573. return result;
  2574. }
  2575. function readStyle(options) {
  2576. return {
  2577. backgroundColor: options.backgroundColor,
  2578. borderCapStyle: options.borderCapStyle,
  2579. borderDash: options.borderDash,
  2580. borderDashOffset: options.borderDashOffset,
  2581. borderJoinStyle: options.borderJoinStyle,
  2582. borderWidth: options.borderWidth,
  2583. borderColor: options.borderColor
  2584. };
  2585. }
  2586. function styleChanged(style, prevStyle) {
  2587. return prevStyle && JSON.stringify(style) !== JSON.stringify(prevStyle);
  2588. }
  2589. exports.HALF_PI = HALF_PI;
  2590. exports.INFINITY = INFINITY;
  2591. exports.PI = PI;
  2592. exports.PITAU = PITAU;
  2593. exports.QUARTER_PI = QUARTER_PI;
  2594. exports.RAD_PER_DEG = RAD_PER_DEG;
  2595. exports.TAU = TAU;
  2596. exports.TWO_THIRDS_PI = TWO_THIRDS_PI;
  2597. exports.Ticks = Ticks;
  2598. exports._addGrace = _addGrace;
  2599. exports._alignPixel = _alignPixel;
  2600. exports._alignStartEnd = _alignStartEnd;
  2601. exports._angleBetween = _angleBetween;
  2602. exports._angleDiff = _angleDiff;
  2603. exports._arrayUnique = _arrayUnique;
  2604. exports._attachContext = _attachContext;
  2605. exports._bezierCurveTo = _bezierCurveTo;
  2606. exports._bezierInterpolation = _bezierInterpolation;
  2607. exports._boundSegment = _boundSegment;
  2608. exports._boundSegments = _boundSegments;
  2609. exports._capitalize = _capitalize;
  2610. exports._computeSegments = _computeSegments;
  2611. exports._createResolver = _createResolver;
  2612. exports._decimalPlaces = _decimalPlaces;
  2613. exports._deprecated = _deprecated;
  2614. exports._descriptors = _descriptors;
  2615. exports._elementsEqual = _elementsEqual;
  2616. exports._factorize = _factorize;
  2617. exports._filterBetween = _filterBetween;
  2618. exports._getParentNode = _getParentNode;
  2619. exports._getStartAndCountOfVisiblePoints = _getStartAndCountOfVisiblePoints;
  2620. exports._int16Range = _int16Range;
  2621. exports._isBetween = _isBetween;
  2622. exports._isClickEvent = _isClickEvent;
  2623. exports._isDomSupported = _isDomSupported;
  2624. exports._isPointInArea = _isPointInArea;
  2625. exports._limitValue = _limitValue;
  2626. exports._longestText = _longestText;
  2627. exports._lookup = _lookup;
  2628. exports._lookupByKey = _lookupByKey;
  2629. exports._measureText = _measureText;
  2630. exports._merger = _merger;
  2631. exports._mergerIf = _mergerIf;
  2632. exports._normalizeAngle = _normalizeAngle;
  2633. exports._parseObjectDataRadialScale = _parseObjectDataRadialScale;
  2634. exports._pointInLine = _pointInLine;
  2635. exports._readValueToProps = _readValueToProps;
  2636. exports._rlookupByKey = _rlookupByKey;
  2637. exports._scaleRangesChanged = _scaleRangesChanged;
  2638. exports._setMinAndMaxByKey = _setMinAndMaxByKey;
  2639. exports._splitKey = _splitKey;
  2640. exports._steppedInterpolation = _steppedInterpolation;
  2641. exports._steppedLineTo = _steppedLineTo;
  2642. exports._textX = _textX;
  2643. exports._toLeftRightCenter = _toLeftRightCenter;
  2644. exports._updateBezierControlPoints = _updateBezierControlPoints;
  2645. exports.addRoundedRectPath = addRoundedRectPath;
  2646. exports.almostEquals = almostEquals;
  2647. exports.almostWhole = almostWhole;
  2648. exports.callback = callback;
  2649. exports.clearCanvas = clearCanvas;
  2650. exports.clipArea = clipArea;
  2651. exports.clone = clone;
  2652. exports.color = color;
  2653. exports.createContext = createContext;
  2654. exports.debounce = debounce;
  2655. exports.defaults = defaults;
  2656. exports.defined = defined;
  2657. exports.descriptors = descriptors;
  2658. exports.distanceBetweenPoints = distanceBetweenPoints;
  2659. exports.drawPoint = drawPoint;
  2660. exports.drawPointLegend = drawPointLegend;
  2661. exports.each = each;
  2662. exports.effects = effects;
  2663. exports.finiteOrDefault = finiteOrDefault;
  2664. exports.fontString = fontString;
  2665. exports.formatNumber = formatNumber;
  2666. exports.getAngleFromPoint = getAngleFromPoint;
  2667. exports.getHoverColor = getHoverColor;
  2668. exports.getMaximumSize = getMaximumSize;
  2669. exports.getRelativePosition = getRelativePosition;
  2670. exports.getRtlAdapter = getRtlAdapter;
  2671. exports.getStyle = getStyle;
  2672. exports.isArray = isArray;
  2673. exports.isFunction = isFunction;
  2674. exports.isNullOrUndef = isNullOrUndef;
  2675. exports.isNumber = isNumber;
  2676. exports.isNumberFinite = isNumberFinite;
  2677. exports.isObject = isObject;
  2678. exports.isPatternOrGradient = isPatternOrGradient;
  2679. exports.listenArrayEvents = listenArrayEvents;
  2680. exports.log10 = log10;
  2681. exports.merge = merge;
  2682. exports.mergeIf = mergeIf;
  2683. exports.niceNum = niceNum;
  2684. exports.noop = noop;
  2685. exports.overrideTextDirection = overrideTextDirection;
  2686. exports.overrides = overrides;
  2687. exports.readUsedSize = readUsedSize;
  2688. exports.renderText = renderText;
  2689. exports.requestAnimFrame = requestAnimFrame;
  2690. exports.resolve = resolve;
  2691. exports.resolveObjectKey = resolveObjectKey;
  2692. exports.restoreTextDirection = restoreTextDirection;
  2693. exports.retinaScale = retinaScale;
  2694. exports.setsEqual = setsEqual;
  2695. exports.sign = sign;
  2696. exports.splineCurve = splineCurve;
  2697. exports.splineCurveMonotone = splineCurveMonotone;
  2698. exports.supportsEventListenerOptions = supportsEventListenerOptions;
  2699. exports.throttled = throttled;
  2700. exports.toDegrees = toDegrees;
  2701. exports.toDimension = toDimension;
  2702. exports.toFont = toFont;
  2703. exports.toFontString = toFontString;
  2704. exports.toLineHeight = toLineHeight;
  2705. exports.toPadding = toPadding;
  2706. exports.toPercentage = toPercentage;
  2707. exports.toRadians = toRadians;
  2708. exports.toTRBL = toTRBL;
  2709. exports.toTRBLCorners = toTRBLCorners;
  2710. exports.uid = uid;
  2711. exports.unclipArea = unclipArea;
  2712. exports.unlistenArrayEvents = unlistenArrayEvents;
  2713. exports.valueOrDefault = valueOrDefault;
  2714. //# sourceMappingURL=helpers.segment.cjs.map