AtCoderStandingsAnalysis-dev

順位表のjsonを集計し、上部にテーブルを追加します。

2025-01-19 يوللانغان نەشرى. ئەڭ يېڭى نەشرىنى كۆرۈش.

  1. // ==UserScript==
  2. // @name AtCoderStandingsAnalysis-dev
  3. // @namespace https://github.com/RTnF/AtCoderStandingsAnalysis
  4. // @version 0.3.0
  5. // @author RTnF
  6. // @description 順位表のjsonを集計し、上部にテーブルを追加します。
  7. // @license MIT
  8. // @match https://atcoder.jp/*standings*
  9. // @exclude https://atcoder.jp/*standings/json
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. var __defProp = Object.defineProperty;
  17. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  18. var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  19. function countLower(arr, val) {
  20. let lo = -1;
  21. let hi = arr.length;
  22. while (hi - lo > 1) {
  23. const mid = Math.floor((hi + lo) / 2);
  24. if (arr[mid] < val) {
  25. lo = mid;
  26. } else {
  27. hi = mid;
  28. }
  29. }
  30. return hi;
  31. }
  32. function innerRating(rate, comp) {
  33. let ret = rate;
  34. if (rate <= 0) {
  35. throw "rate <= 0";
  36. }
  37. if (ret < 400) {
  38. ret = 400 * (1 - Math.log(400 / rate));
  39. }
  40. ret += 1200 * (Math.sqrt(1 - Math.pow(0.81, comp)) / (1 - Math.pow(0.9, comp)) - 1) / (Math.sqrt(19) - 1);
  41. return ret;
  42. }
  43. /*!
  44. * コンテストの分類
  45. * https://github.com/kenkoooo/AtCoderProblems/blob/master/atcoder-problems-frontend/src/utils/ContestClassifier.ts
  46. * (c) 2019 kenkoooo
  47. * Released under the MIT License
  48. */
  49. function classifyContest(contestId, contestName) {
  50. if (/^abc\d{3}$/.exec(contestId)) {
  51. return "ABC";
  52. }
  53. if (/^arc\d{3}$/.exec(contestId)) {
  54. return "ARC";
  55. }
  56. if (/^agc\d{3}$/.exec(contestId)) {
  57. return "AGC";
  58. }
  59. if (/^ahc\d{3}$/.exec(contestId) || ["toyota2023summer-final"].includes(contestId)) {
  60. return "AHC";
  61. }
  62. if (contestId.startsWith("past")) {
  63. return "PAST";
  64. }
  65. if (contestId.startsWith("joi")) {
  66. return "JOI";
  67. }
  68. if (/^(jag|JAG)/.exec(contestId)) {
  69. return "JAG";
  70. }
  71. if (/(^Chokudai Contest|ハーフマラソン|^HACK TO THE FUTURE|Asprova|Heuristics Contest)/.exec(
  72. contestName
  73. ) || /(^future-meets-you-contest|^hokudai-hitachi|^toyota-hc)/.exec(contestId) || [
  74. "toyota2023summer-final-open",
  75. "genocon2021",
  76. "stage0-2021",
  77. "caddi2019",
  78. "pakencamp-2019-day2",
  79. "kuronekoyamato-contest2019",
  80. "wn2017_1"
  81. ].includes(contestId)) {
  82. return "Marathon";
  83. }
  84. if (/(ドワンゴ|^Mujin|SoundHound|^codeFlyer|^COLOCON|みんなのプロコン|CODE THANKS FESTIVAL)/.exec(
  85. contestName
  86. ) || /(CODE FESTIVAL|^DISCO|日本最強プログラマー学生選手権|全国統一プログラミング王|Indeed)/.exec(
  87. contestName
  88. ) || /(^Donuts|^dwango|^DigitalArts|^Code Formula|天下一プログラマーコンテスト|^Toyota)/.exec(
  89. contestName
  90. )) {
  91. return "Other Sponsored";
  92. }
  93. return "Other Contests";
  94. }
  95. function isHeuristicContest(contestId, contestName) {
  96. const category = classifyContest(contestId, contestName);
  97. return ["AHC", "Marathon"].includes(category);
  98. }
  99. function isAlgorithmContest(contestId, contestName) {
  100. const category = classifyContest(contestId, contestName);
  101. return ["ABC", "ARC", "AGC", "PAST", "JOI", "JAG"].includes(category);
  102. }
  103. /*!
  104. * @kurkle/color v0.3.4
  105. * https://github.com/kurkle/color#readme
  106. * (c) 2024 Jukka Kurkela
  107. * Released under the MIT License
  108. */
  109. function round(v) {
  110. return v + 0.5 | 0;
  111. }
  112. const lim = (v, l, h) => Math.max(Math.min(v, h), l);
  113. function p2b(v) {
  114. return lim(round(v * 2.55), 0, 255);
  115. }
  116. function n2b(v) {
  117. return lim(round(v * 255), 0, 255);
  118. }
  119. function b2n(v) {
  120. return lim(round(v / 2.55) / 100, 0, 1);
  121. }
  122. function n2p(v) {
  123. return lim(round(v * 100), 0, 100);
  124. }
  125. const map$1 = { 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, A: 10, B: 11, C: 12, D: 13, E: 14, F: 15, a: 10, b: 11, c: 12, d: 13, e: 14, f: 15 };
  126. const hex = [..."0123456789ABCDEF"];
  127. const h1 = (b) => hex[b & 15];
  128. const h2 = (b) => hex[(b & 240) >> 4] + hex[b & 15];
  129. const eq = (b) => (b & 240) >> 4 === (b & 15);
  130. const isShort = (v) => eq(v.r) && eq(v.g) && eq(v.b) && eq(v.a);
  131. function hexParse(str) {
  132. var len = str.length;
  133. var ret;
  134. if (str[0] === "#") {
  135. if (len === 4 || len === 5) {
  136. ret = {
  137. r: 255 & map$1[str[1]] * 17,
  138. g: 255 & map$1[str[2]] * 17,
  139. b: 255 & map$1[str[3]] * 17,
  140. a: len === 5 ? map$1[str[4]] * 17 : 255
  141. };
  142. } else if (len === 7 || len === 9) {
  143. ret = {
  144. r: map$1[str[1]] << 4 | map$1[str[2]],
  145. g: map$1[str[3]] << 4 | map$1[str[4]],
  146. b: map$1[str[5]] << 4 | map$1[str[6]],
  147. a: len === 9 ? map$1[str[7]] << 4 | map$1[str[8]] : 255
  148. };
  149. }
  150. }
  151. return ret;
  152. }
  153. const alpha = (a, f) => a < 255 ? f(a) : "";
  154. function hexString(v) {
  155. var f = isShort(v) ? h1 : h2;
  156. return v ? "#" + f(v.r) + f(v.g) + f(v.b) + alpha(v.a, f) : void 0;
  157. }
  158. const HUE_RE = /^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;
  159. function hsl2rgbn(h, s, l) {
  160. const a = s * Math.min(l, 1 - l);
  161. const f = (n, k = (n + h / 30) % 12) => l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
  162. return [f(0), f(8), f(4)];
  163. }
  164. function hsv2rgbn(h, s, v) {
  165. const f = (n, k = (n + h / 60) % 6) => v - v * s * Math.max(Math.min(k, 4 - k, 1), 0);
  166. return [f(5), f(3), f(1)];
  167. }
  168. function hwb2rgbn(h, w, b) {
  169. const rgb = hsl2rgbn(h, 1, 0.5);
  170. let i;
  171. if (w + b > 1) {
  172. i = 1 / (w + b);
  173. w *= i;
  174. b *= i;
  175. }
  176. for (i = 0; i < 3; i++) {
  177. rgb[i] *= 1 - w - b;
  178. rgb[i] += w;
  179. }
  180. return rgb;
  181. }
  182. function hueValue(r, g, b, d, max) {
  183. if (r === max) {
  184. return (g - b) / d + (g < b ? 6 : 0);
  185. }
  186. if (g === max) {
  187. return (b - r) / d + 2;
  188. }
  189. return (r - g) / d + 4;
  190. }
  191. function rgb2hsl(v) {
  192. const range = 255;
  193. const r = v.r / range;
  194. const g = v.g / range;
  195. const b = v.b / range;
  196. const max = Math.max(r, g, b);
  197. const min = Math.min(r, g, b);
  198. const l = (max + min) / 2;
  199. let h, s, d;
  200. if (max !== min) {
  201. d = max - min;
  202. s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
  203. h = hueValue(r, g, b, d, max);
  204. h = h * 60 + 0.5;
  205. }
  206. return [h | 0, s || 0, l];
  207. }
  208. function calln(f, a, b, c) {
  209. return (Array.isArray(a) ? f(a[0], a[1], a[2]) : f(a, b, c)).map(n2b);
  210. }
  211. function hsl2rgb(h, s, l) {
  212. return calln(hsl2rgbn, h, s, l);
  213. }
  214. function hwb2rgb(h, w, b) {
  215. return calln(hwb2rgbn, h, w, b);
  216. }
  217. function hsv2rgb(h, s, v) {
  218. return calln(hsv2rgbn, h, s, v);
  219. }
  220. function hue(h) {
  221. return (h % 360 + 360) % 360;
  222. }
  223. function hueParse(str) {
  224. const m = HUE_RE.exec(str);
  225. let a = 255;
  226. let v;
  227. if (!m) {
  228. return;
  229. }
  230. if (m[5] !== v) {
  231. a = m[6] ? p2b(+m[5]) : n2b(+m[5]);
  232. }
  233. const h = hue(+m[2]);
  234. const p1 = +m[3] / 100;
  235. const p2 = +m[4] / 100;
  236. if (m[1] === "hwb") {
  237. v = hwb2rgb(h, p1, p2);
  238. } else if (m[1] === "hsv") {
  239. v = hsv2rgb(h, p1, p2);
  240. } else {
  241. v = hsl2rgb(h, p1, p2);
  242. }
  243. return {
  244. r: v[0],
  245. g: v[1],
  246. b: v[2],
  247. a
  248. };
  249. }
  250. function rotate(v, deg) {
  251. var h = rgb2hsl(v);
  252. h[0] = hue(h[0] + deg);
  253. h = hsl2rgb(h);
  254. v.r = h[0];
  255. v.g = h[1];
  256. v.b = h[2];
  257. }
  258. function hslString(v) {
  259. if (!v) {
  260. return;
  261. }
  262. const a = rgb2hsl(v);
  263. const h = a[0];
  264. const s = n2p(a[1]);
  265. const l = n2p(a[2]);
  266. return v.a < 255 ? `hsla(${h}, ${s}%, ${l}%, ${b2n(v.a)})` : `hsl(${h}, ${s}%, ${l}%)`;
  267. }
  268. const map = {
  269. x: "dark",
  270. Z: "light",
  271. Y: "re",
  272. X: "blu",
  273. W: "gr",
  274. V: "medium",
  275. U: "slate",
  276. A: "ee",
  277. T: "ol",
  278. S: "or",
  279. B: "ra",
  280. C: "lateg",
  281. D: "ights",
  282. R: "in",
  283. Q: "turquois",
  284. E: "hi",
  285. P: "ro",
  286. O: "al",
  287. N: "le",
  288. M: "de",
  289. L: "yello",
  290. F: "en",
  291. K: "ch",
  292. G: "arks",
  293. H: "ea",
  294. I: "ightg",
  295. J: "wh"
  296. };
  297. const names$1 = {
  298. OiceXe: "f0f8ff",
  299. antiquewEte: "faebd7",
  300. aqua: "ffff",
  301. aquamarRe: "7fffd4",
  302. azuY: "f0ffff",
  303. beige: "f5f5dc",
  304. bisque: "ffe4c4",
  305. black: "0",
  306. blanKedOmond: "ffebcd",
  307. Xe: "ff",
  308. XeviTet: "8a2be2",
  309. bPwn: "a52a2a",
  310. burlywood: "deb887",
  311. caMtXe: "5f9ea0",
  312. KartYuse: "7fff00",
  313. KocTate: "d2691e",
  314. cSO: "ff7f50",
  315. cSnflowerXe: "6495ed",
  316. cSnsilk: "fff8dc",
  317. crimson: "dc143c",
  318. cyan: "ffff",
  319. xXe: "8b",
  320. xcyan: "8b8b",
  321. xgTMnPd: "b8860b",
  322. xWay: "a9a9a9",
  323. xgYF: "6400",
  324. xgYy: "a9a9a9",
  325. xkhaki: "bdb76b",
  326. xmagFta: "8b008b",
  327. xTivegYF: "556b2f",
  328. xSange: "ff8c00",
  329. xScEd: "9932cc",
  330. xYd: "8b0000",
  331. xsOmon: "e9967a",
  332. xsHgYF: "8fbc8f",
  333. xUXe: "483d8b",
  334. xUWay: "2f4f4f",
  335. xUgYy: "2f4f4f",
  336. xQe: "ced1",
  337. xviTet: "9400d3",
  338. dAppRk: "ff1493",
  339. dApskyXe: "bfff",
  340. dimWay: "696969",
  341. dimgYy: "696969",
  342. dodgerXe: "1e90ff",
  343. fiYbrick: "b22222",
  344. flSOwEte: "fffaf0",
  345. foYstWAn: "228b22",
  346. fuKsia: "ff00ff",
  347. gaRsbSo: "dcdcdc",
  348. ghostwEte: "f8f8ff",
  349. gTd: "ffd700",
  350. gTMnPd: "daa520",
  351. Way: "808080",
  352. gYF: "8000",
  353. gYFLw: "adff2f",
  354. gYy: "808080",
  355. honeyMw: "f0fff0",
  356. hotpRk: "ff69b4",
  357. RdianYd: "cd5c5c",
  358. Rdigo: "4b0082",
  359. ivSy: "fffff0",
  360. khaki: "f0e68c",
  361. lavFMr: "e6e6fa",
  362. lavFMrXsh: "fff0f5",
  363. lawngYF: "7cfc00",
  364. NmoncEffon: "fffacd",
  365. ZXe: "add8e6",
  366. ZcSO: "f08080",
  367. Zcyan: "e0ffff",
  368. ZgTMnPdLw: "fafad2",
  369. ZWay: "d3d3d3",
  370. ZgYF: "90ee90",
  371. ZgYy: "d3d3d3",
  372. ZpRk: "ffb6c1",
  373. ZsOmon: "ffa07a",
  374. ZsHgYF: "20b2aa",
  375. ZskyXe: "87cefa",
  376. ZUWay: "778899",
  377. ZUgYy: "778899",
  378. ZstAlXe: "b0c4de",
  379. ZLw: "ffffe0",
  380. lime: "ff00",
  381. limegYF: "32cd32",
  382. lRF: "faf0e6",
  383. magFta: "ff00ff",
  384. maPon: "800000",
  385. VaquamarRe: "66cdaa",
  386. VXe: "cd",
  387. VScEd: "ba55d3",
  388. VpurpN: "9370db",
  389. VsHgYF: "3cb371",
  390. VUXe: "7b68ee",
  391. VsprRggYF: "fa9a",
  392. VQe: "48d1cc",
  393. VviTetYd: "c71585",
  394. midnightXe: "191970",
  395. mRtcYam: "f5fffa",
  396. mistyPse: "ffe4e1",
  397. moccasR: "ffe4b5",
  398. navajowEte: "ffdead",
  399. navy: "80",
  400. Tdlace: "fdf5e6",
  401. Tive: "808000",
  402. TivedBb: "6b8e23",
  403. Sange: "ffa500",
  404. SangeYd: "ff4500",
  405. ScEd: "da70d6",
  406. pOegTMnPd: "eee8aa",
  407. pOegYF: "98fb98",
  408. pOeQe: "afeeee",
  409. pOeviTetYd: "db7093",
  410. papayawEp: "ffefd5",
  411. pHKpuff: "ffdab9",
  412. peru: "cd853f",
  413. pRk: "ffc0cb",
  414. plum: "dda0dd",
  415. powMrXe: "b0e0e6",
  416. purpN: "800080",
  417. YbeccapurpN: "663399",
  418. Yd: "ff0000",
  419. Psybrown: "bc8f8f",
  420. PyOXe: "4169e1",
  421. saddNbPwn: "8b4513",
  422. sOmon: "fa8072",
  423. sandybPwn: "f4a460",
  424. sHgYF: "2e8b57",
  425. sHshell: "fff5ee",
  426. siFna: "a0522d",
  427. silver: "c0c0c0",
  428. skyXe: "87ceeb",
  429. UXe: "6a5acd",
  430. UWay: "708090",
  431. UgYy: "708090",
  432. snow: "fffafa",
  433. sprRggYF: "ff7f",
  434. stAlXe: "4682b4",
  435. tan: "d2b48c",
  436. teO: "8080",
  437. tEstN: "d8bfd8",
  438. tomato: "ff6347",
  439. Qe: "40e0d0",
  440. viTet: "ee82ee",
  441. JHt: "f5deb3",
  442. wEte: "ffffff",
  443. wEtesmoke: "f5f5f5",
  444. Lw: "ffff00",
  445. LwgYF: "9acd32"
  446. };
  447. function unpack() {
  448. const unpacked = {};
  449. const keys = Object.keys(names$1);
  450. const tkeys = Object.keys(map);
  451. let i, j, k, ok, nk;
  452. for (i = 0; i < keys.length; i++) {
  453. ok = nk = keys[i];
  454. for (j = 0; j < tkeys.length; j++) {
  455. k = tkeys[j];
  456. nk = nk.replace(k, map[k]);
  457. }
  458. k = parseInt(names$1[ok], 16);
  459. unpacked[nk] = [k >> 16 & 255, k >> 8 & 255, k & 255];
  460. }
  461. return unpacked;
  462. }
  463. let names;
  464. function nameParse(str) {
  465. if (!names) {
  466. names = unpack();
  467. names.transparent = [0, 0, 0, 0];
  468. }
  469. const a = names[str.toLowerCase()];
  470. return a && {
  471. r: a[0],
  472. g: a[1],
  473. b: a[2],
  474. a: a.length === 4 ? a[3] : 255
  475. };
  476. }
  477. const RGB_RE = /^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;
  478. function rgbParse(str) {
  479. const m = RGB_RE.exec(str);
  480. let a = 255;
  481. let r, g, b;
  482. if (!m) {
  483. return;
  484. }
  485. if (m[7] !== r) {
  486. const v = +m[7];
  487. a = m[8] ? p2b(v) : lim(v * 255, 0, 255);
  488. }
  489. r = +m[1];
  490. g = +m[3];
  491. b = +m[5];
  492. r = 255 & (m[2] ? p2b(r) : lim(r, 0, 255));
  493. g = 255 & (m[4] ? p2b(g) : lim(g, 0, 255));
  494. b = 255 & (m[6] ? p2b(b) : lim(b, 0, 255));
  495. return {
  496. r,
  497. g,
  498. b,
  499. a
  500. };
  501. }
  502. function rgbString(v) {
  503. return v && (v.a < 255 ? `rgba(${v.r}, ${v.g}, ${v.b}, ${b2n(v.a)})` : `rgb(${v.r}, ${v.g}, ${v.b})`);
  504. }
  505. const to = (v) => v <= 31308e-7 ? v * 12.92 : Math.pow(v, 1 / 2.4) * 1.055 - 0.055;
  506. const from = (v) => v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
  507. function interpolate$1(rgb1, rgb2, t) {
  508. const r = from(b2n(rgb1.r));
  509. const g = from(b2n(rgb1.g));
  510. const b = from(b2n(rgb1.b));
  511. return {
  512. r: n2b(to(r + t * (from(b2n(rgb2.r)) - r))),
  513. g: n2b(to(g + t * (from(b2n(rgb2.g)) - g))),
  514. b: n2b(to(b + t * (from(b2n(rgb2.b)) - b))),
  515. a: rgb1.a + t * (rgb2.a - rgb1.a)
  516. };
  517. }
  518. function modHSL(v, i, ratio) {
  519. if (v) {
  520. let tmp = rgb2hsl(v);
  521. tmp[i] = Math.max(0, Math.min(tmp[i] + tmp[i] * ratio, i === 0 ? 360 : 1));
  522. tmp = hsl2rgb(tmp);
  523. v.r = tmp[0];
  524. v.g = tmp[1];
  525. v.b = tmp[2];
  526. }
  527. }
  528. function clone$1(v, proto) {
  529. return v ? Object.assign(proto || {}, v) : v;
  530. }
  531. function fromObject(input) {
  532. var v = { r: 0, g: 0, b: 0, a: 255 };
  533. if (Array.isArray(input)) {
  534. if (input.length >= 3) {
  535. v = { r: input[0], g: input[1], b: input[2], a: 255 };
  536. if (input.length > 3) {
  537. v.a = n2b(input[3]);
  538. }
  539. }
  540. } else {
  541. v = clone$1(input, { r: 0, g: 0, b: 0, a: 1 });
  542. v.a = n2b(v.a);
  543. }
  544. return v;
  545. }
  546. function functionParse(str) {
  547. if (str.charAt(0) === "r") {
  548. return rgbParse(str);
  549. }
  550. return hueParse(str);
  551. }
  552. class Color {
  553. constructor(input) {
  554. if (input instanceof Color) {
  555. return input;
  556. }
  557. const type = typeof input;
  558. let v;
  559. if (type === "object") {
  560. v = fromObject(input);
  561. } else if (type === "string") {
  562. v = hexParse(input) || nameParse(input) || functionParse(input);
  563. }
  564. this._rgb = v;
  565. this._valid = !!v;
  566. }
  567. get valid() {
  568. return this._valid;
  569. }
  570. get rgb() {
  571. var v = clone$1(this._rgb);
  572. if (v) {
  573. v.a = b2n(v.a);
  574. }
  575. return v;
  576. }
  577. set rgb(obj) {
  578. this._rgb = fromObject(obj);
  579. }
  580. rgbString() {
  581. return this._valid ? rgbString(this._rgb) : void 0;
  582. }
  583. hexString() {
  584. return this._valid ? hexString(this._rgb) : void 0;
  585. }
  586. hslString() {
  587. return this._valid ? hslString(this._rgb) : void 0;
  588. }
  589. mix(color2, weight) {
  590. if (color2) {
  591. const c1 = this.rgb;
  592. const c2 = color2.rgb;
  593. let w2;
  594. const p = weight === w2 ? 0.5 : weight;
  595. const w = 2 * p - 1;
  596. const a = c1.a - c2.a;
  597. const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2;
  598. w2 = 1 - w1;
  599. c1.r = 255 & w1 * c1.r + w2 * c2.r + 0.5;
  600. c1.g = 255 & w1 * c1.g + w2 * c2.g + 0.5;
  601. c1.b = 255 & w1 * c1.b + w2 * c2.b + 0.5;
  602. c1.a = p * c1.a + (1 - p) * c2.a;
  603. this.rgb = c1;
  604. }
  605. return this;
  606. }
  607. interpolate(color2, t) {
  608. if (color2) {
  609. this._rgb = interpolate$1(this._rgb, color2._rgb, t);
  610. }
  611. return this;
  612. }
  613. clone() {
  614. return new Color(this.rgb);
  615. }
  616. alpha(a) {
  617. this._rgb.a = n2b(a);
  618. return this;
  619. }
  620. clearer(ratio) {
  621. const rgb = this._rgb;
  622. rgb.a *= 1 - ratio;
  623. return this;
  624. }
  625. greyscale() {
  626. const rgb = this._rgb;
  627. const val = round(rgb.r * 0.3 + rgb.g * 0.59 + rgb.b * 0.11);
  628. rgb.r = rgb.g = rgb.b = val;
  629. return this;
  630. }
  631. opaquer(ratio) {
  632. const rgb = this._rgb;
  633. rgb.a *= 1 + ratio;
  634. return this;
  635. }
  636. negate() {
  637. const v = this._rgb;
  638. v.r = 255 - v.r;
  639. v.g = 255 - v.g;
  640. v.b = 255 - v.b;
  641. return this;
  642. }
  643. lighten(ratio) {
  644. modHSL(this._rgb, 2, ratio);
  645. return this;
  646. }
  647. darken(ratio) {
  648. modHSL(this._rgb, 2, -ratio);
  649. return this;
  650. }
  651. saturate(ratio) {
  652. modHSL(this._rgb, 1, ratio);
  653. return this;
  654. }
  655. desaturate(ratio) {
  656. modHSL(this._rgb, 1, -ratio);
  657. return this;
  658. }
  659. rotate(deg) {
  660. rotate(this._rgb, deg);
  661. return this;
  662. }
  663. }
  664. /*!
  665. * Chart.js v4.4.7
  666. * https://www.chartjs.org
  667. * (c) 2024 Chart.js Contributors
  668. * Released under the MIT License
  669. */
  670. const uid = /* @__PURE__ */ (() => {
  671. let id = 0;
  672. return () => id++;
  673. })();
  674. function isNullOrUndef(value) {
  675. return value === null || value === void 0;
  676. }
  677. function isArray(value) {
  678. if (Array.isArray && Array.isArray(value)) {
  679. return true;
  680. }
  681. const type = Object.prototype.toString.call(value);
  682. if (type.slice(0, 7) === "[object" && type.slice(-6) === "Array]") {
  683. return true;
  684. }
  685. return false;
  686. }
  687. function isObject(value) {
  688. return value !== null && Object.prototype.toString.call(value) === "[object Object]";
  689. }
  690. function isNumberFinite(value) {
  691. return (typeof value === "number" || value instanceof Number) && isFinite(+value);
  692. }
  693. function finiteOrDefault(value, defaultValue) {
  694. return isNumberFinite(value) ? value : defaultValue;
  695. }
  696. function valueOrDefault(value, defaultValue) {
  697. return typeof value === "undefined" ? defaultValue : value;
  698. }
  699. const toDimension = (value, dimension) => typeof value === "string" && value.endsWith("%") ? parseFloat(value) / 100 * dimension : +value;
  700. function callback(fn, args, thisArg) {
  701. if (fn && typeof fn.call === "function") {
  702. return fn.apply(thisArg, args);
  703. }
  704. }
  705. function each(loopable, fn, thisArg, reverse) {
  706. let i, len, keys;
  707. if (isArray(loopable)) {
  708. len = loopable.length;
  709. {
  710. for (i = 0; i < len; i++) {
  711. fn.call(thisArg, loopable[i], i);
  712. }
  713. }
  714. } else if (isObject(loopable)) {
  715. keys = Object.keys(loopable);
  716. len = keys.length;
  717. for (i = 0; i < len; i++) {
  718. fn.call(thisArg, loopable[keys[i]], keys[i]);
  719. }
  720. }
  721. }
  722. function _elementsEqual(a0, a1) {
  723. let i, ilen, v0, v1;
  724. if (!a0 || !a1 || a0.length !== a1.length) {
  725. return false;
  726. }
  727. for (i = 0, ilen = a0.length; i < ilen; ++i) {
  728. v0 = a0[i];
  729. v1 = a1[i];
  730. if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {
  731. return false;
  732. }
  733. }
  734. return true;
  735. }
  736. function clone(source) {
  737. if (isArray(source)) {
  738. return source.map(clone);
  739. }
  740. if (isObject(source)) {
  741. const target = /* @__PURE__ */ Object.create(null);
  742. const keys = Object.keys(source);
  743. const klen = keys.length;
  744. let k = 0;
  745. for (; k < klen; ++k) {
  746. target[keys[k]] = clone(source[keys[k]]);
  747. }
  748. return target;
  749. }
  750. return source;
  751. }
  752. function isValidKey(key) {
  753. return [
  754. "__proto__",
  755. "prototype",
  756. "constructor"
  757. ].indexOf(key) === -1;
  758. }
  759. function _merger(key, target, source, options) {
  760. if (!isValidKey(key)) {
  761. return;
  762. }
  763. const tval = target[key];
  764. const sval = source[key];
  765. if (isObject(tval) && isObject(sval)) {
  766. merge(tval, sval, options);
  767. } else {
  768. target[key] = clone(sval);
  769. }
  770. }
  771. function merge(target, source, options) {
  772. const sources = isArray(source) ? source : [
  773. source
  774. ];
  775. const ilen = sources.length;
  776. if (!isObject(target)) {
  777. return target;
  778. }
  779. options = options || {};
  780. const merger = options.merger || _merger;
  781. let current;
  782. for (let i = 0; i < ilen; ++i) {
  783. current = sources[i];
  784. if (!isObject(current)) {
  785. continue;
  786. }
  787. const keys = Object.keys(current);
  788. for (let k = 0, klen = keys.length; k < klen; ++k) {
  789. merger(keys[k], target, current, options);
  790. }
  791. }
  792. return target;
  793. }
  794. function mergeIf(target, source) {
  795. return merge(target, source, {
  796. merger: _mergerIf
  797. });
  798. }
  799. function _mergerIf(key, target, source) {
  800. if (!isValidKey(key)) {
  801. return;
  802. }
  803. const tval = target[key];
  804. const sval = source[key];
  805. if (isObject(tval) && isObject(sval)) {
  806. mergeIf(tval, sval);
  807. } else if (!Object.prototype.hasOwnProperty.call(target, key)) {
  808. target[key] = clone(sval);
  809. }
  810. }
  811. const keyResolvers = {
  812. // Chart.helpers.core resolveObjectKey should resolve empty key to root object
  813. "": (v) => v,
  814. // default resolvers
  815. x: (o) => o.x,
  816. y: (o) => o.y
  817. };
  818. function _splitKey(key) {
  819. const parts = key.split(".");
  820. const keys = [];
  821. let tmp = "";
  822. for (const part of parts) {
  823. tmp += part;
  824. if (tmp.endsWith("\\")) {
  825. tmp = tmp.slice(0, -1) + ".";
  826. } else {
  827. keys.push(tmp);
  828. tmp = "";
  829. }
  830. }
  831. return keys;
  832. }
  833. function _getKeyResolver(key) {
  834. const keys = _splitKey(key);
  835. return (obj) => {
  836. for (const k of keys) {
  837. if (k === "") {
  838. break;
  839. }
  840. obj = obj && obj[k];
  841. }
  842. return obj;
  843. };
  844. }
  845. function resolveObjectKey(obj, key) {
  846. const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));
  847. return resolver(obj);
  848. }
  849. function _capitalize(str) {
  850. return str.charAt(0).toUpperCase() + str.slice(1);
  851. }
  852. const defined = (value) => typeof value !== "undefined";
  853. const isFunction = (value) => typeof value === "function";
  854. const setsEqual = (a, b) => {
  855. if (a.size !== b.size) {
  856. return false;
  857. }
  858. for (const item of a) {
  859. if (!b.has(item)) {
  860. return false;
  861. }
  862. }
  863. return true;
  864. };
  865. function _isClickEvent(e) {
  866. return e.type === "mouseup" || e.type === "click" || e.type === "contextmenu";
  867. }
  868. const PI = Math.PI;
  869. const TAU = 2 * PI;
  870. const PITAU = TAU + PI;
  871. const INFINITY = Number.POSITIVE_INFINITY;
  872. const RAD_PER_DEG = PI / 180;
  873. const HALF_PI = PI / 2;
  874. const QUARTER_PI = PI / 4;
  875. const TWO_THIRDS_PI = PI * 2 / 3;
  876. const log10 = Math.log10;
  877. const sign = Math.sign;
  878. function almostEquals(x, y, epsilon) {
  879. return Math.abs(x - y) < epsilon;
  880. }
  881. function niceNum(range) {
  882. const roundedRange = Math.round(range);
  883. range = almostEquals(range, roundedRange, range / 1e3) ? roundedRange : range;
  884. const niceRange = Math.pow(10, Math.floor(log10(range)));
  885. const fraction = range / niceRange;
  886. const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;
  887. return niceFraction * niceRange;
  888. }
  889. function _factorize(value) {
  890. const result = [];
  891. const sqrt = Math.sqrt(value);
  892. let i;
  893. for (i = 1; i < sqrt; i++) {
  894. if (value % i === 0) {
  895. result.push(i);
  896. result.push(value / i);
  897. }
  898. }
  899. if (sqrt === (sqrt | 0)) {
  900. result.push(sqrt);
  901. }
  902. result.sort((a, b) => a - b).pop();
  903. return result;
  904. }
  905. function isNumber(n) {
  906. return !isNaN(parseFloat(n)) && isFinite(n);
  907. }
  908. function almostWhole(x, epsilon) {
  909. const rounded = Math.round(x);
  910. return rounded - epsilon <= x && rounded + epsilon >= x;
  911. }
  912. function _setMinAndMaxByKey(array, target, property) {
  913. let i, ilen, value;
  914. for (i = 0, ilen = array.length; i < ilen; i++) {
  915. value = array[i][property];
  916. if (!isNaN(value)) {
  917. target.min = Math.min(target.min, value);
  918. target.max = Math.max(target.max, value);
  919. }
  920. }
  921. }
  922. function toRadians(degrees) {
  923. return degrees * (PI / 180);
  924. }
  925. function toDegrees(radians) {
  926. return radians * (180 / PI);
  927. }
  928. function _decimalPlaces(x) {
  929. if (!isNumberFinite(x)) {
  930. return;
  931. }
  932. let e = 1;
  933. let p = 0;
  934. while (Math.round(x * e) / e !== x) {
  935. e *= 10;
  936. p++;
  937. }
  938. return p;
  939. }
  940. function getAngleFromPoint(centrePoint, anglePoint) {
  941. const distanceFromXCenter = anglePoint.x - centrePoint.x;
  942. const distanceFromYCenter = anglePoint.y - centrePoint.y;
  943. const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
  944. let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
  945. if (angle < -0.5 * PI) {
  946. angle += TAU;
  947. }
  948. return {
  949. angle,
  950. distance: radialDistanceFromCenter
  951. };
  952. }
  953. function distanceBetweenPoints(pt1, pt2) {
  954. return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
  955. }
  956. function _angleDiff(a, b) {
  957. return (a - b + PITAU) % TAU - PI;
  958. }
  959. function _normalizeAngle(a) {
  960. return (a % TAU + TAU) % TAU;
  961. }
  962. function _angleBetween(angle, start, end, sameAngleIsFullCircle) {
  963. const a = _normalizeAngle(angle);
  964. const s = _normalizeAngle(start);
  965. const e = _normalizeAngle(end);
  966. const angleToStart = _normalizeAngle(s - a);
  967. const angleToEnd = _normalizeAngle(e - a);
  968. const startToAngle = _normalizeAngle(a - s);
  969. const endToAngle = _normalizeAngle(a - e);
  970. return a === s || a === e || sameAngleIsFullCircle && s === e || angleToStart > angleToEnd && startToAngle < endToAngle;
  971. }
  972. function _limitValue(value, min, max) {
  973. return Math.max(min, Math.min(max, value));
  974. }
  975. function _int16Range(value) {
  976. return _limitValue(value, -32768, 32767);
  977. }
  978. function _isBetween(value, start, end, epsilon = 1e-6) {
  979. return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;
  980. }
  981. function _lookup(table, value, cmp) {
  982. cmp = cmp || ((index) => table[index] < value);
  983. let hi = table.length - 1;
  984. let lo = 0;
  985. let mid;
  986. while (hi - lo > 1) {
  987. mid = lo + hi >> 1;
  988. if (cmp(mid)) {
  989. lo = mid;
  990. } else {
  991. hi = mid;
  992. }
  993. }
  994. return {
  995. lo,
  996. hi
  997. };
  998. }
  999. const _lookupByKey = (table, key, value, last) => _lookup(table, value, last ? (index) => {
  1000. const ti = table[index][key];
  1001. return ti < value || ti === value && table[index + 1][key] === value;
  1002. } : (index) => table[index][key] < value);
  1003. const _rlookupByKey = (table, key, value) => _lookup(table, value, (index) => table[index][key] >= value);
  1004. function _filterBetween(values, min, max) {
  1005. let start = 0;
  1006. let end = values.length;
  1007. while (start < end && values[start] < min) {
  1008. start++;
  1009. }
  1010. while (end > start && values[end - 1] > max) {
  1011. end--;
  1012. }
  1013. return start > 0 || end < values.length ? values.slice(start, end) : values;
  1014. }
  1015. const arrayEvents = [
  1016. "push",
  1017. "pop",
  1018. "shift",
  1019. "splice",
  1020. "unshift"
  1021. ];
  1022. function listenArrayEvents(array, listener) {
  1023. if (array._chartjs) {
  1024. array._chartjs.listeners.push(listener);
  1025. return;
  1026. }
  1027. Object.defineProperty(array, "_chartjs", {
  1028. configurable: true,
  1029. enumerable: false,
  1030. value: {
  1031. listeners: [
  1032. listener
  1033. ]
  1034. }
  1035. });
  1036. arrayEvents.forEach((key) => {
  1037. const method = "_onData" + _capitalize(key);
  1038. const base = array[key];
  1039. Object.defineProperty(array, key, {
  1040. configurable: true,
  1041. enumerable: false,
  1042. value(...args) {
  1043. const res = base.apply(this, args);
  1044. array._chartjs.listeners.forEach((object) => {
  1045. if (typeof object[method] === "function") {
  1046. object[method](...args);
  1047. }
  1048. });
  1049. return res;
  1050. }
  1051. });
  1052. });
  1053. }
  1054. function unlistenArrayEvents(array, listener) {
  1055. const stub = array._chartjs;
  1056. if (!stub) {
  1057. return;
  1058. }
  1059. const listeners = stub.listeners;
  1060. const index = listeners.indexOf(listener);
  1061. if (index !== -1) {
  1062. listeners.splice(index, 1);
  1063. }
  1064. if (listeners.length > 0) {
  1065. return;
  1066. }
  1067. arrayEvents.forEach((key) => {
  1068. delete array[key];
  1069. });
  1070. delete array._chartjs;
  1071. }
  1072. function _arrayUnique(items) {
  1073. const set2 = new Set(items);
  1074. if (set2.size === items.length) {
  1075. return items;
  1076. }
  1077. return Array.from(set2);
  1078. }
  1079. const requestAnimFrame = function() {
  1080. if (typeof window === "undefined") {
  1081. return function(callback2) {
  1082. return callback2();
  1083. };
  1084. }
  1085. return window.requestAnimationFrame;
  1086. }();
  1087. function throttled(fn, thisArg) {
  1088. let argsToUse = [];
  1089. let ticking = false;
  1090. return function(...args) {
  1091. argsToUse = args;
  1092. if (!ticking) {
  1093. ticking = true;
  1094. requestAnimFrame.call(window, () => {
  1095. ticking = false;
  1096. fn.apply(thisArg, argsToUse);
  1097. });
  1098. }
  1099. };
  1100. }
  1101. function debounce(fn, delay) {
  1102. let timeout;
  1103. return function(...args) {
  1104. if (delay) {
  1105. clearTimeout(timeout);
  1106. timeout = setTimeout(fn, delay, args);
  1107. } else {
  1108. fn.apply(this, args);
  1109. }
  1110. return delay;
  1111. };
  1112. }
  1113. const _toLeftRightCenter = (align) => align === "start" ? "left" : align === "end" ? "right" : "center";
  1114. const _alignStartEnd = (align, start, end) => align === "start" ? start : align === "end" ? end : (start + end) / 2;
  1115. function _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {
  1116. const pointCount = points.length;
  1117. let start = 0;
  1118. let count = pointCount;
  1119. if (meta._sorted) {
  1120. const { iScale, _parsed } = meta;
  1121. const axis = iScale.axis;
  1122. const { min, max, minDefined, maxDefined } = iScale.getUserBounds();
  1123. if (minDefined) {
  1124. start = _limitValue(Math.min(
  1125. // @ts-expect-error Need to type _parsed
  1126. _lookupByKey(_parsed, axis, min).lo,
  1127. // @ts-expect-error Need to fix types on _lookupByKey
  1128. animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo
  1129. ), 0, pointCount - 1);
  1130. }
  1131. if (maxDefined) {
  1132. count = _limitValue(Math.max(
  1133. // @ts-expect-error Need to type _parsed
  1134. _lookupByKey(_parsed, iScale.axis, max, true).hi + 1,
  1135. // @ts-expect-error Need to fix types on _lookupByKey
  1136. animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1
  1137. ), start, pointCount) - start;
  1138. } else {
  1139. count = pointCount - start;
  1140. }
  1141. }
  1142. return {
  1143. start,
  1144. count
  1145. };
  1146. }
  1147. function _scaleRangesChanged(meta) {
  1148. const { xScale, yScale, _scaleRanges } = meta;
  1149. const newRanges = {
  1150. xmin: xScale.min,
  1151. xmax: xScale.max,
  1152. ymin: yScale.min,
  1153. ymax: yScale.max
  1154. };
  1155. if (!_scaleRanges) {
  1156. meta._scaleRanges = newRanges;
  1157. return true;
  1158. }
  1159. const changed = _scaleRanges.xmin !== xScale.min || _scaleRanges.xmax !== xScale.max || _scaleRanges.ymin !== yScale.min || _scaleRanges.ymax !== yScale.max;
  1160. Object.assign(_scaleRanges, newRanges);
  1161. return changed;
  1162. }
  1163. const atEdge = (t) => t === 0 || t === 1;
  1164. const elasticIn = (t, s, p) => -(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));
  1165. const elasticOut = (t, s, p) => Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;
  1166. const effects = {
  1167. linear: (t) => t,
  1168. easeInQuad: (t) => t * t,
  1169. easeOutQuad: (t) => -t * (t - 2),
  1170. easeInOutQuad: (t) => (t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1),
  1171. easeInCubic: (t) => t * t * t,
  1172. easeOutCubic: (t) => (t -= 1) * t * t + 1,
  1173. easeInOutCubic: (t) => (t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2),
  1174. easeInQuart: (t) => t * t * t * t,
  1175. easeOutQuart: (t) => -((t -= 1) * t * t * t - 1),
  1176. easeInOutQuart: (t) => (t /= 0.5) < 1 ? 0.5 * t * t * t * t : -0.5 * ((t -= 2) * t * t * t - 2),
  1177. easeInQuint: (t) => t * t * t * t * t,
  1178. easeOutQuint: (t) => (t -= 1) * t * t * t * t + 1,
  1179. easeInOutQuint: (t) => (t /= 0.5) < 1 ? 0.5 * t * t * t * t * t : 0.5 * ((t -= 2) * t * t * t * t + 2),
  1180. easeInSine: (t) => -Math.cos(t * HALF_PI) + 1,
  1181. easeOutSine: (t) => Math.sin(t * HALF_PI),
  1182. easeInOutSine: (t) => -0.5 * (Math.cos(PI * t) - 1),
  1183. easeInExpo: (t) => t === 0 ? 0 : Math.pow(2, 10 * (t - 1)),
  1184. easeOutExpo: (t) => t === 1 ? 1 : -Math.pow(2, -10 * t) + 1,
  1185. 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),
  1186. easeInCirc: (t) => t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1),
  1187. easeOutCirc: (t) => Math.sqrt(1 - (t -= 1) * t),
  1188. easeInOutCirc: (t) => (t /= 0.5) < 1 ? -0.5 * (Math.sqrt(1 - t * t) - 1) : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1),
  1189. easeInElastic: (t) => atEdge(t) ? t : elasticIn(t, 0.075, 0.3),
  1190. easeOutElastic: (t) => atEdge(t) ? t : elasticOut(t, 0.075, 0.3),
  1191. easeInOutElastic(t) {
  1192. const s = 0.1125;
  1193. const p = 0.45;
  1194. return atEdge(t) ? t : t < 0.5 ? 0.5 * elasticIn(t * 2, s, p) : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p);
  1195. },
  1196. easeInBack(t) {
  1197. const s = 1.70158;
  1198. return t * t * ((s + 1) * t - s);
  1199. },
  1200. easeOutBack(t) {
  1201. const s = 1.70158;
  1202. return (t -= 1) * t * ((s + 1) * t + s) + 1;
  1203. },
  1204. easeInOutBack(t) {
  1205. let s = 1.70158;
  1206. if ((t /= 0.5) < 1) {
  1207. return 0.5 * (t * t * (((s *= 1.525) + 1) * t - s));
  1208. }
  1209. return 0.5 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2);
  1210. },
  1211. easeInBounce: (t) => 1 - effects.easeOutBounce(1 - t),
  1212. easeOutBounce(t) {
  1213. const m = 7.5625;
  1214. const d = 2.75;
  1215. if (t < 1 / d) {
  1216. return m * t * t;
  1217. }
  1218. if (t < 2 / d) {
  1219. return m * (t -= 1.5 / d) * t + 0.75;
  1220. }
  1221. if (t < 2.5 / d) {
  1222. return m * (t -= 2.25 / d) * t + 0.9375;
  1223. }
  1224. return m * (t -= 2.625 / d) * t + 0.984375;
  1225. },
  1226. easeInOutBounce: (t) => t < 0.5 ? effects.easeInBounce(t * 2) * 0.5 : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5
  1227. };
  1228. function isPatternOrGradient(value) {
  1229. if (value && typeof value === "object") {
  1230. const type = value.toString();
  1231. return type === "[object CanvasPattern]" || type === "[object CanvasGradient]";
  1232. }
  1233. return false;
  1234. }
  1235. function color(value) {
  1236. return isPatternOrGradient(value) ? value : new Color(value);
  1237. }
  1238. function getHoverColor(value) {
  1239. return isPatternOrGradient(value) ? value : new Color(value).saturate(0.5).darken(0.1).hexString();
  1240. }
  1241. const numbers = [
  1242. "x",
  1243. "y",
  1244. "borderWidth",
  1245. "radius",
  1246. "tension"
  1247. ];
  1248. const colors = [
  1249. "color",
  1250. "borderColor",
  1251. "backgroundColor"
  1252. ];
  1253. function applyAnimationsDefaults(defaults2) {
  1254. defaults2.set("animation", {
  1255. delay: void 0,
  1256. duration: 1e3,
  1257. easing: "easeOutQuart",
  1258. fn: void 0,
  1259. from: void 0,
  1260. loop: void 0,
  1261. to: void 0,
  1262. type: void 0
  1263. });
  1264. defaults2.describe("animation", {
  1265. _fallback: false,
  1266. _indexable: false,
  1267. _scriptable: (name) => name !== "onProgress" && name !== "onComplete" && name !== "fn"
  1268. });
  1269. defaults2.set("animations", {
  1270. colors: {
  1271. type: "color",
  1272. properties: colors
  1273. },
  1274. numbers: {
  1275. type: "number",
  1276. properties: numbers
  1277. }
  1278. });
  1279. defaults2.describe("animations", {
  1280. _fallback: "animation"
  1281. });
  1282. defaults2.set("transitions", {
  1283. active: {
  1284. animation: {
  1285. duration: 400
  1286. }
  1287. },
  1288. resize: {
  1289. animation: {
  1290. duration: 0
  1291. }
  1292. },
  1293. show: {
  1294. animations: {
  1295. colors: {
  1296. from: "transparent"
  1297. },
  1298. visible: {
  1299. type: "boolean",
  1300. duration: 0
  1301. }
  1302. }
  1303. },
  1304. hide: {
  1305. animations: {
  1306. colors: {
  1307. to: "transparent"
  1308. },
  1309. visible: {
  1310. type: "boolean",
  1311. easing: "linear",
  1312. fn: (v) => v | 0
  1313. }
  1314. }
  1315. }
  1316. });
  1317. }
  1318. function applyLayoutsDefaults(defaults2) {
  1319. defaults2.set("layout", {
  1320. autoPadding: true,
  1321. padding: {
  1322. top: 0,
  1323. right: 0,
  1324. bottom: 0,
  1325. left: 0
  1326. }
  1327. });
  1328. }
  1329. const intlCache = /* @__PURE__ */ new Map();
  1330. function getNumberFormat(locale, options) {
  1331. options = options || {};
  1332. const cacheKey = locale + JSON.stringify(options);
  1333. let formatter = intlCache.get(cacheKey);
  1334. if (!formatter) {
  1335. formatter = new Intl.NumberFormat(locale, options);
  1336. intlCache.set(cacheKey, formatter);
  1337. }
  1338. return formatter;
  1339. }
  1340. function formatNumber(num, locale, options) {
  1341. return getNumberFormat(locale, options).format(num);
  1342. }
  1343. const formatters = {
  1344. values(value) {
  1345. return isArray(value) ? value : "" + value;
  1346. },
  1347. numeric(tickValue, index, ticks) {
  1348. if (tickValue === 0) {
  1349. return "0";
  1350. }
  1351. const locale = this.chart.options.locale;
  1352. let notation;
  1353. let delta = tickValue;
  1354. if (ticks.length > 1) {
  1355. const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));
  1356. if (maxTick < 1e-4 || maxTick > 1e15) {
  1357. notation = "scientific";
  1358. }
  1359. delta = calculateDelta(tickValue, ticks);
  1360. }
  1361. const logDelta = log10(Math.abs(delta));
  1362. const numDecimal = isNaN(logDelta) ? 1 : Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0);
  1363. const options = {
  1364. notation,
  1365. minimumFractionDigits: numDecimal,
  1366. maximumFractionDigits: numDecimal
  1367. };
  1368. Object.assign(options, this.options.ticks.format);
  1369. return formatNumber(tickValue, locale, options);
  1370. },
  1371. logarithmic(tickValue, index, ticks) {
  1372. if (tickValue === 0) {
  1373. return "0";
  1374. }
  1375. const remain = ticks[index].significand || tickValue / Math.pow(10, Math.floor(log10(tickValue)));
  1376. if ([
  1377. 1,
  1378. 2,
  1379. 3,
  1380. 5,
  1381. 10,
  1382. 15
  1383. ].includes(remain) || index > 0.8 * ticks.length) {
  1384. return formatters.numeric.call(this, tickValue, index, ticks);
  1385. }
  1386. return "";
  1387. }
  1388. };
  1389. function calculateDelta(tickValue, ticks) {
  1390. let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;
  1391. if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) {
  1392. delta = tickValue - Math.floor(tickValue);
  1393. }
  1394. return delta;
  1395. }
  1396. var Ticks = {
  1397. formatters
  1398. };
  1399. function applyScaleDefaults(defaults2) {
  1400. defaults2.set("scale", {
  1401. display: true,
  1402. offset: false,
  1403. reverse: false,
  1404. beginAtZero: false,
  1405. bounds: "ticks",
  1406. clip: true,
  1407. grace: 0,
  1408. grid: {
  1409. display: true,
  1410. lineWidth: 1,
  1411. drawOnChartArea: true,
  1412. drawTicks: true,
  1413. tickLength: 8,
  1414. tickWidth: (_ctx, options) => options.lineWidth,
  1415. tickColor: (_ctx, options) => options.color,
  1416. offset: false
  1417. },
  1418. border: {
  1419. display: true,
  1420. dash: [],
  1421. dashOffset: 0,
  1422. width: 1
  1423. },
  1424. title: {
  1425. display: false,
  1426. text: "",
  1427. padding: {
  1428. top: 4,
  1429. bottom: 4
  1430. }
  1431. },
  1432. ticks: {
  1433. minRotation: 0,
  1434. maxRotation: 50,
  1435. mirror: false,
  1436. textStrokeWidth: 0,
  1437. textStrokeColor: "",
  1438. padding: 3,
  1439. display: true,
  1440. autoSkip: true,
  1441. autoSkipPadding: 3,
  1442. labelOffset: 0,
  1443. callback: Ticks.formatters.values,
  1444. minor: {},
  1445. major: {},
  1446. align: "center",
  1447. crossAlign: "near",
  1448. showLabelBackdrop: false,
  1449. backdropColor: "rgba(255, 255, 255, 0.75)",
  1450. backdropPadding: 2
  1451. }
  1452. });
  1453. defaults2.route("scale.ticks", "color", "", "color");
  1454. defaults2.route("scale.grid", "color", "", "borderColor");
  1455. defaults2.route("scale.border", "color", "", "borderColor");
  1456. defaults2.route("scale.title", "color", "", "color");
  1457. defaults2.describe("scale", {
  1458. _fallback: false,
  1459. _scriptable: (name) => !name.startsWith("before") && !name.startsWith("after") && name !== "callback" && name !== "parser",
  1460. _indexable: (name) => name !== "borderDash" && name !== "tickBorderDash" && name !== "dash"
  1461. });
  1462. defaults2.describe("scales", {
  1463. _fallback: "scale"
  1464. });
  1465. defaults2.describe("scale.ticks", {
  1466. _scriptable: (name) => name !== "backdropPadding" && name !== "callback",
  1467. _indexable: (name) => name !== "backdropPadding"
  1468. });
  1469. }
  1470. const overrides = /* @__PURE__ */ Object.create(null);
  1471. const descriptors = /* @__PURE__ */ Object.create(null);
  1472. function getScope$1(node, key) {
  1473. if (!key) {
  1474. return node;
  1475. }
  1476. const keys = key.split(".");
  1477. for (let i = 0, n = keys.length; i < n; ++i) {
  1478. const k = keys[i];
  1479. node = node[k] || (node[k] = /* @__PURE__ */ Object.create(null));
  1480. }
  1481. return node;
  1482. }
  1483. function set(root, scope, values) {
  1484. if (typeof scope === "string") {
  1485. return merge(getScope$1(root, scope), values);
  1486. }
  1487. return merge(getScope$1(root, ""), scope);
  1488. }
  1489. class Defaults {
  1490. constructor(_descriptors2, _appliers) {
  1491. this.animation = void 0;
  1492. this.backgroundColor = "rgba(0,0,0,0.1)";
  1493. this.borderColor = "rgba(0,0,0,0.1)";
  1494. this.color = "#666";
  1495. this.datasets = {};
  1496. this.devicePixelRatio = (context) => context.chart.platform.getDevicePixelRatio();
  1497. this.elements = {};
  1498. this.events = [
  1499. "mousemove",
  1500. "mouseout",
  1501. "click",
  1502. "touchstart",
  1503. "touchmove"
  1504. ];
  1505. this.font = {
  1506. family: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
  1507. size: 12,
  1508. style: "normal",
  1509. lineHeight: 1.2,
  1510. weight: null
  1511. };
  1512. this.hover = {};
  1513. this.hoverBackgroundColor = (ctx, options) => getHoverColor(options.backgroundColor);
  1514. this.hoverBorderColor = (ctx, options) => getHoverColor(options.borderColor);
  1515. this.hoverColor = (ctx, options) => getHoverColor(options.color);
  1516. this.indexAxis = "x";
  1517. this.interaction = {
  1518. mode: "nearest",
  1519. intersect: true,
  1520. includeInvisible: false
  1521. };
  1522. this.maintainAspectRatio = true;
  1523. this.onHover = null;
  1524. this.onClick = null;
  1525. this.parsing = true;
  1526. this.plugins = {};
  1527. this.responsive = true;
  1528. this.scale = void 0;
  1529. this.scales = {};
  1530. this.showLine = true;
  1531. this.drawActiveElementsOnTop = true;
  1532. this.describe(_descriptors2);
  1533. this.apply(_appliers);
  1534. }
  1535. set(scope, values) {
  1536. return set(this, scope, values);
  1537. }
  1538. get(scope) {
  1539. return getScope$1(this, scope);
  1540. }
  1541. describe(scope, values) {
  1542. return set(descriptors, scope, values);
  1543. }
  1544. override(scope, values) {
  1545. return set(overrides, scope, values);
  1546. }
  1547. route(scope, name, targetScope, targetName) {
  1548. const scopeObject = getScope$1(this, scope);
  1549. const targetScopeObject = getScope$1(this, targetScope);
  1550. const privateName = "_" + name;
  1551. Object.defineProperties(scopeObject, {
  1552. [privateName]: {
  1553. value: scopeObject[name],
  1554. writable: true
  1555. },
  1556. [name]: {
  1557. enumerable: true,
  1558. get() {
  1559. const local = this[privateName];
  1560. const target = targetScopeObject[targetName];
  1561. if (isObject(local)) {
  1562. return Object.assign({}, target, local);
  1563. }
  1564. return valueOrDefault(local, target);
  1565. },
  1566. set(value) {
  1567. this[privateName] = value;
  1568. }
  1569. }
  1570. });
  1571. }
  1572. apply(appliers) {
  1573. appliers.forEach((apply) => apply(this));
  1574. }
  1575. }
  1576. var defaults = /* @__PURE__ */ new Defaults({
  1577. _scriptable: (name) => !name.startsWith("on"),
  1578. _indexable: (name) => name !== "events",
  1579. hover: {
  1580. _fallback: "interaction"
  1581. },
  1582. interaction: {
  1583. _scriptable: false,
  1584. _indexable: false
  1585. }
  1586. }, [
  1587. applyAnimationsDefaults,
  1588. applyLayoutsDefaults,
  1589. applyScaleDefaults
  1590. ]);
  1591. function toFontString(font) {
  1592. if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {
  1593. return null;
  1594. }
  1595. return (font.style ? font.style + " " : "") + (font.weight ? font.weight + " " : "") + font.size + "px " + font.family;
  1596. }
  1597. function _measureText(ctx, data, gc, longest, string) {
  1598. let textWidth = data[string];
  1599. if (!textWidth) {
  1600. textWidth = data[string] = ctx.measureText(string).width;
  1601. gc.push(string);
  1602. }
  1603. if (textWidth > longest) {
  1604. longest = textWidth;
  1605. }
  1606. return longest;
  1607. }
  1608. function _alignPixel(chart, pixel, width) {
  1609. const devicePixelRatio = chart.currentDevicePixelRatio;
  1610. const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;
  1611. return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
  1612. }
  1613. function clearCanvas(canvas, ctx) {
  1614. if (!ctx && !canvas) {
  1615. return;
  1616. }
  1617. ctx = ctx || canvas.getContext("2d");
  1618. ctx.save();
  1619. ctx.resetTransform();
  1620. ctx.clearRect(0, 0, canvas.width, canvas.height);
  1621. ctx.restore();
  1622. }
  1623. function drawPoint(ctx, options, x, y) {
  1624. drawPointLegend(ctx, options, x, y);
  1625. }
  1626. function drawPointLegend(ctx, options, x, y, w) {
  1627. let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW;
  1628. const style = options.pointStyle;
  1629. const rotation = options.rotation;
  1630. const radius = options.radius;
  1631. let rad = (rotation || 0) * RAD_PER_DEG;
  1632. if (style && typeof style === "object") {
  1633. type = style.toString();
  1634. if (type === "[object HTMLImageElement]" || type === "[object HTMLCanvasElement]") {
  1635. ctx.save();
  1636. ctx.translate(x, y);
  1637. ctx.rotate(rad);
  1638. ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);
  1639. ctx.restore();
  1640. return;
  1641. }
  1642. }
  1643. if (isNaN(radius) || radius <= 0) {
  1644. return;
  1645. }
  1646. ctx.beginPath();
  1647. switch (style) {
  1648. // Default includes circle
  1649. default:
  1650. {
  1651. ctx.arc(x, y, radius, 0, TAU);
  1652. }
  1653. ctx.closePath();
  1654. break;
  1655. case "triangle":
  1656. width = radius;
  1657. ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
  1658. rad += TWO_THIRDS_PI;
  1659. ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
  1660. rad += TWO_THIRDS_PI;
  1661. ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
  1662. ctx.closePath();
  1663. break;
  1664. case "rectRounded":
  1665. cornerRadius = radius * 0.516;
  1666. size = radius - cornerRadius;
  1667. xOffset = Math.cos(rad + QUARTER_PI) * size;
  1668. xOffsetW = Math.cos(rad + QUARTER_PI) * size;
  1669. yOffset = Math.sin(rad + QUARTER_PI) * size;
  1670. yOffsetW = Math.sin(rad + QUARTER_PI) * size;
  1671. ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
  1672. ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);
  1673. ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);
  1674. ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
  1675. ctx.closePath();
  1676. break;
  1677. case "rect":
  1678. if (!rotation) {
  1679. size = Math.SQRT1_2 * radius;
  1680. width = size;
  1681. ctx.rect(x - width, y - size, 2 * width, 2 * size);
  1682. break;
  1683. }
  1684. rad += QUARTER_PI;
  1685. /* falls through */
  1686. case "rectRot":
  1687. xOffsetW = Math.cos(rad) * radius;
  1688. xOffset = Math.cos(rad) * radius;
  1689. yOffset = Math.sin(rad) * radius;
  1690. yOffsetW = Math.sin(rad) * radius;
  1691. ctx.moveTo(x - xOffsetW, y - yOffset);
  1692. ctx.lineTo(x + yOffsetW, y - xOffset);
  1693. ctx.lineTo(x + xOffsetW, y + yOffset);
  1694. ctx.lineTo(x - yOffsetW, y + xOffset);
  1695. ctx.closePath();
  1696. break;
  1697. case "crossRot":
  1698. rad += QUARTER_PI;
  1699. /* falls through */
  1700. case "cross":
  1701. xOffsetW = Math.cos(rad) * radius;
  1702. xOffset = Math.cos(rad) * radius;
  1703. yOffset = Math.sin(rad) * radius;
  1704. yOffsetW = Math.sin(rad) * radius;
  1705. ctx.moveTo(x - xOffsetW, y - yOffset);
  1706. ctx.lineTo(x + xOffsetW, y + yOffset);
  1707. ctx.moveTo(x + yOffsetW, y - xOffset);
  1708. ctx.lineTo(x - yOffsetW, y + xOffset);
  1709. break;
  1710. case "star":
  1711. xOffsetW = Math.cos(rad) * radius;
  1712. xOffset = Math.cos(rad) * radius;
  1713. yOffset = Math.sin(rad) * radius;
  1714. yOffsetW = Math.sin(rad) * radius;
  1715. ctx.moveTo(x - xOffsetW, y - yOffset);
  1716. ctx.lineTo(x + xOffsetW, y + yOffset);
  1717. ctx.moveTo(x + yOffsetW, y - xOffset);
  1718. ctx.lineTo(x - yOffsetW, y + xOffset);
  1719. rad += QUARTER_PI;
  1720. xOffsetW = Math.cos(rad) * radius;
  1721. xOffset = Math.cos(rad) * radius;
  1722. yOffset = Math.sin(rad) * radius;
  1723. yOffsetW = Math.sin(rad) * radius;
  1724. ctx.moveTo(x - xOffsetW, y - yOffset);
  1725. ctx.lineTo(x + xOffsetW, y + yOffset);
  1726. ctx.moveTo(x + yOffsetW, y - xOffset);
  1727. ctx.lineTo(x - yOffsetW, y + xOffset);
  1728. break;
  1729. case "line":
  1730. xOffset = Math.cos(rad) * radius;
  1731. yOffset = Math.sin(rad) * radius;
  1732. ctx.moveTo(x - xOffset, y - yOffset);
  1733. ctx.lineTo(x + xOffset, y + yOffset);
  1734. break;
  1735. case "dash":
  1736. ctx.moveTo(x, y);
  1737. ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);
  1738. break;
  1739. case false:
  1740. ctx.closePath();
  1741. break;
  1742. }
  1743. ctx.fill();
  1744. if (options.borderWidth > 0) {
  1745. ctx.stroke();
  1746. }
  1747. }
  1748. function _isPointInArea(point, area, margin) {
  1749. margin = margin || 0.5;
  1750. return !area || point && point.x > area.left - margin && point.x < area.right + margin && point.y > area.top - margin && point.y < area.bottom + margin;
  1751. }
  1752. function clipArea(ctx, area) {
  1753. ctx.save();
  1754. ctx.beginPath();
  1755. ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
  1756. ctx.clip();
  1757. }
  1758. function unclipArea(ctx) {
  1759. ctx.restore();
  1760. }
  1761. function _steppedLineTo(ctx, previous, target, flip, mode) {
  1762. if (!previous) {
  1763. return ctx.lineTo(target.x, target.y);
  1764. }
  1765. if (mode === "middle") {
  1766. const midpoint = (previous.x + target.x) / 2;
  1767. ctx.lineTo(midpoint, previous.y);
  1768. ctx.lineTo(midpoint, target.y);
  1769. } else if (mode === "after" !== !!flip) {
  1770. ctx.lineTo(previous.x, target.y);
  1771. } else {
  1772. ctx.lineTo(target.x, previous.y);
  1773. }
  1774. ctx.lineTo(target.x, target.y);
  1775. }
  1776. function _bezierCurveTo(ctx, previous, target, flip) {
  1777. if (!previous) {
  1778. return ctx.lineTo(target.x, target.y);
  1779. }
  1780. 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);
  1781. }
  1782. function setRenderOpts(ctx, opts) {
  1783. if (opts.translation) {
  1784. ctx.translate(opts.translation[0], opts.translation[1]);
  1785. }
  1786. if (!isNullOrUndef(opts.rotation)) {
  1787. ctx.rotate(opts.rotation);
  1788. }
  1789. if (opts.color) {
  1790. ctx.fillStyle = opts.color;
  1791. }
  1792. if (opts.textAlign) {
  1793. ctx.textAlign = opts.textAlign;
  1794. }
  1795. if (opts.textBaseline) {
  1796. ctx.textBaseline = opts.textBaseline;
  1797. }
  1798. }
  1799. function decorateText(ctx, x, y, line, opts) {
  1800. if (opts.strikethrough || opts.underline) {
  1801. const metrics = ctx.measureText(line);
  1802. const left = x - metrics.actualBoundingBoxLeft;
  1803. const right = x + metrics.actualBoundingBoxRight;
  1804. const top = y - metrics.actualBoundingBoxAscent;
  1805. const bottom = y + metrics.actualBoundingBoxDescent;
  1806. const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;
  1807. ctx.strokeStyle = ctx.fillStyle;
  1808. ctx.beginPath();
  1809. ctx.lineWidth = opts.decorationWidth || 2;
  1810. ctx.moveTo(left, yDecoration);
  1811. ctx.lineTo(right, yDecoration);
  1812. ctx.stroke();
  1813. }
  1814. }
  1815. function drawBackdrop(ctx, opts) {
  1816. const oldColor = ctx.fillStyle;
  1817. ctx.fillStyle = opts.color;
  1818. ctx.fillRect(opts.left, opts.top, opts.width, opts.height);
  1819. ctx.fillStyle = oldColor;
  1820. }
  1821. function renderText(ctx, text, x, y, font, opts = {}) {
  1822. const lines = isArray(text) ? text : [
  1823. text
  1824. ];
  1825. const stroke = opts.strokeWidth > 0 && opts.strokeColor !== "";
  1826. let i, line;
  1827. ctx.save();
  1828. ctx.font = font.string;
  1829. setRenderOpts(ctx, opts);
  1830. for (i = 0; i < lines.length; ++i) {
  1831. line = lines[i];
  1832. if (opts.backdrop) {
  1833. drawBackdrop(ctx, opts.backdrop);
  1834. }
  1835. if (stroke) {
  1836. if (opts.strokeColor) {
  1837. ctx.strokeStyle = opts.strokeColor;
  1838. }
  1839. if (!isNullOrUndef(opts.strokeWidth)) {
  1840. ctx.lineWidth = opts.strokeWidth;
  1841. }
  1842. ctx.strokeText(line, x, y, opts.maxWidth);
  1843. }
  1844. ctx.fillText(line, x, y, opts.maxWidth);
  1845. decorateText(ctx, x, y, line, opts);
  1846. y += Number(font.lineHeight);
  1847. }
  1848. ctx.restore();
  1849. }
  1850. const LINE_HEIGHT = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/;
  1851. const FONT_STYLE = /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;
  1852. function toLineHeight(value, size) {
  1853. const matches = ("" + value).match(LINE_HEIGHT);
  1854. if (!matches || matches[1] === "normal") {
  1855. return size * 1.2;
  1856. }
  1857. value = +matches[2];
  1858. switch (matches[3]) {
  1859. case "px":
  1860. return value;
  1861. case "%":
  1862. value /= 100;
  1863. break;
  1864. }
  1865. return size * value;
  1866. }
  1867. const numberOrZero = (v) => +v || 0;
  1868. function _readValueToProps(value, props) {
  1869. const ret = {};
  1870. const objProps = isObject(props);
  1871. const keys = objProps ? Object.keys(props) : props;
  1872. const read = isObject(value) ? objProps ? (prop) => valueOrDefault(value[prop], value[props[prop]]) : (prop) => value[prop] : () => value;
  1873. for (const prop of keys) {
  1874. ret[prop] = numberOrZero(read(prop));
  1875. }
  1876. return ret;
  1877. }
  1878. function toTRBL(value) {
  1879. return _readValueToProps(value, {
  1880. top: "y",
  1881. right: "x",
  1882. bottom: "y",
  1883. left: "x"
  1884. });
  1885. }
  1886. function toPadding(value) {
  1887. const obj = toTRBL(value);
  1888. obj.width = obj.left + obj.right;
  1889. obj.height = obj.top + obj.bottom;
  1890. return obj;
  1891. }
  1892. function toFont(options, fallback) {
  1893. options = options || {};
  1894. fallback = fallback || defaults.font;
  1895. let size = valueOrDefault(options.size, fallback.size);
  1896. if (typeof size === "string") {
  1897. size = parseInt(size, 10);
  1898. }
  1899. let style = valueOrDefault(options.style, fallback.style);
  1900. if (style && !("" + style).match(FONT_STYLE)) {
  1901. console.warn('Invalid font style specified: "' + style + '"');
  1902. style = void 0;
  1903. }
  1904. const font = {
  1905. family: valueOrDefault(options.family, fallback.family),
  1906. lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size),
  1907. size,
  1908. style,
  1909. weight: valueOrDefault(options.weight, fallback.weight),
  1910. string: ""
  1911. };
  1912. font.string = toFontString(font);
  1913. return font;
  1914. }
  1915. function resolve(inputs, context, index, info) {
  1916. let i, ilen, value;
  1917. for (i = 0, ilen = inputs.length; i < ilen; ++i) {
  1918. value = inputs[i];
  1919. if (value === void 0) {
  1920. continue;
  1921. }
  1922. if (value !== void 0) {
  1923. return value;
  1924. }
  1925. }
  1926. }
  1927. function _addGrace(minmax, grace, beginAtZero) {
  1928. const { min, max } = minmax;
  1929. const change = toDimension(grace, (max - min) / 2);
  1930. const keepZero = (value, add) => beginAtZero && value === 0 ? 0 : value + add;
  1931. return {
  1932. min: keepZero(min, -Math.abs(change)),
  1933. max: keepZero(max, change)
  1934. };
  1935. }
  1936. function createContext(parentContext, context) {
  1937. return Object.assign(Object.create(parentContext), context);
  1938. }
  1939. function _createResolver(scopes, prefixes = [
  1940. ""
  1941. ], rootScopes, fallback, getTarget = () => scopes[0]) {
  1942. const finalRootScopes = rootScopes || scopes;
  1943. if (typeof fallback === "undefined") {
  1944. fallback = _resolve("_fallback", scopes);
  1945. }
  1946. const cache = {
  1947. [Symbol.toStringTag]: "Object",
  1948. _cacheable: true,
  1949. _scopes: scopes,
  1950. _rootScopes: finalRootScopes,
  1951. _fallback: fallback,
  1952. _getTarget: getTarget,
  1953. override: (scope) => _createResolver([
  1954. scope,
  1955. ...scopes
  1956. ], prefixes, finalRootScopes, fallback)
  1957. };
  1958. return new Proxy(cache, {
  1959. /**
  1960. * A trap for the delete operator.
  1961. */
  1962. deleteProperty(target, prop) {
  1963. delete target[prop];
  1964. delete target._keys;
  1965. delete scopes[0][prop];
  1966. return true;
  1967. },
  1968. /**
  1969. * A trap for getting property values.
  1970. */
  1971. get(target, prop) {
  1972. return _cached(target, prop, () => _resolveWithPrefixes(prop, prefixes, scopes, target));
  1973. },
  1974. /**
  1975. * A trap for Object.getOwnPropertyDescriptor.
  1976. * Also used by Object.hasOwnProperty.
  1977. */
  1978. getOwnPropertyDescriptor(target, prop) {
  1979. return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop);
  1980. },
  1981. /**
  1982. * A trap for Object.getPrototypeOf.
  1983. */
  1984. getPrototypeOf() {
  1985. return Reflect.getPrototypeOf(scopes[0]);
  1986. },
  1987. /**
  1988. * A trap for the in operator.
  1989. */
  1990. has(target, prop) {
  1991. return getKeysFromAllScopes(target).includes(prop);
  1992. },
  1993. /**
  1994. * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.
  1995. */
  1996. ownKeys(target) {
  1997. return getKeysFromAllScopes(target);
  1998. },
  1999. /**
  2000. * A trap for setting property values.
  2001. */
  2002. set(target, prop, value) {
  2003. const storage = target._storage || (target._storage = getTarget());
  2004. target[prop] = storage[prop] = value;
  2005. delete target._keys;
  2006. return true;
  2007. }
  2008. });
  2009. }
  2010. function _attachContext(proxy, context, subProxy, descriptorDefaults) {
  2011. const cache = {
  2012. _cacheable: false,
  2013. _proxy: proxy,
  2014. _context: context,
  2015. _subProxy: subProxy,
  2016. _stack: /* @__PURE__ */ new Set(),
  2017. _descriptors: _descriptors(proxy, descriptorDefaults),
  2018. setContext: (ctx) => _attachContext(proxy, ctx, subProxy, descriptorDefaults),
  2019. override: (scope) => _attachContext(proxy.override(scope), context, subProxy, descriptorDefaults)
  2020. };
  2021. return new Proxy(cache, {
  2022. /**
  2023. * A trap for the delete operator.
  2024. */
  2025. deleteProperty(target, prop) {
  2026. delete target[prop];
  2027. delete proxy[prop];
  2028. return true;
  2029. },
  2030. /**
  2031. * A trap for getting property values.
  2032. */
  2033. get(target, prop, receiver) {
  2034. return _cached(target, prop, () => _resolveWithContext(target, prop, receiver));
  2035. },
  2036. /**
  2037. * A trap for Object.getOwnPropertyDescriptor.
  2038. * Also used by Object.hasOwnProperty.
  2039. */
  2040. getOwnPropertyDescriptor(target, prop) {
  2041. return target._descriptors.allKeys ? Reflect.has(proxy, prop) ? {
  2042. enumerable: true,
  2043. configurable: true
  2044. } : void 0 : Reflect.getOwnPropertyDescriptor(proxy, prop);
  2045. },
  2046. /**
  2047. * A trap for Object.getPrototypeOf.
  2048. */
  2049. getPrototypeOf() {
  2050. return Reflect.getPrototypeOf(proxy);
  2051. },
  2052. /**
  2053. * A trap for the in operator.
  2054. */
  2055. has(target, prop) {
  2056. return Reflect.has(proxy, prop);
  2057. },
  2058. /**
  2059. * A trap for Object.getOwnPropertyNames and Object.getOwnPropertySymbols.
  2060. */
  2061. ownKeys() {
  2062. return Reflect.ownKeys(proxy);
  2063. },
  2064. /**
  2065. * A trap for setting property values.
  2066. */
  2067. set(target, prop, value) {
  2068. proxy[prop] = value;
  2069. delete target[prop];
  2070. return true;
  2071. }
  2072. });
  2073. }
  2074. function _descriptors(proxy, defaults2 = {
  2075. scriptable: true,
  2076. indexable: true
  2077. }) {
  2078. const { _scriptable = defaults2.scriptable, _indexable = defaults2.indexable, _allKeys = defaults2.allKeys } = proxy;
  2079. return {
  2080. allKeys: _allKeys,
  2081. scriptable: _scriptable,
  2082. indexable: _indexable,
  2083. isScriptable: isFunction(_scriptable) ? _scriptable : () => _scriptable,
  2084. isIndexable: isFunction(_indexable) ? _indexable : () => _indexable
  2085. };
  2086. }
  2087. const readKey = (prefix, name) => prefix ? prefix + _capitalize(name) : name;
  2088. const needsSubResolver = (prop, value) => isObject(value) && prop !== "adapters" && (Object.getPrototypeOf(value) === null || value.constructor === Object);
  2089. function _cached(target, prop, resolve2) {
  2090. if (Object.prototype.hasOwnProperty.call(target, prop) || prop === "constructor") {
  2091. return target[prop];
  2092. }
  2093. const value = resolve2();
  2094. target[prop] = value;
  2095. return value;
  2096. }
  2097. function _resolveWithContext(target, prop, receiver) {
  2098. const { _proxy, _context, _subProxy, _descriptors: descriptors2 } = target;
  2099. let value = _proxy[prop];
  2100. if (isFunction(value) && descriptors2.isScriptable(prop)) {
  2101. value = _resolveScriptable(prop, value, target, receiver);
  2102. }
  2103. if (isArray(value) && value.length) {
  2104. value = _resolveArray(prop, value, target, descriptors2.isIndexable);
  2105. }
  2106. if (needsSubResolver(prop, value)) {
  2107. value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors2);
  2108. }
  2109. return value;
  2110. }
  2111. function _resolveScriptable(prop, getValue, target, receiver) {
  2112. const { _proxy, _context, _subProxy, _stack } = target;
  2113. if (_stack.has(prop)) {
  2114. throw new Error("Recursion detected: " + Array.from(_stack).join("->") + "->" + prop);
  2115. }
  2116. _stack.add(prop);
  2117. let value = getValue(_context, _subProxy || receiver);
  2118. _stack.delete(prop);
  2119. if (needsSubResolver(prop, value)) {
  2120. value = createSubResolver(_proxy._scopes, _proxy, prop, value);
  2121. }
  2122. return value;
  2123. }
  2124. function _resolveArray(prop, value, target, isIndexable) {
  2125. const { _proxy, _context, _subProxy, _descriptors: descriptors2 } = target;
  2126. if (typeof _context.index !== "undefined" && isIndexable(prop)) {
  2127. return value[_context.index % value.length];
  2128. } else if (isObject(value[0])) {
  2129. const arr = value;
  2130. const scopes = _proxy._scopes.filter((s) => s !== arr);
  2131. value = [];
  2132. for (const item of arr) {
  2133. const resolver = createSubResolver(scopes, _proxy, prop, item);
  2134. value.push(_attachContext(resolver, _context, _subProxy && _subProxy[prop], descriptors2));
  2135. }
  2136. }
  2137. return value;
  2138. }
  2139. function resolveFallback(fallback, prop, value) {
  2140. return isFunction(fallback) ? fallback(prop, value) : fallback;
  2141. }
  2142. const getScope = (key, parent) => key === true ? parent : typeof key === "string" ? resolveObjectKey(parent, key) : void 0;
  2143. function addScopes(set2, parentScopes, key, parentFallback, value) {
  2144. for (const parent of parentScopes) {
  2145. const scope = getScope(key, parent);
  2146. if (scope) {
  2147. set2.add(scope);
  2148. const fallback = resolveFallback(scope._fallback, key, value);
  2149. if (typeof fallback !== "undefined" && fallback !== key && fallback !== parentFallback) {
  2150. return fallback;
  2151. }
  2152. } else if (scope === false && typeof parentFallback !== "undefined" && key !== parentFallback) {
  2153. return null;
  2154. }
  2155. }
  2156. return false;
  2157. }
  2158. function createSubResolver(parentScopes, resolver, prop, value) {
  2159. const rootScopes = resolver._rootScopes;
  2160. const fallback = resolveFallback(resolver._fallback, prop, value);
  2161. const allScopes = [
  2162. ...parentScopes,
  2163. ...rootScopes
  2164. ];
  2165. const set2 = /* @__PURE__ */ new Set();
  2166. set2.add(value);
  2167. let key = addScopesFromKey(set2, allScopes, prop, fallback || prop, value);
  2168. if (key === null) {
  2169. return false;
  2170. }
  2171. if (typeof fallback !== "undefined" && fallback !== prop) {
  2172. key = addScopesFromKey(set2, allScopes, fallback, key, value);
  2173. if (key === null) {
  2174. return false;
  2175. }
  2176. }
  2177. return _createResolver(Array.from(set2), [
  2178. ""
  2179. ], rootScopes, fallback, () => subGetTarget(resolver, prop, value));
  2180. }
  2181. function addScopesFromKey(set2, allScopes, key, fallback, item) {
  2182. while (key) {
  2183. key = addScopes(set2, allScopes, key, fallback, item);
  2184. }
  2185. return key;
  2186. }
  2187. function subGetTarget(resolver, prop, value) {
  2188. const parent = resolver._getTarget();
  2189. if (!(prop in parent)) {
  2190. parent[prop] = {};
  2191. }
  2192. const target = parent[prop];
  2193. if (isArray(target) && isObject(value)) {
  2194. return value;
  2195. }
  2196. return target || {};
  2197. }
  2198. function _resolveWithPrefixes(prop, prefixes, scopes, proxy) {
  2199. let value;
  2200. for (const prefix of prefixes) {
  2201. value = _resolve(readKey(prefix, prop), scopes);
  2202. if (typeof value !== "undefined") {
  2203. return needsSubResolver(prop, value) ? createSubResolver(scopes, proxy, prop, value) : value;
  2204. }
  2205. }
  2206. }
  2207. function _resolve(key, scopes) {
  2208. for (const scope of scopes) {
  2209. if (!scope) {
  2210. continue;
  2211. }
  2212. const value = scope[key];
  2213. if (typeof value !== "undefined") {
  2214. return value;
  2215. }
  2216. }
  2217. }
  2218. function getKeysFromAllScopes(target) {
  2219. let keys = target._keys;
  2220. if (!keys) {
  2221. keys = target._keys = resolveKeysFromAllScopes(target._scopes);
  2222. }
  2223. return keys;
  2224. }
  2225. function resolveKeysFromAllScopes(scopes) {
  2226. const set2 = /* @__PURE__ */ new Set();
  2227. for (const scope of scopes) {
  2228. for (const key of Object.keys(scope).filter((k) => !k.startsWith("_"))) {
  2229. set2.add(key);
  2230. }
  2231. }
  2232. return Array.from(set2);
  2233. }
  2234. const EPSILON = Number.EPSILON || 1e-14;
  2235. const getPoint = (points, i) => i < points.length && !points[i].skip && points[i];
  2236. const getValueAxis = (indexAxis) => indexAxis === "x" ? "y" : "x";
  2237. function splineCurve(firstPoint, middlePoint, afterPoint, t) {
  2238. const previous = firstPoint.skip ? middlePoint : firstPoint;
  2239. const current = middlePoint;
  2240. const next = afterPoint.skip ? middlePoint : afterPoint;
  2241. const d01 = distanceBetweenPoints(current, previous);
  2242. const d12 = distanceBetweenPoints(next, current);
  2243. let s01 = d01 / (d01 + d12);
  2244. let s12 = d12 / (d01 + d12);
  2245. s01 = isNaN(s01) ? 0 : s01;
  2246. s12 = isNaN(s12) ? 0 : s12;
  2247. const fa = t * s01;
  2248. const fb = t * s12;
  2249. return {
  2250. previous: {
  2251. x: current.x - fa * (next.x - previous.x),
  2252. y: current.y - fa * (next.y - previous.y)
  2253. },
  2254. next: {
  2255. x: current.x + fb * (next.x - previous.x),
  2256. y: current.y + fb * (next.y - previous.y)
  2257. }
  2258. };
  2259. }
  2260. function monotoneAdjust(points, deltaK, mK) {
  2261. const pointsLen = points.length;
  2262. let alphaK, betaK, tauK, squaredMagnitude, pointCurrent;
  2263. let pointAfter = getPoint(points, 0);
  2264. for (let i = 0; i < pointsLen - 1; ++i) {
  2265. pointCurrent = pointAfter;
  2266. pointAfter = getPoint(points, i + 1);
  2267. if (!pointCurrent || !pointAfter) {
  2268. continue;
  2269. }
  2270. if (almostEquals(deltaK[i], 0, EPSILON)) {
  2271. mK[i] = mK[i + 1] = 0;
  2272. continue;
  2273. }
  2274. alphaK = mK[i] / deltaK[i];
  2275. betaK = mK[i + 1] / deltaK[i];
  2276. squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
  2277. if (squaredMagnitude <= 9) {
  2278. continue;
  2279. }
  2280. tauK = 3 / Math.sqrt(squaredMagnitude);
  2281. mK[i] = alphaK * tauK * deltaK[i];
  2282. mK[i + 1] = betaK * tauK * deltaK[i];
  2283. }
  2284. }
  2285. function monotoneCompute(points, mK, indexAxis = "x") {
  2286. const valueAxis = getValueAxis(indexAxis);
  2287. const pointsLen = points.length;
  2288. let delta, pointBefore, pointCurrent;
  2289. let pointAfter = getPoint(points, 0);
  2290. for (let i = 0; i < pointsLen; ++i) {
  2291. pointBefore = pointCurrent;
  2292. pointCurrent = pointAfter;
  2293. pointAfter = getPoint(points, i + 1);
  2294. if (!pointCurrent) {
  2295. continue;
  2296. }
  2297. const iPixel = pointCurrent[indexAxis];
  2298. const vPixel = pointCurrent[valueAxis];
  2299. if (pointBefore) {
  2300. delta = (iPixel - pointBefore[indexAxis]) / 3;
  2301. pointCurrent[`cp1${indexAxis}`] = iPixel - delta;
  2302. pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i];
  2303. }
  2304. if (pointAfter) {
  2305. delta = (pointAfter[indexAxis] - iPixel) / 3;
  2306. pointCurrent[`cp2${indexAxis}`] = iPixel + delta;
  2307. pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i];
  2308. }
  2309. }
  2310. }
  2311. function splineCurveMonotone(points, indexAxis = "x") {
  2312. const valueAxis = getValueAxis(indexAxis);
  2313. const pointsLen = points.length;
  2314. const deltaK = Array(pointsLen).fill(0);
  2315. const mK = Array(pointsLen);
  2316. let i, pointBefore, pointCurrent;
  2317. let pointAfter = getPoint(points, 0);
  2318. for (i = 0; i < pointsLen; ++i) {
  2319. pointBefore = pointCurrent;
  2320. pointCurrent = pointAfter;
  2321. pointAfter = getPoint(points, i + 1);
  2322. if (!pointCurrent) {
  2323. continue;
  2324. }
  2325. if (pointAfter) {
  2326. const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis];
  2327. deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0;
  2328. }
  2329. mK[i] = !pointBefore ? deltaK[i] : !pointAfter ? deltaK[i - 1] : sign(deltaK[i - 1]) !== sign(deltaK[i]) ? 0 : (deltaK[i - 1] + deltaK[i]) / 2;
  2330. }
  2331. monotoneAdjust(points, deltaK, mK);
  2332. monotoneCompute(points, mK, indexAxis);
  2333. }
  2334. function capControlPoint(pt, min, max) {
  2335. return Math.max(Math.min(pt, max), min);
  2336. }
  2337. function capBezierPoints(points, area) {
  2338. let i, ilen, point, inArea, inAreaPrev;
  2339. let inAreaNext = _isPointInArea(points[0], area);
  2340. for (i = 0, ilen = points.length; i < ilen; ++i) {
  2341. inAreaPrev = inArea;
  2342. inArea = inAreaNext;
  2343. inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area);
  2344. if (!inArea) {
  2345. continue;
  2346. }
  2347. point = points[i];
  2348. if (inAreaPrev) {
  2349. point.cp1x = capControlPoint(point.cp1x, area.left, area.right);
  2350. point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom);
  2351. }
  2352. if (inAreaNext) {
  2353. point.cp2x = capControlPoint(point.cp2x, area.left, area.right);
  2354. point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom);
  2355. }
  2356. }
  2357. }
  2358. function _updateBezierControlPoints(points, options, area, loop, indexAxis) {
  2359. let i, ilen, point, controlPoints;
  2360. if (options.spanGaps) {
  2361. points = points.filter((pt) => !pt.skip);
  2362. }
  2363. if (options.cubicInterpolationMode === "monotone") {
  2364. splineCurveMonotone(points, indexAxis);
  2365. } else {
  2366. let prev = loop ? points[points.length - 1] : points[0];
  2367. for (i = 0, ilen = points.length; i < ilen; ++i) {
  2368. point = points[i];
  2369. controlPoints = splineCurve(prev, point, points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen], options.tension);
  2370. point.cp1x = controlPoints.previous.x;
  2371. point.cp1y = controlPoints.previous.y;
  2372. point.cp2x = controlPoints.next.x;
  2373. point.cp2y = controlPoints.next.y;
  2374. prev = point;
  2375. }
  2376. }
  2377. if (options.capBezierPoints) {
  2378. capBezierPoints(points, area);
  2379. }
  2380. }
  2381. function _isDomSupported() {
  2382. return typeof window !== "undefined" && typeof document !== "undefined";
  2383. }
  2384. function _getParentNode(domNode) {
  2385. let parent = domNode.parentNode;
  2386. if (parent && parent.toString() === "[object ShadowRoot]") {
  2387. parent = parent.host;
  2388. }
  2389. return parent;
  2390. }
  2391. function parseMaxStyle(styleValue, node, parentProperty) {
  2392. let valueInPixels;
  2393. if (typeof styleValue === "string") {
  2394. valueInPixels = parseInt(styleValue, 10);
  2395. if (styleValue.indexOf("%") !== -1) {
  2396. valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
  2397. }
  2398. } else {
  2399. valueInPixels = styleValue;
  2400. }
  2401. return valueInPixels;
  2402. }
  2403. const getComputedStyle = (element) => element.ownerDocument.defaultView.getComputedStyle(element, null);
  2404. function getStyle(el, property) {
  2405. return getComputedStyle(el).getPropertyValue(property);
  2406. }
  2407. const positions = [
  2408. "top",
  2409. "right",
  2410. "bottom",
  2411. "left"
  2412. ];
  2413. function getPositionedStyle(styles, style, suffix) {
  2414. const result = {};
  2415. suffix = suffix ? "-" + suffix : "";
  2416. for (let i = 0; i < 4; i++) {
  2417. const pos = positions[i];
  2418. result[pos] = parseFloat(styles[style + "-" + pos + suffix]) || 0;
  2419. }
  2420. result.width = result.left + result.right;
  2421. result.height = result.top + result.bottom;
  2422. return result;
  2423. }
  2424. const useOffsetPos = (x, y, target) => (x > 0 || y > 0) && (!target || !target.shadowRoot);
  2425. function getCanvasPosition(e, canvas) {
  2426. const touches = e.touches;
  2427. const source = touches && touches.length ? touches[0] : e;
  2428. const { offsetX, offsetY } = source;
  2429. let box = false;
  2430. let x, y;
  2431. if (useOffsetPos(offsetX, offsetY, e.target)) {
  2432. x = offsetX;
  2433. y = offsetY;
  2434. } else {
  2435. const rect = canvas.getBoundingClientRect();
  2436. x = source.clientX - rect.left;
  2437. y = source.clientY - rect.top;
  2438. box = true;
  2439. }
  2440. return {
  2441. x,
  2442. y,
  2443. box
  2444. };
  2445. }
  2446. function getRelativePosition(event, chart) {
  2447. if ("native" in event) {
  2448. return event;
  2449. }
  2450. const { canvas, currentDevicePixelRatio } = chart;
  2451. const style = getComputedStyle(canvas);
  2452. const borderBox = style.boxSizing === "border-box";
  2453. const paddings = getPositionedStyle(style, "padding");
  2454. const borders = getPositionedStyle(style, "border", "width");
  2455. const { x, y, box } = getCanvasPosition(event, canvas);
  2456. const xOffset = paddings.left + (box && borders.left);
  2457. const yOffset = paddings.top + (box && borders.top);
  2458. let { width, height } = chart;
  2459. if (borderBox) {
  2460. width -= paddings.width + borders.width;
  2461. height -= paddings.height + borders.height;
  2462. }
  2463. return {
  2464. x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),
  2465. y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)
  2466. };
  2467. }
  2468. function getContainerSize(canvas, width, height) {
  2469. let maxWidth, maxHeight;
  2470. if (width === void 0 || height === void 0) {
  2471. const container = canvas && _getParentNode(canvas);
  2472. if (!container) {
  2473. width = canvas.clientWidth;
  2474. height = canvas.clientHeight;
  2475. } else {
  2476. const rect = container.getBoundingClientRect();
  2477. const containerStyle = getComputedStyle(container);
  2478. const containerBorder = getPositionedStyle(containerStyle, "border", "width");
  2479. const containerPadding = getPositionedStyle(containerStyle, "padding");
  2480. width = rect.width - containerPadding.width - containerBorder.width;
  2481. height = rect.height - containerPadding.height - containerBorder.height;
  2482. maxWidth = parseMaxStyle(containerStyle.maxWidth, container, "clientWidth");
  2483. maxHeight = parseMaxStyle(containerStyle.maxHeight, container, "clientHeight");
  2484. }
  2485. }
  2486. return {
  2487. width,
  2488. height,
  2489. maxWidth: maxWidth || INFINITY,
  2490. maxHeight: maxHeight || INFINITY
  2491. };
  2492. }
  2493. const round1 = (v) => Math.round(v * 10) / 10;
  2494. function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) {
  2495. const style = getComputedStyle(canvas);
  2496. const margins = getPositionedStyle(style, "margin");
  2497. const maxWidth = parseMaxStyle(style.maxWidth, canvas, "clientWidth") || INFINITY;
  2498. const maxHeight = parseMaxStyle(style.maxHeight, canvas, "clientHeight") || INFINITY;
  2499. const containerSize = getContainerSize(canvas, bbWidth, bbHeight);
  2500. let { width, height } = containerSize;
  2501. if (style.boxSizing === "content-box") {
  2502. const borders = getPositionedStyle(style, "border", "width");
  2503. const paddings = getPositionedStyle(style, "padding");
  2504. width -= paddings.width + borders.width;
  2505. height -= paddings.height + borders.height;
  2506. }
  2507. width = Math.max(0, width - margins.width);
  2508. height = Math.max(0, aspectRatio ? width / aspectRatio : height - margins.height);
  2509. width = round1(Math.min(width, maxWidth, containerSize.maxWidth));
  2510. height = round1(Math.min(height, maxHeight, containerSize.maxHeight));
  2511. if (width && !height) {
  2512. height = round1(width / 2);
  2513. }
  2514. const maintainHeight = bbWidth !== void 0 || bbHeight !== void 0;
  2515. if (maintainHeight && aspectRatio && containerSize.height && height > containerSize.height) {
  2516. height = containerSize.height;
  2517. width = round1(Math.floor(height * aspectRatio));
  2518. }
  2519. return {
  2520. width,
  2521. height
  2522. };
  2523. }
  2524. function retinaScale(chart, forceRatio, forceStyle) {
  2525. const pixelRatio = forceRatio || 1;
  2526. const deviceHeight = Math.floor(chart.height * pixelRatio);
  2527. const deviceWidth = Math.floor(chart.width * pixelRatio);
  2528. chart.height = Math.floor(chart.height);
  2529. chart.width = Math.floor(chart.width);
  2530. const canvas = chart.canvas;
  2531. if (canvas.style && (forceStyle || !canvas.style.height && !canvas.style.width)) {
  2532. canvas.style.height = `${chart.height}px`;
  2533. canvas.style.width = `${chart.width}px`;
  2534. }
  2535. if (chart.currentDevicePixelRatio !== pixelRatio || canvas.height !== deviceHeight || canvas.width !== deviceWidth) {
  2536. chart.currentDevicePixelRatio = pixelRatio;
  2537. canvas.height = deviceHeight;
  2538. canvas.width = deviceWidth;
  2539. chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
  2540. return true;
  2541. }
  2542. return false;
  2543. }
  2544. const supportsEventListenerOptions = function() {
  2545. let passiveSupported = false;
  2546. try {
  2547. const options = {
  2548. get passive() {
  2549. passiveSupported = true;
  2550. return false;
  2551. }
  2552. };
  2553. if (_isDomSupported()) {
  2554. window.addEventListener("test", null, options);
  2555. window.removeEventListener("test", null, options);
  2556. }
  2557. } catch (e) {
  2558. }
  2559. return passiveSupported;
  2560. }();
  2561. function readUsedSize(element, property) {
  2562. const value = getStyle(element, property);
  2563. const matches = value && value.match(/^(\d+)(\.\d+)?px$/);
  2564. return matches ? +matches[1] : void 0;
  2565. }
  2566. function _pointInLine(p1, p2, t, mode) {
  2567. return {
  2568. x: p1.x + t * (p2.x - p1.x),
  2569. y: p1.y + t * (p2.y - p1.y)
  2570. };
  2571. }
  2572. function _steppedInterpolation(p1, p2, t, mode) {
  2573. return {
  2574. x: p1.x + t * (p2.x - p1.x),
  2575. y: mode === "middle" ? t < 0.5 ? p1.y : p2.y : mode === "after" ? t < 1 ? p1.y : p2.y : t > 0 ? p2.y : p1.y
  2576. };
  2577. }
  2578. function _bezierInterpolation(p1, p2, t, mode) {
  2579. const cp1 = {
  2580. x: p1.cp2x,
  2581. y: p1.cp2y
  2582. };
  2583. const cp2 = {
  2584. x: p2.cp1x,
  2585. y: p2.cp1y
  2586. };
  2587. const a = _pointInLine(p1, cp1, t);
  2588. const b = _pointInLine(cp1, cp2, t);
  2589. const c = _pointInLine(cp2, p2, t);
  2590. const d = _pointInLine(a, b, t);
  2591. const e = _pointInLine(b, c, t);
  2592. return _pointInLine(d, e, t);
  2593. }
  2594. function propertyFn(property) {
  2595. if (property === "angle") {
  2596. return {
  2597. between: _angleBetween,
  2598. compare: _angleDiff,
  2599. normalize: _normalizeAngle
  2600. };
  2601. }
  2602. return {
  2603. between: _isBetween,
  2604. compare: (a, b) => a - b,
  2605. normalize: (x) => x
  2606. };
  2607. }
  2608. function normalizeSegment({ start, end, count, loop, style }) {
  2609. return {
  2610. start: start % count,
  2611. end: end % count,
  2612. loop: loop && (end - start + 1) % count === 0,
  2613. style
  2614. };
  2615. }
  2616. function getSegment(segment, points, bounds) {
  2617. const { property, start: startBound, end: endBound } = bounds;
  2618. const { between, normalize } = propertyFn(property);
  2619. const count = points.length;
  2620. let { start, end, loop } = segment;
  2621. let i, ilen;
  2622. if (loop) {
  2623. start += count;
  2624. end += count;
  2625. for (i = 0, ilen = count; i < ilen; ++i) {
  2626. if (!between(normalize(points[start % count][property]), startBound, endBound)) {
  2627. break;
  2628. }
  2629. start--;
  2630. end--;
  2631. }
  2632. start %= count;
  2633. end %= count;
  2634. }
  2635. if (end < start) {
  2636. end += count;
  2637. }
  2638. return {
  2639. start,
  2640. end,
  2641. loop,
  2642. style: segment.style
  2643. };
  2644. }
  2645. function _boundSegment(segment, points, bounds) {
  2646. if (!bounds) {
  2647. return [
  2648. segment
  2649. ];
  2650. }
  2651. const { property, start: startBound, end: endBound } = bounds;
  2652. const count = points.length;
  2653. const { compare, between, normalize } = propertyFn(property);
  2654. const { start, end, loop, style } = getSegment(segment, points, bounds);
  2655. const result = [];
  2656. let inside = false;
  2657. let subStart = null;
  2658. let value, point, prevValue;
  2659. const startIsBefore = () => between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0;
  2660. const endIsBefore = () => compare(endBound, value) === 0 || between(endBound, prevValue, value);
  2661. const shouldStart = () => inside || startIsBefore();
  2662. const shouldStop = () => !inside || endIsBefore();
  2663. for (let i = start, prev = start; i <= end; ++i) {
  2664. point = points[i % count];
  2665. if (point.skip) {
  2666. continue;
  2667. }
  2668. value = normalize(point[property]);
  2669. if (value === prevValue) {
  2670. continue;
  2671. }
  2672. inside = between(value, startBound, endBound);
  2673. if (subStart === null && shouldStart()) {
  2674. subStart = compare(value, startBound) === 0 ? i : prev;
  2675. }
  2676. if (subStart !== null && shouldStop()) {
  2677. result.push(normalizeSegment({
  2678. start: subStart,
  2679. end: i,
  2680. loop,
  2681. count,
  2682. style
  2683. }));
  2684. subStart = null;
  2685. }
  2686. prev = i;
  2687. prevValue = value;
  2688. }
  2689. if (subStart !== null) {
  2690. result.push(normalizeSegment({
  2691. start: subStart,
  2692. end,
  2693. loop,
  2694. count,
  2695. style
  2696. }));
  2697. }
  2698. return result;
  2699. }
  2700. function _boundSegments(line, bounds) {
  2701. const result = [];
  2702. const segments = line.segments;
  2703. for (let i = 0; i < segments.length; i++) {
  2704. const sub = _boundSegment(segments[i], line.points, bounds);
  2705. if (sub.length) {
  2706. result.push(...sub);
  2707. }
  2708. }
  2709. return result;
  2710. }
  2711. function findStartAndEnd(points, count, loop, spanGaps) {
  2712. let start = 0;
  2713. let end = count - 1;
  2714. if (loop && !spanGaps) {
  2715. while (start < count && !points[start].skip) {
  2716. start++;
  2717. }
  2718. }
  2719. while (start < count && points[start].skip) {
  2720. start++;
  2721. }
  2722. start %= count;
  2723. if (loop) {
  2724. end += start;
  2725. }
  2726. while (end > start && points[end % count].skip) {
  2727. end--;
  2728. }
  2729. end %= count;
  2730. return {
  2731. start,
  2732. end
  2733. };
  2734. }
  2735. function solidSegments(points, start, max, loop) {
  2736. const count = points.length;
  2737. const result = [];
  2738. let last = start;
  2739. let prev = points[start];
  2740. let end;
  2741. for (end = start + 1; end <= max; ++end) {
  2742. const cur = points[end % count];
  2743. if (cur.skip || cur.stop) {
  2744. if (!prev.skip) {
  2745. loop = false;
  2746. result.push({
  2747. start: start % count,
  2748. end: (end - 1) % count,
  2749. loop
  2750. });
  2751. start = last = cur.stop ? end : null;
  2752. }
  2753. } else {
  2754. last = end;
  2755. if (prev.skip) {
  2756. start = end;
  2757. }
  2758. }
  2759. prev = cur;
  2760. }
  2761. if (last !== null) {
  2762. result.push({
  2763. start: start % count,
  2764. end: last % count,
  2765. loop
  2766. });
  2767. }
  2768. return result;
  2769. }
  2770. function _computeSegments(line, segmentOptions) {
  2771. const points = line.points;
  2772. const spanGaps = line.options.spanGaps;
  2773. const count = points.length;
  2774. if (!count) {
  2775. return [];
  2776. }
  2777. const loop = !!line._loop;
  2778. const { start, end } = findStartAndEnd(points, count, loop, spanGaps);
  2779. if (spanGaps === true) {
  2780. return splitByStyles(line, [
  2781. {
  2782. start,
  2783. end,
  2784. loop
  2785. }
  2786. ], points, segmentOptions);
  2787. }
  2788. const max = end < start ? end + count : end;
  2789. const completeLoop = !!line._fullLoop && start === 0 && end === count - 1;
  2790. return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions);
  2791. }
  2792. function splitByStyles(line, segments, points, segmentOptions) {
  2793. if (!segmentOptions || !segmentOptions.setContext || !points) {
  2794. return segments;
  2795. }
  2796. return doSplitByStyles(line, segments, points, segmentOptions);
  2797. }
  2798. function doSplitByStyles(line, segments, points, segmentOptions) {
  2799. const chartContext = line._chart.getContext();
  2800. const baseStyle = readStyle(line.options);
  2801. const { _datasetIndex: datasetIndex, options: { spanGaps } } = line;
  2802. const count = points.length;
  2803. const result = [];
  2804. let prevStyle = baseStyle;
  2805. let start = segments[0].start;
  2806. let i = start;
  2807. function addStyle(s, e, l, st) {
  2808. const dir = spanGaps ? -1 : 1;
  2809. if (s === e) {
  2810. return;
  2811. }
  2812. s += count;
  2813. while (points[s % count].skip) {
  2814. s -= dir;
  2815. }
  2816. while (points[e % count].skip) {
  2817. e += dir;
  2818. }
  2819. if (s % count !== e % count) {
  2820. result.push({
  2821. start: s % count,
  2822. end: e % count,
  2823. loop: l,
  2824. style: st
  2825. });
  2826. prevStyle = st;
  2827. start = e % count;
  2828. }
  2829. }
  2830. for (const segment of segments) {
  2831. start = spanGaps ? start : segment.start;
  2832. let prev = points[start % count];
  2833. let style;
  2834. for (i = start + 1; i <= segment.end; i++) {
  2835. const pt = points[i % count];
  2836. style = readStyle(segmentOptions.setContext(createContext(chartContext, {
  2837. type: "segment",
  2838. p0: prev,
  2839. p1: pt,
  2840. p0DataIndex: (i - 1) % count,
  2841. p1DataIndex: i % count,
  2842. datasetIndex
  2843. })));
  2844. if (styleChanged(style, prevStyle)) {
  2845. addStyle(start, i - 1, segment.loop, prevStyle);
  2846. }
  2847. prev = pt;
  2848. prevStyle = style;
  2849. }
  2850. if (start < i - 1) {
  2851. addStyle(start, i - 1, segment.loop, prevStyle);
  2852. }
  2853. }
  2854. return result;
  2855. }
  2856. function readStyle(options) {
  2857. return {
  2858. backgroundColor: options.backgroundColor,
  2859. borderCapStyle: options.borderCapStyle,
  2860. borderDash: options.borderDash,
  2861. borderDashOffset: options.borderDashOffset,
  2862. borderJoinStyle: options.borderJoinStyle,
  2863. borderWidth: options.borderWidth,
  2864. borderColor: options.borderColor
  2865. };
  2866. }
  2867. function styleChanged(style, prevStyle) {
  2868. if (!prevStyle) {
  2869. return false;
  2870. }
  2871. const cache = [];
  2872. const replacer = function(key, value) {
  2873. if (!isPatternOrGradient(value)) {
  2874. return value;
  2875. }
  2876. if (!cache.includes(value)) {
  2877. cache.push(value);
  2878. }
  2879. return cache.indexOf(value);
  2880. };
  2881. return JSON.stringify(style, replacer) !== JSON.stringify(prevStyle, replacer);
  2882. }
  2883. /*!
  2884. * Chart.js v4.4.7
  2885. * https://www.chartjs.org
  2886. * (c) 2024 Chart.js Contributors
  2887. * Released under the MIT License
  2888. */
  2889. class Animator {
  2890. constructor() {
  2891. this._request = null;
  2892. this._charts = /* @__PURE__ */ new Map();
  2893. this._running = false;
  2894. this._lastDate = void 0;
  2895. }
  2896. _notify(chart, anims, date, type) {
  2897. const callbacks = anims.listeners[type];
  2898. const numSteps = anims.duration;
  2899. callbacks.forEach((fn) => fn({
  2900. chart,
  2901. initial: anims.initial,
  2902. numSteps,
  2903. currentStep: Math.min(date - anims.start, numSteps)
  2904. }));
  2905. }
  2906. _refresh() {
  2907. if (this._request) {
  2908. return;
  2909. }
  2910. this._running = true;
  2911. this._request = requestAnimFrame.call(window, () => {
  2912. this._update();
  2913. this._request = null;
  2914. if (this._running) {
  2915. this._refresh();
  2916. }
  2917. });
  2918. }
  2919. _update(date = Date.now()) {
  2920. let remaining = 0;
  2921. this._charts.forEach((anims, chart) => {
  2922. if (!anims.running || !anims.items.length) {
  2923. return;
  2924. }
  2925. const items = anims.items;
  2926. let i = items.length - 1;
  2927. let draw2 = false;
  2928. let item;
  2929. for (; i >= 0; --i) {
  2930. item = items[i];
  2931. if (item._active) {
  2932. if (item._total > anims.duration) {
  2933. anims.duration = item._total;
  2934. }
  2935. item.tick(date);
  2936. draw2 = true;
  2937. } else {
  2938. items[i] = items[items.length - 1];
  2939. items.pop();
  2940. }
  2941. }
  2942. if (draw2) {
  2943. chart.draw();
  2944. this._notify(chart, anims, date, "progress");
  2945. }
  2946. if (!items.length) {
  2947. anims.running = false;
  2948. this._notify(chart, anims, date, "complete");
  2949. anims.initial = false;
  2950. }
  2951. remaining += items.length;
  2952. });
  2953. this._lastDate = date;
  2954. if (remaining === 0) {
  2955. this._running = false;
  2956. }
  2957. }
  2958. _getAnims(chart) {
  2959. const charts = this._charts;
  2960. let anims = charts.get(chart);
  2961. if (!anims) {
  2962. anims = {
  2963. running: false,
  2964. initial: true,
  2965. items: [],
  2966. listeners: {
  2967. complete: [],
  2968. progress: []
  2969. }
  2970. };
  2971. charts.set(chart, anims);
  2972. }
  2973. return anims;
  2974. }
  2975. listen(chart, event, cb) {
  2976. this._getAnims(chart).listeners[event].push(cb);
  2977. }
  2978. add(chart, items) {
  2979. if (!items || !items.length) {
  2980. return;
  2981. }
  2982. this._getAnims(chart).items.push(...items);
  2983. }
  2984. has(chart) {
  2985. return this._getAnims(chart).items.length > 0;
  2986. }
  2987. start(chart) {
  2988. const anims = this._charts.get(chart);
  2989. if (!anims) {
  2990. return;
  2991. }
  2992. anims.running = true;
  2993. anims.start = Date.now();
  2994. anims.duration = anims.items.reduce((acc, cur) => Math.max(acc, cur._duration), 0);
  2995. this._refresh();
  2996. }
  2997. running(chart) {
  2998. if (!this._running) {
  2999. return false;
  3000. }
  3001. const anims = this._charts.get(chart);
  3002. if (!anims || !anims.running || !anims.items.length) {
  3003. return false;
  3004. }
  3005. return true;
  3006. }
  3007. stop(chart) {
  3008. const anims = this._charts.get(chart);
  3009. if (!anims || !anims.items.length) {
  3010. return;
  3011. }
  3012. const items = anims.items;
  3013. let i = items.length - 1;
  3014. for (; i >= 0; --i) {
  3015. items[i].cancel();
  3016. }
  3017. anims.items = [];
  3018. this._notify(chart, anims, Date.now(), "complete");
  3019. }
  3020. remove(chart) {
  3021. return this._charts.delete(chart);
  3022. }
  3023. }
  3024. var animator = /* @__PURE__ */ new Animator();
  3025. const transparent = "transparent";
  3026. const interpolators = {
  3027. boolean(from2, to2, factor) {
  3028. return factor > 0.5 ? to2 : from2;
  3029. },
  3030. color(from2, to2, factor) {
  3031. const c0 = color(from2 || transparent);
  3032. const c1 = c0.valid && color(to2 || transparent);
  3033. return c1 && c1.valid ? c1.mix(c0, factor).hexString() : to2;
  3034. },
  3035. number(from2, to2, factor) {
  3036. return from2 + (to2 - from2) * factor;
  3037. }
  3038. };
  3039. class Animation {
  3040. constructor(cfg, target, prop, to2) {
  3041. const currentValue = target[prop];
  3042. to2 = resolve([
  3043. cfg.to,
  3044. to2,
  3045. currentValue,
  3046. cfg.from
  3047. ]);
  3048. const from2 = resolve([
  3049. cfg.from,
  3050. currentValue,
  3051. to2
  3052. ]);
  3053. this._active = true;
  3054. this._fn = cfg.fn || interpolators[cfg.type || typeof from2];
  3055. this._easing = effects[cfg.easing] || effects.linear;
  3056. this._start = Math.floor(Date.now() + (cfg.delay || 0));
  3057. this._duration = this._total = Math.floor(cfg.duration);
  3058. this._loop = !!cfg.loop;
  3059. this._target = target;
  3060. this._prop = prop;
  3061. this._from = from2;
  3062. this._to = to2;
  3063. this._promises = void 0;
  3064. }
  3065. active() {
  3066. return this._active;
  3067. }
  3068. update(cfg, to2, date) {
  3069. if (this._active) {
  3070. this._notify(false);
  3071. const currentValue = this._target[this._prop];
  3072. const elapsed = date - this._start;
  3073. const remain = this._duration - elapsed;
  3074. this._start = date;
  3075. this._duration = Math.floor(Math.max(remain, cfg.duration));
  3076. this._total += elapsed;
  3077. this._loop = !!cfg.loop;
  3078. this._to = resolve([
  3079. cfg.to,
  3080. to2,
  3081. currentValue,
  3082. cfg.from
  3083. ]);
  3084. this._from = resolve([
  3085. cfg.from,
  3086. currentValue,
  3087. to2
  3088. ]);
  3089. }
  3090. }
  3091. cancel() {
  3092. if (this._active) {
  3093. this.tick(Date.now());
  3094. this._active = false;
  3095. this._notify(false);
  3096. }
  3097. }
  3098. tick(date) {
  3099. const elapsed = date - this._start;
  3100. const duration = this._duration;
  3101. const prop = this._prop;
  3102. const from2 = this._from;
  3103. const loop = this._loop;
  3104. const to2 = this._to;
  3105. let factor;
  3106. this._active = from2 !== to2 && (loop || elapsed < duration);
  3107. if (!this._active) {
  3108. this._target[prop] = to2;
  3109. this._notify(true);
  3110. return;
  3111. }
  3112. if (elapsed < 0) {
  3113. this._target[prop] = from2;
  3114. return;
  3115. }
  3116. factor = elapsed / duration % 2;
  3117. factor = loop && factor > 1 ? 2 - factor : factor;
  3118. factor = this._easing(Math.min(1, Math.max(0, factor)));
  3119. this._target[prop] = this._fn(from2, to2, factor);
  3120. }
  3121. wait() {
  3122. const promises = this._promises || (this._promises = []);
  3123. return new Promise((res, rej) => {
  3124. promises.push({
  3125. res,
  3126. rej
  3127. });
  3128. });
  3129. }
  3130. _notify(resolved) {
  3131. const method = resolved ? "res" : "rej";
  3132. const promises = this._promises || [];
  3133. for (let i = 0; i < promises.length; i++) {
  3134. promises[i][method]();
  3135. }
  3136. }
  3137. }
  3138. class Animations {
  3139. constructor(chart, config) {
  3140. this._chart = chart;
  3141. this._properties = /* @__PURE__ */ new Map();
  3142. this.configure(config);
  3143. }
  3144. configure(config) {
  3145. if (!isObject(config)) {
  3146. return;
  3147. }
  3148. const animationOptions = Object.keys(defaults.animation);
  3149. const animatedProps = this._properties;
  3150. Object.getOwnPropertyNames(config).forEach((key) => {
  3151. const cfg = config[key];
  3152. if (!isObject(cfg)) {
  3153. return;
  3154. }
  3155. const resolved = {};
  3156. for (const option of animationOptions) {
  3157. resolved[option] = cfg[option];
  3158. }
  3159. (isArray(cfg.properties) && cfg.properties || [
  3160. key
  3161. ]).forEach((prop) => {
  3162. if (prop === key || !animatedProps.has(prop)) {
  3163. animatedProps.set(prop, resolved);
  3164. }
  3165. });
  3166. });
  3167. }
  3168. _animateOptions(target, values) {
  3169. const newOptions = values.options;
  3170. const options = resolveTargetOptions(target, newOptions);
  3171. if (!options) {
  3172. return [];
  3173. }
  3174. const animations = this._createAnimations(options, newOptions);
  3175. if (newOptions.$shared) {
  3176. awaitAll(target.options.$animations, newOptions).then(() => {
  3177. target.options = newOptions;
  3178. }, () => {
  3179. });
  3180. }
  3181. return animations;
  3182. }
  3183. _createAnimations(target, values) {
  3184. const animatedProps = this._properties;
  3185. const animations = [];
  3186. const running = target.$animations || (target.$animations = {});
  3187. const props = Object.keys(values);
  3188. const date = Date.now();
  3189. let i;
  3190. for (i = props.length - 1; i >= 0; --i) {
  3191. const prop = props[i];
  3192. if (prop.charAt(0) === "$") {
  3193. continue;
  3194. }
  3195. if (prop === "options") {
  3196. animations.push(...this._animateOptions(target, values));
  3197. continue;
  3198. }
  3199. const value = values[prop];
  3200. let animation = running[prop];
  3201. const cfg = animatedProps.get(prop);
  3202. if (animation) {
  3203. if (cfg && animation.active()) {
  3204. animation.update(cfg, value, date);
  3205. continue;
  3206. } else {
  3207. animation.cancel();
  3208. }
  3209. }
  3210. if (!cfg || !cfg.duration) {
  3211. target[prop] = value;
  3212. continue;
  3213. }
  3214. running[prop] = animation = new Animation(cfg, target, prop, value);
  3215. animations.push(animation);
  3216. }
  3217. return animations;
  3218. }
  3219. update(target, values) {
  3220. if (this._properties.size === 0) {
  3221. Object.assign(target, values);
  3222. return;
  3223. }
  3224. const animations = this._createAnimations(target, values);
  3225. if (animations.length) {
  3226. animator.add(this._chart, animations);
  3227. return true;
  3228. }
  3229. }
  3230. }
  3231. function awaitAll(animations, properties) {
  3232. const running = [];
  3233. const keys = Object.keys(properties);
  3234. for (let i = 0; i < keys.length; i++) {
  3235. const anim = animations[keys[i]];
  3236. if (anim && anim.active()) {
  3237. running.push(anim.wait());
  3238. }
  3239. }
  3240. return Promise.all(running);
  3241. }
  3242. function resolveTargetOptions(target, newOptions) {
  3243. if (!newOptions) {
  3244. return;
  3245. }
  3246. let options = target.options;
  3247. if (!options) {
  3248. target.options = newOptions;
  3249. return;
  3250. }
  3251. if (options.$shared) {
  3252. target.options = options = Object.assign({}, options, {
  3253. $shared: false,
  3254. $animations: {}
  3255. });
  3256. }
  3257. return options;
  3258. }
  3259. function scaleClip(scale, allowedOverflow) {
  3260. const opts = scale && scale.options || {};
  3261. const reverse = opts.reverse;
  3262. const min = opts.min === void 0 ? allowedOverflow : 0;
  3263. const max = opts.max === void 0 ? allowedOverflow : 0;
  3264. return {
  3265. start: reverse ? max : min,
  3266. end: reverse ? min : max
  3267. };
  3268. }
  3269. function defaultClip(xScale, yScale, allowedOverflow) {
  3270. if (allowedOverflow === false) {
  3271. return false;
  3272. }
  3273. const x = scaleClip(xScale, allowedOverflow);
  3274. const y = scaleClip(yScale, allowedOverflow);
  3275. return {
  3276. top: y.end,
  3277. right: x.end,
  3278. bottom: y.start,
  3279. left: x.start
  3280. };
  3281. }
  3282. function toClip(value) {
  3283. let t, r, b, l;
  3284. if (isObject(value)) {
  3285. t = value.top;
  3286. r = value.right;
  3287. b = value.bottom;
  3288. l = value.left;
  3289. } else {
  3290. t = r = b = l = value;
  3291. }
  3292. return {
  3293. top: t,
  3294. right: r,
  3295. bottom: b,
  3296. left: l,
  3297. disabled: value === false
  3298. };
  3299. }
  3300. function getSortedDatasetIndices(chart, filterVisible) {
  3301. const keys = [];
  3302. const metasets = chart._getSortedDatasetMetas(filterVisible);
  3303. let i, ilen;
  3304. for (i = 0, ilen = metasets.length; i < ilen; ++i) {
  3305. keys.push(metasets[i].index);
  3306. }
  3307. return keys;
  3308. }
  3309. function applyStack(stack, value, dsIndex, options = {}) {
  3310. const keys = stack.keys;
  3311. const singleMode = options.mode === "single";
  3312. let i, ilen, datasetIndex, otherValue;
  3313. if (value === null) {
  3314. return;
  3315. }
  3316. let found = false;
  3317. for (i = 0, ilen = keys.length; i < ilen; ++i) {
  3318. datasetIndex = +keys[i];
  3319. if (datasetIndex === dsIndex) {
  3320. found = true;
  3321. if (options.all) {
  3322. continue;
  3323. }
  3324. break;
  3325. }
  3326. otherValue = stack.values[datasetIndex];
  3327. if (isNumberFinite(otherValue) && (singleMode || value === 0 || sign(value) === sign(otherValue))) {
  3328. value += otherValue;
  3329. }
  3330. }
  3331. if (!found && !options.all) {
  3332. return 0;
  3333. }
  3334. return value;
  3335. }
  3336. function convertObjectDataToArray(data, meta) {
  3337. const { iScale, vScale } = meta;
  3338. const iAxisKey = iScale.axis === "x" ? "x" : "y";
  3339. const vAxisKey = vScale.axis === "x" ? "x" : "y";
  3340. const keys = Object.keys(data);
  3341. const adata = new Array(keys.length);
  3342. let i, ilen, key;
  3343. for (i = 0, ilen = keys.length; i < ilen; ++i) {
  3344. key = keys[i];
  3345. adata[i] = {
  3346. [iAxisKey]: key,
  3347. [vAxisKey]: data[key]
  3348. };
  3349. }
  3350. return adata;
  3351. }
  3352. function isStacked(scale, meta) {
  3353. const stacked = scale && scale.options.stacked;
  3354. return stacked || stacked === void 0 && meta.stack !== void 0;
  3355. }
  3356. function getStackKey(indexScale, valueScale, meta) {
  3357. return `${indexScale.id}.${valueScale.id}.${meta.stack || meta.type}`;
  3358. }
  3359. function getUserBounds(scale) {
  3360. const { min, max, minDefined, maxDefined } = scale.getUserBounds();
  3361. return {
  3362. min: minDefined ? min : Number.NEGATIVE_INFINITY,
  3363. max: maxDefined ? max : Number.POSITIVE_INFINITY
  3364. };
  3365. }
  3366. function getOrCreateStack(stacks, stackKey, indexValue) {
  3367. const subStack = stacks[stackKey] || (stacks[stackKey] = {});
  3368. return subStack[indexValue] || (subStack[indexValue] = {});
  3369. }
  3370. function getLastIndexInStack(stack, vScale, positive, type) {
  3371. for (const meta of vScale.getMatchingVisibleMetas(type).reverse()) {
  3372. const value = stack[meta.index];
  3373. if (positive && value > 0 || !positive && value < 0) {
  3374. return meta.index;
  3375. }
  3376. }
  3377. return null;
  3378. }
  3379. function updateStacks(controller, parsed) {
  3380. const { chart, _cachedMeta: meta } = controller;
  3381. const stacks = chart._stacks || (chart._stacks = {});
  3382. const { iScale, vScale, index: datasetIndex } = meta;
  3383. const iAxis = iScale.axis;
  3384. const vAxis = vScale.axis;
  3385. const key = getStackKey(iScale, vScale, meta);
  3386. const ilen = parsed.length;
  3387. let stack;
  3388. for (let i = 0; i < ilen; ++i) {
  3389. const item = parsed[i];
  3390. const { [iAxis]: index, [vAxis]: value } = item;
  3391. const itemStacks = item._stacks || (item._stacks = {});
  3392. stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index);
  3393. stack[datasetIndex] = value;
  3394. stack._top = getLastIndexInStack(stack, vScale, true, meta.type);
  3395. stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type);
  3396. const visualValues = stack._visualValues || (stack._visualValues = {});
  3397. visualValues[datasetIndex] = value;
  3398. }
  3399. }
  3400. function getFirstScaleId(chart, axis) {
  3401. const scales = chart.scales;
  3402. return Object.keys(scales).filter((key) => scales[key].axis === axis).shift();
  3403. }
  3404. function createDatasetContext(parent, index) {
  3405. return createContext(parent, {
  3406. active: false,
  3407. dataset: void 0,
  3408. datasetIndex: index,
  3409. index,
  3410. mode: "default",
  3411. type: "dataset"
  3412. });
  3413. }
  3414. function createDataContext(parent, index, element) {
  3415. return createContext(parent, {
  3416. active: false,
  3417. dataIndex: index,
  3418. parsed: void 0,
  3419. raw: void 0,
  3420. element,
  3421. index,
  3422. mode: "default",
  3423. type: "data"
  3424. });
  3425. }
  3426. function clearStacks(meta, items) {
  3427. const datasetIndex = meta.controller.index;
  3428. const axis = meta.vScale && meta.vScale.axis;
  3429. if (!axis) {
  3430. return;
  3431. }
  3432. items = items || meta._parsed;
  3433. for (const parsed of items) {
  3434. const stacks = parsed._stacks;
  3435. if (!stacks || stacks[axis] === void 0 || stacks[axis][datasetIndex] === void 0) {
  3436. return;
  3437. }
  3438. delete stacks[axis][datasetIndex];
  3439. if (stacks[axis]._visualValues !== void 0 && stacks[axis]._visualValues[datasetIndex] !== void 0) {
  3440. delete stacks[axis]._visualValues[datasetIndex];
  3441. }
  3442. }
  3443. }
  3444. const isDirectUpdateMode = (mode) => mode === "reset" || mode === "none";
  3445. const cloneIfNotShared = (cached, shared) => shared ? cached : Object.assign({}, cached);
  3446. const createStack = (canStack, meta, chart) => canStack && !meta.hidden && meta._stacked && {
  3447. keys: getSortedDatasetIndices(chart, true),
  3448. values: null
  3449. };
  3450. class DatasetController {
  3451. constructor(chart, datasetIndex) {
  3452. this.chart = chart;
  3453. this._ctx = chart.ctx;
  3454. this.index = datasetIndex;
  3455. this._cachedDataOpts = {};
  3456. this._cachedMeta = this.getMeta();
  3457. this._type = this._cachedMeta.type;
  3458. this.options = void 0;
  3459. this._parsing = false;
  3460. this._data = void 0;
  3461. this._objectData = void 0;
  3462. this._sharedOptions = void 0;
  3463. this._drawStart = void 0;
  3464. this._drawCount = void 0;
  3465. this.enableOptionSharing = false;
  3466. this.supportsDecimation = false;
  3467. this.$context = void 0;
  3468. this._syncList = [];
  3469. this.datasetElementType = new.target.datasetElementType;
  3470. this.dataElementType = new.target.dataElementType;
  3471. this.initialize();
  3472. }
  3473. initialize() {
  3474. const meta = this._cachedMeta;
  3475. this.configure();
  3476. this.linkScales();
  3477. meta._stacked = isStacked(meta.vScale, meta);
  3478. this.addElements();
  3479. if (this.options.fill && !this.chart.isPluginEnabled("filler")) {
  3480. console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options");
  3481. }
  3482. }
  3483. updateIndex(datasetIndex) {
  3484. if (this.index !== datasetIndex) {
  3485. clearStacks(this._cachedMeta);
  3486. }
  3487. this.index = datasetIndex;
  3488. }
  3489. linkScales() {
  3490. const chart = this.chart;
  3491. const meta = this._cachedMeta;
  3492. const dataset = this.getDataset();
  3493. const chooseId = (axis, x, y, r) => axis === "x" ? x : axis === "r" ? r : y;
  3494. const xid = meta.xAxisID = valueOrDefault(dataset.xAxisID, getFirstScaleId(chart, "x"));
  3495. const yid = meta.yAxisID = valueOrDefault(dataset.yAxisID, getFirstScaleId(chart, "y"));
  3496. const rid = meta.rAxisID = valueOrDefault(dataset.rAxisID, getFirstScaleId(chart, "r"));
  3497. const indexAxis = meta.indexAxis;
  3498. const iid = meta.iAxisID = chooseId(indexAxis, xid, yid, rid);
  3499. const vid = meta.vAxisID = chooseId(indexAxis, yid, xid, rid);
  3500. meta.xScale = this.getScaleForId(xid);
  3501. meta.yScale = this.getScaleForId(yid);
  3502. meta.rScale = this.getScaleForId(rid);
  3503. meta.iScale = this.getScaleForId(iid);
  3504. meta.vScale = this.getScaleForId(vid);
  3505. }
  3506. getDataset() {
  3507. return this.chart.data.datasets[this.index];
  3508. }
  3509. getMeta() {
  3510. return this.chart.getDatasetMeta(this.index);
  3511. }
  3512. getScaleForId(scaleID) {
  3513. return this.chart.scales[scaleID];
  3514. }
  3515. _getOtherScale(scale) {
  3516. const meta = this._cachedMeta;
  3517. return scale === meta.iScale ? meta.vScale : meta.iScale;
  3518. }
  3519. reset() {
  3520. this._update("reset");
  3521. }
  3522. _destroy() {
  3523. const meta = this._cachedMeta;
  3524. if (this._data) {
  3525. unlistenArrayEvents(this._data, this);
  3526. }
  3527. if (meta._stacked) {
  3528. clearStacks(meta);
  3529. }
  3530. }
  3531. _dataCheck() {
  3532. const dataset = this.getDataset();
  3533. const data = dataset.data || (dataset.data = []);
  3534. const _data = this._data;
  3535. if (isObject(data)) {
  3536. const meta = this._cachedMeta;
  3537. this._data = convertObjectDataToArray(data, meta);
  3538. } else if (_data !== data) {
  3539. if (_data) {
  3540. unlistenArrayEvents(_data, this);
  3541. const meta = this._cachedMeta;
  3542. clearStacks(meta);
  3543. meta._parsed = [];
  3544. }
  3545. if (data && Object.isExtensible(data)) {
  3546. listenArrayEvents(data, this);
  3547. }
  3548. this._syncList = [];
  3549. this._data = data;
  3550. }
  3551. }
  3552. addElements() {
  3553. const meta = this._cachedMeta;
  3554. this._dataCheck();
  3555. if (this.datasetElementType) {
  3556. meta.dataset = new this.datasetElementType();
  3557. }
  3558. }
  3559. buildOrUpdateElements(resetNewElements) {
  3560. const meta = this._cachedMeta;
  3561. const dataset = this.getDataset();
  3562. let stackChanged = false;
  3563. this._dataCheck();
  3564. const oldStacked = meta._stacked;
  3565. meta._stacked = isStacked(meta.vScale, meta);
  3566. if (meta.stack !== dataset.stack) {
  3567. stackChanged = true;
  3568. clearStacks(meta);
  3569. meta.stack = dataset.stack;
  3570. }
  3571. this._resyncElements(resetNewElements);
  3572. if (stackChanged || oldStacked !== meta._stacked) {
  3573. updateStacks(this, meta._parsed);
  3574. meta._stacked = isStacked(meta.vScale, meta);
  3575. }
  3576. }
  3577. configure() {
  3578. const config = this.chart.config;
  3579. const scopeKeys = config.datasetScopeKeys(this._type);
  3580. const scopes = config.getOptionScopes(this.getDataset(), scopeKeys, true);
  3581. this.options = config.createResolver(scopes, this.getContext());
  3582. this._parsing = this.options.parsing;
  3583. this._cachedDataOpts = {};
  3584. }
  3585. parse(start, count) {
  3586. const { _cachedMeta: meta, _data: data } = this;
  3587. const { iScale, _stacked } = meta;
  3588. const iAxis = iScale.axis;
  3589. let sorted = start === 0 && count === data.length ? true : meta._sorted;
  3590. let prev = start > 0 && meta._parsed[start - 1];
  3591. let i, cur, parsed;
  3592. if (this._parsing === false) {
  3593. meta._parsed = data;
  3594. meta._sorted = true;
  3595. parsed = data;
  3596. } else {
  3597. if (isArray(data[start])) {
  3598. parsed = this.parseArrayData(meta, data, start, count);
  3599. } else if (isObject(data[start])) {
  3600. parsed = this.parseObjectData(meta, data, start, count);
  3601. } else {
  3602. parsed = this.parsePrimitiveData(meta, data, start, count);
  3603. }
  3604. const isNotInOrderComparedToPrev = () => cur[iAxis] === null || prev && cur[iAxis] < prev[iAxis];
  3605. for (i = 0; i < count; ++i) {
  3606. meta._parsed[i + start] = cur = parsed[i];
  3607. if (sorted) {
  3608. if (isNotInOrderComparedToPrev()) {
  3609. sorted = false;
  3610. }
  3611. prev = cur;
  3612. }
  3613. }
  3614. meta._sorted = sorted;
  3615. }
  3616. if (_stacked) {
  3617. updateStacks(this, parsed);
  3618. }
  3619. }
  3620. parsePrimitiveData(meta, data, start, count) {
  3621. const { iScale, vScale } = meta;
  3622. const iAxis = iScale.axis;
  3623. const vAxis = vScale.axis;
  3624. const labels = iScale.getLabels();
  3625. const singleScale = iScale === vScale;
  3626. const parsed = new Array(count);
  3627. let i, ilen, index;
  3628. for (i = 0, ilen = count; i < ilen; ++i) {
  3629. index = i + start;
  3630. parsed[i] = {
  3631. [iAxis]: singleScale || iScale.parse(labels[index], index),
  3632. [vAxis]: vScale.parse(data[index], index)
  3633. };
  3634. }
  3635. return parsed;
  3636. }
  3637. parseArrayData(meta, data, start, count) {
  3638. const { xScale, yScale } = meta;
  3639. const parsed = new Array(count);
  3640. let i, ilen, index, item;
  3641. for (i = 0, ilen = count; i < ilen; ++i) {
  3642. index = i + start;
  3643. item = data[index];
  3644. parsed[i] = {
  3645. x: xScale.parse(item[0], index),
  3646. y: yScale.parse(item[1], index)
  3647. };
  3648. }
  3649. return parsed;
  3650. }
  3651. parseObjectData(meta, data, start, count) {
  3652. const { xScale, yScale } = meta;
  3653. const { xAxisKey = "x", yAxisKey = "y" } = this._parsing;
  3654. const parsed = new Array(count);
  3655. let i, ilen, index, item;
  3656. for (i = 0, ilen = count; i < ilen; ++i) {
  3657. index = i + start;
  3658. item = data[index];
  3659. parsed[i] = {
  3660. x: xScale.parse(resolveObjectKey(item, xAxisKey), index),
  3661. y: yScale.parse(resolveObjectKey(item, yAxisKey), index)
  3662. };
  3663. }
  3664. return parsed;
  3665. }
  3666. getParsed(index) {
  3667. return this._cachedMeta._parsed[index];
  3668. }
  3669. getDataElement(index) {
  3670. return this._cachedMeta.data[index];
  3671. }
  3672. applyStack(scale, parsed, mode) {
  3673. const chart = this.chart;
  3674. const meta = this._cachedMeta;
  3675. const value = parsed[scale.axis];
  3676. const stack = {
  3677. keys: getSortedDatasetIndices(chart, true),
  3678. values: parsed._stacks[scale.axis]._visualValues
  3679. };
  3680. return applyStack(stack, value, meta.index, {
  3681. mode
  3682. });
  3683. }
  3684. updateRangeFromParsed(range, scale, parsed, stack) {
  3685. const parsedValue = parsed[scale.axis];
  3686. let value = parsedValue === null ? NaN : parsedValue;
  3687. const values = stack && parsed._stacks[scale.axis];
  3688. if (stack && values) {
  3689. stack.values = values;
  3690. value = applyStack(stack, parsedValue, this._cachedMeta.index);
  3691. }
  3692. range.min = Math.min(range.min, value);
  3693. range.max = Math.max(range.max, value);
  3694. }
  3695. getMinMax(scale, canStack) {
  3696. const meta = this._cachedMeta;
  3697. const _parsed = meta._parsed;
  3698. const sorted = meta._sorted && scale === meta.iScale;
  3699. const ilen = _parsed.length;
  3700. const otherScale = this._getOtherScale(scale);
  3701. const stack = createStack(canStack, meta, this.chart);
  3702. const range = {
  3703. min: Number.POSITIVE_INFINITY,
  3704. max: Number.NEGATIVE_INFINITY
  3705. };
  3706. const { min: otherMin, max: otherMax } = getUserBounds(otherScale);
  3707. let i, parsed;
  3708. function _skip() {
  3709. parsed = _parsed[i];
  3710. const otherValue = parsed[otherScale.axis];
  3711. return !isNumberFinite(parsed[scale.axis]) || otherMin > otherValue || otherMax < otherValue;
  3712. }
  3713. for (i = 0; i < ilen; ++i) {
  3714. if (_skip()) {
  3715. continue;
  3716. }
  3717. this.updateRangeFromParsed(range, scale, parsed, stack);
  3718. if (sorted) {
  3719. break;
  3720. }
  3721. }
  3722. if (sorted) {
  3723. for (i = ilen - 1; i >= 0; --i) {
  3724. if (_skip()) {
  3725. continue;
  3726. }
  3727. this.updateRangeFromParsed(range, scale, parsed, stack);
  3728. break;
  3729. }
  3730. }
  3731. return range;
  3732. }
  3733. getAllParsedValues(scale) {
  3734. const parsed = this._cachedMeta._parsed;
  3735. const values = [];
  3736. let i, ilen, value;
  3737. for (i = 0, ilen = parsed.length; i < ilen; ++i) {
  3738. value = parsed[i][scale.axis];
  3739. if (isNumberFinite(value)) {
  3740. values.push(value);
  3741. }
  3742. }
  3743. return values;
  3744. }
  3745. getMaxOverflow() {
  3746. return false;
  3747. }
  3748. getLabelAndValue(index) {
  3749. const meta = this._cachedMeta;
  3750. const iScale = meta.iScale;
  3751. const vScale = meta.vScale;
  3752. const parsed = this.getParsed(index);
  3753. return {
  3754. label: iScale ? "" + iScale.getLabelForValue(parsed[iScale.axis]) : "",
  3755. value: vScale ? "" + vScale.getLabelForValue(parsed[vScale.axis]) : ""
  3756. };
  3757. }
  3758. _update(mode) {
  3759. const meta = this._cachedMeta;
  3760. this.update(mode || "default");
  3761. meta._clip = toClip(valueOrDefault(this.options.clip, defaultClip(meta.xScale, meta.yScale, this.getMaxOverflow())));
  3762. }
  3763. update(mode) {
  3764. }
  3765. draw() {
  3766. const ctx = this._ctx;
  3767. const chart = this.chart;
  3768. const meta = this._cachedMeta;
  3769. const elements = meta.data || [];
  3770. const area = chart.chartArea;
  3771. const active = [];
  3772. const start = this._drawStart || 0;
  3773. const count = this._drawCount || elements.length - start;
  3774. const drawActiveElementsOnTop = this.options.drawActiveElementsOnTop;
  3775. let i;
  3776. if (meta.dataset) {
  3777. meta.dataset.draw(ctx, area, start, count);
  3778. }
  3779. for (i = start; i < start + count; ++i) {
  3780. const element = elements[i];
  3781. if (element.hidden) {
  3782. continue;
  3783. }
  3784. if (element.active && drawActiveElementsOnTop) {
  3785. active.push(element);
  3786. } else {
  3787. element.draw(ctx, area);
  3788. }
  3789. }
  3790. for (i = 0; i < active.length; ++i) {
  3791. active[i].draw(ctx, area);
  3792. }
  3793. }
  3794. getStyle(index, active) {
  3795. const mode = active ? "active" : "default";
  3796. return index === void 0 && this._cachedMeta.dataset ? this.resolveDatasetElementOptions(mode) : this.resolveDataElementOptions(index || 0, mode);
  3797. }
  3798. getContext(index, active, mode) {
  3799. const dataset = this.getDataset();
  3800. let context;
  3801. if (index >= 0 && index < this._cachedMeta.data.length) {
  3802. const element = this._cachedMeta.data[index];
  3803. context = element.$context || (element.$context = createDataContext(this.getContext(), index, element));
  3804. context.parsed = this.getParsed(index);
  3805. context.raw = dataset.data[index];
  3806. context.index = context.dataIndex = index;
  3807. } else {
  3808. context = this.$context || (this.$context = createDatasetContext(this.chart.getContext(), this.index));
  3809. context.dataset = dataset;
  3810. context.index = context.datasetIndex = this.index;
  3811. }
  3812. context.active = !!active;
  3813. context.mode = mode;
  3814. return context;
  3815. }
  3816. resolveDatasetElementOptions(mode) {
  3817. return this._resolveElementOptions(this.datasetElementType.id, mode);
  3818. }
  3819. resolveDataElementOptions(index, mode) {
  3820. return this._resolveElementOptions(this.dataElementType.id, mode, index);
  3821. }
  3822. _resolveElementOptions(elementType, mode = "default", index) {
  3823. const active = mode === "active";
  3824. const cache = this._cachedDataOpts;
  3825. const cacheKey = elementType + "-" + mode;
  3826. const cached = cache[cacheKey];
  3827. const sharing = this.enableOptionSharing && defined(index);
  3828. if (cached) {
  3829. return cloneIfNotShared(cached, sharing);
  3830. }
  3831. const config = this.chart.config;
  3832. const scopeKeys = config.datasetElementScopeKeys(this._type, elementType);
  3833. const prefixes = active ? [
  3834. `${elementType}Hover`,
  3835. "hover",
  3836. elementType,
  3837. ""
  3838. ] : [
  3839. elementType,
  3840. ""
  3841. ];
  3842. const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);
  3843. const names2 = Object.keys(defaults.elements[elementType]);
  3844. const context = () => this.getContext(index, active, mode);
  3845. const values = config.resolveNamedOptions(scopes, names2, context, prefixes);
  3846. if (values.$shared) {
  3847. values.$shared = sharing;
  3848. cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing));
  3849. }
  3850. return values;
  3851. }
  3852. _resolveAnimations(index, transition, active) {
  3853. const chart = this.chart;
  3854. const cache = this._cachedDataOpts;
  3855. const cacheKey = `animation-${transition}`;
  3856. const cached = cache[cacheKey];
  3857. if (cached) {
  3858. return cached;
  3859. }
  3860. let options;
  3861. if (chart.options.animation !== false) {
  3862. const config = this.chart.config;
  3863. const scopeKeys = config.datasetAnimationScopeKeys(this._type, transition);
  3864. const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);
  3865. options = config.createResolver(scopes, this.getContext(index, active, transition));
  3866. }
  3867. const animations = new Animations(chart, options && options.animations);
  3868. if (options && options._cacheable) {
  3869. cache[cacheKey] = Object.freeze(animations);
  3870. }
  3871. return animations;
  3872. }
  3873. getSharedOptions(options) {
  3874. if (!options.$shared) {
  3875. return;
  3876. }
  3877. return this._sharedOptions || (this._sharedOptions = Object.assign({}, options));
  3878. }
  3879. includeOptions(mode, sharedOptions) {
  3880. return !sharedOptions || isDirectUpdateMode(mode) || this.chart._animationsDisabled;
  3881. }
  3882. _getSharedOptions(start, mode) {
  3883. const firstOpts = this.resolveDataElementOptions(start, mode);
  3884. const previouslySharedOptions = this._sharedOptions;
  3885. const sharedOptions = this.getSharedOptions(firstOpts);
  3886. const includeOptions = this.includeOptions(mode, sharedOptions) || sharedOptions !== previouslySharedOptions;
  3887. this.updateSharedOptions(sharedOptions, mode, firstOpts);
  3888. return {
  3889. sharedOptions,
  3890. includeOptions
  3891. };
  3892. }
  3893. updateElement(element, index, properties, mode) {
  3894. if (isDirectUpdateMode(mode)) {
  3895. Object.assign(element, properties);
  3896. } else {
  3897. this._resolveAnimations(index, mode).update(element, properties);
  3898. }
  3899. }
  3900. updateSharedOptions(sharedOptions, mode, newOptions) {
  3901. if (sharedOptions && !isDirectUpdateMode(mode)) {
  3902. this._resolveAnimations(void 0, mode).update(sharedOptions, newOptions);
  3903. }
  3904. }
  3905. _setStyle(element, index, mode, active) {
  3906. element.active = active;
  3907. const options = this.getStyle(index, active);
  3908. this._resolveAnimations(index, mode, active).update(element, {
  3909. options: !active && this.getSharedOptions(options) || options
  3910. });
  3911. }
  3912. removeHoverStyle(element, datasetIndex, index) {
  3913. this._setStyle(element, index, "active", false);
  3914. }
  3915. setHoverStyle(element, datasetIndex, index) {
  3916. this._setStyle(element, index, "active", true);
  3917. }
  3918. _removeDatasetHoverStyle() {
  3919. const element = this._cachedMeta.dataset;
  3920. if (element) {
  3921. this._setStyle(element, void 0, "active", false);
  3922. }
  3923. }
  3924. _setDatasetHoverStyle() {
  3925. const element = this._cachedMeta.dataset;
  3926. if (element) {
  3927. this._setStyle(element, void 0, "active", true);
  3928. }
  3929. }
  3930. _resyncElements(resetNewElements) {
  3931. const data = this._data;
  3932. const elements = this._cachedMeta.data;
  3933. for (const [method, arg1, arg2] of this._syncList) {
  3934. this[method](arg1, arg2);
  3935. }
  3936. this._syncList = [];
  3937. const numMeta = elements.length;
  3938. const numData = data.length;
  3939. const count = Math.min(numData, numMeta);
  3940. if (count) {
  3941. this.parse(0, count);
  3942. }
  3943. if (numData > numMeta) {
  3944. this._insertElements(numMeta, numData - numMeta, resetNewElements);
  3945. } else if (numData < numMeta) {
  3946. this._removeElements(numData, numMeta - numData);
  3947. }
  3948. }
  3949. _insertElements(start, count, resetNewElements = true) {
  3950. const meta = this._cachedMeta;
  3951. const data = meta.data;
  3952. const end = start + count;
  3953. let i;
  3954. const move = (arr) => {
  3955. arr.length += count;
  3956. for (i = arr.length - 1; i >= end; i--) {
  3957. arr[i] = arr[i - count];
  3958. }
  3959. };
  3960. move(data);
  3961. for (i = start; i < end; ++i) {
  3962. data[i] = new this.dataElementType();
  3963. }
  3964. if (this._parsing) {
  3965. move(meta._parsed);
  3966. }
  3967. this.parse(start, count);
  3968. if (resetNewElements) {
  3969. this.updateElements(data, start, count, "reset");
  3970. }
  3971. }
  3972. updateElements(element, start, count, mode) {
  3973. }
  3974. _removeElements(start, count) {
  3975. const meta = this._cachedMeta;
  3976. if (this._parsing) {
  3977. const removed = meta._parsed.splice(start, count);
  3978. if (meta._stacked) {
  3979. clearStacks(meta, removed);
  3980. }
  3981. }
  3982. meta.data.splice(start, count);
  3983. }
  3984. _sync(args) {
  3985. if (this._parsing) {
  3986. this._syncList.push(args);
  3987. } else {
  3988. const [method, arg1, arg2] = args;
  3989. this[method](arg1, arg2);
  3990. }
  3991. this.chart._dataChanges.push([
  3992. this.index,
  3993. ...args
  3994. ]);
  3995. }
  3996. _onDataPush() {
  3997. const count = arguments.length;
  3998. this._sync([
  3999. "_insertElements",
  4000. this.getDataset().data.length - count,
  4001. count
  4002. ]);
  4003. }
  4004. _onDataPop() {
  4005. this._sync([
  4006. "_removeElements",
  4007. this._cachedMeta.data.length - 1,
  4008. 1
  4009. ]);
  4010. }
  4011. _onDataShift() {
  4012. this._sync([
  4013. "_removeElements",
  4014. 0,
  4015. 1
  4016. ]);
  4017. }
  4018. _onDataSplice(start, count) {
  4019. if (count) {
  4020. this._sync([
  4021. "_removeElements",
  4022. start,
  4023. count
  4024. ]);
  4025. }
  4026. const newCount = arguments.length - 2;
  4027. if (newCount) {
  4028. this._sync([
  4029. "_insertElements",
  4030. start,
  4031. newCount
  4032. ]);
  4033. }
  4034. }
  4035. _onDataUnshift() {
  4036. this._sync([
  4037. "_insertElements",
  4038. 0,
  4039. arguments.length
  4040. ]);
  4041. }
  4042. }
  4043. __publicField(DatasetController, "defaults", {});
  4044. __publicField(DatasetController, "datasetElementType", null);
  4045. __publicField(DatasetController, "dataElementType", null);
  4046. class LineController extends DatasetController {
  4047. initialize() {
  4048. this.enableOptionSharing = true;
  4049. this.supportsDecimation = true;
  4050. super.initialize();
  4051. }
  4052. update(mode) {
  4053. const meta = this._cachedMeta;
  4054. const { dataset: line, data: points = [], _dataset } = meta;
  4055. const animationsDisabled = this.chart._animationsDisabled;
  4056. let { start, count } = _getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
  4057. this._drawStart = start;
  4058. this._drawCount = count;
  4059. if (_scaleRangesChanged(meta)) {
  4060. start = 0;
  4061. count = points.length;
  4062. }
  4063. line._chart = this.chart;
  4064. line._datasetIndex = this.index;
  4065. line._decimated = !!_dataset._decimated;
  4066. line.points = points;
  4067. const options = this.resolveDatasetElementOptions(mode);
  4068. if (!this.options.showLine) {
  4069. options.borderWidth = 0;
  4070. }
  4071. options.segment = this.options.segment;
  4072. this.updateElement(line, void 0, {
  4073. animated: !animationsDisabled,
  4074. options
  4075. }, mode);
  4076. this.updateElements(points, start, count, mode);
  4077. }
  4078. updateElements(points, start, count, mode) {
  4079. const reset = mode === "reset";
  4080. const { iScale, vScale, _stacked, _dataset } = this._cachedMeta;
  4081. const { sharedOptions, includeOptions } = this._getSharedOptions(start, mode);
  4082. const iAxis = iScale.axis;
  4083. const vAxis = vScale.axis;
  4084. const { spanGaps, segment } = this.options;
  4085. const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
  4086. const directUpdate = this.chart._animationsDisabled || reset || mode === "none";
  4087. const end = start + count;
  4088. const pointsCount = points.length;
  4089. let prevParsed = start > 0 && this.getParsed(start - 1);
  4090. for (let i = 0; i < pointsCount; ++i) {
  4091. const point = points[i];
  4092. const properties = directUpdate ? point : {};
  4093. if (i < start || i >= end) {
  4094. properties.skip = true;
  4095. continue;
  4096. }
  4097. const parsed = this.getParsed(i);
  4098. const nullData = isNullOrUndef(parsed[vAxis]);
  4099. const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);
  4100. const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);
  4101. properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;
  4102. properties.stop = i > 0 && Math.abs(parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;
  4103. if (segment) {
  4104. properties.parsed = parsed;
  4105. properties.raw = _dataset.data[i];
  4106. }
  4107. if (includeOptions) {
  4108. properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? "active" : mode);
  4109. }
  4110. if (!directUpdate) {
  4111. this.updateElement(point, i, properties, mode);
  4112. }
  4113. prevParsed = parsed;
  4114. }
  4115. }
  4116. getMaxOverflow() {
  4117. const meta = this._cachedMeta;
  4118. const dataset = meta.dataset;
  4119. const border = dataset.options && dataset.options.borderWidth || 0;
  4120. const data = meta.data || [];
  4121. if (!data.length) {
  4122. return border;
  4123. }
  4124. const firstPoint = data[0].size(this.resolveDataElementOptions(0));
  4125. const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));
  4126. return Math.max(border, firstPoint, lastPoint) / 2;
  4127. }
  4128. draw() {
  4129. const meta = this._cachedMeta;
  4130. meta.dataset.updateControlPoints(this.chart.chartArea, meta.iScale.axis);
  4131. super.draw();
  4132. }
  4133. }
  4134. __publicField(LineController, "id", "line");
  4135. __publicField(LineController, "defaults", {
  4136. datasetElementType: "line",
  4137. dataElementType: "point",
  4138. showLine: true,
  4139. spanGaps: false
  4140. });
  4141. __publicField(LineController, "overrides", {
  4142. scales: {
  4143. _index_: {
  4144. type: "category"
  4145. },
  4146. _value_: {
  4147. type: "linear"
  4148. }
  4149. }
  4150. });
  4151. function abstract() {
  4152. throw new Error("This method is not implemented: Check that a complete date adapter is provided.");
  4153. }
  4154. class DateAdapterBase {
  4155. constructor(options) {
  4156. __publicField(this, "options");
  4157. this.options = options || {};
  4158. }
  4159. /**
  4160. * Override default date adapter methods.
  4161. * Accepts type parameter to define options type.
  4162. * @example
  4163. * Chart._adapters._date.override<{myAdapterOption: string}>({
  4164. * init() {
  4165. * console.log(this.options.myAdapterOption);
  4166. * }
  4167. * })
  4168. */
  4169. static override(members) {
  4170. Object.assign(DateAdapterBase.prototype, members);
  4171. }
  4172. // eslint-disable-next-line @typescript-eslint/no-empty-function
  4173. init() {
  4174. }
  4175. formats() {
  4176. return abstract();
  4177. }
  4178. parse() {
  4179. return abstract();
  4180. }
  4181. format() {
  4182. return abstract();
  4183. }
  4184. add() {
  4185. return abstract();
  4186. }
  4187. diff() {
  4188. return abstract();
  4189. }
  4190. startOf() {
  4191. return abstract();
  4192. }
  4193. endOf() {
  4194. return abstract();
  4195. }
  4196. }
  4197. var adapters = {
  4198. _date: DateAdapterBase
  4199. };
  4200. function binarySearch(metaset, axis, value, intersect) {
  4201. const { controller, data, _sorted } = metaset;
  4202. const iScale = controller._cachedMeta.iScale;
  4203. if (iScale && axis === iScale.axis && axis !== "r" && _sorted && data.length) {
  4204. const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey;
  4205. if (!intersect) {
  4206. return lookupMethod(data, axis, value);
  4207. } else if (controller._sharedOptions) {
  4208. const el = data[0];
  4209. const range = typeof el.getRange === "function" && el.getRange(axis);
  4210. if (range) {
  4211. const start = lookupMethod(data, axis, value - range);
  4212. const end = lookupMethod(data, axis, value + range);
  4213. return {
  4214. lo: start.lo,
  4215. hi: end.hi
  4216. };
  4217. }
  4218. }
  4219. }
  4220. return {
  4221. lo: 0,
  4222. hi: data.length - 1
  4223. };
  4224. }
  4225. function evaluateInteractionItems(chart, axis, position, handler, intersect) {
  4226. const metasets = chart.getSortedVisibleDatasetMetas();
  4227. const value = position[axis];
  4228. for (let i = 0, ilen = metasets.length; i < ilen; ++i) {
  4229. const { index, data } = metasets[i];
  4230. const { lo, hi } = binarySearch(metasets[i], axis, value, intersect);
  4231. for (let j = lo; j <= hi; ++j) {
  4232. const element = data[j];
  4233. if (!element.skip) {
  4234. handler(element, index, j);
  4235. }
  4236. }
  4237. }
  4238. }
  4239. function getDistanceMetricForAxis(axis) {
  4240. const useX = axis.indexOf("x") !== -1;
  4241. const useY = axis.indexOf("y") !== -1;
  4242. return function(pt1, pt2) {
  4243. const deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
  4244. const deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
  4245. return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
  4246. };
  4247. }
  4248. function getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) {
  4249. const items = [];
  4250. if (!includeInvisible && !chart.isPointInArea(position)) {
  4251. return items;
  4252. }
  4253. const evaluationFunc = function(element, datasetIndex, index) {
  4254. if (!includeInvisible && !_isPointInArea(element, chart.chartArea, 0)) {
  4255. return;
  4256. }
  4257. if (element.inRange(position.x, position.y, useFinalPosition)) {
  4258. items.push({
  4259. element,
  4260. datasetIndex,
  4261. index
  4262. });
  4263. }
  4264. };
  4265. evaluateInteractionItems(chart, axis, position, evaluationFunc, true);
  4266. return items;
  4267. }
  4268. function getNearestRadialItems(chart, position, axis, useFinalPosition) {
  4269. let items = [];
  4270. function evaluationFunc(element, datasetIndex, index) {
  4271. const { startAngle, endAngle } = element.getProps([
  4272. "startAngle",
  4273. "endAngle"
  4274. ], useFinalPosition);
  4275. const { angle } = getAngleFromPoint(element, {
  4276. x: position.x,
  4277. y: position.y
  4278. });
  4279. if (_angleBetween(angle, startAngle, endAngle)) {
  4280. items.push({
  4281. element,
  4282. datasetIndex,
  4283. index
  4284. });
  4285. }
  4286. }
  4287. evaluateInteractionItems(chart, axis, position, evaluationFunc);
  4288. return items;
  4289. }
  4290. function getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {
  4291. let items = [];
  4292. const distanceMetric = getDistanceMetricForAxis(axis);
  4293. let minDistance = Number.POSITIVE_INFINITY;
  4294. function evaluationFunc(element, datasetIndex, index) {
  4295. const inRange = element.inRange(position.x, position.y, useFinalPosition);
  4296. if (intersect && !inRange) {
  4297. return;
  4298. }
  4299. const center = element.getCenterPoint(useFinalPosition);
  4300. const pointInArea = !!includeInvisible || chart.isPointInArea(center);
  4301. if (!pointInArea && !inRange) {
  4302. return;
  4303. }
  4304. const distance = distanceMetric(position, center);
  4305. if (distance < minDistance) {
  4306. items = [
  4307. {
  4308. element,
  4309. datasetIndex,
  4310. index
  4311. }
  4312. ];
  4313. minDistance = distance;
  4314. } else if (distance === minDistance) {
  4315. items.push({
  4316. element,
  4317. datasetIndex,
  4318. index
  4319. });
  4320. }
  4321. }
  4322. evaluateInteractionItems(chart, axis, position, evaluationFunc);
  4323. return items;
  4324. }
  4325. function getNearestItems(chart, position, axis, intersect, useFinalPosition, includeInvisible) {
  4326. if (!includeInvisible && !chart.isPointInArea(position)) {
  4327. return [];
  4328. }
  4329. return axis === "r" && !intersect ? getNearestRadialItems(chart, position, axis, useFinalPosition) : getNearestCartesianItems(chart, position, axis, intersect, useFinalPosition, includeInvisible);
  4330. }
  4331. function getAxisItems(chart, position, axis, intersect, useFinalPosition) {
  4332. const items = [];
  4333. const rangeMethod = axis === "x" ? "inXRange" : "inYRange";
  4334. let intersectsItem = false;
  4335. evaluateInteractionItems(chart, axis, position, (element, datasetIndex, index) => {
  4336. if (element[rangeMethod] && element[rangeMethod](position[axis], useFinalPosition)) {
  4337. items.push({
  4338. element,
  4339. datasetIndex,
  4340. index
  4341. });
  4342. intersectsItem = intersectsItem || element.inRange(position.x, position.y, useFinalPosition);
  4343. }
  4344. });
  4345. if (intersect && !intersectsItem) {
  4346. return [];
  4347. }
  4348. return items;
  4349. }
  4350. var Interaction = {
  4351. evaluateInteractionItems,
  4352. modes: {
  4353. index(chart, e, options, useFinalPosition) {
  4354. const position = getRelativePosition(e, chart);
  4355. const axis = options.axis || "x";
  4356. const includeInvisible = options.includeInvisible || false;
  4357. const items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);
  4358. const elements = [];
  4359. if (!items.length) {
  4360. return [];
  4361. }
  4362. chart.getSortedVisibleDatasetMetas().forEach((meta) => {
  4363. const index = items[0].index;
  4364. const element = meta.data[index];
  4365. if (element && !element.skip) {
  4366. elements.push({
  4367. element,
  4368. datasetIndex: meta.index,
  4369. index
  4370. });
  4371. }
  4372. });
  4373. return elements;
  4374. },
  4375. dataset(chart, e, options, useFinalPosition) {
  4376. const position = getRelativePosition(e, chart);
  4377. const axis = options.axis || "xy";
  4378. const includeInvisible = options.includeInvisible || false;
  4379. let items = options.intersect ? getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible) : getNearestItems(chart, position, axis, false, useFinalPosition, includeInvisible);
  4380. if (items.length > 0) {
  4381. const datasetIndex = items[0].datasetIndex;
  4382. const data = chart.getDatasetMeta(datasetIndex).data;
  4383. items = [];
  4384. for (let i = 0; i < data.length; ++i) {
  4385. items.push({
  4386. element: data[i],
  4387. datasetIndex,
  4388. index: i
  4389. });
  4390. }
  4391. }
  4392. return items;
  4393. },
  4394. point(chart, e, options, useFinalPosition) {
  4395. const position = getRelativePosition(e, chart);
  4396. const axis = options.axis || "xy";
  4397. const includeInvisible = options.includeInvisible || false;
  4398. return getIntersectItems(chart, position, axis, useFinalPosition, includeInvisible);
  4399. },
  4400. nearest(chart, e, options, useFinalPosition) {
  4401. const position = getRelativePosition(e, chart);
  4402. const axis = options.axis || "xy";
  4403. const includeInvisible = options.includeInvisible || false;
  4404. return getNearestItems(chart, position, axis, options.intersect, useFinalPosition, includeInvisible);
  4405. },
  4406. x(chart, e, options, useFinalPosition) {
  4407. const position = getRelativePosition(e, chart);
  4408. return getAxisItems(chart, position, "x", options.intersect, useFinalPosition);
  4409. },
  4410. y(chart, e, options, useFinalPosition) {
  4411. const position = getRelativePosition(e, chart);
  4412. return getAxisItems(chart, position, "y", options.intersect, useFinalPosition);
  4413. }
  4414. }
  4415. };
  4416. const STATIC_POSITIONS = [
  4417. "left",
  4418. "top",
  4419. "right",
  4420. "bottom"
  4421. ];
  4422. function filterByPosition(array, position) {
  4423. return array.filter((v) => v.pos === position);
  4424. }
  4425. function filterDynamicPositionByAxis(array, axis) {
  4426. return array.filter((v) => STATIC_POSITIONS.indexOf(v.pos) === -1 && v.box.axis === axis);
  4427. }
  4428. function sortByWeight(array, reverse) {
  4429. return array.sort((a, b) => {
  4430. const v0 = reverse ? b : a;
  4431. const v1 = reverse ? a : b;
  4432. return v0.weight === v1.weight ? v0.index - v1.index : v0.weight - v1.weight;
  4433. });
  4434. }
  4435. function wrapBoxes(boxes) {
  4436. const layoutBoxes = [];
  4437. let i, ilen, box, pos, stack, stackWeight;
  4438. for (i = 0, ilen = (boxes || []).length; i < ilen; ++i) {
  4439. box = boxes[i];
  4440. ({ position: pos, options: { stack, stackWeight = 1 } } = box);
  4441. layoutBoxes.push({
  4442. index: i,
  4443. box,
  4444. pos,
  4445. horizontal: box.isHorizontal(),
  4446. weight: box.weight,
  4447. stack: stack && pos + stack,
  4448. stackWeight
  4449. });
  4450. }
  4451. return layoutBoxes;
  4452. }
  4453. function buildStacks(layouts2) {
  4454. const stacks = {};
  4455. for (const wrap of layouts2) {
  4456. const { stack, pos, stackWeight } = wrap;
  4457. if (!stack || !STATIC_POSITIONS.includes(pos)) {
  4458. continue;
  4459. }
  4460. const _stack = stacks[stack] || (stacks[stack] = {
  4461. count: 0,
  4462. placed: 0,
  4463. weight: 0,
  4464. size: 0
  4465. });
  4466. _stack.count++;
  4467. _stack.weight += stackWeight;
  4468. }
  4469. return stacks;
  4470. }
  4471. function setLayoutDims(layouts2, params) {
  4472. const stacks = buildStacks(layouts2);
  4473. const { vBoxMaxWidth, hBoxMaxHeight } = params;
  4474. let i, ilen, layout;
  4475. for (i = 0, ilen = layouts2.length; i < ilen; ++i) {
  4476. layout = layouts2[i];
  4477. const { fullSize } = layout.box;
  4478. const stack = stacks[layout.stack];
  4479. const factor = stack && layout.stackWeight / stack.weight;
  4480. if (layout.horizontal) {
  4481. layout.width = factor ? factor * vBoxMaxWidth : fullSize && params.availableWidth;
  4482. layout.height = hBoxMaxHeight;
  4483. } else {
  4484. layout.width = vBoxMaxWidth;
  4485. layout.height = factor ? factor * hBoxMaxHeight : fullSize && params.availableHeight;
  4486. }
  4487. }
  4488. return stacks;
  4489. }
  4490. function buildLayoutBoxes(boxes) {
  4491. const layoutBoxes = wrapBoxes(boxes);
  4492. const fullSize = sortByWeight(layoutBoxes.filter((wrap) => wrap.box.fullSize), true);
  4493. const left = sortByWeight(filterByPosition(layoutBoxes, "left"), true);
  4494. const right = sortByWeight(filterByPosition(layoutBoxes, "right"));
  4495. const top = sortByWeight(filterByPosition(layoutBoxes, "top"), true);
  4496. const bottom = sortByWeight(filterByPosition(layoutBoxes, "bottom"));
  4497. const centerHorizontal = filterDynamicPositionByAxis(layoutBoxes, "x");
  4498. const centerVertical = filterDynamicPositionByAxis(layoutBoxes, "y");
  4499. return {
  4500. fullSize,
  4501. leftAndTop: left.concat(top),
  4502. rightAndBottom: right.concat(centerVertical).concat(bottom).concat(centerHorizontal),
  4503. chartArea: filterByPosition(layoutBoxes, "chartArea"),
  4504. vertical: left.concat(right).concat(centerVertical),
  4505. horizontal: top.concat(bottom).concat(centerHorizontal)
  4506. };
  4507. }
  4508. function getCombinedMax(maxPadding, chartArea, a, b) {
  4509. return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);
  4510. }
  4511. function updateMaxPadding(maxPadding, boxPadding) {
  4512. maxPadding.top = Math.max(maxPadding.top, boxPadding.top);
  4513. maxPadding.left = Math.max(maxPadding.left, boxPadding.left);
  4514. maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);
  4515. maxPadding.right = Math.max(maxPadding.right, boxPadding.right);
  4516. }
  4517. function updateDims(chartArea, params, layout, stacks) {
  4518. const { pos, box } = layout;
  4519. const maxPadding = chartArea.maxPadding;
  4520. if (!isObject(pos)) {
  4521. if (layout.size) {
  4522. chartArea[pos] -= layout.size;
  4523. }
  4524. const stack = stacks[layout.stack] || {
  4525. size: 0,
  4526. count: 1
  4527. };
  4528. stack.size = Math.max(stack.size, layout.horizontal ? box.height : box.width);
  4529. layout.size = stack.size / stack.count;
  4530. chartArea[pos] += layout.size;
  4531. }
  4532. if (box.getPadding) {
  4533. updateMaxPadding(maxPadding, box.getPadding());
  4534. }
  4535. const newWidth = Math.max(0, params.outerWidth - getCombinedMax(maxPadding, chartArea, "left", "right"));
  4536. const newHeight = Math.max(0, params.outerHeight - getCombinedMax(maxPadding, chartArea, "top", "bottom"));
  4537. const widthChanged = newWidth !== chartArea.w;
  4538. const heightChanged = newHeight !== chartArea.h;
  4539. chartArea.w = newWidth;
  4540. chartArea.h = newHeight;
  4541. return layout.horizontal ? {
  4542. same: widthChanged,
  4543. other: heightChanged
  4544. } : {
  4545. same: heightChanged,
  4546. other: widthChanged
  4547. };
  4548. }
  4549. function handleMaxPadding(chartArea) {
  4550. const maxPadding = chartArea.maxPadding;
  4551. function updatePos(pos) {
  4552. const change = Math.max(maxPadding[pos] - chartArea[pos], 0);
  4553. chartArea[pos] += change;
  4554. return change;
  4555. }
  4556. chartArea.y += updatePos("top");
  4557. chartArea.x += updatePos("left");
  4558. updatePos("right");
  4559. updatePos("bottom");
  4560. }
  4561. function getMargins(horizontal, chartArea) {
  4562. const maxPadding = chartArea.maxPadding;
  4563. function marginForPositions(positions2) {
  4564. const margin = {
  4565. left: 0,
  4566. top: 0,
  4567. right: 0,
  4568. bottom: 0
  4569. };
  4570. positions2.forEach((pos) => {
  4571. margin[pos] = Math.max(chartArea[pos], maxPadding[pos]);
  4572. });
  4573. return margin;
  4574. }
  4575. return horizontal ? marginForPositions([
  4576. "left",
  4577. "right"
  4578. ]) : marginForPositions([
  4579. "top",
  4580. "bottom"
  4581. ]);
  4582. }
  4583. function fitBoxes(boxes, chartArea, params, stacks) {
  4584. const refitBoxes = [];
  4585. let i, ilen, layout, box, refit, changed;
  4586. for (i = 0, ilen = boxes.length, refit = 0; i < ilen; ++i) {
  4587. layout = boxes[i];
  4588. box = layout.box;
  4589. box.update(layout.width || chartArea.w, layout.height || chartArea.h, getMargins(layout.horizontal, chartArea));
  4590. const { same, other } = updateDims(chartArea, params, layout, stacks);
  4591. refit |= same && refitBoxes.length;
  4592. changed = changed || other;
  4593. if (!box.fullSize) {
  4594. refitBoxes.push(layout);
  4595. }
  4596. }
  4597. return refit && fitBoxes(refitBoxes, chartArea, params, stacks) || changed;
  4598. }
  4599. function setBoxDims(box, left, top, width, height) {
  4600. box.top = top;
  4601. box.left = left;
  4602. box.right = left + width;
  4603. box.bottom = top + height;
  4604. box.width = width;
  4605. box.height = height;
  4606. }
  4607. function placeBoxes(boxes, chartArea, params, stacks) {
  4608. const userPadding = params.padding;
  4609. let { x, y } = chartArea;
  4610. for (const layout of boxes) {
  4611. const box = layout.box;
  4612. const stack = stacks[layout.stack] || {
  4613. count: 1,
  4614. placed: 0,
  4615. weight: 1
  4616. };
  4617. const weight = layout.stackWeight / stack.weight || 1;
  4618. if (layout.horizontal) {
  4619. const width = chartArea.w * weight;
  4620. const height = stack.size || box.height;
  4621. if (defined(stack.start)) {
  4622. y = stack.start;
  4623. }
  4624. if (box.fullSize) {
  4625. setBoxDims(box, userPadding.left, y, params.outerWidth - userPadding.right - userPadding.left, height);
  4626. } else {
  4627. setBoxDims(box, chartArea.left + stack.placed, y, width, height);
  4628. }
  4629. stack.start = y;
  4630. stack.placed += width;
  4631. y = box.bottom;
  4632. } else {
  4633. const height = chartArea.h * weight;
  4634. const width = stack.size || box.width;
  4635. if (defined(stack.start)) {
  4636. x = stack.start;
  4637. }
  4638. if (box.fullSize) {
  4639. setBoxDims(box, x, userPadding.top, width, params.outerHeight - userPadding.bottom - userPadding.top);
  4640. } else {
  4641. setBoxDims(box, x, chartArea.top + stack.placed, width, height);
  4642. }
  4643. stack.start = x;
  4644. stack.placed += height;
  4645. x = box.right;
  4646. }
  4647. }
  4648. chartArea.x = x;
  4649. chartArea.y = y;
  4650. }
  4651. var layouts = {
  4652. addBox(chart, item) {
  4653. if (!chart.boxes) {
  4654. chart.boxes = [];
  4655. }
  4656. item.fullSize = item.fullSize || false;
  4657. item.position = item.position || "top";
  4658. item.weight = item.weight || 0;
  4659. item._layers = item._layers || function() {
  4660. return [
  4661. {
  4662. z: 0,
  4663. draw(chartArea) {
  4664. item.draw(chartArea);
  4665. }
  4666. }
  4667. ];
  4668. };
  4669. chart.boxes.push(item);
  4670. },
  4671. removeBox(chart, layoutItem) {
  4672. const index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
  4673. if (index !== -1) {
  4674. chart.boxes.splice(index, 1);
  4675. }
  4676. },
  4677. configure(chart, item, options) {
  4678. item.fullSize = options.fullSize;
  4679. item.position = options.position;
  4680. item.weight = options.weight;
  4681. },
  4682. update(chart, width, height, minPadding) {
  4683. if (!chart) {
  4684. return;
  4685. }
  4686. const padding = toPadding(chart.options.layout.padding);
  4687. const availableWidth = Math.max(width - padding.width, 0);
  4688. const availableHeight = Math.max(height - padding.height, 0);
  4689. const boxes = buildLayoutBoxes(chart.boxes);
  4690. const verticalBoxes = boxes.vertical;
  4691. const horizontalBoxes = boxes.horizontal;
  4692. each(chart.boxes, (box) => {
  4693. if (typeof box.beforeLayout === "function") {
  4694. box.beforeLayout();
  4695. }
  4696. });
  4697. const visibleVerticalBoxCount = verticalBoxes.reduce((total, wrap) => wrap.box.options && wrap.box.options.display === false ? total : total + 1, 0) || 1;
  4698. const params = Object.freeze({
  4699. outerWidth: width,
  4700. outerHeight: height,
  4701. padding,
  4702. availableWidth,
  4703. availableHeight,
  4704. vBoxMaxWidth: availableWidth / 2 / visibleVerticalBoxCount,
  4705. hBoxMaxHeight: availableHeight / 2
  4706. });
  4707. const maxPadding = Object.assign({}, padding);
  4708. updateMaxPadding(maxPadding, toPadding(minPadding));
  4709. const chartArea = Object.assign({
  4710. maxPadding,
  4711. w: availableWidth,
  4712. h: availableHeight,
  4713. x: padding.left,
  4714. y: padding.top
  4715. }, padding);
  4716. const stacks = setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);
  4717. fitBoxes(boxes.fullSize, chartArea, params, stacks);
  4718. fitBoxes(verticalBoxes, chartArea, params, stacks);
  4719. if (fitBoxes(horizontalBoxes, chartArea, params, stacks)) {
  4720. fitBoxes(verticalBoxes, chartArea, params, stacks);
  4721. }
  4722. handleMaxPadding(chartArea);
  4723. placeBoxes(boxes.leftAndTop, chartArea, params, stacks);
  4724. chartArea.x += chartArea.w;
  4725. chartArea.y += chartArea.h;
  4726. placeBoxes(boxes.rightAndBottom, chartArea, params, stacks);
  4727. chart.chartArea = {
  4728. left: chartArea.left,
  4729. top: chartArea.top,
  4730. right: chartArea.left + chartArea.w,
  4731. bottom: chartArea.top + chartArea.h,
  4732. height: chartArea.h,
  4733. width: chartArea.w
  4734. };
  4735. each(boxes.chartArea, (layout) => {
  4736. const box = layout.box;
  4737. Object.assign(box, chart.chartArea);
  4738. box.update(chartArea.w, chartArea.h, {
  4739. left: 0,
  4740. top: 0,
  4741. right: 0,
  4742. bottom: 0
  4743. });
  4744. });
  4745. }
  4746. };
  4747. class BasePlatform {
  4748. acquireContext(canvas, aspectRatio) {
  4749. }
  4750. releaseContext(context) {
  4751. return false;
  4752. }
  4753. addEventListener(chart, type, listener) {
  4754. }
  4755. removeEventListener(chart, type, listener) {
  4756. }
  4757. getDevicePixelRatio() {
  4758. return 1;
  4759. }
  4760. getMaximumSize(element, width, height, aspectRatio) {
  4761. width = Math.max(0, width || element.width);
  4762. height = height || element.height;
  4763. return {
  4764. width,
  4765. height: Math.max(0, aspectRatio ? Math.floor(width / aspectRatio) : height)
  4766. };
  4767. }
  4768. isAttached(canvas) {
  4769. return true;
  4770. }
  4771. updateConfig(config) {
  4772. }
  4773. }
  4774. class BasicPlatform extends BasePlatform {
  4775. acquireContext(item) {
  4776. return item && item.getContext && item.getContext("2d") || null;
  4777. }
  4778. updateConfig(config) {
  4779. config.options.animation = false;
  4780. }
  4781. }
  4782. const EXPANDO_KEY = "$chartjs";
  4783. const EVENT_TYPES = {
  4784. touchstart: "mousedown",
  4785. touchmove: "mousemove",
  4786. touchend: "mouseup",
  4787. pointerenter: "mouseenter",
  4788. pointerdown: "mousedown",
  4789. pointermove: "mousemove",
  4790. pointerup: "mouseup",
  4791. pointerleave: "mouseout",
  4792. pointerout: "mouseout"
  4793. };
  4794. const isNullOrEmpty = (value) => value === null || value === "";
  4795. function initCanvas(canvas, aspectRatio) {
  4796. const style = canvas.style;
  4797. const renderHeight = canvas.getAttribute("height");
  4798. const renderWidth = canvas.getAttribute("width");
  4799. canvas[EXPANDO_KEY] = {
  4800. initial: {
  4801. height: renderHeight,
  4802. width: renderWidth,
  4803. style: {
  4804. display: style.display,
  4805. height: style.height,
  4806. width: style.width
  4807. }
  4808. }
  4809. };
  4810. style.display = style.display || "block";
  4811. style.boxSizing = style.boxSizing || "border-box";
  4812. if (isNullOrEmpty(renderWidth)) {
  4813. const displayWidth = readUsedSize(canvas, "width");
  4814. if (displayWidth !== void 0) {
  4815. canvas.width = displayWidth;
  4816. }
  4817. }
  4818. if (isNullOrEmpty(renderHeight)) {
  4819. if (canvas.style.height === "") {
  4820. canvas.height = canvas.width / (aspectRatio || 2);
  4821. } else {
  4822. const displayHeight = readUsedSize(canvas, "height");
  4823. if (displayHeight !== void 0) {
  4824. canvas.height = displayHeight;
  4825. }
  4826. }
  4827. }
  4828. return canvas;
  4829. }
  4830. const eventListenerOptions = supportsEventListenerOptions ? {
  4831. passive: true
  4832. } : false;
  4833. function addListener(node, type, listener) {
  4834. if (node) {
  4835. node.addEventListener(type, listener, eventListenerOptions);
  4836. }
  4837. }
  4838. function removeListener(chart, type, listener) {
  4839. if (chart && chart.canvas) {
  4840. chart.canvas.removeEventListener(type, listener, eventListenerOptions);
  4841. }
  4842. }
  4843. function fromNativeEvent(event, chart) {
  4844. const type = EVENT_TYPES[event.type] || event.type;
  4845. const { x, y } = getRelativePosition(event, chart);
  4846. return {
  4847. type,
  4848. chart,
  4849. native: event,
  4850. x: x !== void 0 ? x : null,
  4851. y: y !== void 0 ? y : null
  4852. };
  4853. }
  4854. function nodeListContains(nodeList, canvas) {
  4855. for (const node of nodeList) {
  4856. if (node === canvas || node.contains(canvas)) {
  4857. return true;
  4858. }
  4859. }
  4860. }
  4861. function createAttachObserver(chart, type, listener) {
  4862. const canvas = chart.canvas;
  4863. const observer = new MutationObserver((entries) => {
  4864. let trigger = false;
  4865. for (const entry of entries) {
  4866. trigger = trigger || nodeListContains(entry.addedNodes, canvas);
  4867. trigger = trigger && !nodeListContains(entry.removedNodes, canvas);
  4868. }
  4869. if (trigger) {
  4870. listener();
  4871. }
  4872. });
  4873. observer.observe(document, {
  4874. childList: true,
  4875. subtree: true
  4876. });
  4877. return observer;
  4878. }
  4879. function createDetachObserver(chart, type, listener) {
  4880. const canvas = chart.canvas;
  4881. const observer = new MutationObserver((entries) => {
  4882. let trigger = false;
  4883. for (const entry of entries) {
  4884. trigger = trigger || nodeListContains(entry.removedNodes, canvas);
  4885. trigger = trigger && !nodeListContains(entry.addedNodes, canvas);
  4886. }
  4887. if (trigger) {
  4888. listener();
  4889. }
  4890. });
  4891. observer.observe(document, {
  4892. childList: true,
  4893. subtree: true
  4894. });
  4895. return observer;
  4896. }
  4897. const drpListeningCharts = /* @__PURE__ */ new Map();
  4898. let oldDevicePixelRatio = 0;
  4899. function onWindowResize() {
  4900. const dpr = window.devicePixelRatio;
  4901. if (dpr === oldDevicePixelRatio) {
  4902. return;
  4903. }
  4904. oldDevicePixelRatio = dpr;
  4905. drpListeningCharts.forEach((resize, chart) => {
  4906. if (chart.currentDevicePixelRatio !== dpr) {
  4907. resize();
  4908. }
  4909. });
  4910. }
  4911. function listenDevicePixelRatioChanges(chart, resize) {
  4912. if (!drpListeningCharts.size) {
  4913. window.addEventListener("resize", onWindowResize);
  4914. }
  4915. drpListeningCharts.set(chart, resize);
  4916. }
  4917. function unlistenDevicePixelRatioChanges(chart) {
  4918. drpListeningCharts.delete(chart);
  4919. if (!drpListeningCharts.size) {
  4920. window.removeEventListener("resize", onWindowResize);
  4921. }
  4922. }
  4923. function createResizeObserver(chart, type, listener) {
  4924. const canvas = chart.canvas;
  4925. const container = canvas && _getParentNode(canvas);
  4926. if (!container) {
  4927. return;
  4928. }
  4929. const resize = throttled((width, height) => {
  4930. const w = container.clientWidth;
  4931. listener(width, height);
  4932. if (w < container.clientWidth) {
  4933. listener();
  4934. }
  4935. }, window);
  4936. const observer = new ResizeObserver((entries) => {
  4937. const entry = entries[0];
  4938. const width = entry.contentRect.width;
  4939. const height = entry.contentRect.height;
  4940. if (width === 0 && height === 0) {
  4941. return;
  4942. }
  4943. resize(width, height);
  4944. });
  4945. observer.observe(container);
  4946. listenDevicePixelRatioChanges(chart, resize);
  4947. return observer;
  4948. }
  4949. function releaseObserver(chart, type, observer) {
  4950. if (observer) {
  4951. observer.disconnect();
  4952. }
  4953. if (type === "resize") {
  4954. unlistenDevicePixelRatioChanges(chart);
  4955. }
  4956. }
  4957. function createProxyAndListen(chart, type, listener) {
  4958. const canvas = chart.canvas;
  4959. const proxy = throttled((event) => {
  4960. if (chart.ctx !== null) {
  4961. listener(fromNativeEvent(event, chart));
  4962. }
  4963. }, chart);
  4964. addListener(canvas, type, proxy);
  4965. return proxy;
  4966. }
  4967. class DomPlatform extends BasePlatform {
  4968. acquireContext(canvas, aspectRatio) {
  4969. const context = canvas && canvas.getContext && canvas.getContext("2d");
  4970. if (context && context.canvas === canvas) {
  4971. initCanvas(canvas, aspectRatio);
  4972. return context;
  4973. }
  4974. return null;
  4975. }
  4976. releaseContext(context) {
  4977. const canvas = context.canvas;
  4978. if (!canvas[EXPANDO_KEY]) {
  4979. return false;
  4980. }
  4981. const initial = canvas[EXPANDO_KEY].initial;
  4982. [
  4983. "height",
  4984. "width"
  4985. ].forEach((prop) => {
  4986. const value = initial[prop];
  4987. if (isNullOrUndef(value)) {
  4988. canvas.removeAttribute(prop);
  4989. } else {
  4990. canvas.setAttribute(prop, value);
  4991. }
  4992. });
  4993. const style = initial.style || {};
  4994. Object.keys(style).forEach((key) => {
  4995. canvas.style[key] = style[key];
  4996. });
  4997. canvas.width = canvas.width;
  4998. delete canvas[EXPANDO_KEY];
  4999. return true;
  5000. }
  5001. addEventListener(chart, type, listener) {
  5002. this.removeEventListener(chart, type);
  5003. const proxies = chart.$proxies || (chart.$proxies = {});
  5004. const handlers = {
  5005. attach: createAttachObserver,
  5006. detach: createDetachObserver,
  5007. resize: createResizeObserver
  5008. };
  5009. const handler = handlers[type] || createProxyAndListen;
  5010. proxies[type] = handler(chart, type, listener);
  5011. }
  5012. removeEventListener(chart, type) {
  5013. const proxies = chart.$proxies || (chart.$proxies = {});
  5014. const proxy = proxies[type];
  5015. if (!proxy) {
  5016. return;
  5017. }
  5018. const handlers = {
  5019. attach: releaseObserver,
  5020. detach: releaseObserver,
  5021. resize: releaseObserver
  5022. };
  5023. const handler = handlers[type] || removeListener;
  5024. handler(chart, type, proxy);
  5025. proxies[type] = void 0;
  5026. }
  5027. getDevicePixelRatio() {
  5028. return window.devicePixelRatio;
  5029. }
  5030. getMaximumSize(canvas, width, height, aspectRatio) {
  5031. return getMaximumSize(canvas, width, height, aspectRatio);
  5032. }
  5033. isAttached(canvas) {
  5034. const container = canvas && _getParentNode(canvas);
  5035. return !!(container && container.isConnected);
  5036. }
  5037. }
  5038. function _detectPlatform(canvas) {
  5039. if (!_isDomSupported() || typeof OffscreenCanvas !== "undefined" && canvas instanceof OffscreenCanvas) {
  5040. return BasicPlatform;
  5041. }
  5042. return DomPlatform;
  5043. }
  5044. class Element {
  5045. constructor() {
  5046. __publicField(this, "x");
  5047. __publicField(this, "y");
  5048. __publicField(this, "active", false);
  5049. __publicField(this, "options");
  5050. __publicField(this, "$animations");
  5051. }
  5052. tooltipPosition(useFinalPosition) {
  5053. const { x, y } = this.getProps([
  5054. "x",
  5055. "y"
  5056. ], useFinalPosition);
  5057. return {
  5058. x,
  5059. y
  5060. };
  5061. }
  5062. hasValue() {
  5063. return isNumber(this.x) && isNumber(this.y);
  5064. }
  5065. getProps(props, final) {
  5066. const anims = this.$animations;
  5067. if (!final || !anims) {
  5068. return this;
  5069. }
  5070. const ret = {};
  5071. props.forEach((prop) => {
  5072. ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : this[prop];
  5073. });
  5074. return ret;
  5075. }
  5076. }
  5077. __publicField(Element, "defaults", {});
  5078. __publicField(Element, "defaultRoutes");
  5079. function autoSkip(scale, ticks) {
  5080. const tickOpts = scale.options.ticks;
  5081. const determinedMaxTicks = determineMaxTicks(scale);
  5082. const ticksLimit = Math.min(tickOpts.maxTicksLimit || determinedMaxTicks, determinedMaxTicks);
  5083. const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];
  5084. const numMajorIndices = majorIndices.length;
  5085. const first = majorIndices[0];
  5086. const last = majorIndices[numMajorIndices - 1];
  5087. const newTicks = [];
  5088. if (numMajorIndices > ticksLimit) {
  5089. skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit);
  5090. return newTicks;
  5091. }
  5092. const spacing = calculateSpacing(majorIndices, ticks, ticksLimit);
  5093. if (numMajorIndices > 0) {
  5094. let i, ilen;
  5095. const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null;
  5096. skip(ticks, newTicks, spacing, isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
  5097. for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) {
  5098. skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]);
  5099. }
  5100. skip(ticks, newTicks, spacing, last, isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
  5101. return newTicks;
  5102. }
  5103. skip(ticks, newTicks, spacing);
  5104. return newTicks;
  5105. }
  5106. function determineMaxTicks(scale) {
  5107. const offset = scale.options.offset;
  5108. const tickLength = scale._tickSize();
  5109. const maxScale = scale._length / tickLength + (offset ? 0 : 1);
  5110. const maxChart = scale._maxLength / tickLength;
  5111. return Math.floor(Math.min(maxScale, maxChart));
  5112. }
  5113. function calculateSpacing(majorIndices, ticks, ticksLimit) {
  5114. const evenMajorSpacing = getEvenSpacing(majorIndices);
  5115. const spacing = ticks.length / ticksLimit;
  5116. if (!evenMajorSpacing) {
  5117. return Math.max(spacing, 1);
  5118. }
  5119. const factors = _factorize(evenMajorSpacing);
  5120. for (let i = 0, ilen = factors.length - 1; i < ilen; i++) {
  5121. const factor = factors[i];
  5122. if (factor > spacing) {
  5123. return factor;
  5124. }
  5125. }
  5126. return Math.max(spacing, 1);
  5127. }
  5128. function getMajorIndices(ticks) {
  5129. const result = [];
  5130. let i, ilen;
  5131. for (i = 0, ilen = ticks.length; i < ilen; i++) {
  5132. if (ticks[i].major) {
  5133. result.push(i);
  5134. }
  5135. }
  5136. return result;
  5137. }
  5138. function skipMajors(ticks, newTicks, majorIndices, spacing) {
  5139. let count = 0;
  5140. let next = majorIndices[0];
  5141. let i;
  5142. spacing = Math.ceil(spacing);
  5143. for (i = 0; i < ticks.length; i++) {
  5144. if (i === next) {
  5145. newTicks.push(ticks[i]);
  5146. count++;
  5147. next = majorIndices[count * spacing];
  5148. }
  5149. }
  5150. }
  5151. function skip(ticks, newTicks, spacing, majorStart, majorEnd) {
  5152. const start = valueOrDefault(majorStart, 0);
  5153. const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length);
  5154. let count = 0;
  5155. let length, i, next;
  5156. spacing = Math.ceil(spacing);
  5157. if (majorEnd) {
  5158. length = majorEnd - majorStart;
  5159. spacing = length / Math.floor(length / spacing);
  5160. }
  5161. next = start;
  5162. while (next < 0) {
  5163. count++;
  5164. next = Math.round(start + count * spacing);
  5165. }
  5166. for (i = Math.max(start, 0); i < end; i++) {
  5167. if (i === next) {
  5168. newTicks.push(ticks[i]);
  5169. count++;
  5170. next = Math.round(start + count * spacing);
  5171. }
  5172. }
  5173. }
  5174. function getEvenSpacing(arr) {
  5175. const len = arr.length;
  5176. let i, diff;
  5177. if (len < 2) {
  5178. return false;
  5179. }
  5180. for (diff = arr[0], i = 1; i < len; ++i) {
  5181. if (arr[i] - arr[i - 1] !== diff) {
  5182. return false;
  5183. }
  5184. }
  5185. return diff;
  5186. }
  5187. const reverseAlign = (align) => align === "left" ? "right" : align === "right" ? "left" : align;
  5188. const offsetFromEdge = (scale, edge, offset) => edge === "top" || edge === "left" ? scale[edge] + offset : scale[edge] - offset;
  5189. const getTicksLimit = (ticksLength, maxTicksLimit) => Math.min(maxTicksLimit || ticksLength, ticksLength);
  5190. function sample(arr, numItems) {
  5191. const result = [];
  5192. const increment = arr.length / numItems;
  5193. const len = arr.length;
  5194. let i = 0;
  5195. for (; i < len; i += increment) {
  5196. result.push(arr[Math.floor(i)]);
  5197. }
  5198. return result;
  5199. }
  5200. function getPixelForGridLine(scale, index, offsetGridLines) {
  5201. const length = scale.ticks.length;
  5202. const validIndex2 = Math.min(index, length - 1);
  5203. const start = scale._startPixel;
  5204. const end = scale._endPixel;
  5205. const epsilon = 1e-6;
  5206. let lineValue = scale.getPixelForTick(validIndex2);
  5207. let offset;
  5208. if (offsetGridLines) {
  5209. if (length === 1) {
  5210. offset = Math.max(lineValue - start, end - lineValue);
  5211. } else if (index === 0) {
  5212. offset = (scale.getPixelForTick(1) - lineValue) / 2;
  5213. } else {
  5214. offset = (lineValue - scale.getPixelForTick(validIndex2 - 1)) / 2;
  5215. }
  5216. lineValue += validIndex2 < index ? offset : -offset;
  5217. if (lineValue < start - epsilon || lineValue > end + epsilon) {
  5218. return;
  5219. }
  5220. }
  5221. return lineValue;
  5222. }
  5223. function garbageCollect(caches, length) {
  5224. each(caches, (cache) => {
  5225. const gc = cache.gc;
  5226. const gcLen = gc.length / 2;
  5227. let i;
  5228. if (gcLen > length) {
  5229. for (i = 0; i < gcLen; ++i) {
  5230. delete cache.data[gc[i]];
  5231. }
  5232. gc.splice(0, gcLen);
  5233. }
  5234. });
  5235. }
  5236. function getTickMarkLength(options) {
  5237. return options.drawTicks ? options.tickLength : 0;
  5238. }
  5239. function getTitleHeight(options, fallback) {
  5240. if (!options.display) {
  5241. return 0;
  5242. }
  5243. const font = toFont(options.font, fallback);
  5244. const padding = toPadding(options.padding);
  5245. const lines = isArray(options.text) ? options.text.length : 1;
  5246. return lines * font.lineHeight + padding.height;
  5247. }
  5248. function createScaleContext(parent, scale) {
  5249. return createContext(parent, {
  5250. scale,
  5251. type: "scale"
  5252. });
  5253. }
  5254. function createTickContext(parent, index, tick) {
  5255. return createContext(parent, {
  5256. tick,
  5257. index,
  5258. type: "tick"
  5259. });
  5260. }
  5261. function titleAlign(align, position, reverse) {
  5262. let ret = _toLeftRightCenter(align);
  5263. if (reverse && position !== "right" || !reverse && position === "right") {
  5264. ret = reverseAlign(ret);
  5265. }
  5266. return ret;
  5267. }
  5268. function titleArgs(scale, offset, position, align) {
  5269. const { top, left, bottom, right, chart } = scale;
  5270. const { chartArea, scales } = chart;
  5271. let rotation = 0;
  5272. let maxWidth, titleX, titleY;
  5273. const height = bottom - top;
  5274. const width = right - left;
  5275. if (scale.isHorizontal()) {
  5276. titleX = _alignStartEnd(align, left, right);
  5277. if (isObject(position)) {
  5278. const positionAxisID = Object.keys(position)[0];
  5279. const value = position[positionAxisID];
  5280. titleY = scales[positionAxisID].getPixelForValue(value) + height - offset;
  5281. } else if (position === "center") {
  5282. titleY = (chartArea.bottom + chartArea.top) / 2 + height - offset;
  5283. } else {
  5284. titleY = offsetFromEdge(scale, position, offset);
  5285. }
  5286. maxWidth = right - left;
  5287. } else {
  5288. if (isObject(position)) {
  5289. const positionAxisID = Object.keys(position)[0];
  5290. const value = position[positionAxisID];
  5291. titleX = scales[positionAxisID].getPixelForValue(value) - width + offset;
  5292. } else if (position === "center") {
  5293. titleX = (chartArea.left + chartArea.right) / 2 - width + offset;
  5294. } else {
  5295. titleX = offsetFromEdge(scale, position, offset);
  5296. }
  5297. titleY = _alignStartEnd(align, bottom, top);
  5298. rotation = position === "left" ? -HALF_PI : HALF_PI;
  5299. }
  5300. return {
  5301. titleX,
  5302. titleY,
  5303. maxWidth,
  5304. rotation
  5305. };
  5306. }
  5307. class Scale extends Element {
  5308. constructor(cfg) {
  5309. super();
  5310. this.id = cfg.id;
  5311. this.type = cfg.type;
  5312. this.options = void 0;
  5313. this.ctx = cfg.ctx;
  5314. this.chart = cfg.chart;
  5315. this.top = void 0;
  5316. this.bottom = void 0;
  5317. this.left = void 0;
  5318. this.right = void 0;
  5319. this.width = void 0;
  5320. this.height = void 0;
  5321. this._margins = {
  5322. left: 0,
  5323. right: 0,
  5324. top: 0,
  5325. bottom: 0
  5326. };
  5327. this.maxWidth = void 0;
  5328. this.maxHeight = void 0;
  5329. this.paddingTop = void 0;
  5330. this.paddingBottom = void 0;
  5331. this.paddingLeft = void 0;
  5332. this.paddingRight = void 0;
  5333. this.axis = void 0;
  5334. this.labelRotation = void 0;
  5335. this.min = void 0;
  5336. this.max = void 0;
  5337. this._range = void 0;
  5338. this.ticks = [];
  5339. this._gridLineItems = null;
  5340. this._labelItems = null;
  5341. this._labelSizes = null;
  5342. this._length = 0;
  5343. this._maxLength = 0;
  5344. this._longestTextCache = {};
  5345. this._startPixel = void 0;
  5346. this._endPixel = void 0;
  5347. this._reversePixels = false;
  5348. this._userMax = void 0;
  5349. this._userMin = void 0;
  5350. this._suggestedMax = void 0;
  5351. this._suggestedMin = void 0;
  5352. this._ticksLength = 0;
  5353. this._borderValue = 0;
  5354. this._cache = {};
  5355. this._dataLimitsCached = false;
  5356. this.$context = void 0;
  5357. }
  5358. init(options) {
  5359. this.options = options.setContext(this.getContext());
  5360. this.axis = options.axis;
  5361. this._userMin = this.parse(options.min);
  5362. this._userMax = this.parse(options.max);
  5363. this._suggestedMin = this.parse(options.suggestedMin);
  5364. this._suggestedMax = this.parse(options.suggestedMax);
  5365. }
  5366. parse(raw, index) {
  5367. return raw;
  5368. }
  5369. getUserBounds() {
  5370. let { _userMin, _userMax, _suggestedMin, _suggestedMax } = this;
  5371. _userMin = finiteOrDefault(_userMin, Number.POSITIVE_INFINITY);
  5372. _userMax = finiteOrDefault(_userMax, Number.NEGATIVE_INFINITY);
  5373. _suggestedMin = finiteOrDefault(_suggestedMin, Number.POSITIVE_INFINITY);
  5374. _suggestedMax = finiteOrDefault(_suggestedMax, Number.NEGATIVE_INFINITY);
  5375. return {
  5376. min: finiteOrDefault(_userMin, _suggestedMin),
  5377. max: finiteOrDefault(_userMax, _suggestedMax),
  5378. minDefined: isNumberFinite(_userMin),
  5379. maxDefined: isNumberFinite(_userMax)
  5380. };
  5381. }
  5382. getMinMax(canStack) {
  5383. let { min, max, minDefined, maxDefined } = this.getUserBounds();
  5384. let range;
  5385. if (minDefined && maxDefined) {
  5386. return {
  5387. min,
  5388. max
  5389. };
  5390. }
  5391. const metas = this.getMatchingVisibleMetas();
  5392. for (let i = 0, ilen = metas.length; i < ilen; ++i) {
  5393. range = metas[i].controller.getMinMax(this, canStack);
  5394. if (!minDefined) {
  5395. min = Math.min(min, range.min);
  5396. }
  5397. if (!maxDefined) {
  5398. max = Math.max(max, range.max);
  5399. }
  5400. }
  5401. min = maxDefined && min > max ? max : min;
  5402. max = minDefined && min > max ? min : max;
  5403. return {
  5404. min: finiteOrDefault(min, finiteOrDefault(max, min)),
  5405. max: finiteOrDefault(max, finiteOrDefault(min, max))
  5406. };
  5407. }
  5408. getPadding() {
  5409. return {
  5410. left: this.paddingLeft || 0,
  5411. top: this.paddingTop || 0,
  5412. right: this.paddingRight || 0,
  5413. bottom: this.paddingBottom || 0
  5414. };
  5415. }
  5416. getTicks() {
  5417. return this.ticks;
  5418. }
  5419. getLabels() {
  5420. const data = this.chart.data;
  5421. return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || [];
  5422. }
  5423. getLabelItems(chartArea = this.chart.chartArea) {
  5424. const items = this._labelItems || (this._labelItems = this._computeLabelItems(chartArea));
  5425. return items;
  5426. }
  5427. beforeLayout() {
  5428. this._cache = {};
  5429. this._dataLimitsCached = false;
  5430. }
  5431. beforeUpdate() {
  5432. callback(this.options.beforeUpdate, [
  5433. this
  5434. ]);
  5435. }
  5436. update(maxWidth, maxHeight, margins) {
  5437. const { beginAtZero, grace, ticks: tickOpts } = this.options;
  5438. const sampleSize = tickOpts.sampleSize;
  5439. this.beforeUpdate();
  5440. this.maxWidth = maxWidth;
  5441. this.maxHeight = maxHeight;
  5442. this._margins = margins = Object.assign({
  5443. left: 0,
  5444. right: 0,
  5445. top: 0,
  5446. bottom: 0
  5447. }, margins);
  5448. this.ticks = null;
  5449. this._labelSizes = null;
  5450. this._gridLineItems = null;
  5451. this._labelItems = null;
  5452. this.beforeSetDimensions();
  5453. this.setDimensions();
  5454. this.afterSetDimensions();
  5455. this._maxLength = this.isHorizontal() ? this.width + margins.left + margins.right : this.height + margins.top + margins.bottom;
  5456. if (!this._dataLimitsCached) {
  5457. this.beforeDataLimits();
  5458. this.determineDataLimits();
  5459. this.afterDataLimits();
  5460. this._range = _addGrace(this, grace, beginAtZero);
  5461. this._dataLimitsCached = true;
  5462. }
  5463. this.beforeBuildTicks();
  5464. this.ticks = this.buildTicks() || [];
  5465. this.afterBuildTicks();
  5466. const samplingEnabled = sampleSize < this.ticks.length;
  5467. this._convertTicksToLabels(samplingEnabled ? sample(this.ticks, sampleSize) : this.ticks);
  5468. this.configure();
  5469. this.beforeCalculateLabelRotation();
  5470. this.calculateLabelRotation();
  5471. this.afterCalculateLabelRotation();
  5472. if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === "auto")) {
  5473. this.ticks = autoSkip(this, this.ticks);
  5474. this._labelSizes = null;
  5475. this.afterAutoSkip();
  5476. }
  5477. if (samplingEnabled) {
  5478. this._convertTicksToLabels(this.ticks);
  5479. }
  5480. this.beforeFit();
  5481. this.fit();
  5482. this.afterFit();
  5483. this.afterUpdate();
  5484. }
  5485. configure() {
  5486. let reversePixels = this.options.reverse;
  5487. let startPixel, endPixel;
  5488. if (this.isHorizontal()) {
  5489. startPixel = this.left;
  5490. endPixel = this.right;
  5491. } else {
  5492. startPixel = this.top;
  5493. endPixel = this.bottom;
  5494. reversePixels = !reversePixels;
  5495. }
  5496. this._startPixel = startPixel;
  5497. this._endPixel = endPixel;
  5498. this._reversePixels = reversePixels;
  5499. this._length = endPixel - startPixel;
  5500. this._alignToPixels = this.options.alignToPixels;
  5501. }
  5502. afterUpdate() {
  5503. callback(this.options.afterUpdate, [
  5504. this
  5505. ]);
  5506. }
  5507. beforeSetDimensions() {
  5508. callback(this.options.beforeSetDimensions, [
  5509. this
  5510. ]);
  5511. }
  5512. setDimensions() {
  5513. if (this.isHorizontal()) {
  5514. this.width = this.maxWidth;
  5515. this.left = 0;
  5516. this.right = this.width;
  5517. } else {
  5518. this.height = this.maxHeight;
  5519. this.top = 0;
  5520. this.bottom = this.height;
  5521. }
  5522. this.paddingLeft = 0;
  5523. this.paddingTop = 0;
  5524. this.paddingRight = 0;
  5525. this.paddingBottom = 0;
  5526. }
  5527. afterSetDimensions() {
  5528. callback(this.options.afterSetDimensions, [
  5529. this
  5530. ]);
  5531. }
  5532. _callHooks(name) {
  5533. this.chart.notifyPlugins(name, this.getContext());
  5534. callback(this.options[name], [
  5535. this
  5536. ]);
  5537. }
  5538. beforeDataLimits() {
  5539. this._callHooks("beforeDataLimits");
  5540. }
  5541. determineDataLimits() {
  5542. }
  5543. afterDataLimits() {
  5544. this._callHooks("afterDataLimits");
  5545. }
  5546. beforeBuildTicks() {
  5547. this._callHooks("beforeBuildTicks");
  5548. }
  5549. buildTicks() {
  5550. return [];
  5551. }
  5552. afterBuildTicks() {
  5553. this._callHooks("afterBuildTicks");
  5554. }
  5555. beforeTickToLabelConversion() {
  5556. callback(this.options.beforeTickToLabelConversion, [
  5557. this
  5558. ]);
  5559. }
  5560. generateTickLabels(ticks) {
  5561. const tickOpts = this.options.ticks;
  5562. let i, ilen, tick;
  5563. for (i = 0, ilen = ticks.length; i < ilen; i++) {
  5564. tick = ticks[i];
  5565. tick.label = callback(tickOpts.callback, [
  5566. tick.value,
  5567. i,
  5568. ticks
  5569. ], this);
  5570. }
  5571. }
  5572. afterTickToLabelConversion() {
  5573. callback(this.options.afterTickToLabelConversion, [
  5574. this
  5575. ]);
  5576. }
  5577. beforeCalculateLabelRotation() {
  5578. callback(this.options.beforeCalculateLabelRotation, [
  5579. this
  5580. ]);
  5581. }
  5582. calculateLabelRotation() {
  5583. const options = this.options;
  5584. const tickOpts = options.ticks;
  5585. const numTicks = getTicksLimit(this.ticks.length, options.ticks.maxTicksLimit);
  5586. const minRotation = tickOpts.minRotation || 0;
  5587. const maxRotation = tickOpts.maxRotation;
  5588. let labelRotation = minRotation;
  5589. let tickWidth, maxHeight, maxLabelDiagonal;
  5590. if (!this._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !this.isHorizontal()) {
  5591. this.labelRotation = minRotation;
  5592. return;
  5593. }
  5594. const labelSizes = this._getLabelSizes();
  5595. const maxLabelWidth = labelSizes.widest.width;
  5596. const maxLabelHeight = labelSizes.highest.height;
  5597. const maxWidth = _limitValue(this.chart.width - maxLabelWidth, 0, this.maxWidth);
  5598. tickWidth = options.offset ? this.maxWidth / numTicks : maxWidth / (numTicks - 1);
  5599. if (maxLabelWidth + 6 > tickWidth) {
  5600. tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1));
  5601. maxHeight = this.maxHeight - getTickMarkLength(options.grid) - tickOpts.padding - getTitleHeight(options.title, this.chart.options.font);
  5602. maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight);
  5603. labelRotation = toDegrees(Math.min(Math.asin(_limitValue((labelSizes.highest.height + 6) / tickWidth, -1, 1)), Math.asin(_limitValue(maxHeight / maxLabelDiagonal, -1, 1)) - Math.asin(_limitValue(maxLabelHeight / maxLabelDiagonal, -1, 1))));
  5604. labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation));
  5605. }
  5606. this.labelRotation = labelRotation;
  5607. }
  5608. afterCalculateLabelRotation() {
  5609. callback(this.options.afterCalculateLabelRotation, [
  5610. this
  5611. ]);
  5612. }
  5613. afterAutoSkip() {
  5614. }
  5615. beforeFit() {
  5616. callback(this.options.beforeFit, [
  5617. this
  5618. ]);
  5619. }
  5620. fit() {
  5621. const minSize = {
  5622. width: 0,
  5623. height: 0
  5624. };
  5625. const { chart, options: { ticks: tickOpts, title: titleOpts, grid: gridOpts } } = this;
  5626. const display = this._isVisible();
  5627. const isHorizontal = this.isHorizontal();
  5628. if (display) {
  5629. const titleHeight = getTitleHeight(titleOpts, chart.options.font);
  5630. if (isHorizontal) {
  5631. minSize.width = this.maxWidth;
  5632. minSize.height = getTickMarkLength(gridOpts) + titleHeight;
  5633. } else {
  5634. minSize.height = this.maxHeight;
  5635. minSize.width = getTickMarkLength(gridOpts) + titleHeight;
  5636. }
  5637. if (tickOpts.display && this.ticks.length) {
  5638. const { first, last, widest, highest } = this._getLabelSizes();
  5639. const tickPadding = tickOpts.padding * 2;
  5640. const angleRadians = toRadians(this.labelRotation);
  5641. const cos = Math.cos(angleRadians);
  5642. const sin = Math.sin(angleRadians);
  5643. if (isHorizontal) {
  5644. const labelHeight = tickOpts.mirror ? 0 : sin * widest.width + cos * highest.height;
  5645. minSize.height = Math.min(this.maxHeight, minSize.height + labelHeight + tickPadding);
  5646. } else {
  5647. const labelWidth = tickOpts.mirror ? 0 : cos * widest.width + sin * highest.height;
  5648. minSize.width = Math.min(this.maxWidth, minSize.width + labelWidth + tickPadding);
  5649. }
  5650. this._calculatePadding(first, last, sin, cos);
  5651. }
  5652. }
  5653. this._handleMargins();
  5654. if (isHorizontal) {
  5655. this.width = this._length = chart.width - this._margins.left - this._margins.right;
  5656. this.height = minSize.height;
  5657. } else {
  5658. this.width = minSize.width;
  5659. this.height = this._length = chart.height - this._margins.top - this._margins.bottom;
  5660. }
  5661. }
  5662. _calculatePadding(first, last, sin, cos) {
  5663. const { ticks: { align, padding }, position } = this.options;
  5664. const isRotated = this.labelRotation !== 0;
  5665. const labelsBelowTicks = position !== "top" && this.axis === "x";
  5666. if (this.isHorizontal()) {
  5667. const offsetLeft = this.getPixelForTick(0) - this.left;
  5668. const offsetRight = this.right - this.getPixelForTick(this.ticks.length - 1);
  5669. let paddingLeft = 0;
  5670. let paddingRight = 0;
  5671. if (isRotated) {
  5672. if (labelsBelowTicks) {
  5673. paddingLeft = cos * first.width;
  5674. paddingRight = sin * last.height;
  5675. } else {
  5676. paddingLeft = sin * first.height;
  5677. paddingRight = cos * last.width;
  5678. }
  5679. } else if (align === "start") {
  5680. paddingRight = last.width;
  5681. } else if (align === "end") {
  5682. paddingLeft = first.width;
  5683. } else if (align !== "inner") {
  5684. paddingLeft = first.width / 2;
  5685. paddingRight = last.width / 2;
  5686. }
  5687. this.paddingLeft = Math.max((paddingLeft - offsetLeft + padding) * this.width / (this.width - offsetLeft), 0);
  5688. this.paddingRight = Math.max((paddingRight - offsetRight + padding) * this.width / (this.width - offsetRight), 0);
  5689. } else {
  5690. let paddingTop = last.height / 2;
  5691. let paddingBottom = first.height / 2;
  5692. if (align === "start") {
  5693. paddingTop = 0;
  5694. paddingBottom = first.height;
  5695. } else if (align === "end") {
  5696. paddingTop = last.height;
  5697. paddingBottom = 0;
  5698. }
  5699. this.paddingTop = paddingTop + padding;
  5700. this.paddingBottom = paddingBottom + padding;
  5701. }
  5702. }
  5703. _handleMargins() {
  5704. if (this._margins) {
  5705. this._margins.left = Math.max(this.paddingLeft, this._margins.left);
  5706. this._margins.top = Math.max(this.paddingTop, this._margins.top);
  5707. this._margins.right = Math.max(this.paddingRight, this._margins.right);
  5708. this._margins.bottom = Math.max(this.paddingBottom, this._margins.bottom);
  5709. }
  5710. }
  5711. afterFit() {
  5712. callback(this.options.afterFit, [
  5713. this
  5714. ]);
  5715. }
  5716. isHorizontal() {
  5717. const { axis, position } = this.options;
  5718. return position === "top" || position === "bottom" || axis === "x";
  5719. }
  5720. isFullSize() {
  5721. return this.options.fullSize;
  5722. }
  5723. _convertTicksToLabels(ticks) {
  5724. this.beforeTickToLabelConversion();
  5725. this.generateTickLabels(ticks);
  5726. let i, ilen;
  5727. for (i = 0, ilen = ticks.length; i < ilen; i++) {
  5728. if (isNullOrUndef(ticks[i].label)) {
  5729. ticks.splice(i, 1);
  5730. ilen--;
  5731. i--;
  5732. }
  5733. }
  5734. this.afterTickToLabelConversion();
  5735. }
  5736. _getLabelSizes() {
  5737. let labelSizes = this._labelSizes;
  5738. if (!labelSizes) {
  5739. const sampleSize = this.options.ticks.sampleSize;
  5740. let ticks = this.ticks;
  5741. if (sampleSize < ticks.length) {
  5742. ticks = sample(ticks, sampleSize);
  5743. }
  5744. this._labelSizes = labelSizes = this._computeLabelSizes(ticks, ticks.length, this.options.ticks.maxTicksLimit);
  5745. }
  5746. return labelSizes;
  5747. }
  5748. _computeLabelSizes(ticks, length, maxTicksLimit) {
  5749. const { ctx, _longestTextCache: caches } = this;
  5750. const widths = [];
  5751. const heights = [];
  5752. const increment = Math.floor(length / getTicksLimit(length, maxTicksLimit));
  5753. let widestLabelSize = 0;
  5754. let highestLabelSize = 0;
  5755. let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel;
  5756. for (i = 0; i < length; i += increment) {
  5757. label = ticks[i].label;
  5758. tickFont = this._resolveTickFontOptions(i);
  5759. ctx.font = fontString = tickFont.string;
  5760. cache = caches[fontString] = caches[fontString] || {
  5761. data: {},
  5762. gc: []
  5763. };
  5764. lineHeight = tickFont.lineHeight;
  5765. width = height = 0;
  5766. if (!isNullOrUndef(label) && !isArray(label)) {
  5767. width = _measureText(ctx, cache.data, cache.gc, width, label);
  5768. height = lineHeight;
  5769. } else if (isArray(label)) {
  5770. for (j = 0, jlen = label.length; j < jlen; ++j) {
  5771. nestedLabel = label[j];
  5772. if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) {
  5773. width = _measureText(ctx, cache.data, cache.gc, width, nestedLabel);
  5774. height += lineHeight;
  5775. }
  5776. }
  5777. }
  5778. widths.push(width);
  5779. heights.push(height);
  5780. widestLabelSize = Math.max(width, widestLabelSize);
  5781. highestLabelSize = Math.max(height, highestLabelSize);
  5782. }
  5783. garbageCollect(caches, length);
  5784. const widest = widths.indexOf(widestLabelSize);
  5785. const highest = heights.indexOf(highestLabelSize);
  5786. const valueAt = (idx) => ({
  5787. width: widths[idx] || 0,
  5788. height: heights[idx] || 0
  5789. });
  5790. return {
  5791. first: valueAt(0),
  5792. last: valueAt(length - 1),
  5793. widest: valueAt(widest),
  5794. highest: valueAt(highest),
  5795. widths,
  5796. heights
  5797. };
  5798. }
  5799. getLabelForValue(value) {
  5800. return value;
  5801. }
  5802. getPixelForValue(value, index) {
  5803. return NaN;
  5804. }
  5805. getValueForPixel(pixel) {
  5806. }
  5807. getPixelForTick(index) {
  5808. const ticks = this.ticks;
  5809. if (index < 0 || index > ticks.length - 1) {
  5810. return null;
  5811. }
  5812. return this.getPixelForValue(ticks[index].value);
  5813. }
  5814. getPixelForDecimal(decimal) {
  5815. if (this._reversePixels) {
  5816. decimal = 1 - decimal;
  5817. }
  5818. const pixel = this._startPixel + decimal * this._length;
  5819. return _int16Range(this._alignToPixels ? _alignPixel(this.chart, pixel, 0) : pixel);
  5820. }
  5821. getDecimalForPixel(pixel) {
  5822. const decimal = (pixel - this._startPixel) / this._length;
  5823. return this._reversePixels ? 1 - decimal : decimal;
  5824. }
  5825. getBasePixel() {
  5826. return this.getPixelForValue(this.getBaseValue());
  5827. }
  5828. getBaseValue() {
  5829. const { min, max } = this;
  5830. return min < 0 && max < 0 ? max : min > 0 && max > 0 ? min : 0;
  5831. }
  5832. getContext(index) {
  5833. const ticks = this.ticks || [];
  5834. if (index >= 0 && index < ticks.length) {
  5835. const tick = ticks[index];
  5836. return tick.$context || (tick.$context = createTickContext(this.getContext(), index, tick));
  5837. }
  5838. return this.$context || (this.$context = createScaleContext(this.chart.getContext(), this));
  5839. }
  5840. _tickSize() {
  5841. const optionTicks = this.options.ticks;
  5842. const rot = toRadians(this.labelRotation);
  5843. const cos = Math.abs(Math.cos(rot));
  5844. const sin = Math.abs(Math.sin(rot));
  5845. const labelSizes = this._getLabelSizes();
  5846. const padding = optionTicks.autoSkipPadding || 0;
  5847. const w = labelSizes ? labelSizes.widest.width + padding : 0;
  5848. const h = labelSizes ? labelSizes.highest.height + padding : 0;
  5849. return this.isHorizontal() ? h * cos > w * sin ? w / cos : h / sin : h * sin < w * cos ? h / cos : w / sin;
  5850. }
  5851. _isVisible() {
  5852. const display = this.options.display;
  5853. if (display !== "auto") {
  5854. return !!display;
  5855. }
  5856. return this.getMatchingVisibleMetas().length > 0;
  5857. }
  5858. _computeGridLineItems(chartArea) {
  5859. const axis = this.axis;
  5860. const chart = this.chart;
  5861. const options = this.options;
  5862. const { grid, position, border } = options;
  5863. const offset = grid.offset;
  5864. const isHorizontal = this.isHorizontal();
  5865. const ticks = this.ticks;
  5866. const ticksLength = ticks.length + (offset ? 1 : 0);
  5867. const tl = getTickMarkLength(grid);
  5868. const items = [];
  5869. const borderOpts = border.setContext(this.getContext());
  5870. const axisWidth = borderOpts.display ? borderOpts.width : 0;
  5871. const axisHalfWidth = axisWidth / 2;
  5872. const alignBorderValue = function(pixel) {
  5873. return _alignPixel(chart, pixel, axisWidth);
  5874. };
  5875. let borderValue, i, lineValue, alignedLineValue;
  5876. let tx1, ty1, tx2, ty2, x1, y1, x2, y2;
  5877. if (position === "top") {
  5878. borderValue = alignBorderValue(this.bottom);
  5879. ty1 = this.bottom - tl;
  5880. ty2 = borderValue - axisHalfWidth;
  5881. y1 = alignBorderValue(chartArea.top) + axisHalfWidth;
  5882. y2 = chartArea.bottom;
  5883. } else if (position === "bottom") {
  5884. borderValue = alignBorderValue(this.top);
  5885. y1 = chartArea.top;
  5886. y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth;
  5887. ty1 = borderValue + axisHalfWidth;
  5888. ty2 = this.top + tl;
  5889. } else if (position === "left") {
  5890. borderValue = alignBorderValue(this.right);
  5891. tx1 = this.right - tl;
  5892. tx2 = borderValue - axisHalfWidth;
  5893. x1 = alignBorderValue(chartArea.left) + axisHalfWidth;
  5894. x2 = chartArea.right;
  5895. } else if (position === "right") {
  5896. borderValue = alignBorderValue(this.left);
  5897. x1 = chartArea.left;
  5898. x2 = alignBorderValue(chartArea.right) - axisHalfWidth;
  5899. tx1 = borderValue + axisHalfWidth;
  5900. tx2 = this.left + tl;
  5901. } else if (axis === "x") {
  5902. if (position === "center") {
  5903. borderValue = alignBorderValue((chartArea.top + chartArea.bottom) / 2 + 0.5);
  5904. } else if (isObject(position)) {
  5905. const positionAxisID = Object.keys(position)[0];
  5906. const value = position[positionAxisID];
  5907. borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));
  5908. }
  5909. y1 = chartArea.top;
  5910. y2 = chartArea.bottom;
  5911. ty1 = borderValue + axisHalfWidth;
  5912. ty2 = ty1 + tl;
  5913. } else if (axis === "y") {
  5914. if (position === "center") {
  5915. borderValue = alignBorderValue((chartArea.left + chartArea.right) / 2);
  5916. } else if (isObject(position)) {
  5917. const positionAxisID = Object.keys(position)[0];
  5918. const value = position[positionAxisID];
  5919. borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));
  5920. }
  5921. tx1 = borderValue - axisHalfWidth;
  5922. tx2 = tx1 - tl;
  5923. x1 = chartArea.left;
  5924. x2 = chartArea.right;
  5925. }
  5926. const limit = valueOrDefault(options.ticks.maxTicksLimit, ticksLength);
  5927. const step = Math.max(1, Math.ceil(ticksLength / limit));
  5928. for (i = 0; i < ticksLength; i += step) {
  5929. const context = this.getContext(i);
  5930. const optsAtIndex = grid.setContext(context);
  5931. const optsAtIndexBorder = border.setContext(context);
  5932. const lineWidth = optsAtIndex.lineWidth;
  5933. const lineColor = optsAtIndex.color;
  5934. const borderDash = optsAtIndexBorder.dash || [];
  5935. const borderDashOffset = optsAtIndexBorder.dashOffset;
  5936. const tickWidth = optsAtIndex.tickWidth;
  5937. const tickColor = optsAtIndex.tickColor;
  5938. const tickBorderDash = optsAtIndex.tickBorderDash || [];
  5939. const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset;
  5940. lineValue = getPixelForGridLine(this, i, offset);
  5941. if (lineValue === void 0) {
  5942. continue;
  5943. }
  5944. alignedLineValue = _alignPixel(chart, lineValue, lineWidth);
  5945. if (isHorizontal) {
  5946. tx1 = tx2 = x1 = x2 = alignedLineValue;
  5947. } else {
  5948. ty1 = ty2 = y1 = y2 = alignedLineValue;
  5949. }
  5950. items.push({
  5951. tx1,
  5952. ty1,
  5953. tx2,
  5954. ty2,
  5955. x1,
  5956. y1,
  5957. x2,
  5958. y2,
  5959. width: lineWidth,
  5960. color: lineColor,
  5961. borderDash,
  5962. borderDashOffset,
  5963. tickWidth,
  5964. tickColor,
  5965. tickBorderDash,
  5966. tickBorderDashOffset
  5967. });
  5968. }
  5969. this._ticksLength = ticksLength;
  5970. this._borderValue = borderValue;
  5971. return items;
  5972. }
  5973. _computeLabelItems(chartArea) {
  5974. const axis = this.axis;
  5975. const options = this.options;
  5976. const { position, ticks: optionTicks } = options;
  5977. const isHorizontal = this.isHorizontal();
  5978. const ticks = this.ticks;
  5979. const { align, crossAlign, padding, mirror } = optionTicks;
  5980. const tl = getTickMarkLength(options.grid);
  5981. const tickAndPadding = tl + padding;
  5982. const hTickAndPadding = mirror ? -padding : tickAndPadding;
  5983. const rotation = -toRadians(this.labelRotation);
  5984. const items = [];
  5985. let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;
  5986. let textBaseline = "middle";
  5987. if (position === "top") {
  5988. y = this.bottom - hTickAndPadding;
  5989. textAlign = this._getXAxisLabelAlignment();
  5990. } else if (position === "bottom") {
  5991. y = this.top + hTickAndPadding;
  5992. textAlign = this._getXAxisLabelAlignment();
  5993. } else if (position === "left") {
  5994. const ret = this._getYAxisLabelAlignment(tl);
  5995. textAlign = ret.textAlign;
  5996. x = ret.x;
  5997. } else if (position === "right") {
  5998. const ret = this._getYAxisLabelAlignment(tl);
  5999. textAlign = ret.textAlign;
  6000. x = ret.x;
  6001. } else if (axis === "x") {
  6002. if (position === "center") {
  6003. y = (chartArea.top + chartArea.bottom) / 2 + tickAndPadding;
  6004. } else if (isObject(position)) {
  6005. const positionAxisID = Object.keys(position)[0];
  6006. const value = position[positionAxisID];
  6007. y = this.chart.scales[positionAxisID].getPixelForValue(value) + tickAndPadding;
  6008. }
  6009. textAlign = this._getXAxisLabelAlignment();
  6010. } else if (axis === "y") {
  6011. if (position === "center") {
  6012. x = (chartArea.left + chartArea.right) / 2 - tickAndPadding;
  6013. } else if (isObject(position)) {
  6014. const positionAxisID = Object.keys(position)[0];
  6015. const value = position[positionAxisID];
  6016. x = this.chart.scales[positionAxisID].getPixelForValue(value);
  6017. }
  6018. textAlign = this._getYAxisLabelAlignment(tl).textAlign;
  6019. }
  6020. if (axis === "y") {
  6021. if (align === "start") {
  6022. textBaseline = "top";
  6023. } else if (align === "end") {
  6024. textBaseline = "bottom";
  6025. }
  6026. }
  6027. const labelSizes = this._getLabelSizes();
  6028. for (i = 0, ilen = ticks.length; i < ilen; ++i) {
  6029. tick = ticks[i];
  6030. label = tick.label;
  6031. const optsAtIndex = optionTicks.setContext(this.getContext(i));
  6032. pixel = this.getPixelForTick(i) + optionTicks.labelOffset;
  6033. font = this._resolveTickFontOptions(i);
  6034. lineHeight = font.lineHeight;
  6035. lineCount = isArray(label) ? label.length : 1;
  6036. const halfCount = lineCount / 2;
  6037. const color2 = optsAtIndex.color;
  6038. const strokeColor = optsAtIndex.textStrokeColor;
  6039. const strokeWidth = optsAtIndex.textStrokeWidth;
  6040. let tickTextAlign = textAlign;
  6041. if (isHorizontal) {
  6042. x = pixel;
  6043. if (textAlign === "inner") {
  6044. if (i === ilen - 1) {
  6045. tickTextAlign = !this.options.reverse ? "right" : "left";
  6046. } else if (i === 0) {
  6047. tickTextAlign = !this.options.reverse ? "left" : "right";
  6048. } else {
  6049. tickTextAlign = "center";
  6050. }
  6051. }
  6052. if (position === "top") {
  6053. if (crossAlign === "near" || rotation !== 0) {
  6054. textOffset = -lineCount * lineHeight + lineHeight / 2;
  6055. } else if (crossAlign === "center") {
  6056. textOffset = -labelSizes.highest.height / 2 - halfCount * lineHeight + lineHeight;
  6057. } else {
  6058. textOffset = -labelSizes.highest.height + lineHeight / 2;
  6059. }
  6060. } else {
  6061. if (crossAlign === "near" || rotation !== 0) {
  6062. textOffset = lineHeight / 2;
  6063. } else if (crossAlign === "center") {
  6064. textOffset = labelSizes.highest.height / 2 - halfCount * lineHeight;
  6065. } else {
  6066. textOffset = labelSizes.highest.height - lineCount * lineHeight;
  6067. }
  6068. }
  6069. if (mirror) {
  6070. textOffset *= -1;
  6071. }
  6072. if (rotation !== 0 && !optsAtIndex.showLabelBackdrop) {
  6073. x += lineHeight / 2 * Math.sin(rotation);
  6074. }
  6075. } else {
  6076. y = pixel;
  6077. textOffset = (1 - lineCount) * lineHeight / 2;
  6078. }
  6079. let backdrop;
  6080. if (optsAtIndex.showLabelBackdrop) {
  6081. const labelPadding = toPadding(optsAtIndex.backdropPadding);
  6082. const height = labelSizes.heights[i];
  6083. const width = labelSizes.widths[i];
  6084. let top = textOffset - labelPadding.top;
  6085. let left = 0 - labelPadding.left;
  6086. switch (textBaseline) {
  6087. case "middle":
  6088. top -= height / 2;
  6089. break;
  6090. case "bottom":
  6091. top -= height;
  6092. break;
  6093. }
  6094. switch (textAlign) {
  6095. case "center":
  6096. left -= width / 2;
  6097. break;
  6098. case "right":
  6099. left -= width;
  6100. break;
  6101. case "inner":
  6102. if (i === ilen - 1) {
  6103. left -= width;
  6104. } else if (i > 0) {
  6105. left -= width / 2;
  6106. }
  6107. break;
  6108. }
  6109. backdrop = {
  6110. left,
  6111. top,
  6112. width: width + labelPadding.width,
  6113. height: height + labelPadding.height,
  6114. color: optsAtIndex.backdropColor
  6115. };
  6116. }
  6117. items.push({
  6118. label,
  6119. font,
  6120. textOffset,
  6121. options: {
  6122. rotation,
  6123. color: color2,
  6124. strokeColor,
  6125. strokeWidth,
  6126. textAlign: tickTextAlign,
  6127. textBaseline,
  6128. translation: [
  6129. x,
  6130. y
  6131. ],
  6132. backdrop
  6133. }
  6134. });
  6135. }
  6136. return items;
  6137. }
  6138. _getXAxisLabelAlignment() {
  6139. const { position, ticks } = this.options;
  6140. const rotation = -toRadians(this.labelRotation);
  6141. if (rotation) {
  6142. return position === "top" ? "left" : "right";
  6143. }
  6144. let align = "center";
  6145. if (ticks.align === "start") {
  6146. align = "left";
  6147. } else if (ticks.align === "end") {
  6148. align = "right";
  6149. } else if (ticks.align === "inner") {
  6150. align = "inner";
  6151. }
  6152. return align;
  6153. }
  6154. _getYAxisLabelAlignment(tl) {
  6155. const { position, ticks: { crossAlign, mirror, padding } } = this.options;
  6156. const labelSizes = this._getLabelSizes();
  6157. const tickAndPadding = tl + padding;
  6158. const widest = labelSizes.widest.width;
  6159. let textAlign;
  6160. let x;
  6161. if (position === "left") {
  6162. if (mirror) {
  6163. x = this.right + padding;
  6164. if (crossAlign === "near") {
  6165. textAlign = "left";
  6166. } else if (crossAlign === "center") {
  6167. textAlign = "center";
  6168. x += widest / 2;
  6169. } else {
  6170. textAlign = "right";
  6171. x += widest;
  6172. }
  6173. } else {
  6174. x = this.right - tickAndPadding;
  6175. if (crossAlign === "near") {
  6176. textAlign = "right";
  6177. } else if (crossAlign === "center") {
  6178. textAlign = "center";
  6179. x -= widest / 2;
  6180. } else {
  6181. textAlign = "left";
  6182. x = this.left;
  6183. }
  6184. }
  6185. } else if (position === "right") {
  6186. if (mirror) {
  6187. x = this.left + padding;
  6188. if (crossAlign === "near") {
  6189. textAlign = "right";
  6190. } else if (crossAlign === "center") {
  6191. textAlign = "center";
  6192. x -= widest / 2;
  6193. } else {
  6194. textAlign = "left";
  6195. x -= widest;
  6196. }
  6197. } else {
  6198. x = this.left + tickAndPadding;
  6199. if (crossAlign === "near") {
  6200. textAlign = "left";
  6201. } else if (crossAlign === "center") {
  6202. textAlign = "center";
  6203. x += widest / 2;
  6204. } else {
  6205. textAlign = "right";
  6206. x = this.right;
  6207. }
  6208. }
  6209. } else {
  6210. textAlign = "right";
  6211. }
  6212. return {
  6213. textAlign,
  6214. x
  6215. };
  6216. }
  6217. _computeLabelArea() {
  6218. if (this.options.ticks.mirror) {
  6219. return;
  6220. }
  6221. const chart = this.chart;
  6222. const position = this.options.position;
  6223. if (position === "left" || position === "right") {
  6224. return {
  6225. top: 0,
  6226. left: this.left,
  6227. bottom: chart.height,
  6228. right: this.right
  6229. };
  6230. }
  6231. if (position === "top" || position === "bottom") {
  6232. return {
  6233. top: this.top,
  6234. left: 0,
  6235. bottom: this.bottom,
  6236. right: chart.width
  6237. };
  6238. }
  6239. }
  6240. drawBackground() {
  6241. const { ctx, options: { backgroundColor }, left, top, width, height } = this;
  6242. if (backgroundColor) {
  6243. ctx.save();
  6244. ctx.fillStyle = backgroundColor;
  6245. ctx.fillRect(left, top, width, height);
  6246. ctx.restore();
  6247. }
  6248. }
  6249. getLineWidthForValue(value) {
  6250. const grid = this.options.grid;
  6251. if (!this._isVisible() || !grid.display) {
  6252. return 0;
  6253. }
  6254. const ticks = this.ticks;
  6255. const index = ticks.findIndex((t) => t.value === value);
  6256. if (index >= 0) {
  6257. const opts = grid.setContext(this.getContext(index));
  6258. return opts.lineWidth;
  6259. }
  6260. return 0;
  6261. }
  6262. drawGrid(chartArea) {
  6263. const grid = this.options.grid;
  6264. const ctx = this.ctx;
  6265. const items = this._gridLineItems || (this._gridLineItems = this._computeGridLineItems(chartArea));
  6266. let i, ilen;
  6267. const drawLine = (p1, p2, style) => {
  6268. if (!style.width || !style.color) {
  6269. return;
  6270. }
  6271. ctx.save();
  6272. ctx.lineWidth = style.width;
  6273. ctx.strokeStyle = style.color;
  6274. ctx.setLineDash(style.borderDash || []);
  6275. ctx.lineDashOffset = style.borderDashOffset;
  6276. ctx.beginPath();
  6277. ctx.moveTo(p1.x, p1.y);
  6278. ctx.lineTo(p2.x, p2.y);
  6279. ctx.stroke();
  6280. ctx.restore();
  6281. };
  6282. if (grid.display) {
  6283. for (i = 0, ilen = items.length; i < ilen; ++i) {
  6284. const item = items[i];
  6285. if (grid.drawOnChartArea) {
  6286. drawLine({
  6287. x: item.x1,
  6288. y: item.y1
  6289. }, {
  6290. x: item.x2,
  6291. y: item.y2
  6292. }, item);
  6293. }
  6294. if (grid.drawTicks) {
  6295. drawLine({
  6296. x: item.tx1,
  6297. y: item.ty1
  6298. }, {
  6299. x: item.tx2,
  6300. y: item.ty2
  6301. }, {
  6302. color: item.tickColor,
  6303. width: item.tickWidth,
  6304. borderDash: item.tickBorderDash,
  6305. borderDashOffset: item.tickBorderDashOffset
  6306. });
  6307. }
  6308. }
  6309. }
  6310. }
  6311. drawBorder() {
  6312. const { chart, ctx, options: { border, grid } } = this;
  6313. const borderOpts = border.setContext(this.getContext());
  6314. const axisWidth = border.display ? borderOpts.width : 0;
  6315. if (!axisWidth) {
  6316. return;
  6317. }
  6318. const lastLineWidth = grid.setContext(this.getContext(0)).lineWidth;
  6319. const borderValue = this._borderValue;
  6320. let x1, x2, y1, y2;
  6321. if (this.isHorizontal()) {
  6322. x1 = _alignPixel(chart, this.left, axisWidth) - axisWidth / 2;
  6323. x2 = _alignPixel(chart, this.right, lastLineWidth) + lastLineWidth / 2;
  6324. y1 = y2 = borderValue;
  6325. } else {
  6326. y1 = _alignPixel(chart, this.top, axisWidth) - axisWidth / 2;
  6327. y2 = _alignPixel(chart, this.bottom, lastLineWidth) + lastLineWidth / 2;
  6328. x1 = x2 = borderValue;
  6329. }
  6330. ctx.save();
  6331. ctx.lineWidth = borderOpts.width;
  6332. ctx.strokeStyle = borderOpts.color;
  6333. ctx.beginPath();
  6334. ctx.moveTo(x1, y1);
  6335. ctx.lineTo(x2, y2);
  6336. ctx.stroke();
  6337. ctx.restore();
  6338. }
  6339. drawLabels(chartArea) {
  6340. const optionTicks = this.options.ticks;
  6341. if (!optionTicks.display) {
  6342. return;
  6343. }
  6344. const ctx = this.ctx;
  6345. const area = this._computeLabelArea();
  6346. if (area) {
  6347. clipArea(ctx, area);
  6348. }
  6349. const items = this.getLabelItems(chartArea);
  6350. for (const item of items) {
  6351. const renderTextOptions = item.options;
  6352. const tickFont = item.font;
  6353. const label = item.label;
  6354. const y = item.textOffset;
  6355. renderText(ctx, label, 0, y, tickFont, renderTextOptions);
  6356. }
  6357. if (area) {
  6358. unclipArea(ctx);
  6359. }
  6360. }
  6361. drawTitle() {
  6362. const { ctx, options: { position, title, reverse } } = this;
  6363. if (!title.display) {
  6364. return;
  6365. }
  6366. const font = toFont(title.font);
  6367. const padding = toPadding(title.padding);
  6368. const align = title.align;
  6369. let offset = font.lineHeight / 2;
  6370. if (position === "bottom" || position === "center" || isObject(position)) {
  6371. offset += padding.bottom;
  6372. if (isArray(title.text)) {
  6373. offset += font.lineHeight * (title.text.length - 1);
  6374. }
  6375. } else {
  6376. offset += padding.top;
  6377. }
  6378. const { titleX, titleY, maxWidth, rotation } = titleArgs(this, offset, position, align);
  6379. renderText(ctx, title.text, 0, 0, font, {
  6380. color: title.color,
  6381. maxWidth,
  6382. rotation,
  6383. textAlign: titleAlign(align, position, reverse),
  6384. textBaseline: "middle",
  6385. translation: [
  6386. titleX,
  6387. titleY
  6388. ]
  6389. });
  6390. }
  6391. draw(chartArea) {
  6392. if (!this._isVisible()) {
  6393. return;
  6394. }
  6395. this.drawBackground();
  6396. this.drawGrid(chartArea);
  6397. this.drawBorder();
  6398. this.drawTitle();
  6399. this.drawLabels(chartArea);
  6400. }
  6401. _layers() {
  6402. const opts = this.options;
  6403. const tz = opts.ticks && opts.ticks.z || 0;
  6404. const gz = valueOrDefault(opts.grid && opts.grid.z, -1);
  6405. const bz = valueOrDefault(opts.border && opts.border.z, 0);
  6406. if (!this._isVisible() || this.draw !== Scale.prototype.draw) {
  6407. return [
  6408. {
  6409. z: tz,
  6410. draw: (chartArea) => {
  6411. this.draw(chartArea);
  6412. }
  6413. }
  6414. ];
  6415. }
  6416. return [
  6417. {
  6418. z: gz,
  6419. draw: (chartArea) => {
  6420. this.drawBackground();
  6421. this.drawGrid(chartArea);
  6422. this.drawTitle();
  6423. }
  6424. },
  6425. {
  6426. z: bz,
  6427. draw: () => {
  6428. this.drawBorder();
  6429. }
  6430. },
  6431. {
  6432. z: tz,
  6433. draw: (chartArea) => {
  6434. this.drawLabels(chartArea);
  6435. }
  6436. }
  6437. ];
  6438. }
  6439. getMatchingVisibleMetas(type) {
  6440. const metas = this.chart.getSortedVisibleDatasetMetas();
  6441. const axisID = this.axis + "AxisID";
  6442. const result = [];
  6443. let i, ilen;
  6444. for (i = 0, ilen = metas.length; i < ilen; ++i) {
  6445. const meta = metas[i];
  6446. if (meta[axisID] === this.id && (!type || meta.type === type)) {
  6447. result.push(meta);
  6448. }
  6449. }
  6450. return result;
  6451. }
  6452. _resolveTickFontOptions(index) {
  6453. const opts = this.options.ticks.setContext(this.getContext(index));
  6454. return toFont(opts.font);
  6455. }
  6456. _maxDigits() {
  6457. const fontSize = this._resolveTickFontOptions(0).lineHeight;
  6458. return (this.isHorizontal() ? this.width : this.height) / fontSize;
  6459. }
  6460. }
  6461. class TypedRegistry {
  6462. constructor(type, scope, override) {
  6463. this.type = type;
  6464. this.scope = scope;
  6465. this.override = override;
  6466. this.items = /* @__PURE__ */ Object.create(null);
  6467. }
  6468. isForType(type) {
  6469. return Object.prototype.isPrototypeOf.call(this.type.prototype, type.prototype);
  6470. }
  6471. register(item) {
  6472. const proto = Object.getPrototypeOf(item);
  6473. let parentScope;
  6474. if (isIChartComponent(proto)) {
  6475. parentScope = this.register(proto);
  6476. }
  6477. const items = this.items;
  6478. const id = item.id;
  6479. const scope = this.scope + "." + id;
  6480. if (!id) {
  6481. throw new Error("class does not have id: " + item);
  6482. }
  6483. if (id in items) {
  6484. return scope;
  6485. }
  6486. items[id] = item;
  6487. registerDefaults(item, scope, parentScope);
  6488. if (this.override) {
  6489. defaults.override(item.id, item.overrides);
  6490. }
  6491. return scope;
  6492. }
  6493. get(id) {
  6494. return this.items[id];
  6495. }
  6496. unregister(item) {
  6497. const items = this.items;
  6498. const id = item.id;
  6499. const scope = this.scope;
  6500. if (id in items) {
  6501. delete items[id];
  6502. }
  6503. if (scope && id in defaults[scope]) {
  6504. delete defaults[scope][id];
  6505. if (this.override) {
  6506. delete overrides[id];
  6507. }
  6508. }
  6509. }
  6510. }
  6511. function registerDefaults(item, scope, parentScope) {
  6512. const itemDefaults = merge(/* @__PURE__ */ Object.create(null), [
  6513. parentScope ? defaults.get(parentScope) : {},
  6514. defaults.get(scope),
  6515. item.defaults
  6516. ]);
  6517. defaults.set(scope, itemDefaults);
  6518. if (item.defaultRoutes) {
  6519. routeDefaults(scope, item.defaultRoutes);
  6520. }
  6521. if (item.descriptors) {
  6522. defaults.describe(scope, item.descriptors);
  6523. }
  6524. }
  6525. function routeDefaults(scope, routes) {
  6526. Object.keys(routes).forEach((property) => {
  6527. const propertyParts = property.split(".");
  6528. const sourceName = propertyParts.pop();
  6529. const sourceScope = [
  6530. scope
  6531. ].concat(propertyParts).join(".");
  6532. const parts = routes[property].split(".");
  6533. const targetName = parts.pop();
  6534. const targetScope = parts.join(".");
  6535. defaults.route(sourceScope, sourceName, targetScope, targetName);
  6536. });
  6537. }
  6538. function isIChartComponent(proto) {
  6539. return "id" in proto && "defaults" in proto;
  6540. }
  6541. class Registry {
  6542. constructor() {
  6543. this.controllers = new TypedRegistry(DatasetController, "datasets", true);
  6544. this.elements = new TypedRegistry(Element, "elements");
  6545. this.plugins = new TypedRegistry(Object, "plugins");
  6546. this.scales = new TypedRegistry(Scale, "scales");
  6547. this._typedRegistries = [
  6548. this.controllers,
  6549. this.scales,
  6550. this.elements
  6551. ];
  6552. }
  6553. add(...args) {
  6554. this._each("register", args);
  6555. }
  6556. remove(...args) {
  6557. this._each("unregister", args);
  6558. }
  6559. addControllers(...args) {
  6560. this._each("register", args, this.controllers);
  6561. }
  6562. addElements(...args) {
  6563. this._each("register", args, this.elements);
  6564. }
  6565. addPlugins(...args) {
  6566. this._each("register", args, this.plugins);
  6567. }
  6568. addScales(...args) {
  6569. this._each("register", args, this.scales);
  6570. }
  6571. getController(id) {
  6572. return this._get(id, this.controllers, "controller");
  6573. }
  6574. getElement(id) {
  6575. return this._get(id, this.elements, "element");
  6576. }
  6577. getPlugin(id) {
  6578. return this._get(id, this.plugins, "plugin");
  6579. }
  6580. getScale(id) {
  6581. return this._get(id, this.scales, "scale");
  6582. }
  6583. removeControllers(...args) {
  6584. this._each("unregister", args, this.controllers);
  6585. }
  6586. removeElements(...args) {
  6587. this._each("unregister", args, this.elements);
  6588. }
  6589. removePlugins(...args) {
  6590. this._each("unregister", args, this.plugins);
  6591. }
  6592. removeScales(...args) {
  6593. this._each("unregister", args, this.scales);
  6594. }
  6595. _each(method, args, typedRegistry) {
  6596. [
  6597. ...args
  6598. ].forEach((arg) => {
  6599. const reg = typedRegistry || this._getRegistryForType(arg);
  6600. if (typedRegistry || reg.isForType(arg) || reg === this.plugins && arg.id) {
  6601. this._exec(method, reg, arg);
  6602. } else {
  6603. each(arg, (item) => {
  6604. const itemReg = typedRegistry || this._getRegistryForType(item);
  6605. this._exec(method, itemReg, item);
  6606. });
  6607. }
  6608. });
  6609. }
  6610. _exec(method, registry2, component) {
  6611. const camelMethod = _capitalize(method);
  6612. callback(component["before" + camelMethod], [], component);
  6613. registry2[method](component);
  6614. callback(component["after" + camelMethod], [], component);
  6615. }
  6616. _getRegistryForType(type) {
  6617. for (let i = 0; i < this._typedRegistries.length; i++) {
  6618. const reg = this._typedRegistries[i];
  6619. if (reg.isForType(type)) {
  6620. return reg;
  6621. }
  6622. }
  6623. return this.plugins;
  6624. }
  6625. _get(id, typedRegistry, type) {
  6626. const item = typedRegistry.get(id);
  6627. if (item === void 0) {
  6628. throw new Error('"' + id + '" is not a registered ' + type + ".");
  6629. }
  6630. return item;
  6631. }
  6632. }
  6633. var registry = /* @__PURE__ */ new Registry();
  6634. class PluginService {
  6635. constructor() {
  6636. this._init = [];
  6637. }
  6638. notify(chart, hook, args, filter) {
  6639. if (hook === "beforeInit") {
  6640. this._init = this._createDescriptors(chart, true);
  6641. this._notify(this._init, chart, "install");
  6642. }
  6643. const descriptors2 = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart);
  6644. const result = this._notify(descriptors2, chart, hook, args);
  6645. if (hook === "afterDestroy") {
  6646. this._notify(descriptors2, chart, "stop");
  6647. this._notify(this._init, chart, "uninstall");
  6648. }
  6649. return result;
  6650. }
  6651. _notify(descriptors2, chart, hook, args) {
  6652. args = args || {};
  6653. for (const descriptor of descriptors2) {
  6654. const plugin = descriptor.plugin;
  6655. const method = plugin[hook];
  6656. const params = [
  6657. chart,
  6658. args,
  6659. descriptor.options
  6660. ];
  6661. if (callback(method, params, plugin) === false && args.cancelable) {
  6662. return false;
  6663. }
  6664. }
  6665. return true;
  6666. }
  6667. invalidate() {
  6668. if (!isNullOrUndef(this._cache)) {
  6669. this._oldCache = this._cache;
  6670. this._cache = void 0;
  6671. }
  6672. }
  6673. _descriptors(chart) {
  6674. if (this._cache) {
  6675. return this._cache;
  6676. }
  6677. const descriptors2 = this._cache = this._createDescriptors(chart);
  6678. this._notifyStateChanges(chart);
  6679. return descriptors2;
  6680. }
  6681. _createDescriptors(chart, all) {
  6682. const config = chart && chart.config;
  6683. const options = valueOrDefault(config.options && config.options.plugins, {});
  6684. const plugins = allPlugins(config);
  6685. return options === false && !all ? [] : createDescriptors(chart, plugins, options, all);
  6686. }
  6687. _notifyStateChanges(chart) {
  6688. const previousDescriptors = this._oldCache || [];
  6689. const descriptors2 = this._cache;
  6690. const diff = (a, b) => a.filter((x) => !b.some((y) => x.plugin.id === y.plugin.id));
  6691. this._notify(diff(previousDescriptors, descriptors2), chart, "stop");
  6692. this._notify(diff(descriptors2, previousDescriptors), chart, "start");
  6693. }
  6694. }
  6695. function allPlugins(config) {
  6696. const localIds = {};
  6697. const plugins = [];
  6698. const keys = Object.keys(registry.plugins.items);
  6699. for (let i = 0; i < keys.length; i++) {
  6700. plugins.push(registry.getPlugin(keys[i]));
  6701. }
  6702. const local = config.plugins || [];
  6703. for (let i = 0; i < local.length; i++) {
  6704. const plugin = local[i];
  6705. if (plugins.indexOf(plugin) === -1) {
  6706. plugins.push(plugin);
  6707. localIds[plugin.id] = true;
  6708. }
  6709. }
  6710. return {
  6711. plugins,
  6712. localIds
  6713. };
  6714. }
  6715. function getOpts(options, all) {
  6716. if (!all && options === false) {
  6717. return null;
  6718. }
  6719. if (options === true) {
  6720. return {};
  6721. }
  6722. return options;
  6723. }
  6724. function createDescriptors(chart, { plugins, localIds }, options, all) {
  6725. const result = [];
  6726. const context = chart.getContext();
  6727. for (const plugin of plugins) {
  6728. const id = plugin.id;
  6729. const opts = getOpts(options[id], all);
  6730. if (opts === null) {
  6731. continue;
  6732. }
  6733. result.push({
  6734. plugin,
  6735. options: pluginOpts(chart.config, {
  6736. plugin,
  6737. local: localIds[id]
  6738. }, opts, context)
  6739. });
  6740. }
  6741. return result;
  6742. }
  6743. function pluginOpts(config, { plugin, local }, opts, context) {
  6744. const keys = config.pluginScopeKeys(plugin);
  6745. const scopes = config.getOptionScopes(opts, keys);
  6746. if (local && plugin.defaults) {
  6747. scopes.push(plugin.defaults);
  6748. }
  6749. return config.createResolver(scopes, context, [
  6750. ""
  6751. ], {
  6752. scriptable: false,
  6753. indexable: false,
  6754. allKeys: true
  6755. });
  6756. }
  6757. function getIndexAxis(type, options) {
  6758. const datasetDefaults = defaults.datasets[type] || {};
  6759. const datasetOptions = (options.datasets || {})[type] || {};
  6760. return datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || "x";
  6761. }
  6762. function getAxisFromDefaultScaleID(id, indexAxis) {
  6763. let axis = id;
  6764. if (id === "_index_") {
  6765. axis = indexAxis;
  6766. } else if (id === "_value_") {
  6767. axis = indexAxis === "x" ? "y" : "x";
  6768. }
  6769. return axis;
  6770. }
  6771. function getDefaultScaleIDFromAxis(axis, indexAxis) {
  6772. return axis === indexAxis ? "_index_" : "_value_";
  6773. }
  6774. function idMatchesAxis(id) {
  6775. if (id === "x" || id === "y" || id === "r") {
  6776. return id;
  6777. }
  6778. }
  6779. function axisFromPosition(position) {
  6780. if (position === "top" || position === "bottom") {
  6781. return "x";
  6782. }
  6783. if (position === "left" || position === "right") {
  6784. return "y";
  6785. }
  6786. }
  6787. function determineAxis(id, ...scaleOptions) {
  6788. if (idMatchesAxis(id)) {
  6789. return id;
  6790. }
  6791. for (const opts of scaleOptions) {
  6792. const axis = opts.axis || axisFromPosition(opts.position) || id.length > 1 && idMatchesAxis(id[0].toLowerCase());
  6793. if (axis) {
  6794. return axis;
  6795. }
  6796. }
  6797. throw new Error(`Cannot determine type of '${id}' axis. Please provide 'axis' or 'position' option.`);
  6798. }
  6799. function getAxisFromDataset(id, axis, dataset) {
  6800. if (dataset[axis + "AxisID"] === id) {
  6801. return {
  6802. axis
  6803. };
  6804. }
  6805. }
  6806. function retrieveAxisFromDatasets(id, config) {
  6807. if (config.data && config.data.datasets) {
  6808. const boundDs = config.data.datasets.filter((d) => d.xAxisID === id || d.yAxisID === id);
  6809. if (boundDs.length) {
  6810. return getAxisFromDataset(id, "x", boundDs[0]) || getAxisFromDataset(id, "y", boundDs[0]);
  6811. }
  6812. }
  6813. return {};
  6814. }
  6815. function mergeScaleConfig(config, options) {
  6816. const chartDefaults = overrides[config.type] || {
  6817. scales: {}
  6818. };
  6819. const configScales = options.scales || {};
  6820. const chartIndexAxis = getIndexAxis(config.type, options);
  6821. const scales = /* @__PURE__ */ Object.create(null);
  6822. Object.keys(configScales).forEach((id) => {
  6823. const scaleConf = configScales[id];
  6824. if (!isObject(scaleConf)) {
  6825. return console.error(`Invalid scale configuration for scale: ${id}`);
  6826. }
  6827. if (scaleConf._proxy) {
  6828. return console.warn(`Ignoring resolver passed as options for scale: ${id}`);
  6829. }
  6830. const axis = determineAxis(id, scaleConf, retrieveAxisFromDatasets(id, config), defaults.scales[scaleConf.type]);
  6831. const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis);
  6832. const defaultScaleOptions = chartDefaults.scales || {};
  6833. scales[id] = mergeIf(/* @__PURE__ */ Object.create(null), [
  6834. {
  6835. axis
  6836. },
  6837. scaleConf,
  6838. defaultScaleOptions[axis],
  6839. defaultScaleOptions[defaultId]
  6840. ]);
  6841. });
  6842. config.data.datasets.forEach((dataset) => {
  6843. const type = dataset.type || config.type;
  6844. const indexAxis = dataset.indexAxis || getIndexAxis(type, options);
  6845. const datasetDefaults = overrides[type] || {};
  6846. const defaultScaleOptions = datasetDefaults.scales || {};
  6847. Object.keys(defaultScaleOptions).forEach((defaultID) => {
  6848. const axis = getAxisFromDefaultScaleID(defaultID, indexAxis);
  6849. const id = dataset[axis + "AxisID"] || axis;
  6850. scales[id] = scales[id] || /* @__PURE__ */ Object.create(null);
  6851. mergeIf(scales[id], [
  6852. {
  6853. axis
  6854. },
  6855. configScales[id],
  6856. defaultScaleOptions[defaultID]
  6857. ]);
  6858. });
  6859. });
  6860. Object.keys(scales).forEach((key) => {
  6861. const scale = scales[key];
  6862. mergeIf(scale, [
  6863. defaults.scales[scale.type],
  6864. defaults.scale
  6865. ]);
  6866. });
  6867. return scales;
  6868. }
  6869. function initOptions(config) {
  6870. const options = config.options || (config.options = {});
  6871. options.plugins = valueOrDefault(options.plugins, {});
  6872. options.scales = mergeScaleConfig(config, options);
  6873. }
  6874. function initData(data) {
  6875. data = data || {};
  6876. data.datasets = data.datasets || [];
  6877. data.labels = data.labels || [];
  6878. return data;
  6879. }
  6880. function initConfig(config) {
  6881. config = config || {};
  6882. config.data = initData(config.data);
  6883. initOptions(config);
  6884. return config;
  6885. }
  6886. const keyCache = /* @__PURE__ */ new Map();
  6887. const keysCached = /* @__PURE__ */ new Set();
  6888. function cachedKeys(cacheKey, generate) {
  6889. let keys = keyCache.get(cacheKey);
  6890. if (!keys) {
  6891. keys = generate();
  6892. keyCache.set(cacheKey, keys);
  6893. keysCached.add(keys);
  6894. }
  6895. return keys;
  6896. }
  6897. const addIfFound = (set2, obj, key) => {
  6898. const opts = resolveObjectKey(obj, key);
  6899. if (opts !== void 0) {
  6900. set2.add(opts);
  6901. }
  6902. };
  6903. class Config {
  6904. constructor(config) {
  6905. this._config = initConfig(config);
  6906. this._scopeCache = /* @__PURE__ */ new Map();
  6907. this._resolverCache = /* @__PURE__ */ new Map();
  6908. }
  6909. get platform() {
  6910. return this._config.platform;
  6911. }
  6912. get type() {
  6913. return this._config.type;
  6914. }
  6915. set type(type) {
  6916. this._config.type = type;
  6917. }
  6918. get data() {
  6919. return this._config.data;
  6920. }
  6921. set data(data) {
  6922. this._config.data = initData(data);
  6923. }
  6924. get options() {
  6925. return this._config.options;
  6926. }
  6927. set options(options) {
  6928. this._config.options = options;
  6929. }
  6930. get plugins() {
  6931. return this._config.plugins;
  6932. }
  6933. update() {
  6934. const config = this._config;
  6935. this.clearCache();
  6936. initOptions(config);
  6937. }
  6938. clearCache() {
  6939. this._scopeCache.clear();
  6940. this._resolverCache.clear();
  6941. }
  6942. datasetScopeKeys(datasetType) {
  6943. return cachedKeys(datasetType, () => [
  6944. [
  6945. `datasets.${datasetType}`,
  6946. ""
  6947. ]
  6948. ]);
  6949. }
  6950. datasetAnimationScopeKeys(datasetType, transition) {
  6951. return cachedKeys(`${datasetType}.transition.${transition}`, () => [
  6952. [
  6953. `datasets.${datasetType}.transitions.${transition}`,
  6954. `transitions.${transition}`
  6955. ],
  6956. [
  6957. `datasets.${datasetType}`,
  6958. ""
  6959. ]
  6960. ]);
  6961. }
  6962. datasetElementScopeKeys(datasetType, elementType) {
  6963. return cachedKeys(`${datasetType}-${elementType}`, () => [
  6964. [
  6965. `datasets.${datasetType}.elements.${elementType}`,
  6966. `datasets.${datasetType}`,
  6967. `elements.${elementType}`,
  6968. ""
  6969. ]
  6970. ]);
  6971. }
  6972. pluginScopeKeys(plugin) {
  6973. const id = plugin.id;
  6974. const type = this.type;
  6975. return cachedKeys(`${type}-plugin-${id}`, () => [
  6976. [
  6977. `plugins.${id}`,
  6978. ...plugin.additionalOptionScopes || []
  6979. ]
  6980. ]);
  6981. }
  6982. _cachedScopes(mainScope, resetCache) {
  6983. const _scopeCache = this._scopeCache;
  6984. let cache = _scopeCache.get(mainScope);
  6985. if (!cache || resetCache) {
  6986. cache = /* @__PURE__ */ new Map();
  6987. _scopeCache.set(mainScope, cache);
  6988. }
  6989. return cache;
  6990. }
  6991. getOptionScopes(mainScope, keyLists, resetCache) {
  6992. const { options, type } = this;
  6993. const cache = this._cachedScopes(mainScope, resetCache);
  6994. const cached = cache.get(keyLists);
  6995. if (cached) {
  6996. return cached;
  6997. }
  6998. const scopes = /* @__PURE__ */ new Set();
  6999. keyLists.forEach((keys) => {
  7000. if (mainScope) {
  7001. scopes.add(mainScope);
  7002. keys.forEach((key) => addIfFound(scopes, mainScope, key));
  7003. }
  7004. keys.forEach((key) => addIfFound(scopes, options, key));
  7005. keys.forEach((key) => addIfFound(scopes, overrides[type] || {}, key));
  7006. keys.forEach((key) => addIfFound(scopes, defaults, key));
  7007. keys.forEach((key) => addIfFound(scopes, descriptors, key));
  7008. });
  7009. const array = Array.from(scopes);
  7010. if (array.length === 0) {
  7011. array.push(/* @__PURE__ */ Object.create(null));
  7012. }
  7013. if (keysCached.has(keyLists)) {
  7014. cache.set(keyLists, array);
  7015. }
  7016. return array;
  7017. }
  7018. chartOptionScopes() {
  7019. const { options, type } = this;
  7020. return [
  7021. options,
  7022. overrides[type] || {},
  7023. defaults.datasets[type] || {},
  7024. {
  7025. type
  7026. },
  7027. defaults,
  7028. descriptors
  7029. ];
  7030. }
  7031. resolveNamedOptions(scopes, names2, context, prefixes = [
  7032. ""
  7033. ]) {
  7034. const result = {
  7035. $shared: true
  7036. };
  7037. const { resolver, subPrefixes } = getResolver(this._resolverCache, scopes, prefixes);
  7038. let options = resolver;
  7039. if (needContext(resolver, names2)) {
  7040. result.$shared = false;
  7041. context = isFunction(context) ? context() : context;
  7042. const subResolver = this.createResolver(scopes, context, subPrefixes);
  7043. options = _attachContext(resolver, context, subResolver);
  7044. }
  7045. for (const prop of names2) {
  7046. result[prop] = options[prop];
  7047. }
  7048. return result;
  7049. }
  7050. createResolver(scopes, context, prefixes = [
  7051. ""
  7052. ], descriptorDefaults) {
  7053. const { resolver } = getResolver(this._resolverCache, scopes, prefixes);
  7054. return isObject(context) ? _attachContext(resolver, context, void 0, descriptorDefaults) : resolver;
  7055. }
  7056. }
  7057. function getResolver(resolverCache, scopes, prefixes) {
  7058. let cache = resolverCache.get(scopes);
  7059. if (!cache) {
  7060. cache = /* @__PURE__ */ new Map();
  7061. resolverCache.set(scopes, cache);
  7062. }
  7063. const cacheKey = prefixes.join();
  7064. let cached = cache.get(cacheKey);
  7065. if (!cached) {
  7066. const resolver = _createResolver(scopes, prefixes);
  7067. cached = {
  7068. resolver,
  7069. subPrefixes: prefixes.filter((p) => !p.toLowerCase().includes("hover"))
  7070. };
  7071. cache.set(cacheKey, cached);
  7072. }
  7073. return cached;
  7074. }
  7075. const hasFunction = (value) => isObject(value) && Object.getOwnPropertyNames(value).some((key) => isFunction(value[key]));
  7076. function needContext(proxy, names2) {
  7077. const { isScriptable, isIndexable } = _descriptors(proxy);
  7078. for (const prop of names2) {
  7079. const scriptable = isScriptable(prop);
  7080. const indexable = isIndexable(prop);
  7081. const value = (indexable || scriptable) && proxy[prop];
  7082. if (scriptable && (isFunction(value) || hasFunction(value)) || indexable && isArray(value)) {
  7083. return true;
  7084. }
  7085. }
  7086. return false;
  7087. }
  7088. var version = "4.4.7";
  7089. const KNOWN_POSITIONS = [
  7090. "top",
  7091. "bottom",
  7092. "left",
  7093. "right",
  7094. "chartArea"
  7095. ];
  7096. function positionIsHorizontal(position, axis) {
  7097. return position === "top" || position === "bottom" || KNOWN_POSITIONS.indexOf(position) === -1 && axis === "x";
  7098. }
  7099. function compare2Level(l1, l2) {
  7100. return function(a, b) {
  7101. return a[l1] === b[l1] ? a[l2] - b[l2] : a[l1] - b[l1];
  7102. };
  7103. }
  7104. function onAnimationsComplete(context) {
  7105. const chart = context.chart;
  7106. const animationOptions = chart.options.animation;
  7107. chart.notifyPlugins("afterRender");
  7108. callback(animationOptions && animationOptions.onComplete, [
  7109. context
  7110. ], chart);
  7111. }
  7112. function onAnimationProgress(context) {
  7113. const chart = context.chart;
  7114. const animationOptions = chart.options.animation;
  7115. callback(animationOptions && animationOptions.onProgress, [
  7116. context
  7117. ], chart);
  7118. }
  7119. function getCanvas(item) {
  7120. if (_isDomSupported() && typeof item === "string") {
  7121. item = document.getElementById(item);
  7122. } else if (item && item.length) {
  7123. item = item[0];
  7124. }
  7125. if (item && item.canvas) {
  7126. item = item.canvas;
  7127. }
  7128. return item;
  7129. }
  7130. const instances = {};
  7131. const getChart = (key) => {
  7132. const canvas = getCanvas(key);
  7133. return Object.values(instances).filter((c) => c.canvas === canvas).pop();
  7134. };
  7135. function moveNumericKeys(obj, start, move) {
  7136. const keys = Object.keys(obj);
  7137. for (const key of keys) {
  7138. const intKey = +key;
  7139. if (intKey >= start) {
  7140. const value = obj[key];
  7141. delete obj[key];
  7142. if (move > 0 || intKey > start) {
  7143. obj[intKey + move] = value;
  7144. }
  7145. }
  7146. }
  7147. }
  7148. function determineLastEvent(e, lastEvent, inChartArea, isClick) {
  7149. if (!inChartArea || e.type === "mouseout") {
  7150. return null;
  7151. }
  7152. if (isClick) {
  7153. return lastEvent;
  7154. }
  7155. return e;
  7156. }
  7157. function getSizeForArea(scale, chartArea, field) {
  7158. return scale.options.clip ? scale[field] : chartArea[field];
  7159. }
  7160. function getDatasetArea(meta, chartArea) {
  7161. const { xScale, yScale } = meta;
  7162. if (xScale && yScale) {
  7163. return {
  7164. left: getSizeForArea(xScale, chartArea, "left"),
  7165. right: getSizeForArea(xScale, chartArea, "right"),
  7166. top: getSizeForArea(yScale, chartArea, "top"),
  7167. bottom: getSizeForArea(yScale, chartArea, "bottom")
  7168. };
  7169. }
  7170. return chartArea;
  7171. }
  7172. class Chart {
  7173. static register(...items) {
  7174. registry.add(...items);
  7175. invalidatePlugins();
  7176. }
  7177. static unregister(...items) {
  7178. registry.remove(...items);
  7179. invalidatePlugins();
  7180. }
  7181. constructor(item, userConfig) {
  7182. const config = this.config = new Config(userConfig);
  7183. const initialCanvas = getCanvas(item);
  7184. const existingChart = getChart(initialCanvas);
  7185. if (existingChart) {
  7186. throw new Error("Canvas is already in use. Chart with ID '" + existingChart.id + "' must be destroyed before the canvas with ID '" + existingChart.canvas.id + "' can be reused.");
  7187. }
  7188. const options = config.createResolver(config.chartOptionScopes(), this.getContext());
  7189. this.platform = new (config.platform || _detectPlatform(initialCanvas))();
  7190. this.platform.updateConfig(config);
  7191. const context = this.platform.acquireContext(initialCanvas, options.aspectRatio);
  7192. const canvas = context && context.canvas;
  7193. const height = canvas && canvas.height;
  7194. const width = canvas && canvas.width;
  7195. this.id = uid();
  7196. this.ctx = context;
  7197. this.canvas = canvas;
  7198. this.width = width;
  7199. this.height = height;
  7200. this._options = options;
  7201. this._aspectRatio = this.aspectRatio;
  7202. this._layers = [];
  7203. this._metasets = [];
  7204. this._stacks = void 0;
  7205. this.boxes = [];
  7206. this.currentDevicePixelRatio = void 0;
  7207. this.chartArea = void 0;
  7208. this._active = [];
  7209. this._lastEvent = void 0;
  7210. this._listeners = {};
  7211. this._responsiveListeners = void 0;
  7212. this._sortedMetasets = [];
  7213. this.scales = {};
  7214. this._plugins = new PluginService();
  7215. this.$proxies = {};
  7216. this._hiddenIndices = {};
  7217. this.attached = false;
  7218. this._animationsDisabled = void 0;
  7219. this.$context = void 0;
  7220. this._doResize = debounce((mode) => this.update(mode), options.resizeDelay || 0);
  7221. this._dataChanges = [];
  7222. instances[this.id] = this;
  7223. if (!context || !canvas) {
  7224. console.error("Failed to create chart: can't acquire context from the given item");
  7225. return;
  7226. }
  7227. animator.listen(this, "complete", onAnimationsComplete);
  7228. animator.listen(this, "progress", onAnimationProgress);
  7229. this._initialize();
  7230. if (this.attached) {
  7231. this.update();
  7232. }
  7233. }
  7234. get aspectRatio() {
  7235. const { options: { aspectRatio, maintainAspectRatio }, width, height, _aspectRatio } = this;
  7236. if (!isNullOrUndef(aspectRatio)) {
  7237. return aspectRatio;
  7238. }
  7239. if (maintainAspectRatio && _aspectRatio) {
  7240. return _aspectRatio;
  7241. }
  7242. return height ? width / height : null;
  7243. }
  7244. get data() {
  7245. return this.config.data;
  7246. }
  7247. set data(data) {
  7248. this.config.data = data;
  7249. }
  7250. get options() {
  7251. return this._options;
  7252. }
  7253. set options(options) {
  7254. this.config.options = options;
  7255. }
  7256. get registry() {
  7257. return registry;
  7258. }
  7259. _initialize() {
  7260. this.notifyPlugins("beforeInit");
  7261. if (this.options.responsive) {
  7262. this.resize();
  7263. } else {
  7264. retinaScale(this, this.options.devicePixelRatio);
  7265. }
  7266. this.bindEvents();
  7267. this.notifyPlugins("afterInit");
  7268. return this;
  7269. }
  7270. clear() {
  7271. clearCanvas(this.canvas, this.ctx);
  7272. return this;
  7273. }
  7274. stop() {
  7275. animator.stop(this);
  7276. return this;
  7277. }
  7278. resize(width, height) {
  7279. if (!animator.running(this)) {
  7280. this._resize(width, height);
  7281. } else {
  7282. this._resizeBeforeDraw = {
  7283. width,
  7284. height
  7285. };
  7286. }
  7287. }
  7288. _resize(width, height) {
  7289. const options = this.options;
  7290. const canvas = this.canvas;
  7291. const aspectRatio = options.maintainAspectRatio && this.aspectRatio;
  7292. const newSize = this.platform.getMaximumSize(canvas, width, height, aspectRatio);
  7293. const newRatio = options.devicePixelRatio || this.platform.getDevicePixelRatio();
  7294. const mode = this.width ? "resize" : "attach";
  7295. this.width = newSize.width;
  7296. this.height = newSize.height;
  7297. this._aspectRatio = this.aspectRatio;
  7298. if (!retinaScale(this, newRatio, true)) {
  7299. return;
  7300. }
  7301. this.notifyPlugins("resize", {
  7302. size: newSize
  7303. });
  7304. callback(options.onResize, [
  7305. this,
  7306. newSize
  7307. ], this);
  7308. if (this.attached) {
  7309. if (this._doResize(mode)) {
  7310. this.render();
  7311. }
  7312. }
  7313. }
  7314. ensureScalesHaveIDs() {
  7315. const options = this.options;
  7316. const scalesOptions = options.scales || {};
  7317. each(scalesOptions, (axisOptions, axisID) => {
  7318. axisOptions.id = axisID;
  7319. });
  7320. }
  7321. buildOrUpdateScales() {
  7322. const options = this.options;
  7323. const scaleOpts = options.scales;
  7324. const scales = this.scales;
  7325. const updated = Object.keys(scales).reduce((obj, id) => {
  7326. obj[id] = false;
  7327. return obj;
  7328. }, {});
  7329. let items = [];
  7330. if (scaleOpts) {
  7331. items = items.concat(Object.keys(scaleOpts).map((id) => {
  7332. const scaleOptions = scaleOpts[id];
  7333. const axis = determineAxis(id, scaleOptions);
  7334. const isRadial = axis === "r";
  7335. const isHorizontal = axis === "x";
  7336. return {
  7337. options: scaleOptions,
  7338. dposition: isRadial ? "chartArea" : isHorizontal ? "bottom" : "left",
  7339. dtype: isRadial ? "radialLinear" : isHorizontal ? "category" : "linear"
  7340. };
  7341. }));
  7342. }
  7343. each(items, (item) => {
  7344. const scaleOptions = item.options;
  7345. const id = scaleOptions.id;
  7346. const axis = determineAxis(id, scaleOptions);
  7347. const scaleType = valueOrDefault(scaleOptions.type, item.dtype);
  7348. if (scaleOptions.position === void 0 || positionIsHorizontal(scaleOptions.position, axis) !== positionIsHorizontal(item.dposition)) {
  7349. scaleOptions.position = item.dposition;
  7350. }
  7351. updated[id] = true;
  7352. let scale = null;
  7353. if (id in scales && scales[id].type === scaleType) {
  7354. scale = scales[id];
  7355. } else {
  7356. const scaleClass = registry.getScale(scaleType);
  7357. scale = new scaleClass({
  7358. id,
  7359. type: scaleType,
  7360. ctx: this.ctx,
  7361. chart: this
  7362. });
  7363. scales[scale.id] = scale;
  7364. }
  7365. scale.init(scaleOptions, options);
  7366. });
  7367. each(updated, (hasUpdated, id) => {
  7368. if (!hasUpdated) {
  7369. delete scales[id];
  7370. }
  7371. });
  7372. each(scales, (scale) => {
  7373. layouts.configure(this, scale, scale.options);
  7374. layouts.addBox(this, scale);
  7375. });
  7376. }
  7377. _updateMetasets() {
  7378. const metasets = this._metasets;
  7379. const numData = this.data.datasets.length;
  7380. const numMeta = metasets.length;
  7381. metasets.sort((a, b) => a.index - b.index);
  7382. if (numMeta > numData) {
  7383. for (let i = numData; i < numMeta; ++i) {
  7384. this._destroyDatasetMeta(i);
  7385. }
  7386. metasets.splice(numData, numMeta - numData);
  7387. }
  7388. this._sortedMetasets = metasets.slice(0).sort(compare2Level("order", "index"));
  7389. }
  7390. _removeUnreferencedMetasets() {
  7391. const { _metasets: metasets, data: { datasets } } = this;
  7392. if (metasets.length > datasets.length) {
  7393. delete this._stacks;
  7394. }
  7395. metasets.forEach((meta, index) => {
  7396. if (datasets.filter((x) => x === meta._dataset).length === 0) {
  7397. this._destroyDatasetMeta(index);
  7398. }
  7399. });
  7400. }
  7401. buildOrUpdateControllers() {
  7402. const newControllers = [];
  7403. const datasets = this.data.datasets;
  7404. let i, ilen;
  7405. this._removeUnreferencedMetasets();
  7406. for (i = 0, ilen = datasets.length; i < ilen; i++) {
  7407. const dataset = datasets[i];
  7408. let meta = this.getDatasetMeta(i);
  7409. const type = dataset.type || this.config.type;
  7410. if (meta.type && meta.type !== type) {
  7411. this._destroyDatasetMeta(i);
  7412. meta = this.getDatasetMeta(i);
  7413. }
  7414. meta.type = type;
  7415. meta.indexAxis = dataset.indexAxis || getIndexAxis(type, this.options);
  7416. meta.order = dataset.order || 0;
  7417. meta.index = i;
  7418. meta.label = "" + dataset.label;
  7419. meta.visible = this.isDatasetVisible(i);
  7420. if (meta.controller) {
  7421. meta.controller.updateIndex(i);
  7422. meta.controller.linkScales();
  7423. } else {
  7424. const ControllerClass = registry.getController(type);
  7425. const { datasetElementType, dataElementType } = defaults.datasets[type];
  7426. Object.assign(ControllerClass, {
  7427. dataElementType: registry.getElement(dataElementType),
  7428. datasetElementType: datasetElementType && registry.getElement(datasetElementType)
  7429. });
  7430. meta.controller = new ControllerClass(this, i);
  7431. newControllers.push(meta.controller);
  7432. }
  7433. }
  7434. this._updateMetasets();
  7435. return newControllers;
  7436. }
  7437. _resetElements() {
  7438. each(this.data.datasets, (dataset, datasetIndex) => {
  7439. this.getDatasetMeta(datasetIndex).controller.reset();
  7440. }, this);
  7441. }
  7442. reset() {
  7443. this._resetElements();
  7444. this.notifyPlugins("reset");
  7445. }
  7446. update(mode) {
  7447. const config = this.config;
  7448. config.update();
  7449. const options = this._options = config.createResolver(config.chartOptionScopes(), this.getContext());
  7450. const animsDisabled = this._animationsDisabled = !options.animation;
  7451. this._updateScales();
  7452. this._checkEventBindings();
  7453. this._updateHiddenIndices();
  7454. this._plugins.invalidate();
  7455. if (this.notifyPlugins("beforeUpdate", {
  7456. mode,
  7457. cancelable: true
  7458. }) === false) {
  7459. return;
  7460. }
  7461. const newControllers = this.buildOrUpdateControllers();
  7462. this.notifyPlugins("beforeElementsUpdate");
  7463. let minPadding = 0;
  7464. for (let i = 0, ilen = this.data.datasets.length; i < ilen; i++) {
  7465. const { controller } = this.getDatasetMeta(i);
  7466. const reset = !animsDisabled && newControllers.indexOf(controller) === -1;
  7467. controller.buildOrUpdateElements(reset);
  7468. minPadding = Math.max(+controller.getMaxOverflow(), minPadding);
  7469. }
  7470. minPadding = this._minPadding = options.layout.autoPadding ? minPadding : 0;
  7471. this._updateLayout(minPadding);
  7472. if (!animsDisabled) {
  7473. each(newControllers, (controller) => {
  7474. controller.reset();
  7475. });
  7476. }
  7477. this._updateDatasets(mode);
  7478. this.notifyPlugins("afterUpdate", {
  7479. mode
  7480. });
  7481. this._layers.sort(compare2Level("z", "_idx"));
  7482. const { _active, _lastEvent } = this;
  7483. if (_lastEvent) {
  7484. this._eventHandler(_lastEvent, true);
  7485. } else if (_active.length) {
  7486. this._updateHoverStyles(_active, _active, true);
  7487. }
  7488. this.render();
  7489. }
  7490. _updateScales() {
  7491. each(this.scales, (scale) => {
  7492. layouts.removeBox(this, scale);
  7493. });
  7494. this.ensureScalesHaveIDs();
  7495. this.buildOrUpdateScales();
  7496. }
  7497. _checkEventBindings() {
  7498. const options = this.options;
  7499. const existingEvents = new Set(Object.keys(this._listeners));
  7500. const newEvents = new Set(options.events);
  7501. if (!setsEqual(existingEvents, newEvents) || !!this._responsiveListeners !== options.responsive) {
  7502. this.unbindEvents();
  7503. this.bindEvents();
  7504. }
  7505. }
  7506. _updateHiddenIndices() {
  7507. const { _hiddenIndices } = this;
  7508. const changes = this._getUniformDataChanges() || [];
  7509. for (const { method, start, count } of changes) {
  7510. const move = method === "_removeElements" ? -count : count;
  7511. moveNumericKeys(_hiddenIndices, start, move);
  7512. }
  7513. }
  7514. _getUniformDataChanges() {
  7515. const _dataChanges = this._dataChanges;
  7516. if (!_dataChanges || !_dataChanges.length) {
  7517. return;
  7518. }
  7519. this._dataChanges = [];
  7520. const datasetCount = this.data.datasets.length;
  7521. const makeSet = (idx) => new Set(_dataChanges.filter((c) => c[0] === idx).map((c, i) => i + "," + c.splice(1).join(",")));
  7522. const changeSet = makeSet(0);
  7523. for (let i = 1; i < datasetCount; i++) {
  7524. if (!setsEqual(changeSet, makeSet(i))) {
  7525. return;
  7526. }
  7527. }
  7528. return Array.from(changeSet).map((c) => c.split(",")).map((a) => ({
  7529. method: a[1],
  7530. start: +a[2],
  7531. count: +a[3]
  7532. }));
  7533. }
  7534. _updateLayout(minPadding) {
  7535. if (this.notifyPlugins("beforeLayout", {
  7536. cancelable: true
  7537. }) === false) {
  7538. return;
  7539. }
  7540. layouts.update(this, this.width, this.height, minPadding);
  7541. const area = this.chartArea;
  7542. const noArea = area.width <= 0 || area.height <= 0;
  7543. this._layers = [];
  7544. each(this.boxes, (box) => {
  7545. if (noArea && box.position === "chartArea") {
  7546. return;
  7547. }
  7548. if (box.configure) {
  7549. box.configure();
  7550. }
  7551. this._layers.push(...box._layers());
  7552. }, this);
  7553. this._layers.forEach((item, index) => {
  7554. item._idx = index;
  7555. });
  7556. this.notifyPlugins("afterLayout");
  7557. }
  7558. _updateDatasets(mode) {
  7559. if (this.notifyPlugins("beforeDatasetsUpdate", {
  7560. mode,
  7561. cancelable: true
  7562. }) === false) {
  7563. return;
  7564. }
  7565. for (let i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
  7566. this.getDatasetMeta(i).controller.configure();
  7567. }
  7568. for (let i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
  7569. this._updateDataset(i, isFunction(mode) ? mode({
  7570. datasetIndex: i
  7571. }) : mode);
  7572. }
  7573. this.notifyPlugins("afterDatasetsUpdate", {
  7574. mode
  7575. });
  7576. }
  7577. _updateDataset(index, mode) {
  7578. const meta = this.getDatasetMeta(index);
  7579. const args = {
  7580. meta,
  7581. index,
  7582. mode,
  7583. cancelable: true
  7584. };
  7585. if (this.notifyPlugins("beforeDatasetUpdate", args) === false) {
  7586. return;
  7587. }
  7588. meta.controller._update(mode);
  7589. args.cancelable = false;
  7590. this.notifyPlugins("afterDatasetUpdate", args);
  7591. }
  7592. render() {
  7593. if (this.notifyPlugins("beforeRender", {
  7594. cancelable: true
  7595. }) === false) {
  7596. return;
  7597. }
  7598. if (animator.has(this)) {
  7599. if (this.attached && !animator.running(this)) {
  7600. animator.start(this);
  7601. }
  7602. } else {
  7603. this.draw();
  7604. onAnimationsComplete({
  7605. chart: this
  7606. });
  7607. }
  7608. }
  7609. draw() {
  7610. let i;
  7611. if (this._resizeBeforeDraw) {
  7612. const { width, height } = this._resizeBeforeDraw;
  7613. this._resizeBeforeDraw = null;
  7614. this._resize(width, height);
  7615. }
  7616. this.clear();
  7617. if (this.width <= 0 || this.height <= 0) {
  7618. return;
  7619. }
  7620. if (this.notifyPlugins("beforeDraw", {
  7621. cancelable: true
  7622. }) === false) {
  7623. return;
  7624. }
  7625. const layers = this._layers;
  7626. for (i = 0; i < layers.length && layers[i].z <= 0; ++i) {
  7627. layers[i].draw(this.chartArea);
  7628. }
  7629. this._drawDatasets();
  7630. for (; i < layers.length; ++i) {
  7631. layers[i].draw(this.chartArea);
  7632. }
  7633. this.notifyPlugins("afterDraw");
  7634. }
  7635. _getSortedDatasetMetas(filterVisible) {
  7636. const metasets = this._sortedMetasets;
  7637. const result = [];
  7638. let i, ilen;
  7639. for (i = 0, ilen = metasets.length; i < ilen; ++i) {
  7640. const meta = metasets[i];
  7641. if (!filterVisible || meta.visible) {
  7642. result.push(meta);
  7643. }
  7644. }
  7645. return result;
  7646. }
  7647. getSortedVisibleDatasetMetas() {
  7648. return this._getSortedDatasetMetas(true);
  7649. }
  7650. _drawDatasets() {
  7651. if (this.notifyPlugins("beforeDatasetsDraw", {
  7652. cancelable: true
  7653. }) === false) {
  7654. return;
  7655. }
  7656. const metasets = this.getSortedVisibleDatasetMetas();
  7657. for (let i = metasets.length - 1; i >= 0; --i) {
  7658. this._drawDataset(metasets[i]);
  7659. }
  7660. this.notifyPlugins("afterDatasetsDraw");
  7661. }
  7662. _drawDataset(meta) {
  7663. const ctx = this.ctx;
  7664. const clip = meta._clip;
  7665. const useClip = !clip.disabled;
  7666. const area = getDatasetArea(meta, this.chartArea);
  7667. const args = {
  7668. meta,
  7669. index: meta.index,
  7670. cancelable: true
  7671. };
  7672. if (this.notifyPlugins("beforeDatasetDraw", args) === false) {
  7673. return;
  7674. }
  7675. if (useClip) {
  7676. clipArea(ctx, {
  7677. left: clip.left === false ? 0 : area.left - clip.left,
  7678. right: clip.right === false ? this.width : area.right + clip.right,
  7679. top: clip.top === false ? 0 : area.top - clip.top,
  7680. bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom
  7681. });
  7682. }
  7683. meta.controller.draw();
  7684. if (useClip) {
  7685. unclipArea(ctx);
  7686. }
  7687. args.cancelable = false;
  7688. this.notifyPlugins("afterDatasetDraw", args);
  7689. }
  7690. isPointInArea(point) {
  7691. return _isPointInArea(point, this.chartArea, this._minPadding);
  7692. }
  7693. getElementsAtEventForMode(e, mode, options, useFinalPosition) {
  7694. const method = Interaction.modes[mode];
  7695. if (typeof method === "function") {
  7696. return method(this, e, options, useFinalPosition);
  7697. }
  7698. return [];
  7699. }
  7700. getDatasetMeta(datasetIndex) {
  7701. const dataset = this.data.datasets[datasetIndex];
  7702. const metasets = this._metasets;
  7703. let meta = metasets.filter((x) => x && x._dataset === dataset).pop();
  7704. if (!meta) {
  7705. meta = {
  7706. type: null,
  7707. data: [],
  7708. dataset: null,
  7709. controller: null,
  7710. hidden: null,
  7711. xAxisID: null,
  7712. yAxisID: null,
  7713. order: dataset && dataset.order || 0,
  7714. index: datasetIndex,
  7715. _dataset: dataset,
  7716. _parsed: [],
  7717. _sorted: false
  7718. };
  7719. metasets.push(meta);
  7720. }
  7721. return meta;
  7722. }
  7723. getContext() {
  7724. return this.$context || (this.$context = createContext(null, {
  7725. chart: this,
  7726. type: "chart"
  7727. }));
  7728. }
  7729. getVisibleDatasetCount() {
  7730. return this.getSortedVisibleDatasetMetas().length;
  7731. }
  7732. isDatasetVisible(datasetIndex) {
  7733. const dataset = this.data.datasets[datasetIndex];
  7734. if (!dataset) {
  7735. return false;
  7736. }
  7737. const meta = this.getDatasetMeta(datasetIndex);
  7738. return typeof meta.hidden === "boolean" ? !meta.hidden : !dataset.hidden;
  7739. }
  7740. setDatasetVisibility(datasetIndex, visible) {
  7741. const meta = this.getDatasetMeta(datasetIndex);
  7742. meta.hidden = !visible;
  7743. }
  7744. toggleDataVisibility(index) {
  7745. this._hiddenIndices[index] = !this._hiddenIndices[index];
  7746. }
  7747. getDataVisibility(index) {
  7748. return !this._hiddenIndices[index];
  7749. }
  7750. _updateVisibility(datasetIndex, dataIndex, visible) {
  7751. const mode = visible ? "show" : "hide";
  7752. const meta = this.getDatasetMeta(datasetIndex);
  7753. const anims = meta.controller._resolveAnimations(void 0, mode);
  7754. if (defined(dataIndex)) {
  7755. meta.data[dataIndex].hidden = !visible;
  7756. this.update();
  7757. } else {
  7758. this.setDatasetVisibility(datasetIndex, visible);
  7759. anims.update(meta, {
  7760. visible
  7761. });
  7762. this.update((ctx) => ctx.datasetIndex === datasetIndex ? mode : void 0);
  7763. }
  7764. }
  7765. hide(datasetIndex, dataIndex) {
  7766. this._updateVisibility(datasetIndex, dataIndex, false);
  7767. }
  7768. show(datasetIndex, dataIndex) {
  7769. this._updateVisibility(datasetIndex, dataIndex, true);
  7770. }
  7771. _destroyDatasetMeta(datasetIndex) {
  7772. const meta = this._metasets[datasetIndex];
  7773. if (meta && meta.controller) {
  7774. meta.controller._destroy();
  7775. }
  7776. delete this._metasets[datasetIndex];
  7777. }
  7778. _stop() {
  7779. let i, ilen;
  7780. this.stop();
  7781. animator.remove(this);
  7782. for (i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
  7783. this._destroyDatasetMeta(i);
  7784. }
  7785. }
  7786. destroy() {
  7787. this.notifyPlugins("beforeDestroy");
  7788. const { canvas, ctx } = this;
  7789. this._stop();
  7790. this.config.clearCache();
  7791. if (canvas) {
  7792. this.unbindEvents();
  7793. clearCanvas(canvas, ctx);
  7794. this.platform.releaseContext(ctx);
  7795. this.canvas = null;
  7796. this.ctx = null;
  7797. }
  7798. delete instances[this.id];
  7799. this.notifyPlugins("afterDestroy");
  7800. }
  7801. toBase64Image(...args) {
  7802. return this.canvas.toDataURL(...args);
  7803. }
  7804. bindEvents() {
  7805. this.bindUserEvents();
  7806. if (this.options.responsive) {
  7807. this.bindResponsiveEvents();
  7808. } else {
  7809. this.attached = true;
  7810. }
  7811. }
  7812. bindUserEvents() {
  7813. const listeners = this._listeners;
  7814. const platform = this.platform;
  7815. const _add = (type, listener2) => {
  7816. platform.addEventListener(this, type, listener2);
  7817. listeners[type] = listener2;
  7818. };
  7819. const listener = (e, x, y) => {
  7820. e.offsetX = x;
  7821. e.offsetY = y;
  7822. this._eventHandler(e);
  7823. };
  7824. each(this.options.events, (type) => _add(type, listener));
  7825. }
  7826. bindResponsiveEvents() {
  7827. if (!this._responsiveListeners) {
  7828. this._responsiveListeners = {};
  7829. }
  7830. const listeners = this._responsiveListeners;
  7831. const platform = this.platform;
  7832. const _add = (type, listener2) => {
  7833. platform.addEventListener(this, type, listener2);
  7834. listeners[type] = listener2;
  7835. };
  7836. const _remove = (type, listener2) => {
  7837. if (listeners[type]) {
  7838. platform.removeEventListener(this, type, listener2);
  7839. delete listeners[type];
  7840. }
  7841. };
  7842. const listener = (width, height) => {
  7843. if (this.canvas) {
  7844. this.resize(width, height);
  7845. }
  7846. };
  7847. let detached;
  7848. const attached = () => {
  7849. _remove("attach", attached);
  7850. this.attached = true;
  7851. this.resize();
  7852. _add("resize", listener);
  7853. _add("detach", detached);
  7854. };
  7855. detached = () => {
  7856. this.attached = false;
  7857. _remove("resize", listener);
  7858. this._stop();
  7859. this._resize(0, 0);
  7860. _add("attach", attached);
  7861. };
  7862. if (platform.isAttached(this.canvas)) {
  7863. attached();
  7864. } else {
  7865. detached();
  7866. }
  7867. }
  7868. unbindEvents() {
  7869. each(this._listeners, (listener, type) => {
  7870. this.platform.removeEventListener(this, type, listener);
  7871. });
  7872. this._listeners = {};
  7873. each(this._responsiveListeners, (listener, type) => {
  7874. this.platform.removeEventListener(this, type, listener);
  7875. });
  7876. this._responsiveListeners = void 0;
  7877. }
  7878. updateHoverStyle(items, mode, enabled) {
  7879. const prefix = enabled ? "set" : "remove";
  7880. let meta, item, i, ilen;
  7881. if (mode === "dataset") {
  7882. meta = this.getDatasetMeta(items[0].datasetIndex);
  7883. meta.controller["_" + prefix + "DatasetHoverStyle"]();
  7884. }
  7885. for (i = 0, ilen = items.length; i < ilen; ++i) {
  7886. item = items[i];
  7887. const controller = item && this.getDatasetMeta(item.datasetIndex).controller;
  7888. if (controller) {
  7889. controller[prefix + "HoverStyle"](item.element, item.datasetIndex, item.index);
  7890. }
  7891. }
  7892. }
  7893. getActiveElements() {
  7894. return this._active || [];
  7895. }
  7896. setActiveElements(activeElements) {
  7897. const lastActive = this._active || [];
  7898. const active = activeElements.map(({ datasetIndex, index }) => {
  7899. const meta = this.getDatasetMeta(datasetIndex);
  7900. if (!meta) {
  7901. throw new Error("No dataset found at index " + datasetIndex);
  7902. }
  7903. return {
  7904. datasetIndex,
  7905. element: meta.data[index],
  7906. index
  7907. };
  7908. });
  7909. const changed = !_elementsEqual(active, lastActive);
  7910. if (changed) {
  7911. this._active = active;
  7912. this._lastEvent = null;
  7913. this._updateHoverStyles(active, lastActive);
  7914. }
  7915. }
  7916. notifyPlugins(hook, args, filter) {
  7917. return this._plugins.notify(this, hook, args, filter);
  7918. }
  7919. isPluginEnabled(pluginId) {
  7920. return this._plugins._cache.filter((p) => p.plugin.id === pluginId).length === 1;
  7921. }
  7922. _updateHoverStyles(active, lastActive, replay) {
  7923. const hoverOptions = this.options.hover;
  7924. const diff = (a, b) => a.filter((x) => !b.some((y) => x.datasetIndex === y.datasetIndex && x.index === y.index));
  7925. const deactivated = diff(lastActive, active);
  7926. const activated = replay ? active : diff(active, lastActive);
  7927. if (deactivated.length) {
  7928. this.updateHoverStyle(deactivated, hoverOptions.mode, false);
  7929. }
  7930. if (activated.length && hoverOptions.mode) {
  7931. this.updateHoverStyle(activated, hoverOptions.mode, true);
  7932. }
  7933. }
  7934. _eventHandler(e, replay) {
  7935. const args = {
  7936. event: e,
  7937. replay,
  7938. cancelable: true,
  7939. inChartArea: this.isPointInArea(e)
  7940. };
  7941. const eventFilter = (plugin) => (plugin.options.events || this.options.events).includes(e.native.type);
  7942. if (this.notifyPlugins("beforeEvent", args, eventFilter) === false) {
  7943. return;
  7944. }
  7945. const changed = this._handleEvent(e, replay, args.inChartArea);
  7946. args.cancelable = false;
  7947. this.notifyPlugins("afterEvent", args, eventFilter);
  7948. if (changed || args.changed) {
  7949. this.render();
  7950. }
  7951. return this;
  7952. }
  7953. _handleEvent(e, replay, inChartArea) {
  7954. const { _active: lastActive = [], options } = this;
  7955. const useFinalPosition = replay;
  7956. const active = this._getActiveElements(e, lastActive, inChartArea, useFinalPosition);
  7957. const isClick = _isClickEvent(e);
  7958. const lastEvent = determineLastEvent(e, this._lastEvent, inChartArea, isClick);
  7959. if (inChartArea) {
  7960. this._lastEvent = null;
  7961. callback(options.onHover, [
  7962. e,
  7963. active,
  7964. this
  7965. ], this);
  7966. if (isClick) {
  7967. callback(options.onClick, [
  7968. e,
  7969. active,
  7970. this
  7971. ], this);
  7972. }
  7973. }
  7974. const changed = !_elementsEqual(active, lastActive);
  7975. if (changed || replay) {
  7976. this._active = active;
  7977. this._updateHoverStyles(active, lastActive, replay);
  7978. }
  7979. this._lastEvent = lastEvent;
  7980. return changed;
  7981. }
  7982. _getActiveElements(e, lastActive, inChartArea, useFinalPosition) {
  7983. if (e.type === "mouseout") {
  7984. return [];
  7985. }
  7986. if (!inChartArea) {
  7987. return lastActive;
  7988. }
  7989. const hoverOptions = this.options.hover;
  7990. return this.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition);
  7991. }
  7992. }
  7993. __publicField(Chart, "defaults", defaults);
  7994. __publicField(Chart, "instances", instances);
  7995. __publicField(Chart, "overrides", overrides);
  7996. __publicField(Chart, "registry", registry);
  7997. __publicField(Chart, "version", version);
  7998. __publicField(Chart, "getChart", getChart);
  7999. function invalidatePlugins() {
  8000. return each(Chart.instances, (chart) => chart._plugins.invalidate());
  8001. }
  8002. function setStyle(ctx, options, style = options) {
  8003. ctx.lineCap = valueOrDefault(style.borderCapStyle, options.borderCapStyle);
  8004. ctx.setLineDash(valueOrDefault(style.borderDash, options.borderDash));
  8005. ctx.lineDashOffset = valueOrDefault(style.borderDashOffset, options.borderDashOffset);
  8006. ctx.lineJoin = valueOrDefault(style.borderJoinStyle, options.borderJoinStyle);
  8007. ctx.lineWidth = valueOrDefault(style.borderWidth, options.borderWidth);
  8008. ctx.strokeStyle = valueOrDefault(style.borderColor, options.borderColor);
  8009. }
  8010. function lineTo(ctx, previous, target) {
  8011. ctx.lineTo(target.x, target.y);
  8012. }
  8013. function getLineMethod(options) {
  8014. if (options.stepped) {
  8015. return _steppedLineTo;
  8016. }
  8017. if (options.tension || options.cubicInterpolationMode === "monotone") {
  8018. return _bezierCurveTo;
  8019. }
  8020. return lineTo;
  8021. }
  8022. function pathVars(points, segment, params = {}) {
  8023. const count = points.length;
  8024. const { start: paramsStart = 0, end: paramsEnd = count - 1 } = params;
  8025. const { start: segmentStart, end: segmentEnd } = segment;
  8026. const start = Math.max(paramsStart, segmentStart);
  8027. const end = Math.min(paramsEnd, segmentEnd);
  8028. const outside = paramsStart < segmentStart && paramsEnd < segmentStart || paramsStart > segmentEnd && paramsEnd > segmentEnd;
  8029. return {
  8030. count,
  8031. start,
  8032. loop: segment.loop,
  8033. ilen: end < start && !outside ? count + end - start : end - start
  8034. };
  8035. }
  8036. function pathSegment(ctx, line, segment, params) {
  8037. const { points, options } = line;
  8038. const { count, start, loop, ilen } = pathVars(points, segment, params);
  8039. const lineMethod = getLineMethod(options);
  8040. let { move = true, reverse } = params || {};
  8041. let i, point, prev;
  8042. for (i = 0; i <= ilen; ++i) {
  8043. point = points[(start + (reverse ? ilen - i : i)) % count];
  8044. if (point.skip) {
  8045. continue;
  8046. } else if (move) {
  8047. ctx.moveTo(point.x, point.y);
  8048. move = false;
  8049. } else {
  8050. lineMethod(ctx, prev, point, reverse, options.stepped);
  8051. }
  8052. prev = point;
  8053. }
  8054. if (loop) {
  8055. point = points[(start + (reverse ? ilen : 0)) % count];
  8056. lineMethod(ctx, prev, point, reverse, options.stepped);
  8057. }
  8058. return !!loop;
  8059. }
  8060. function fastPathSegment(ctx, line, segment, params) {
  8061. const points = line.points;
  8062. const { count, start, ilen } = pathVars(points, segment, params);
  8063. const { move = true, reverse } = params || {};
  8064. let avgX = 0;
  8065. let countX = 0;
  8066. let i, point, prevX, minY, maxY, lastY;
  8067. const pointIndex = (index) => (start + (reverse ? ilen - index : index)) % count;
  8068. const drawX = () => {
  8069. if (minY !== maxY) {
  8070. ctx.lineTo(avgX, maxY);
  8071. ctx.lineTo(avgX, minY);
  8072. ctx.lineTo(avgX, lastY);
  8073. }
  8074. };
  8075. if (move) {
  8076. point = points[pointIndex(0)];
  8077. ctx.moveTo(point.x, point.y);
  8078. }
  8079. for (i = 0; i <= ilen; ++i) {
  8080. point = points[pointIndex(i)];
  8081. if (point.skip) {
  8082. continue;
  8083. }
  8084. const x = point.x;
  8085. const y = point.y;
  8086. const truncX = x | 0;
  8087. if (truncX === prevX) {
  8088. if (y < minY) {
  8089. minY = y;
  8090. } else if (y > maxY) {
  8091. maxY = y;
  8092. }
  8093. avgX = (countX * avgX + x) / ++countX;
  8094. } else {
  8095. drawX();
  8096. ctx.lineTo(x, y);
  8097. prevX = truncX;
  8098. countX = 0;
  8099. minY = maxY = y;
  8100. }
  8101. lastY = y;
  8102. }
  8103. drawX();
  8104. }
  8105. function _getSegmentMethod(line) {
  8106. const opts = line.options;
  8107. const borderDash = opts.borderDash && opts.borderDash.length;
  8108. const useFastPath = !line._decimated && !line._loop && !opts.tension && opts.cubicInterpolationMode !== "monotone" && !opts.stepped && !borderDash;
  8109. return useFastPath ? fastPathSegment : pathSegment;
  8110. }
  8111. function _getInterpolationMethod(options) {
  8112. if (options.stepped) {
  8113. return _steppedInterpolation;
  8114. }
  8115. if (options.tension || options.cubicInterpolationMode === "monotone") {
  8116. return _bezierInterpolation;
  8117. }
  8118. return _pointInLine;
  8119. }
  8120. function strokePathWithCache(ctx, line, start, count) {
  8121. let path = line._path;
  8122. if (!path) {
  8123. path = line._path = new Path2D();
  8124. if (line.path(path, start, count)) {
  8125. path.closePath();
  8126. }
  8127. }
  8128. setStyle(ctx, line.options);
  8129. ctx.stroke(path);
  8130. }
  8131. function strokePathDirect(ctx, line, start, count) {
  8132. const { segments, options } = line;
  8133. const segmentMethod = _getSegmentMethod(line);
  8134. for (const segment of segments) {
  8135. setStyle(ctx, options, segment.style);
  8136. ctx.beginPath();
  8137. if (segmentMethod(ctx, line, segment, {
  8138. start,
  8139. end: start + count - 1
  8140. })) {
  8141. ctx.closePath();
  8142. }
  8143. ctx.stroke();
  8144. }
  8145. }
  8146. const usePath2D = typeof Path2D === "function";
  8147. function draw(ctx, line, start, count) {
  8148. if (usePath2D && !line.options.segment) {
  8149. strokePathWithCache(ctx, line, start, count);
  8150. } else {
  8151. strokePathDirect(ctx, line, start, count);
  8152. }
  8153. }
  8154. class LineElement extends Element {
  8155. constructor(cfg) {
  8156. super();
  8157. this.animated = true;
  8158. this.options = void 0;
  8159. this._chart = void 0;
  8160. this._loop = void 0;
  8161. this._fullLoop = void 0;
  8162. this._path = void 0;
  8163. this._points = void 0;
  8164. this._segments = void 0;
  8165. this._decimated = false;
  8166. this._pointsUpdated = false;
  8167. this._datasetIndex = void 0;
  8168. if (cfg) {
  8169. Object.assign(this, cfg);
  8170. }
  8171. }
  8172. updateControlPoints(chartArea, indexAxis) {
  8173. const options = this.options;
  8174. if ((options.tension || options.cubicInterpolationMode === "monotone") && !options.stepped && !this._pointsUpdated) {
  8175. const loop = options.spanGaps ? this._loop : this._fullLoop;
  8176. _updateBezierControlPoints(this._points, options, chartArea, loop, indexAxis);
  8177. this._pointsUpdated = true;
  8178. }
  8179. }
  8180. set points(points) {
  8181. this._points = points;
  8182. delete this._segments;
  8183. delete this._path;
  8184. this._pointsUpdated = false;
  8185. }
  8186. get points() {
  8187. return this._points;
  8188. }
  8189. get segments() {
  8190. return this._segments || (this._segments = _computeSegments(this, this.options.segment));
  8191. }
  8192. first() {
  8193. const segments = this.segments;
  8194. const points = this.points;
  8195. return segments.length && points[segments[0].start];
  8196. }
  8197. last() {
  8198. const segments = this.segments;
  8199. const points = this.points;
  8200. const count = segments.length;
  8201. return count && points[segments[count - 1].end];
  8202. }
  8203. interpolate(point, property) {
  8204. const options = this.options;
  8205. const value = point[property];
  8206. const points = this.points;
  8207. const segments = _boundSegments(this, {
  8208. property,
  8209. start: value,
  8210. end: value
  8211. });
  8212. if (!segments.length) {
  8213. return;
  8214. }
  8215. const result = [];
  8216. const _interpolate = _getInterpolationMethod(options);
  8217. let i, ilen;
  8218. for (i = 0, ilen = segments.length; i < ilen; ++i) {
  8219. const { start, end } = segments[i];
  8220. const p1 = points[start];
  8221. const p2 = points[end];
  8222. if (p1 === p2) {
  8223. result.push(p1);
  8224. continue;
  8225. }
  8226. const t = Math.abs((value - p1[property]) / (p2[property] - p1[property]));
  8227. const interpolated = _interpolate(p1, p2, t, options.stepped);
  8228. interpolated[property] = point[property];
  8229. result.push(interpolated);
  8230. }
  8231. return result.length === 1 ? result[0] : result;
  8232. }
  8233. pathSegment(ctx, segment, params) {
  8234. const segmentMethod = _getSegmentMethod(this);
  8235. return segmentMethod(ctx, this, segment, params);
  8236. }
  8237. path(ctx, start, count) {
  8238. const segments = this.segments;
  8239. const segmentMethod = _getSegmentMethod(this);
  8240. let loop = this._loop;
  8241. start = start || 0;
  8242. count = count || this.points.length - start;
  8243. for (const segment of segments) {
  8244. loop &= segmentMethod(ctx, this, segment, {
  8245. start,
  8246. end: start + count - 1
  8247. });
  8248. }
  8249. return !!loop;
  8250. }
  8251. draw(ctx, chartArea, start, count) {
  8252. const options = this.options || {};
  8253. const points = this.points || [];
  8254. if (points.length && options.borderWidth) {
  8255. ctx.save();
  8256. draw(ctx, this, start, count);
  8257. ctx.restore();
  8258. }
  8259. if (this.animated) {
  8260. this._pointsUpdated = false;
  8261. this._path = void 0;
  8262. }
  8263. }
  8264. }
  8265. __publicField(LineElement, "id", "line");
  8266. __publicField(LineElement, "defaults", {
  8267. borderCapStyle: "butt",
  8268. borderDash: [],
  8269. borderDashOffset: 0,
  8270. borderJoinStyle: "miter",
  8271. borderWidth: 3,
  8272. capBezierPoints: true,
  8273. cubicInterpolationMode: "default",
  8274. fill: false,
  8275. spanGaps: false,
  8276. stepped: false,
  8277. tension: 0
  8278. });
  8279. __publicField(LineElement, "defaultRoutes", {
  8280. backgroundColor: "backgroundColor",
  8281. borderColor: "borderColor"
  8282. });
  8283. __publicField(LineElement, "descriptors", {
  8284. _scriptable: true,
  8285. _indexable: (name) => name !== "borderDash" && name !== "fill"
  8286. });
  8287. function inRange$1(el, pos, axis, useFinalPosition) {
  8288. const options = el.options;
  8289. const { [axis]: value } = el.getProps([
  8290. axis
  8291. ], useFinalPosition);
  8292. return Math.abs(pos - value) < options.radius + options.hitRadius;
  8293. }
  8294. class PointElement extends Element {
  8295. constructor(cfg) {
  8296. super();
  8297. __publicField(this, "parsed");
  8298. __publicField(this, "skip");
  8299. __publicField(this, "stop");
  8300. this.options = void 0;
  8301. this.parsed = void 0;
  8302. this.skip = void 0;
  8303. this.stop = void 0;
  8304. if (cfg) {
  8305. Object.assign(this, cfg);
  8306. }
  8307. }
  8308. inRange(mouseX, mouseY, useFinalPosition) {
  8309. const options = this.options;
  8310. const { x, y } = this.getProps([
  8311. "x",
  8312. "y"
  8313. ], useFinalPosition);
  8314. return Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2) < Math.pow(options.hitRadius + options.radius, 2);
  8315. }
  8316. inXRange(mouseX, useFinalPosition) {
  8317. return inRange$1(this, mouseX, "x", useFinalPosition);
  8318. }
  8319. inYRange(mouseY, useFinalPosition) {
  8320. return inRange$1(this, mouseY, "y", useFinalPosition);
  8321. }
  8322. getCenterPoint(useFinalPosition) {
  8323. const { x, y } = this.getProps([
  8324. "x",
  8325. "y"
  8326. ], useFinalPosition);
  8327. return {
  8328. x,
  8329. y
  8330. };
  8331. }
  8332. size(options) {
  8333. options = options || this.options || {};
  8334. let radius = options.radius || 0;
  8335. radius = Math.max(radius, radius && options.hoverRadius || 0);
  8336. const borderWidth = radius && options.borderWidth || 0;
  8337. return (radius + borderWidth) * 2;
  8338. }
  8339. draw(ctx, area) {
  8340. const options = this.options;
  8341. if (this.skip || options.radius < 0.1 || !_isPointInArea(this, area, this.size(options) / 2)) {
  8342. return;
  8343. }
  8344. ctx.strokeStyle = options.borderColor;
  8345. ctx.lineWidth = options.borderWidth;
  8346. ctx.fillStyle = options.backgroundColor;
  8347. drawPoint(ctx, options, this.x, this.y);
  8348. }
  8349. getRange() {
  8350. const options = this.options || {};
  8351. return options.radius + options.hitRadius;
  8352. }
  8353. }
  8354. __publicField(PointElement, "id", "point");
  8355. /**
  8356. * @type {any}
  8357. */
  8358. __publicField(PointElement, "defaults", {
  8359. borderWidth: 1,
  8360. hitRadius: 1,
  8361. hoverBorderWidth: 1,
  8362. hoverRadius: 4,
  8363. pointStyle: "circle",
  8364. radius: 3,
  8365. rotation: 0
  8366. });
  8367. /**
  8368. * @type {any}
  8369. */
  8370. __publicField(PointElement, "defaultRoutes", {
  8371. backgroundColor: "backgroundColor",
  8372. borderColor: "borderColor"
  8373. });
  8374. const addIfString = (labels, raw, index, addedLabels) => {
  8375. if (typeof raw === "string") {
  8376. index = labels.push(raw) - 1;
  8377. addedLabels.unshift({
  8378. index,
  8379. label: raw
  8380. });
  8381. } else if (isNaN(raw)) {
  8382. index = null;
  8383. }
  8384. return index;
  8385. };
  8386. function findOrAddLabel(labels, raw, index, addedLabels) {
  8387. const first = labels.indexOf(raw);
  8388. if (first === -1) {
  8389. return addIfString(labels, raw, index, addedLabels);
  8390. }
  8391. const last = labels.lastIndexOf(raw);
  8392. return first !== last ? index : first;
  8393. }
  8394. const validIndex = (index, max) => index === null ? null : _limitValue(Math.round(index), 0, max);
  8395. function _getLabelForValue(value) {
  8396. const labels = this.getLabels();
  8397. if (value >= 0 && value < labels.length) {
  8398. return labels[value];
  8399. }
  8400. return value;
  8401. }
  8402. class CategoryScale extends Scale {
  8403. constructor(cfg) {
  8404. super(cfg);
  8405. this._startValue = void 0;
  8406. this._valueRange = 0;
  8407. this._addedLabels = [];
  8408. }
  8409. init(scaleOptions) {
  8410. const added = this._addedLabels;
  8411. if (added.length) {
  8412. const labels = this.getLabels();
  8413. for (const { index, label } of added) {
  8414. if (labels[index] === label) {
  8415. labels.splice(index, 1);
  8416. }
  8417. }
  8418. this._addedLabels = [];
  8419. }
  8420. super.init(scaleOptions);
  8421. }
  8422. parse(raw, index) {
  8423. if (isNullOrUndef(raw)) {
  8424. return null;
  8425. }
  8426. const labels = this.getLabels();
  8427. index = isFinite(index) && labels[index] === raw ? index : findOrAddLabel(labels, raw, valueOrDefault(index, raw), this._addedLabels);
  8428. return validIndex(index, labels.length - 1);
  8429. }
  8430. determineDataLimits() {
  8431. const { minDefined, maxDefined } = this.getUserBounds();
  8432. let { min, max } = this.getMinMax(true);
  8433. if (this.options.bounds === "ticks") {
  8434. if (!minDefined) {
  8435. min = 0;
  8436. }
  8437. if (!maxDefined) {
  8438. max = this.getLabels().length - 1;
  8439. }
  8440. }
  8441. this.min = min;
  8442. this.max = max;
  8443. }
  8444. buildTicks() {
  8445. const min = this.min;
  8446. const max = this.max;
  8447. const offset = this.options.offset;
  8448. const ticks = [];
  8449. let labels = this.getLabels();
  8450. labels = min === 0 && max === labels.length - 1 ? labels : labels.slice(min, max + 1);
  8451. this._valueRange = Math.max(labels.length - (offset ? 0 : 1), 1);
  8452. this._startValue = this.min - (offset ? 0.5 : 0);
  8453. for (let value = min; value <= max; value++) {
  8454. ticks.push({
  8455. value
  8456. });
  8457. }
  8458. return ticks;
  8459. }
  8460. getLabelForValue(value) {
  8461. return _getLabelForValue.call(this, value);
  8462. }
  8463. configure() {
  8464. super.configure();
  8465. if (!this.isHorizontal()) {
  8466. this._reversePixels = !this._reversePixels;
  8467. }
  8468. }
  8469. getPixelForValue(value) {
  8470. if (typeof value !== "number") {
  8471. value = this.parse(value);
  8472. }
  8473. return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);
  8474. }
  8475. getPixelForTick(index) {
  8476. const ticks = this.ticks;
  8477. if (index < 0 || index > ticks.length - 1) {
  8478. return null;
  8479. }
  8480. return this.getPixelForValue(ticks[index].value);
  8481. }
  8482. getValueForPixel(pixel) {
  8483. return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange);
  8484. }
  8485. getBasePixel() {
  8486. return this.bottom;
  8487. }
  8488. }
  8489. __publicField(CategoryScale, "id", "category");
  8490. __publicField(CategoryScale, "defaults", {
  8491. ticks: {
  8492. callback: _getLabelForValue
  8493. }
  8494. });
  8495. function generateTicks$1(generationOptions, dataRange) {
  8496. const ticks = [];
  8497. const MIN_SPACING = 1e-14;
  8498. const { bounds, step, min, max, precision, count, maxTicks, maxDigits, includeBounds } = generationOptions;
  8499. const unit = step || 1;
  8500. const maxSpaces = maxTicks - 1;
  8501. const { min: rmin, max: rmax } = dataRange;
  8502. const minDefined = !isNullOrUndef(min);
  8503. const maxDefined = !isNullOrUndef(max);
  8504. const countDefined = !isNullOrUndef(count);
  8505. const minSpacing = (rmax - rmin) / (maxDigits + 1);
  8506. let spacing = niceNum((rmax - rmin) / maxSpaces / unit) * unit;
  8507. let factor, niceMin, niceMax, numSpaces;
  8508. if (spacing < MIN_SPACING && !minDefined && !maxDefined) {
  8509. return [
  8510. {
  8511. value: rmin
  8512. },
  8513. {
  8514. value: rmax
  8515. }
  8516. ];
  8517. }
  8518. numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);
  8519. if (numSpaces > maxSpaces) {
  8520. spacing = niceNum(numSpaces * spacing / maxSpaces / unit) * unit;
  8521. }
  8522. if (!isNullOrUndef(precision)) {
  8523. factor = Math.pow(10, precision);
  8524. spacing = Math.ceil(spacing * factor) / factor;
  8525. }
  8526. if (bounds === "ticks") {
  8527. niceMin = Math.floor(rmin / spacing) * spacing;
  8528. niceMax = Math.ceil(rmax / spacing) * spacing;
  8529. } else {
  8530. niceMin = rmin;
  8531. niceMax = rmax;
  8532. }
  8533. if (minDefined && maxDefined && step && almostWhole((max - min) / step, spacing / 1e3)) {
  8534. numSpaces = Math.round(Math.min((max - min) / spacing, maxTicks));
  8535. spacing = (max - min) / numSpaces;
  8536. niceMin = min;
  8537. niceMax = max;
  8538. } else if (countDefined) {
  8539. niceMin = minDefined ? min : niceMin;
  8540. niceMax = maxDefined ? max : niceMax;
  8541. numSpaces = count - 1;
  8542. spacing = (niceMax - niceMin) / numSpaces;
  8543. } else {
  8544. numSpaces = (niceMax - niceMin) / spacing;
  8545. if (almostEquals(numSpaces, Math.round(numSpaces), spacing / 1e3)) {
  8546. numSpaces = Math.round(numSpaces);
  8547. } else {
  8548. numSpaces = Math.ceil(numSpaces);
  8549. }
  8550. }
  8551. const decimalPlaces = Math.max(_decimalPlaces(spacing), _decimalPlaces(niceMin));
  8552. factor = Math.pow(10, isNullOrUndef(precision) ? decimalPlaces : precision);
  8553. niceMin = Math.round(niceMin * factor) / factor;
  8554. niceMax = Math.round(niceMax * factor) / factor;
  8555. let j = 0;
  8556. if (minDefined) {
  8557. if (includeBounds && niceMin !== min) {
  8558. ticks.push({
  8559. value: min
  8560. });
  8561. if (niceMin < min) {
  8562. j++;
  8563. }
  8564. if (almostEquals(Math.round((niceMin + j * spacing) * factor) / factor, min, relativeLabelSize(min, minSpacing, generationOptions))) {
  8565. j++;
  8566. }
  8567. } else if (niceMin < min) {
  8568. j++;
  8569. }
  8570. }
  8571. for (; j < numSpaces; ++j) {
  8572. const tickValue = Math.round((niceMin + j * spacing) * factor) / factor;
  8573. if (maxDefined && tickValue > max) {
  8574. break;
  8575. }
  8576. ticks.push({
  8577. value: tickValue
  8578. });
  8579. }
  8580. if (maxDefined && includeBounds && niceMax !== max) {
  8581. if (ticks.length && almostEquals(ticks[ticks.length - 1].value, max, relativeLabelSize(max, minSpacing, generationOptions))) {
  8582. ticks[ticks.length - 1].value = max;
  8583. } else {
  8584. ticks.push({
  8585. value: max
  8586. });
  8587. }
  8588. } else if (!maxDefined || niceMax === max) {
  8589. ticks.push({
  8590. value: niceMax
  8591. });
  8592. }
  8593. return ticks;
  8594. }
  8595. function relativeLabelSize(value, minSpacing, { horizontal, minRotation }) {
  8596. const rad = toRadians(minRotation);
  8597. const ratio = (horizontal ? Math.sin(rad) : Math.cos(rad)) || 1e-3;
  8598. const length = 0.75 * minSpacing * ("" + value).length;
  8599. return Math.min(minSpacing / ratio, length);
  8600. }
  8601. class LinearScaleBase extends Scale {
  8602. constructor(cfg) {
  8603. super(cfg);
  8604. this.start = void 0;
  8605. this.end = void 0;
  8606. this._startValue = void 0;
  8607. this._endValue = void 0;
  8608. this._valueRange = 0;
  8609. }
  8610. parse(raw, index) {
  8611. if (isNullOrUndef(raw)) {
  8612. return null;
  8613. }
  8614. if ((typeof raw === "number" || raw instanceof Number) && !isFinite(+raw)) {
  8615. return null;
  8616. }
  8617. return +raw;
  8618. }
  8619. handleTickRangeOptions() {
  8620. const { beginAtZero } = this.options;
  8621. const { minDefined, maxDefined } = this.getUserBounds();
  8622. let { min, max } = this;
  8623. const setMin = (v) => min = minDefined ? min : v;
  8624. const setMax = (v) => max = maxDefined ? max : v;
  8625. if (beginAtZero) {
  8626. const minSign = sign(min);
  8627. const maxSign = sign(max);
  8628. if (minSign < 0 && maxSign < 0) {
  8629. setMax(0);
  8630. } else if (minSign > 0 && maxSign > 0) {
  8631. setMin(0);
  8632. }
  8633. }
  8634. if (min === max) {
  8635. let offset = max === 0 ? 1 : Math.abs(max * 0.05);
  8636. setMax(max + offset);
  8637. if (!beginAtZero) {
  8638. setMin(min - offset);
  8639. }
  8640. }
  8641. this.min = min;
  8642. this.max = max;
  8643. }
  8644. getTickLimit() {
  8645. const tickOpts = this.options.ticks;
  8646. let { maxTicksLimit, stepSize } = tickOpts;
  8647. let maxTicks;
  8648. if (stepSize) {
  8649. maxTicks = Math.ceil(this.max / stepSize) - Math.floor(this.min / stepSize) + 1;
  8650. if (maxTicks > 1e3) {
  8651. console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`);
  8652. maxTicks = 1e3;
  8653. }
  8654. } else {
  8655. maxTicks = this.computeTickLimit();
  8656. maxTicksLimit = maxTicksLimit || 11;
  8657. }
  8658. if (maxTicksLimit) {
  8659. maxTicks = Math.min(maxTicksLimit, maxTicks);
  8660. }
  8661. return maxTicks;
  8662. }
  8663. computeTickLimit() {
  8664. return Number.POSITIVE_INFINITY;
  8665. }
  8666. buildTicks() {
  8667. const opts = this.options;
  8668. const tickOpts = opts.ticks;
  8669. let maxTicks = this.getTickLimit();
  8670. maxTicks = Math.max(2, maxTicks);
  8671. const numericGeneratorOptions = {
  8672. maxTicks,
  8673. bounds: opts.bounds,
  8674. min: opts.min,
  8675. max: opts.max,
  8676. precision: tickOpts.precision,
  8677. step: tickOpts.stepSize,
  8678. count: tickOpts.count,
  8679. maxDigits: this._maxDigits(),
  8680. horizontal: this.isHorizontal(),
  8681. minRotation: tickOpts.minRotation || 0,
  8682. includeBounds: tickOpts.includeBounds !== false
  8683. };
  8684. const dataRange = this._range || this;
  8685. const ticks = generateTicks$1(numericGeneratorOptions, dataRange);
  8686. if (opts.bounds === "ticks") {
  8687. _setMinAndMaxByKey(ticks, this, "value");
  8688. }
  8689. if (opts.reverse) {
  8690. ticks.reverse();
  8691. this.start = this.max;
  8692. this.end = this.min;
  8693. } else {
  8694. this.start = this.min;
  8695. this.end = this.max;
  8696. }
  8697. return ticks;
  8698. }
  8699. configure() {
  8700. const ticks = this.ticks;
  8701. let start = this.min;
  8702. let end = this.max;
  8703. super.configure();
  8704. if (this.options.offset && ticks.length) {
  8705. const offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;
  8706. start -= offset;
  8707. end += offset;
  8708. }
  8709. this._startValue = start;
  8710. this._endValue = end;
  8711. this._valueRange = end - start;
  8712. }
  8713. getLabelForValue(value) {
  8714. return formatNumber(value, this.chart.options.locale, this.options.ticks.format);
  8715. }
  8716. }
  8717. class LinearScale extends LinearScaleBase {
  8718. determineDataLimits() {
  8719. const { min, max } = this.getMinMax(true);
  8720. this.min = isNumberFinite(min) ? min : 0;
  8721. this.max = isNumberFinite(max) ? max : 1;
  8722. this.handleTickRangeOptions();
  8723. }
  8724. computeTickLimit() {
  8725. const horizontal = this.isHorizontal();
  8726. const length = horizontal ? this.width : this.height;
  8727. const minRotation = toRadians(this.options.ticks.minRotation);
  8728. const ratio = (horizontal ? Math.sin(minRotation) : Math.cos(minRotation)) || 1e-3;
  8729. const tickFont = this._resolveTickFontOptions(0);
  8730. return Math.ceil(length / Math.min(40, tickFont.lineHeight / ratio));
  8731. }
  8732. getPixelForValue(value) {
  8733. return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);
  8734. }
  8735. getValueForPixel(pixel) {
  8736. return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange;
  8737. }
  8738. }
  8739. __publicField(LinearScale, "id", "linear");
  8740. __publicField(LinearScale, "defaults", {
  8741. ticks: {
  8742. callback: Ticks.formatters.numeric
  8743. }
  8744. });
  8745. const INTERVALS = {
  8746. millisecond: {
  8747. common: true,
  8748. size: 1,
  8749. steps: 1e3
  8750. },
  8751. second: {
  8752. common: true,
  8753. size: 1e3,
  8754. steps: 60
  8755. },
  8756. minute: {
  8757. common: true,
  8758. size: 6e4,
  8759. steps: 60
  8760. },
  8761. hour: {
  8762. common: true,
  8763. size: 36e5,
  8764. steps: 24
  8765. },
  8766. day: {
  8767. common: true,
  8768. size: 864e5,
  8769. steps: 30
  8770. },
  8771. week: {
  8772. common: false,
  8773. size: 6048e5,
  8774. steps: 4
  8775. },
  8776. month: {
  8777. common: true,
  8778. size: 2628e6,
  8779. steps: 12
  8780. },
  8781. quarter: {
  8782. common: false,
  8783. size: 7884e6,
  8784. steps: 4
  8785. },
  8786. year: {
  8787. common: true,
  8788. size: 3154e7
  8789. }
  8790. };
  8791. const UNITS = /* @__PURE__ */ Object.keys(INTERVALS);
  8792. function sorter(a, b) {
  8793. return a - b;
  8794. }
  8795. function parse(scale, input) {
  8796. if (isNullOrUndef(input)) {
  8797. return null;
  8798. }
  8799. const adapter = scale._adapter;
  8800. const { parser, round: round2, isoWeekday } = scale._parseOpts;
  8801. let value = input;
  8802. if (typeof parser === "function") {
  8803. value = parser(value);
  8804. }
  8805. if (!isNumberFinite(value)) {
  8806. value = typeof parser === "string" ? adapter.parse(value, parser) : adapter.parse(value);
  8807. }
  8808. if (value === null) {
  8809. return null;
  8810. }
  8811. if (round2) {
  8812. value = round2 === "week" && (isNumber(isoWeekday) || isoWeekday === true) ? adapter.startOf(value, "isoWeek", isoWeekday) : adapter.startOf(value, round2);
  8813. }
  8814. return +value;
  8815. }
  8816. function determineUnitForAutoTicks(minUnit, min, max, capacity) {
  8817. const ilen = UNITS.length;
  8818. for (let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
  8819. const interval = INTERVALS[UNITS[i]];
  8820. const factor = interval.steps ? interval.steps : Number.MAX_SAFE_INTEGER;
  8821. if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
  8822. return UNITS[i];
  8823. }
  8824. }
  8825. return UNITS[ilen - 1];
  8826. }
  8827. function determineUnitForFormatting(scale, numTicks, minUnit, min, max) {
  8828. for (let i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) {
  8829. const unit = UNITS[i];
  8830. if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) {
  8831. return unit;
  8832. }
  8833. }
  8834. return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];
  8835. }
  8836. function determineMajorUnit(unit) {
  8837. for (let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
  8838. if (INTERVALS[UNITS[i]].common) {
  8839. return UNITS[i];
  8840. }
  8841. }
  8842. }
  8843. function addTick(ticks, time, timestamps) {
  8844. if (!timestamps) {
  8845. ticks[time] = true;
  8846. } else if (timestamps.length) {
  8847. const { lo, hi } = _lookup(timestamps, time);
  8848. const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi];
  8849. ticks[timestamp] = true;
  8850. }
  8851. }
  8852. function setMajorTicks(scale, ticks, map2, majorUnit) {
  8853. const adapter = scale._adapter;
  8854. const first = +adapter.startOf(ticks[0].value, majorUnit);
  8855. const last = ticks[ticks.length - 1].value;
  8856. let major, index;
  8857. for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) {
  8858. index = map2[major];
  8859. if (index >= 0) {
  8860. ticks[index].major = true;
  8861. }
  8862. }
  8863. return ticks;
  8864. }
  8865. function ticksFromTimestamps(scale, values, majorUnit) {
  8866. const ticks = [];
  8867. const map2 = {};
  8868. const ilen = values.length;
  8869. let i, value;
  8870. for (i = 0; i < ilen; ++i) {
  8871. value = values[i];
  8872. map2[value] = i;
  8873. ticks.push({
  8874. value,
  8875. major: false
  8876. });
  8877. }
  8878. return ilen === 0 || !majorUnit ? ticks : setMajorTicks(scale, ticks, map2, majorUnit);
  8879. }
  8880. class TimeScale extends Scale {
  8881. constructor(props) {
  8882. super(props);
  8883. this._cache = {
  8884. data: [],
  8885. labels: [],
  8886. all: []
  8887. };
  8888. this._unit = "day";
  8889. this._majorUnit = void 0;
  8890. this._offsets = {};
  8891. this._normalized = false;
  8892. this._parseOpts = void 0;
  8893. }
  8894. init(scaleOpts, opts = {}) {
  8895. const time = scaleOpts.time || (scaleOpts.time = {});
  8896. const adapter = this._adapter = new adapters._date(scaleOpts.adapters.date);
  8897. adapter.init(opts);
  8898. mergeIf(time.displayFormats, adapter.formats());
  8899. this._parseOpts = {
  8900. parser: time.parser,
  8901. round: time.round,
  8902. isoWeekday: time.isoWeekday
  8903. };
  8904. super.init(scaleOpts);
  8905. this._normalized = opts.normalized;
  8906. }
  8907. parse(raw, index) {
  8908. if (raw === void 0) {
  8909. return null;
  8910. }
  8911. return parse(this, raw);
  8912. }
  8913. beforeLayout() {
  8914. super.beforeLayout();
  8915. this._cache = {
  8916. data: [],
  8917. labels: [],
  8918. all: []
  8919. };
  8920. }
  8921. determineDataLimits() {
  8922. const options = this.options;
  8923. const adapter = this._adapter;
  8924. const unit = options.time.unit || "day";
  8925. let { min, max, minDefined, maxDefined } = this.getUserBounds();
  8926. function _applyBounds(bounds) {
  8927. if (!minDefined && !isNaN(bounds.min)) {
  8928. min = Math.min(min, bounds.min);
  8929. }
  8930. if (!maxDefined && !isNaN(bounds.max)) {
  8931. max = Math.max(max, bounds.max);
  8932. }
  8933. }
  8934. if (!minDefined || !maxDefined) {
  8935. _applyBounds(this._getLabelBounds());
  8936. if (options.bounds !== "ticks" || options.ticks.source !== "labels") {
  8937. _applyBounds(this.getMinMax(false));
  8938. }
  8939. }
  8940. min = isNumberFinite(min) && !isNaN(min) ? min : +adapter.startOf(Date.now(), unit);
  8941. max = isNumberFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit) + 1;
  8942. this.min = Math.min(min, max - 1);
  8943. this.max = Math.max(min + 1, max);
  8944. }
  8945. _getLabelBounds() {
  8946. const arr = this.getLabelTimestamps();
  8947. let min = Number.POSITIVE_INFINITY;
  8948. let max = Number.NEGATIVE_INFINITY;
  8949. if (arr.length) {
  8950. min = arr[0];
  8951. max = arr[arr.length - 1];
  8952. }
  8953. return {
  8954. min,
  8955. max
  8956. };
  8957. }
  8958. buildTicks() {
  8959. const options = this.options;
  8960. const timeOpts = options.time;
  8961. const tickOpts = options.ticks;
  8962. const timestamps = tickOpts.source === "labels" ? this.getLabelTimestamps() : this._generate();
  8963. if (options.bounds === "ticks" && timestamps.length) {
  8964. this.min = this._userMin || timestamps[0];
  8965. this.max = this._userMax || timestamps[timestamps.length - 1];
  8966. }
  8967. const min = this.min;
  8968. const max = this.max;
  8969. const ticks = _filterBetween(timestamps, min, max);
  8970. this._unit = timeOpts.unit || (tickOpts.autoSkip ? determineUnitForAutoTicks(timeOpts.minUnit, this.min, this.max, this._getLabelCapacity(min)) : determineUnitForFormatting(this, ticks.length, timeOpts.minUnit, this.min, this.max));
  8971. this._majorUnit = !tickOpts.major.enabled || this._unit === "year" ? void 0 : determineMajorUnit(this._unit);
  8972. this.initOffsets(timestamps);
  8973. if (options.reverse) {
  8974. ticks.reverse();
  8975. }
  8976. return ticksFromTimestamps(this, ticks, this._majorUnit);
  8977. }
  8978. afterAutoSkip() {
  8979. if (this.options.offsetAfterAutoskip) {
  8980. this.initOffsets(this.ticks.map((tick) => +tick.value));
  8981. }
  8982. }
  8983. initOffsets(timestamps = []) {
  8984. let start = 0;
  8985. let end = 0;
  8986. let first, last;
  8987. if (this.options.offset && timestamps.length) {
  8988. first = this.getDecimalForValue(timestamps[0]);
  8989. if (timestamps.length === 1) {
  8990. start = 1 - first;
  8991. } else {
  8992. start = (this.getDecimalForValue(timestamps[1]) - first) / 2;
  8993. }
  8994. last = this.getDecimalForValue(timestamps[timestamps.length - 1]);
  8995. if (timestamps.length === 1) {
  8996. end = last;
  8997. } else {
  8998. end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2;
  8999. }
  9000. }
  9001. const limit = timestamps.length < 3 ? 0.5 : 0.25;
  9002. start = _limitValue(start, 0, limit);
  9003. end = _limitValue(end, 0, limit);
  9004. this._offsets = {
  9005. start,
  9006. end,
  9007. factor: 1 / (start + 1 + end)
  9008. };
  9009. }
  9010. _generate() {
  9011. const adapter = this._adapter;
  9012. const min = this.min;
  9013. const max = this.max;
  9014. const options = this.options;
  9015. const timeOpts = options.time;
  9016. const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min));
  9017. const stepSize = valueOrDefault(options.ticks.stepSize, 1);
  9018. const weekday = minor === "week" ? timeOpts.isoWeekday : false;
  9019. const hasWeekday = isNumber(weekday) || weekday === true;
  9020. const ticks = {};
  9021. let first = min;
  9022. let time, count;
  9023. if (hasWeekday) {
  9024. first = +adapter.startOf(first, "isoWeek", weekday);
  9025. }
  9026. first = +adapter.startOf(first, hasWeekday ? "day" : minor);
  9027. if (adapter.diff(max, min, minor) > 1e5 * stepSize) {
  9028. throw new Error(min + " and " + max + " are too far apart with stepSize of " + stepSize + " " + minor);
  9029. }
  9030. const timestamps = options.ticks.source === "data" && this.getDataTimestamps();
  9031. for (time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++) {
  9032. addTick(ticks, time, timestamps);
  9033. }
  9034. if (time === max || options.bounds === "ticks" || count === 1) {
  9035. addTick(ticks, time, timestamps);
  9036. }
  9037. return Object.keys(ticks).sort(sorter).map((x) => +x);
  9038. }
  9039. getLabelForValue(value) {
  9040. const adapter = this._adapter;
  9041. const timeOpts = this.options.time;
  9042. if (timeOpts.tooltipFormat) {
  9043. return adapter.format(value, timeOpts.tooltipFormat);
  9044. }
  9045. return adapter.format(value, timeOpts.displayFormats.datetime);
  9046. }
  9047. format(value, format) {
  9048. const options = this.options;
  9049. const formats = options.time.displayFormats;
  9050. const unit = this._unit;
  9051. const fmt = format || formats[unit];
  9052. return this._adapter.format(value, fmt);
  9053. }
  9054. _tickFormatFunction(time, index, ticks, format) {
  9055. const options = this.options;
  9056. const formatter = options.ticks.callback;
  9057. if (formatter) {
  9058. return callback(formatter, [
  9059. time,
  9060. index,
  9061. ticks
  9062. ], this);
  9063. }
  9064. const formats = options.time.displayFormats;
  9065. const unit = this._unit;
  9066. const majorUnit = this._majorUnit;
  9067. const minorFormat = unit && formats[unit];
  9068. const majorFormat = majorUnit && formats[majorUnit];
  9069. const tick = ticks[index];
  9070. const major = majorUnit && majorFormat && tick && tick.major;
  9071. return this._adapter.format(time, format || (major ? majorFormat : minorFormat));
  9072. }
  9073. generateTickLabels(ticks) {
  9074. let i, ilen, tick;
  9075. for (i = 0, ilen = ticks.length; i < ilen; ++i) {
  9076. tick = ticks[i];
  9077. tick.label = this._tickFormatFunction(tick.value, i, ticks);
  9078. }
  9079. }
  9080. getDecimalForValue(value) {
  9081. return value === null ? NaN : (value - this.min) / (this.max - this.min);
  9082. }
  9083. getPixelForValue(value) {
  9084. const offsets = this._offsets;
  9085. const pos = this.getDecimalForValue(value);
  9086. return this.getPixelForDecimal((offsets.start + pos) * offsets.factor);
  9087. }
  9088. getValueForPixel(pixel) {
  9089. const offsets = this._offsets;
  9090. const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
  9091. return this.min + pos * (this.max - this.min);
  9092. }
  9093. _getLabelSize(label) {
  9094. const ticksOpts = this.options.ticks;
  9095. const tickLabelWidth = this.ctx.measureText(label).width;
  9096. const angle = toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);
  9097. const cosRotation = Math.cos(angle);
  9098. const sinRotation = Math.sin(angle);
  9099. const tickFontSize = this._resolveTickFontOptions(0).size;
  9100. return {
  9101. w: tickLabelWidth * cosRotation + tickFontSize * sinRotation,
  9102. h: tickLabelWidth * sinRotation + tickFontSize * cosRotation
  9103. };
  9104. }
  9105. _getLabelCapacity(exampleTime) {
  9106. const timeOpts = this.options.time;
  9107. const displayFormats = timeOpts.displayFormats;
  9108. const format = displayFormats[timeOpts.unit] || displayFormats.millisecond;
  9109. const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [
  9110. exampleTime
  9111. ], this._majorUnit), format);
  9112. const size = this._getLabelSize(exampleLabel);
  9113. const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1;
  9114. return capacity > 0 ? capacity : 1;
  9115. }
  9116. getDataTimestamps() {
  9117. let timestamps = this._cache.data || [];
  9118. let i, ilen;
  9119. if (timestamps.length) {
  9120. return timestamps;
  9121. }
  9122. const metas = this.getMatchingVisibleMetas();
  9123. if (this._normalized && metas.length) {
  9124. return this._cache.data = metas[0].controller.getAllParsedValues(this);
  9125. }
  9126. for (i = 0, ilen = metas.length; i < ilen; ++i) {
  9127. timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this));
  9128. }
  9129. return this._cache.data = this.normalize(timestamps);
  9130. }
  9131. getLabelTimestamps() {
  9132. const timestamps = this._cache.labels || [];
  9133. let i, ilen;
  9134. if (timestamps.length) {
  9135. return timestamps;
  9136. }
  9137. const labels = this.getLabels();
  9138. for (i = 0, ilen = labels.length; i < ilen; ++i) {
  9139. timestamps.push(parse(this, labels[i]));
  9140. }
  9141. return this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps);
  9142. }
  9143. normalize(values) {
  9144. return _arrayUnique(values.sort(sorter));
  9145. }
  9146. }
  9147. __publicField(TimeScale, "id", "time");
  9148. __publicField(TimeScale, "defaults", {
  9149. bounds: "data",
  9150. adapters: {},
  9151. time: {
  9152. parser: false,
  9153. unit: false,
  9154. round: false,
  9155. isoWeekday: false,
  9156. minUnit: "millisecond",
  9157. displayFormats: {}
  9158. },
  9159. ticks: {
  9160. source: "auto",
  9161. callback: false,
  9162. major: {
  9163. enabled: false
  9164. }
  9165. }
  9166. });
  9167. function interpolate(table, val, reverse) {
  9168. let lo = 0;
  9169. let hi = table.length - 1;
  9170. let prevSource, nextSource, prevTarget, nextTarget;
  9171. if (reverse) {
  9172. if (val >= table[lo].pos && val <= table[hi].pos) {
  9173. ({ lo, hi } = _lookupByKey(table, "pos", val));
  9174. }
  9175. ({ pos: prevSource, time: prevTarget } = table[lo]);
  9176. ({ pos: nextSource, time: nextTarget } = table[hi]);
  9177. } else {
  9178. if (val >= table[lo].time && val <= table[hi].time) {
  9179. ({ lo, hi } = _lookupByKey(table, "time", val));
  9180. }
  9181. ({ time: prevSource, pos: prevTarget } = table[lo]);
  9182. ({ time: nextSource, pos: nextTarget } = table[hi]);
  9183. }
  9184. const span = nextSource - prevSource;
  9185. return span ? prevTarget + (nextTarget - prevTarget) * (val - prevSource) / span : prevTarget;
  9186. }
  9187. class TimeSeriesScale extends TimeScale {
  9188. constructor(props) {
  9189. super(props);
  9190. this._table = [];
  9191. this._minPos = void 0;
  9192. this._tableRange = void 0;
  9193. }
  9194. initOffsets() {
  9195. const timestamps = this._getTimestampsForTable();
  9196. const table = this._table = this.buildLookupTable(timestamps);
  9197. this._minPos = interpolate(table, this.min);
  9198. this._tableRange = interpolate(table, this.max) - this._minPos;
  9199. super.initOffsets(timestamps);
  9200. }
  9201. buildLookupTable(timestamps) {
  9202. const { min, max } = this;
  9203. const items = [];
  9204. const table = [];
  9205. let i, ilen, prev, curr, next;
  9206. for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
  9207. curr = timestamps[i];
  9208. if (curr >= min && curr <= max) {
  9209. items.push(curr);
  9210. }
  9211. }
  9212. if (items.length < 2) {
  9213. return [
  9214. {
  9215. time: min,
  9216. pos: 0
  9217. },
  9218. {
  9219. time: max,
  9220. pos: 1
  9221. }
  9222. ];
  9223. }
  9224. for (i = 0, ilen = items.length; i < ilen; ++i) {
  9225. next = items[i + 1];
  9226. prev = items[i - 1];
  9227. curr = items[i];
  9228. if (Math.round((next + prev) / 2) !== curr) {
  9229. table.push({
  9230. time: curr,
  9231. pos: i / (ilen - 1)
  9232. });
  9233. }
  9234. }
  9235. return table;
  9236. }
  9237. _generate() {
  9238. const min = this.min;
  9239. const max = this.max;
  9240. let timestamps = super.getDataTimestamps();
  9241. if (!timestamps.includes(min) || !timestamps.length) {
  9242. timestamps.splice(0, 0, min);
  9243. }
  9244. if (!timestamps.includes(max) || timestamps.length === 1) {
  9245. timestamps.push(max);
  9246. }
  9247. return timestamps.sort((a, b) => a - b);
  9248. }
  9249. _getTimestampsForTable() {
  9250. let timestamps = this._cache.all || [];
  9251. if (timestamps.length) {
  9252. return timestamps;
  9253. }
  9254. const data = this.getDataTimestamps();
  9255. const label = this.getLabelTimestamps();
  9256. if (data.length && label.length) {
  9257. timestamps = this.normalize(data.concat(label));
  9258. } else {
  9259. timestamps = data.length ? data : label;
  9260. }
  9261. timestamps = this._cache.all = timestamps;
  9262. return timestamps;
  9263. }
  9264. getDecimalForValue(value) {
  9265. return (interpolate(this._table, value) - this._minPos) / this._tableRange;
  9266. }
  9267. getValueForPixel(pixel) {
  9268. const offsets = this._offsets;
  9269. const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
  9270. return interpolate(this._table, decimal * this._tableRange + this._minPos, true);
  9271. }
  9272. }
  9273. __publicField(TimeSeriesScale, "id", "timeseries");
  9274. __publicField(TimeSeriesScale, "defaults", TimeScale.defaults);
  9275. Chart.register(
  9276. LineController,
  9277. LineElement,
  9278. PointElement,
  9279. CategoryScale,
  9280. LinearScale
  9281. );
  9282. function makeTd(textContent, style = "") {
  9283. const td = document.createElement("td");
  9284. if (style !== "") {
  9285. td.setAttribute("style", style);
  9286. }
  9287. td.textContent = textContent;
  9288. return td;
  9289. }
  9290. function makeCanvasTd(canvas, style = "") {
  9291. const td = document.createElement("td");
  9292. if (style !== "") {
  9293. td.setAttribute("style", style);
  9294. }
  9295. td.append(canvas);
  9296. return td;
  9297. }
  9298. const rateColors = [
  9299. { color: "#808080", rate: -1e4 },
  9300. { color: "#804000", rate: 400 },
  9301. { color: "#008000", rate: 800 },
  9302. { color: "#00C0C0", rate: 1200 },
  9303. { color: "#0000FF", rate: 1600 },
  9304. { color: "#C0C000", rate: 2e3 },
  9305. { color: "#FF8000", rate: 2400 },
  9306. { color: "#FF0000", rate: 2800 }
  9307. ];
  9308. const canvasWidth = 250;
  9309. const canvasHeight = 25;
  9310. function isHeuristic(data, taskName) {
  9311. var _a;
  9312. const contestName = ((_a = document.querySelector(".contest-title")) == null ? void 0 : _a.textContent) ?? "";
  9313. if (isAlgorithmContest(vueStandings.contestScreenName, contestName)) {
  9314. return false;
  9315. }
  9316. const scoreSet = /* @__PURE__ */ new Set();
  9317. let maxScore = 0;
  9318. for (let i = 0; i < data.length; i++) {
  9319. if (!data[i].TaskResults || data[i].UserIsDeleted) {
  9320. continue;
  9321. }
  9322. const result = data[i].TaskResults[taskName];
  9323. if (result) {
  9324. scoreSet.add(result.Score);
  9325. if (maxScore < result.Score) {
  9326. maxScore = result.Score;
  9327. }
  9328. }
  9329. if (scoreSet.size >= 51 || maxScore >= 1e5) {
  9330. return true;
  9331. }
  9332. }
  9333. return isHeuristicContest(vueStandings.contestScreenName, contestName);
  9334. }
  9335. function refreshASCATable(newStandings, elAlgoTable, elAlgoTbody, elHeuTable, elHeuTbody) {
  9336. if (!newStandings) {
  9337. return;
  9338. }
  9339. const task = newStandings.TaskInfo;
  9340. const data = vueStandings.filtered ? vueStandings.filteredStandings : newStandings.StandingsData;
  9341. elAlgoTbody.innerHTML = "";
  9342. elHeuTbody.innerHTML = "";
  9343. let numAlgoTask = 0;
  9344. let numHeuTask = 0;
  9345. for (let i = 0; i < task.length; i++) {
  9346. if (isHeuristic(data, task[i].TaskScreenName)) {
  9347. numHeuTask++;
  9348. const isTried = vueStandings.tries[i] > 0;
  9349. const elTr = document.createElement("tr");
  9350. const elCanvas = document.createElement("canvas");
  9351. elCanvas.className = "acsa2-heu-canvas";
  9352. elCanvas.setAttribute(
  9353. "style",
  9354. "width: 100%; max-width: 100%; vertical-align: middle; display: block;"
  9355. );
  9356. elTr.append(
  9357. makeTd(task[i].Assignment, "padding: 4px;"),
  9358. makeTd("-", "padding: 4px;"),
  9359. makeCanvasTd(elCanvas, `padding: 4px; height: 400px; width: 100%;`)
  9360. // TODO 幅の決定
  9361. );
  9362. elHeuTbody.append(elTr);
  9363. if (!isTried) {
  9364. continue;
  9365. }
  9366. let maxScore = -1;
  9367. let myScore = -1;
  9368. let myPenalty = -1;
  9369. const scores = [];
  9370. for (let j = 0; j < data.length; j++) {
  9371. if (!data[j].TaskResults) {
  9372. continue;
  9373. }
  9374. if (data[j].UserIsDeleted) {
  9375. continue;
  9376. }
  9377. const result = data[j].TaskResults[task[i].TaskScreenName];
  9378. if (result) {
  9379. const penalty = result.Score === 0 ? result.Failure : result.Penalty;
  9380. if (data[j].UserScreenName === vueStandings.userScreenName) {
  9381. myScore = result.Score;
  9382. myPenalty = penalty;
  9383. }
  9384. if (maxScore < result.Score) {
  9385. maxScore = result.Score;
  9386. }
  9387. if (result.Score > 0) {
  9388. scores.push(result.Score / 100);
  9389. }
  9390. }
  9391. }
  9392. myScore /= 100;
  9393. maxScore /= 100;
  9394. scores.sort((a, b) => b - a);
  9395. elTr.children[1].textContent = `${myScore >= 0 ? myScore.toFixed() : "-"}${myPenalty > 0 ? ` (${myPenalty})` : ""}`;
  9396. if (maxScore > 0) {
  9397. new Chart(elCanvas, {
  9398. type: "line",
  9399. data: {
  9400. labels: Array.from({ length: scores.length }, (_, i2) => i2 + 1),
  9401. datasets: [
  9402. {
  9403. data: scores,
  9404. borderWidth: 0,
  9405. pointRadius: 2,
  9406. pointBackgroundColor: "#5555ff"
  9407. }
  9408. ]
  9409. },
  9410. options: {
  9411. responsive: true,
  9412. maintainAspectRatio: false,
  9413. scales: {
  9414. y: {
  9415. beginAtZero: true,
  9416. min: 0
  9417. },
  9418. x: {
  9419. grid: {
  9420. drawTicks: true
  9421. },
  9422. afterBuildTicks(scale) {
  9423. const maxTicks = 25;
  9424. const minTicks = 10;
  9425. const dataLength = scores.length;
  9426. if (dataLength <= minTicks) {
  9427. return;
  9428. }
  9429. const intervals = [1, 2, 5, 10, 20, 50, 100, 200];
  9430. let chosenInterval = 1;
  9431. for (const interval of intervals) {
  9432. const tickCount = Math.ceil(dataLength / interval);
  9433. if (minTicks <= tickCount && tickCount <= maxTicks) {
  9434. chosenInterval = interval;
  9435. break;
  9436. }
  9437. }
  9438. scale.ticks = scale.ticks.filter(
  9439. (_, i2) => i2 === 0 || (i2 + 1) % chosenInterval === 0
  9440. );
  9441. }
  9442. }
  9443. },
  9444. plugins: {
  9445. legend: {
  9446. display: false
  9447. }
  9448. }
  9449. }
  9450. });
  9451. }
  9452. } else {
  9453. numAlgoTask++;
  9454. const isTried = vueStandings.tries[i] > 0;
  9455. const elTr = document.createElement("tr");
  9456. const elCanvas = document.createElement("canvas");
  9457. elCanvas.setAttribute("style", "vertical-align: middle;");
  9458. elCanvas.width = canvasWidth;
  9459. elCanvas.height = canvasHeight;
  9460. elTr.append(
  9461. makeTd(task[i].Assignment, "padding: 4px;"),
  9462. makeTd("-", "padding: 4px;"),
  9463. makeTd(
  9464. vueStandings.ac[i] + " / " + vueStandings.tries[i],
  9465. "padding: 4px;"
  9466. ),
  9467. makeTd(
  9468. isTried ? (vueStandings.ac[i] / vueStandings.tries[i] * 100).toFixed(2) + "%" : "-",
  9469. "padding: 4px;"
  9470. ),
  9471. makeTd("-", "padding: 4px;"),
  9472. makeTd("-", "padding: 4px;"),
  9473. makeCanvasTd(elCanvas, `padding: 4px; width:${canvasWidth}px;`)
  9474. );
  9475. elAlgoTbody.append(elTr);
  9476. if (!isTried) {
  9477. continue;
  9478. }
  9479. let maxScore = -1;
  9480. let myScore = -1;
  9481. let myPenalty = -1;
  9482. let avePenalty = 0;
  9483. let ratioPenalty = 0;
  9484. const rates = [];
  9485. for (let j = 0; j < data.length; j++) {
  9486. if (!data[j].TaskResults) {
  9487. continue;
  9488. }
  9489. if (data[j].UserIsDeleted) {
  9490. continue;
  9491. }
  9492. const result = data[j].TaskResults[task[i].TaskScreenName];
  9493. if (result) {
  9494. const penalty = result.Score === 0 ? result.Failure : result.Penalty;
  9495. if (data[j].UserScreenName === vueStandings.userScreenName) {
  9496. myScore = result.Score;
  9497. myPenalty = penalty;
  9498. }
  9499. avePenalty += penalty;
  9500. if (penalty > 0) {
  9501. ratioPenalty++;
  9502. }
  9503. if (maxScore < result.Score) {
  9504. maxScore = result.Score;
  9505. }
  9506. }
  9507. }
  9508. if (maxScore > 0) {
  9509. for (let j = 0; j < data.length; j++) {
  9510. if (data[j].Competitions > 0 && data[j].TaskResults[task[i].TaskScreenName] && data[j].TaskResults[task[i].TaskScreenName].Score === maxScore) {
  9511. rates.push(
  9512. innerRating(Math.max(data[j].Rating, 1), data[j].Competitions)
  9513. );
  9514. }
  9515. }
  9516. rates.sort((a, b) => a - b);
  9517. }
  9518. myScore /= 100;
  9519. maxScore /= 100;
  9520. avePenalty /= vueStandings.tries[i];
  9521. ratioPenalty /= vueStandings.tries[i];
  9522. ratioPenalty *= 100;
  9523. elTr.children[1].textContent = `${myScore >= 0 ? myScore.toFixed() : "-"}${myPenalty > 0 ? ` (${myPenalty})` : ""}`;
  9524. elTr.children[4].textContent = avePenalty.toFixed(2);
  9525. elTr.children[5].textContent = ratioPenalty.toFixed(2) + "%";
  9526. const canvas = elTr.children[6].children[0];
  9527. if (maxScore > 0) {
  9528. const context = canvas == null ? void 0 : canvas.getContext("2d");
  9529. if (context) {
  9530. for (let k = 0; k < rateColors.length; k++) {
  9531. context.fillStyle = rateColors[k].color;
  9532. const x = Math.round(
  9533. countLower(rates, rateColors[k].rate) / rates.length * canvasWidth
  9534. );
  9535. context.fillRect(x, 0, canvasWidth - x, canvasHeight);
  9536. }
  9537. }
  9538. }
  9539. }
  9540. }
  9541. elAlgoTable.style.display = numAlgoTask === 0 ? "none" : "";
  9542. elHeuTable.style.display = numHeuTask === 0 ? "none" : "";
  9543. }
  9544. function initACSATable() {
  9545. const tableClassName = "table table-bordered table-hover th-center td-center td-middle";
  9546. const elVueStandings = document.getElementById("vue-standings");
  9547. const elDiv = document.createElement("div");
  9548. const elAlgoTable = document.createElement("table");
  9549. elAlgoTable.id = "acsa2-algo-table";
  9550. elAlgoTable.className = tableClassName;
  9551. elAlgoTable.setAttribute("style", "white-space: nowrap;");
  9552. const elAlgoThead = document.createElement("thead");
  9553. const elAlgoTr0 = document.createElement("tr");
  9554. elAlgoTr0.setAttribute("style", "font-weight: bold;");
  9555. elAlgoTr0.append(
  9556. makeTd("問題"),
  9557. makeTd("得点"),
  9558. makeTd("人数"),
  9559. makeTd("正解率"),
  9560. makeTd("平均ペナ"),
  9561. makeTd("ペナ率"),
  9562. makeTd("内部レート")
  9563. );
  9564. elAlgoThead.append(elAlgoTr0);
  9565. const elAlgoTbody = document.createElement("tbody");
  9566. elAlgoTable.append(elAlgoThead, elAlgoTbody);
  9567. const elHeuTable = document.createElement("table");
  9568. elHeuTable.id = "acsa2-heu-table";
  9569. elHeuTable.className = tableClassName;
  9570. elHeuTable.setAttribute("style", "white-space: nowrap;");
  9571. const elHeuThead = document.createElement("thead");
  9572. const elHeuTr0 = document.createElement("tr");
  9573. elHeuTr0.setAttribute("style", "font-weight: bold;");
  9574. elHeuTr0.append(makeTd("問題"), makeTd("得点"), makeTd("得点分布"));
  9575. elHeuThead.append(elHeuTr0);
  9576. const elHeuTbody = document.createElement("tbody");
  9577. elHeuTable.append(elHeuThead, elHeuTbody);
  9578. elDiv.append(elAlgoTable, elHeuTable);
  9579. elVueStandings == null ? void 0 : elVueStandings.prepend(elDiv);
  9580. vueStandings.$watch(
  9581. "standings",
  9582. (newStandings) => refreshASCATable(
  9583. newStandings,
  9584. elAlgoTable,
  9585. elAlgoTbody,
  9586. elHeuTable,
  9587. elHeuTbody
  9588. ),
  9589. {
  9590. deep: true,
  9591. immediate: true
  9592. }
  9593. );
  9594. }
  9595. if (document.readyState === "loading") {
  9596. document.addEventListener("DOMContentLoaded", initACSATable);
  9597. } else {
  9598. initACSATable();
  9599. }
  9600.  
  9601. })();