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.

2610 lines
87 KiB

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