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

helpers.segment.cjs 95KB

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