AtCoderStandingsAnalysis-dev

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

Versione datata 19/01/2025. Vedi la nuova versione l'ultima versione.

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