helpers.dataset.cjs 96 KB

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