uPlot.iife.js 95 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118
  1. /**
  2. * Copyright (c) 2020, Leon Sorokin
  3. * All rights reserved. (MIT Licensed)
  4. *
  5. * uPlot.js (μPlot)
  6. * A small, fast chart for time series, lines, areas, ohlc & bars
  7. * https://github.com/leeoniya/uPlot (v1.5.2)
  8. */
  9. var uPlot = (function () {
  10. 'use strict';
  11. var FEAT_TIME = true;
  12. function debounce(fn, time) {
  13. var pending = null;
  14. function run() {
  15. pending = null;
  16. fn();
  17. }
  18. return function() {
  19. clearTimeout(pending);
  20. pending = setTimeout(run, time);
  21. }
  22. }
  23. // binary search for index of closest value
  24. function closestIdx(num, arr, lo, hi) {
  25. var mid;
  26. lo = lo || 0;
  27. hi = hi || arr.length - 1;
  28. var bitwise = hi <= 2147483647;
  29. while (hi - lo > 1) {
  30. mid = bitwise ? (lo + hi) >> 1 : floor((lo + hi) / 2);
  31. if (arr[mid] < num)
  32. { lo = mid; }
  33. else
  34. { hi = mid; }
  35. }
  36. if (num - arr[lo] <= arr[hi] - num)
  37. { return lo; }
  38. return hi;
  39. }
  40. function nonNullIdx(data, _i0, _i1, dir) {
  41. for (var i = dir == 1 ? _i0 : _i1; i >= _i0 && i <= _i1; i += dir) {
  42. if (data[i] != null)
  43. { return i; }
  44. }
  45. return -1;
  46. }
  47. function getMinMax(data, _i0, _i1, sorted) {
  48. // console.log("getMinMax()");
  49. var _min = inf;
  50. var _max = -inf;
  51. if (sorted == 1) {
  52. _min = data[_i0];
  53. _max = data[_i1];
  54. }
  55. else if (sorted == -1) {
  56. _min = data[_i1];
  57. _max = data[_i0];
  58. }
  59. else {
  60. for (var i = _i0; i <= _i1; i++) {
  61. if (data[i] != null) {
  62. _min = min(_min, data[i]);
  63. _max = max(_max, data[i]);
  64. }
  65. }
  66. }
  67. return [_min, _max];
  68. }
  69. function getMinMaxLog(data, _i0, _i1) {
  70. // console.log("getMinMax()");
  71. var _min = inf;
  72. var _max = -inf;
  73. for (var i = _i0; i <= _i1; i++) {
  74. if (data[i] > 0) {
  75. _min = min(_min, data[i]);
  76. _max = max(_max, data[i]);
  77. }
  78. }
  79. return [
  80. _min == inf ? 1 : _min,
  81. _max == -inf ? 10 : _max ];
  82. }
  83. var _fixedTuple = [0, 0];
  84. function fixIncr(minIncr, maxIncr, minExp, maxExp) {
  85. _fixedTuple[0] = minExp < 0 ? roundDec(minIncr, -minExp) : minIncr;
  86. _fixedTuple[1] = maxExp < 0 ? roundDec(maxIncr, -maxExp) : maxIncr;
  87. return _fixedTuple;
  88. }
  89. function rangeLog(min, max, base, fullMags) {
  90. var logFn = base == 10 ? log10 : log2;
  91. if (min == max) {
  92. min /= base;
  93. max *= base;
  94. }
  95. var minExp, maxExp, minMaxIncrs;
  96. if (fullMags) {
  97. minExp = floor(logFn(min));
  98. maxExp = ceil(logFn(max));
  99. minMaxIncrs = fixIncr(pow(base, minExp), pow(base, maxExp), minExp, maxExp);
  100. min = minMaxIncrs[0];
  101. max = minMaxIncrs[1];
  102. }
  103. else {
  104. minExp = floor(logFn(min));
  105. maxExp = floor(logFn(max));
  106. minMaxIncrs = fixIncr(pow(base, minExp), pow(base, maxExp), minExp, maxExp);
  107. min = incrRoundDn(min, minMaxIncrs[0]);
  108. max = incrRoundUp(max, minMaxIncrs[1]);
  109. }
  110. return [min, max];
  111. }
  112. var _eqRangePart = {
  113. pad: 0,
  114. soft: null,
  115. mode: 0,
  116. };
  117. var _eqRange = {
  118. min: _eqRangePart,
  119. max: _eqRangePart,
  120. };
  121. // this ensures that non-temporal/numeric y-axes get multiple-snapped padding added above/below
  122. // TODO: also account for incrs when snapping to ensure top of axis gets a tick & value
  123. function rangeNum(_min, _max, mult, extra) {
  124. if (isObj(mult))
  125. { return _rangeNum(_min, _max, mult); }
  126. _eqRangePart.pad = mult;
  127. _eqRangePart.soft = extra ? 0 : null;
  128. _eqRangePart.mode = extra ? 2 : 0;
  129. return _rangeNum(_min, _max, _eqRange);
  130. }
  131. // nullish coalesce
  132. function ifNull(lh, rh) {
  133. return lh == null ? rh : lh;
  134. }
  135. function _rangeNum(_min, _max, cfg) {
  136. var cmin = cfg.min;
  137. var cmax = cfg.max;
  138. var padMin = ifNull(cmin.pad, 0);
  139. var padMax = ifNull(cmax.pad, 0);
  140. var hardMin = ifNull(cmin.hard, -inf);
  141. var hardMax = ifNull(cmax.hard, inf);
  142. var softMin = ifNull(cmin.soft, inf);
  143. var softMax = ifNull(cmax.soft, -inf);
  144. var softMinMode = ifNull(cmin.mode, 0);
  145. var softMaxMode = ifNull(cmax.mode, 0);
  146. var delta = _max - _min;
  147. var nonZeroDelta = delta || abs(_max) || 1e3;
  148. var mag = log10(nonZeroDelta);
  149. var base = pow(10, floor(mag));
  150. var _padMin = nonZeroDelta * (delta == 0 ? (_min == 0 ? .1 : 1) : padMin);
  151. var _newMin = roundDec(incrRoundDn(_min - _padMin, base/100), 6);
  152. var _softMin = _min >= softMin && (softMinMode == 1 || softMinMode == 2 && _newMin < softMin) ? softMin : inf;
  153. var minLim = max(hardMin, _newMin < _softMin && _min >= _softMin ? _softMin : min(_softMin, _newMin));
  154. var _padMax = nonZeroDelta * (delta == 0 ? (_max == 0 ? .1 : 1) : padMax);
  155. var _newMax = roundDec(incrRoundUp(_max + _padMax, base/100), 6);
  156. var _softMax = _max <= softMax && (softMaxMode == 1 || softMaxMode == 2 && _newMax > softMax) ? softMax : -inf;
  157. var maxLim = min(hardMax, _newMax > _softMax && _max <= _softMax ? _softMax : max(_softMax, _newMax));
  158. if (minLim == maxLim && minLim == 0)
  159. { maxLim = 100; }
  160. return [minLim, maxLim];
  161. }
  162. // alternative: https://stackoverflow.com/a/2254896
  163. var fmtNum = new Intl.NumberFormat(navigator.language).format;
  164. var M = Math;
  165. var abs = M.abs;
  166. var floor = M.floor;
  167. var round = M.round;
  168. var ceil = M.ceil;
  169. var min = M.min;
  170. var max = M.max;
  171. var pow = M.pow;
  172. var sqrt = M.sqrt;
  173. var log10 = M.log10;
  174. var log2 = M.log2;
  175. var PI = M.PI;
  176. var inf = Infinity;
  177. function incrRound(num, incr) {
  178. return round(num/incr)*incr;
  179. }
  180. function clamp(num, _min, _max) {
  181. return min(max(num, _min), _max);
  182. }
  183. function fnOrSelf(v) {
  184. return typeof v == "function" ? v : function () { return v; };
  185. }
  186. var retArg1 = function (_0, _1) { return _1; };
  187. var retNull = function (_) { return null; };
  188. function incrRoundUp(num, incr) {
  189. return ceil(num/incr)*incr;
  190. }
  191. function incrRoundDn(num, incr) {
  192. return floor(num/incr)*incr;
  193. }
  194. function roundDec(val, dec) {
  195. return round(val * (dec = Math.pow( 10, dec ))) / dec;
  196. }
  197. var fixedDec = new Map();
  198. function guessDec(num) {
  199. return ((""+num).split(".")[1] || "").length;
  200. }
  201. function genIncrs(base, minExp, maxExp, mults) {
  202. var incrs = [];
  203. var multDec = mults.map(guessDec);
  204. for (var exp = minExp; exp < maxExp; exp++) {
  205. var expa = abs(exp);
  206. var mag = roundDec(pow(base, exp), expa);
  207. for (var i = 0; i < mults.length; i++) {
  208. var _incr = mults[i] * mag;
  209. var dec = (_incr >= 0 && exp >= 0 ? 0 : expa) + (exp >= multDec[i] ? 0 : multDec[i]);
  210. var incr = roundDec(_incr, dec);
  211. incrs.push(incr);
  212. fixedDec.set(incr, dec);
  213. }
  214. }
  215. return incrs;
  216. }
  217. //export const assign = Object.assign;
  218. var EMPTY_OBJ = {};
  219. var isArr = Array.isArray;
  220. function isStr(v) {
  221. return typeof v == 'string';
  222. }
  223. function isObj(v) {
  224. var is = false;
  225. if (v != null) {
  226. var c = v.constructor;
  227. is = c == null || c == Object;
  228. }
  229. return is;
  230. }
  231. function copy(o) {
  232. var out;
  233. if (isArr(o))
  234. { out = o.map(copy); }
  235. else if (isObj(o)) {
  236. out = {};
  237. for (var k in o)
  238. { out[k] = copy(o[k]); }
  239. }
  240. else
  241. { out = o; }
  242. return out;
  243. }
  244. function assign(targ) {
  245. var args = arguments;
  246. for (var i = 1; i < args.length; i++) {
  247. var src = args[i];
  248. for (var key in src) {
  249. if (isObj(targ[key]))
  250. { assign(targ[key], copy(src[key])); }
  251. else
  252. { targ[key] = copy(src[key]); }
  253. }
  254. }
  255. return targ;
  256. }
  257. // nullModes
  258. var NULL_IGNORE = 0; // all nulls are ignored by isGap
  259. var NULL_GAP = 1; // alignment nulls are ignored by isGap (default)
  260. var NULL_EXPAND = 2; // nulls are expand to include adjacent alignment nulls
  261. // nullModes is a tables-matched array indicating how to treat nulls in each series
  262. function join(tables, nullModes) {
  263. if (tables.length == 1) {
  264. return {
  265. data: tables[0],
  266. isGap: nullModes ? function (u, seriesIdx, dataIdx) { return nullModes[0][seriesIdx] != NULL_IGNORE; } : function () { return true; },
  267. };
  268. }
  269. var xVals = new Set();
  270. var xNulls = [new Set()];
  271. for (var ti = 0; ti < tables.length; ti++) {
  272. var t = tables[ti];
  273. var xs = t[0];
  274. var len = xs.length;
  275. for (var i = 0; i < len; i++)
  276. { xVals.add(xs[i]); }
  277. for (var si = 1; si < t.length; si++) {
  278. var nulls = new Set();
  279. // cache original nulls for isGap lookup
  280. if (nullModes == null || nullModes[ti][si] == NULL_GAP || nullModes[ti][si] == NULL_EXPAND) {
  281. var ys = t[si];
  282. for (var i$1 = 0; i$1 < len; i$1++) {
  283. if (ys[i$1] == null)
  284. { nulls.add(xs[i$1]); }
  285. }
  286. }
  287. xNulls.push(nulls);
  288. }
  289. }
  290. var data = [Array.from(xVals).sort(function (a, b) { return a - b; })];
  291. var alignedLen = data[0].length;
  292. var xIdxs = new Map();
  293. for (var i$2 = 0; i$2 < alignedLen; i$2++)
  294. { xIdxs.set(data[0][i$2], i$2); }
  295. var gsi = 1;
  296. for (var ti$1 = 0; ti$1 < tables.length; ti$1++) {
  297. var t$1 = tables[ti$1];
  298. var xs$1 = t$1[0];
  299. for (var si$1 = 1; si$1 < t$1.length; si$1++) {
  300. var ys$1 = t$1[si$1];
  301. var yVals = Array(alignedLen).fill(null);
  302. for (var i$3 = 0; i$3 < ys$1.length; i$3++)
  303. { yVals[xIdxs.get(xs$1[i$3])] = ys$1[i$3]; }
  304. // mark all filler nulls as explicit when adjacent to existing explicit nulls (minesweeper)
  305. if (nullModes && nullModes[ti$1][si$1] == NULL_EXPAND) {
  306. var nulls$1 = xNulls[gsi];
  307. var size = nulls$1.size;
  308. var i$4 = 0;
  309. var xi = (void 0);
  310. var lastAddedX = -inf;
  311. for (var xVal of nulls$1.values()) {
  312. if (i$4++ == size)
  313. { break; }
  314. if (xVal > lastAddedX) {
  315. var xIdx = xIdxs.get(xVal);
  316. xi = xIdx - 1;
  317. while (yVals[xi] === null) {
  318. nulls$1.add(data[0][xi]);
  319. xi--;
  320. }
  321. xi = xIdx + 1;
  322. while (yVals[xi] === null) {
  323. nulls$1.add(lastAddedX = data[0][xi]);
  324. xi++;
  325. }
  326. }
  327. }
  328. }
  329. data.push(yVals);
  330. gsi++;
  331. }
  332. }
  333. return {
  334. data: data,
  335. isGap: function isGap(u, seriesIdx, dataIdx) {
  336. var xVal = u._data[0][dataIdx];
  337. return xNulls[seriesIdx].has(xVal);
  338. },
  339. };
  340. }
  341. var microTask = typeof queueMicrotask == "undefined" ? function (fn) { return Promise.resolve().then(fn); } : queueMicrotask;
  342. var WIDTH = "width";
  343. var HEIGHT = "height";
  344. var TOP = "top";
  345. var BOTTOM = "bottom";
  346. var LEFT = "left";
  347. var RIGHT = "right";
  348. var hexBlack = "#000";
  349. var transparent = hexBlack + "0";
  350. var mousemove = "mousemove";
  351. var mousedown = "mousedown";
  352. var mouseup = "mouseup";
  353. var mouseenter = "mouseenter";
  354. var mouseleave = "mouseleave";
  355. var dblclick = "dblclick";
  356. var resize = "resize";
  357. var scroll = "scroll";
  358. var pre = "u-";
  359. var UPLOT = "uplot";
  360. var TITLE = pre + "title";
  361. var WRAP = pre + "wrap";
  362. var UNDER = pre + "under";
  363. var OVER = pre + "over";
  364. var OFF = pre + "off";
  365. var SELECT = pre + "select";
  366. var CURSOR_X = pre + "cursor-x";
  367. var CURSOR_Y = pre + "cursor-y";
  368. var CURSOR_PT = pre + "cursor-pt";
  369. var LEGEND = pre + "legend";
  370. var LEGEND_LIVE = pre + "live";
  371. var LEGEND_INLINE = pre + "inline";
  372. var LEGEND_THEAD = pre + "thead";
  373. var LEGEND_SERIES = pre + "series";
  374. var LEGEND_MARKER = pre + "marker";
  375. var LEGEND_LABEL = pre + "label";
  376. var LEGEND_VALUE = pre + "value";
  377. var rAF = requestAnimationFrame;
  378. var doc = document;
  379. var win = window;
  380. var pxRatio = devicePixelRatio;
  381. function addClass(el, c) {
  382. c != null && el.classList.add(c);
  383. }
  384. function remClass(el, c) {
  385. el.classList.remove(c);
  386. }
  387. function setStylePx(el, name, value) {
  388. el.style[name] = value + "px";
  389. }
  390. function placeTag(tag, cls, targ, refEl) {
  391. var el = doc.createElement(tag);
  392. if (cls != null)
  393. { addClass(el, cls); }
  394. if (targ != null)
  395. { targ.insertBefore(el, refEl); }
  396. return el;
  397. }
  398. function placeDiv(cls, targ) {
  399. return placeTag("div", cls, targ);
  400. }
  401. function trans(el, xPos, yPos, xMax, yMax) {
  402. el.style.transform = "translate(" + xPos + "px," + yPos + "px)";
  403. if (xPos < 0 || yPos < 0 || xPos > xMax || yPos > yMax)
  404. { addClass(el, OFF); }
  405. else
  406. { remClass(el, OFF); }
  407. }
  408. var evOpts = {passive: true};
  409. function on(ev, el, cb) {
  410. el.addEventListener(ev, cb, evOpts);
  411. }
  412. function off(ev, el, cb) {
  413. el.removeEventListener(ev, cb, evOpts);
  414. }
  415. var months = [
  416. "January",
  417. "February",
  418. "March",
  419. "April",
  420. "May",
  421. "June",
  422. "July",
  423. "August",
  424. "September",
  425. "October",
  426. "November",
  427. "December" ];
  428. var days = [
  429. "Sunday",
  430. "Monday",
  431. "Tuesday",
  432. "Wednesday",
  433. "Thursday",
  434. "Friday",
  435. "Saturday" ];
  436. function slice3(str) {
  437. return str.slice(0, 3);
  438. }
  439. var days3 = days.map(slice3);
  440. var months3 = months.map(slice3);
  441. var engNames = {
  442. MMMM: months,
  443. MMM: months3,
  444. WWWW: days,
  445. WWW: days3,
  446. };
  447. function zeroPad2(int) {
  448. return (int < 10 ? '0' : '') + int;
  449. }
  450. function zeroPad3(int) {
  451. return (int < 10 ? '00' : int < 100 ? '0' : '') + int;
  452. }
  453. /*
  454. function suffix(int) {
  455. let mod10 = int % 10;
  456. return int + (
  457. mod10 == 1 && int != 11 ? "st" :
  458. mod10 == 2 && int != 12 ? "nd" :
  459. mod10 == 3 && int != 13 ? "rd" : "th"
  460. );
  461. }
  462. */
  463. var getFullYear = 'getFullYear';
  464. var getMonth = 'getMonth';
  465. var getDate = 'getDate';
  466. var getDay = 'getDay';
  467. var getHours = 'getHours';
  468. var getMinutes = 'getMinutes';
  469. var getSeconds = 'getSeconds';
  470. var getMilliseconds = 'getMilliseconds';
  471. var subs = {
  472. // 2019
  473. YYYY: function (d) { return d[getFullYear](); },
  474. // 19
  475. YY: function (d) { return (d[getFullYear]()+'').slice(2); },
  476. // July
  477. MMMM: function (d, names) { return names.MMMM[d[getMonth]()]; },
  478. // Jul
  479. MMM: function (d, names) { return names.MMM[d[getMonth]()]; },
  480. // 07
  481. MM: function (d) { return zeroPad2(d[getMonth]()+1); },
  482. // 7
  483. M: function (d) { return d[getMonth]()+1; },
  484. // 09
  485. DD: function (d) { return zeroPad2(d[getDate]()); },
  486. // 9
  487. D: function (d) { return d[getDate](); },
  488. // Monday
  489. WWWW: function (d, names) { return names.WWWW[d[getDay]()]; },
  490. // Mon
  491. WWW: function (d, names) { return names.WWW[d[getDay]()]; },
  492. // 03
  493. HH: function (d) { return zeroPad2(d[getHours]()); },
  494. // 3
  495. H: function (d) { return d[getHours](); },
  496. // 9 (12hr, unpadded)
  497. h: function (d) {var h = d[getHours](); return h == 0 ? 12 : h > 12 ? h - 12 : h;},
  498. // AM
  499. AA: function (d) { return d[getHours]() >= 12 ? 'PM' : 'AM'; },
  500. // am
  501. aa: function (d) { return d[getHours]() >= 12 ? 'pm' : 'am'; },
  502. // a
  503. a: function (d) { return d[getHours]() >= 12 ? 'p' : 'a'; },
  504. // 09
  505. mm: function (d) { return zeroPad2(d[getMinutes]()); },
  506. // 9
  507. m: function (d) { return d[getMinutes](); },
  508. // 09
  509. ss: function (d) { return zeroPad2(d[getSeconds]()); },
  510. // 9
  511. s: function (d) { return d[getSeconds](); },
  512. // 374
  513. fff: function (d) { return zeroPad3(d[getMilliseconds]()); },
  514. };
  515. function fmtDate(tpl, names) {
  516. names = names || engNames;
  517. var parts = [];
  518. var R = /\{([a-z]+)\}|[^{]+/gi, m;
  519. while (m = R.exec(tpl))
  520. { parts.push(m[0][0] == '{' ? subs[m[1]] : m[0]); }
  521. return function (d) {
  522. var out = '';
  523. for (var i = 0; i < parts.length; i++)
  524. { out += typeof parts[i] == "string" ? parts[i] : parts[i](d, names); }
  525. return out;
  526. }
  527. }
  528. var localTz = new Intl.DateTimeFormat().resolvedOptions().timeZone;
  529. // https://stackoverflow.com/questions/15141762/how-to-initialize-a-javascript-date-to-a-particular-time-zone/53652131#53652131
  530. function tzDate(date, tz) {
  531. var date2;
  532. // perf optimization
  533. if (tz == 'Etc/UTC')
  534. { date2 = new Date(+date + date.getTimezoneOffset() * 6e4); }
  535. else if (tz == localTz)
  536. { date2 = date; }
  537. else {
  538. date2 = new Date(date.toLocaleString('en-US', {timeZone: tz}));
  539. date2.setMilliseconds(date[getMilliseconds]());
  540. }
  541. return date2;
  542. }
  543. //export const series = [];
  544. // default formatters:
  545. var onlyWhole = function (v) { return v % 1 == 0; };
  546. var allMults = [1,2,2.5,5];
  547. // ...0.01, 0.02, 0.025, 0.05, 0.1, 0.2, 0.25, 0.5
  548. var decIncrs = genIncrs(10, -16, 0, allMults);
  549. // 1, 2, 2.5, 5, 10, 20, 25, 50...
  550. var oneIncrs = genIncrs(10, 0, 16, allMults);
  551. // 1, 2, 5, 10, 20, 25, 50...
  552. var wholeIncrs = oneIncrs.filter(onlyWhole);
  553. var numIncrs = decIncrs.concat(oneIncrs);
  554. var NL = "\n";
  555. var yyyy = "{YYYY}";
  556. var NLyyyy = NL + yyyy;
  557. var md = "{M}/{D}";
  558. var NLmd = NL + md;
  559. var NLmdyy = NLmd + "/{YY}";
  560. var aa = "{aa}";
  561. var hmm = "{h}:{mm}";
  562. var hmmaa = hmm + aa;
  563. var NLhmmaa = NL + hmmaa;
  564. var ss = ":{ss}";
  565. var _ = null;
  566. function genTimeStuffs(ms) {
  567. var s = ms * 1e3,
  568. m = s * 60,
  569. h = m * 60,
  570. d = h * 24,
  571. mo = d * 30,
  572. y = d * 365;
  573. // min of 1e-3 prevents setting a temporal x ticks too small since Date objects cannot advance ticks smaller than 1ms
  574. var subSecIncrs = ms == 1 ? genIncrs(10, 0, 3, allMults).filter(onlyWhole) : genIncrs(10, -3, 0, allMults);
  575. var timeIncrs = subSecIncrs.concat([
  576. // minute divisors (# of secs)
  577. s,
  578. s * 5,
  579. s * 10,
  580. s * 15,
  581. s * 30,
  582. // hour divisors (# of mins)
  583. m,
  584. m * 5,
  585. m * 10,
  586. m * 15,
  587. m * 30,
  588. // day divisors (# of hrs)
  589. h,
  590. h * 2,
  591. h * 3,
  592. h * 4,
  593. h * 6,
  594. h * 8,
  595. h * 12,
  596. // month divisors TODO: need more?
  597. d,
  598. d * 2,
  599. d * 3,
  600. d * 4,
  601. d * 5,
  602. d * 6,
  603. d * 7,
  604. d * 8,
  605. d * 9,
  606. d * 10,
  607. d * 15,
  608. // year divisors (# months, approx)
  609. mo,
  610. mo * 2,
  611. mo * 3,
  612. mo * 4,
  613. mo * 6,
  614. // century divisors
  615. y,
  616. y * 2,
  617. y * 5,
  618. y * 10,
  619. y * 25,
  620. y * 50,
  621. y * 100 ]);
  622. // [0]: minimum num secs in the tick incr
  623. // [1]: default tick format
  624. // [2-7]: rollover tick formats
  625. // [8]: mode: 0: replace [1] -> [2-7], 1: concat [1] + [2-7]
  626. var _timeAxisStamps = [
  627. // tick incr default year month day hour min sec mode
  628. [y, yyyy, _, _, _, _, _, _, 1],
  629. [d * 28, "{MMM}", NLyyyy, _, _, _, _, _, 1],
  630. [d, md, NLyyyy, _, _, _, _, _, 1],
  631. [h, "{h}" + aa, NLmdyy, _, NLmd, _, _, _, 1],
  632. [m, hmmaa, NLmdyy, _, NLmd, _, _, _, 1],
  633. [s, ss, NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1],
  634. [ms, ss + ".{fff}", NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1] ];
  635. // the ensures that axis ticks, values & grid are aligned to logical temporal breakpoints and not an arbitrary timestamp
  636. // https://www.timeanddate.com/time/dst/
  637. // https://www.timeanddate.com/time/dst/2019.html
  638. // https://www.epochconverter.com/timezones
  639. function timeAxisSplits(tzDate) {
  640. return function (self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace) {
  641. var splits = [];
  642. var isYr = foundIncr >= y;
  643. var isMo = foundIncr >= mo && foundIncr < y;
  644. // get the timezone-adjusted date
  645. var minDate = tzDate(scaleMin);
  646. var minDateTs = minDate * ms;
  647. // get ts of 12am (this lands us at or before the original scaleMin)
  648. var minMin = mkDate(minDate[getFullYear](), isYr ? 0 : minDate[getMonth](), isMo || isYr ? 1 : minDate[getDate]());
  649. var minMinTs = minMin * ms;
  650. if (isMo || isYr) {
  651. var moIncr = isMo ? foundIncr / mo : 0;
  652. var yrIncr = isYr ? foundIncr / y : 0;
  653. // let tzOffset = scaleMin - minDateTs; // needed?
  654. var split = minDateTs == minMinTs ? minDateTs : mkDate(minMin[getFullYear]() + yrIncr, minMin[getMonth]() + moIncr, 1) * ms;
  655. var splitDate = new Date(split / ms);
  656. var baseYear = splitDate[getFullYear]();
  657. var baseMonth = splitDate[getMonth]();
  658. for (var i = 0; split <= scaleMax; i++) {
  659. var next = mkDate(baseYear + yrIncr * i, baseMonth + moIncr * i, 1);
  660. var offs = next - tzDate(next * ms);
  661. split = (+next + offs) * ms;
  662. if (split <= scaleMax)
  663. { splits.push(split); }
  664. }
  665. }
  666. else {
  667. var incr0 = foundIncr >= d ? d : foundIncr;
  668. var tzOffset = floor(scaleMin) - floor(minDateTs);
  669. var split$1 = minMinTs + tzOffset + incrRoundUp(minDateTs - minMinTs, incr0);
  670. splits.push(split$1);
  671. var date0 = tzDate(split$1);
  672. var prevHour = date0[getHours]() + (date0[getMinutes]() / m) + (date0[getSeconds]() / h);
  673. var incrHours = foundIncr / h;
  674. var minSpace = self.axes[axisIdx]._space;
  675. var pctSpace = foundSpace / minSpace;
  676. while (1) {
  677. split$1 = roundDec(split$1 + foundIncr, ms == 1 ? 0 : 3);
  678. if (split$1 > scaleMax)
  679. { break; }
  680. if (incrHours > 1) {
  681. var expectedHour = floor(roundDec(prevHour + incrHours, 6)) % 24;
  682. var splitDate$1 = tzDate(split$1);
  683. var actualHour = splitDate$1.getHours();
  684. var dstShift = actualHour - expectedHour;
  685. if (dstShift > 1)
  686. { dstShift = -1; }
  687. split$1 -= dstShift * h;
  688. prevHour = (prevHour + incrHours) % 24;
  689. // add a tick only if it's further than 70% of the min allowed label spacing
  690. var prevSplit = splits[splits.length - 1];
  691. var pctIncr = roundDec((split$1 - prevSplit) / foundIncr, 3);
  692. if (pctIncr * pctSpace >= .7)
  693. { splits.push(split$1); }
  694. }
  695. else
  696. { splits.push(split$1); }
  697. }
  698. }
  699. return splits;
  700. }
  701. }
  702. return [
  703. timeIncrs,
  704. _timeAxisStamps,
  705. timeAxisSplits ];
  706. }
  707. var ref = genTimeStuffs(1);
  708. var timeIncrsMs = ref[0];
  709. var _timeAxisStampsMs = ref[1];
  710. var timeAxisSplitsMs = ref[2];
  711. var ref$1 = genTimeStuffs(1e-3);
  712. var timeIncrsS = ref$1[0];
  713. var _timeAxisStampsS = ref$1[1];
  714. var timeAxisSplitsS = ref$1[2];
  715. // base 2
  716. var binIncrs = genIncrs(2, -53, 53, [1]);
  717. /*
  718. console.log({
  719. decIncrs,
  720. oneIncrs,
  721. wholeIncrs,
  722. numIncrs,
  723. timeIncrs,
  724. fixedDec,
  725. });
  726. */
  727. function timeAxisStamps(stampCfg, fmtDate) {
  728. return stampCfg.map(function (s) { return s.map(function (v, i) { return i == 0 || i == 8 || v == null ? v : fmtDate(i == 1 || s[8] == 0 ? v : s[1] + v); }
  729. ); });
  730. }
  731. // TODO: will need to accept spaces[] and pull incr into the loop when grid will be non-uniform, eg for log scales.
  732. // currently we ignore this for months since they're *nearly* uniform and the added complexity is not worth it
  733. function timeAxisVals(tzDate, stamps) {
  734. return function (self, splits, axisIdx, foundSpace, foundIncr) {
  735. var s = stamps.find(function (s) { return foundIncr >= s[0]; }) || stamps[stamps.length - 1];
  736. // these track boundaries when a full label is needed again
  737. var prevYear;
  738. var prevMnth;
  739. var prevDate;
  740. var prevHour;
  741. var prevMins;
  742. var prevSecs;
  743. return splits.map(function (split) {
  744. var date = tzDate(split);
  745. var newYear = date[getFullYear]();
  746. var newMnth = date[getMonth]();
  747. var newDate = date[getDate]();
  748. var newHour = date[getHours]();
  749. var newMins = date[getMinutes]();
  750. var newSecs = date[getSeconds]();
  751. var stamp = (
  752. newYear != prevYear && s[2] ||
  753. newMnth != prevMnth && s[3] ||
  754. newDate != prevDate && s[4] ||
  755. newHour != prevHour && s[5] ||
  756. newMins != prevMins && s[6] ||
  757. newSecs != prevSecs && s[7] ||
  758. s[1]
  759. );
  760. prevYear = newYear;
  761. prevMnth = newMnth;
  762. prevDate = newDate;
  763. prevHour = newHour;
  764. prevMins = newMins;
  765. prevSecs = newSecs;
  766. return stamp(date);
  767. });
  768. }
  769. }
  770. // for when axis.values is defined as a static fmtDate template string
  771. function timeAxisVal(tzDate, dateTpl) {
  772. var stamp = fmtDate(dateTpl);
  773. return function (self, splits, axisIdx, foundSpace, foundIncr) { return splits.map(function (split) { return stamp(tzDate(split)); }); };
  774. }
  775. function mkDate(y, m, d) {
  776. return new Date(y, m, d);
  777. }
  778. function timeSeriesStamp(stampCfg, fmtDate) {
  779. return fmtDate(stampCfg);
  780. }
  781. var _timeSeriesStamp = '{YYYY}-{MM}-{DD} {h}:{mm}{aa}';
  782. function timeSeriesVal(tzDate, stamp) {
  783. return function (self, val) { return stamp(tzDate(val)); };
  784. }
  785. function legendStroke(self, seriesIdx) {
  786. var s = self.series[seriesIdx];
  787. return s.width ? s.stroke(self, seriesIdx) : s.points.width ? s.points.stroke(self, seriesIdx) : null;
  788. }
  789. function legendFill(self, seriesIdx) {
  790. return self.series[seriesIdx].fill(self, seriesIdx);
  791. }
  792. function cursorPointShow(self, si) {
  793. var o = self.cursor.points;
  794. var pt = placeDiv();
  795. var stroke = o.stroke(self, si);
  796. var fill = o.fill(self, si);
  797. pt.style.background = fill || stroke;
  798. var size = o.size(self, si);
  799. var width = o.width(self, si, size);
  800. if (width)
  801. { pt.style.border = width + "px solid " + stroke; }
  802. var mar = size / -2;
  803. setStylePx(pt, WIDTH, size);
  804. setStylePx(pt, HEIGHT, size);
  805. setStylePx(pt, "marginLeft", mar);
  806. setStylePx(pt, "marginTop", mar);
  807. return pt;
  808. }
  809. function cursorPointFill(self, si) {
  810. var s = self.series[si];
  811. return s.stroke(self, si);
  812. }
  813. function cursorPointStroke(self, si) {
  814. var s = self.series[si];
  815. return s.stroke(self, si);
  816. }
  817. function cursorPointSize(self, si) {
  818. var s = self.series[si];
  819. return ptDia(s.width, 1);
  820. }
  821. function dataIdx(self, seriesIdx, cursorIdx) {
  822. return cursorIdx;
  823. }
  824. var moveTuple = [0,0];
  825. function cursorMove(self, mouseLeft1, mouseTop1) {
  826. moveTuple[0] = mouseLeft1;
  827. moveTuple[1] = mouseTop1;
  828. return moveTuple;
  829. }
  830. function filtBtn0(self, targ, handle) {
  831. return function (e) {
  832. e.button == 0 && handle(e);
  833. };
  834. }
  835. function passThru(self, targ, handle) {
  836. return handle;
  837. }
  838. var cursorOpts = {
  839. show: true,
  840. x: true,
  841. y: true,
  842. lock: false,
  843. move: cursorMove,
  844. points: {
  845. show: cursorPointShow,
  846. size: cursorPointSize,
  847. width: 0,
  848. stroke: cursorPointStroke,
  849. fill: cursorPointFill,
  850. },
  851. bind: {
  852. mousedown: filtBtn0,
  853. mouseup: filtBtn0,
  854. click: filtBtn0,
  855. dblclick: filtBtn0,
  856. mousemove: passThru,
  857. mouseleave: passThru,
  858. mouseenter: passThru,
  859. },
  860. drag: {
  861. setScale: true,
  862. x: true,
  863. y: false,
  864. dist: 0,
  865. uni: null,
  866. _x: false,
  867. _y: false,
  868. },
  869. focus: {
  870. prox: -1,
  871. },
  872. left: -10,
  873. top: -10,
  874. idx: null,
  875. dataIdx: dataIdx,
  876. };
  877. var grid = {
  878. show: true,
  879. stroke: "rgba(0,0,0,0.07)",
  880. width: 2,
  881. // dash: [],
  882. filter: retArg1,
  883. };
  884. var ticks = assign({}, grid, {size: 10});
  885. var font = '12px system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"';
  886. var labelFont = "bold " + font;
  887. var lineMult = 1.5; // font-size multiplier
  888. var xAxisOpts = {
  889. show: true,
  890. scale: "x",
  891. space: 50,
  892. gap: 5,
  893. size: 50,
  894. labelSize: 30,
  895. labelFont: labelFont,
  896. side: 2,
  897. // class: "x-vals",
  898. // incrs: timeIncrs,
  899. // values: timeVals,
  900. // filter: retArg1,
  901. grid: grid,
  902. ticks: ticks,
  903. font: font,
  904. rotate: 0,
  905. };
  906. var numSeriesLabel = "Value";
  907. var timeSeriesLabel = "Time";
  908. var xSeriesOpts = {
  909. show: true,
  910. scale: "x",
  911. auto: false,
  912. sorted: 1,
  913. // label: "Time",
  914. // value: v => stamp(new Date(v * 1e3)),
  915. // internal caches
  916. min: inf,
  917. max: -inf,
  918. idxs: [],
  919. };
  920. function numAxisVals(self, splits, axisIdx, foundSpace, foundIncr) {
  921. return splits.map(function (v) { return v == null ? "" : fmtNum(v); });
  922. }
  923. function numAxisSplits(self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace, forceMin) {
  924. var splits = [];
  925. var numDec = fixedDec.get(foundIncr) || 0;
  926. scaleMin = forceMin ? scaleMin : roundDec(incrRoundUp(scaleMin, foundIncr), numDec);
  927. for (var val = scaleMin; val <= scaleMax; val = roundDec(val + foundIncr, numDec))
  928. { splits.push(Object.is(val, -0) ? 0 : val); } // coalesces -0
  929. return splits;
  930. }
  931. function logAxisSplits(self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace, forceMin) {
  932. var splits = [];
  933. var logBase = self.scales[self.axes[axisIdx].scale].log;
  934. var logFn = logBase == 10 ? log10 : log2;
  935. var exp = floor(logFn(scaleMin));
  936. foundIncr = pow(logBase, exp);
  937. if (exp < 0)
  938. { foundIncr = roundDec(foundIncr, -exp); }
  939. var split = scaleMin;
  940. do {
  941. splits.push(split);
  942. split = roundDec(split + foundIncr, fixedDec.get(foundIncr));
  943. if (split >= foundIncr * logBase)
  944. { foundIncr = split; }
  945. } while (split <= scaleMax);
  946. return splits;
  947. }
  948. var RE_ALL = /./;
  949. var RE_12357 = /[12357]/;
  950. var RE_125 = /[125]/;
  951. var RE_1 = /1/;
  952. function logAxisValsFilt(self, splits, axisIdx, foundSpace, foundIncr) {
  953. var axis = self.axes[axisIdx];
  954. var scaleKey = axis.scale;
  955. if (self.scales[scaleKey].log == 2)
  956. { return splits; }
  957. var valToPos = self.valToPos;
  958. var minSpace = axis._space;
  959. var _10 = valToPos(10, scaleKey);
  960. var re = (
  961. valToPos(9, scaleKey) - _10 >= minSpace ? RE_ALL :
  962. valToPos(7, scaleKey) - _10 >= minSpace ? RE_12357 :
  963. valToPos(5, scaleKey) - _10 >= minSpace ? RE_125 :
  964. RE_1
  965. );
  966. return splits.map(function (v) { return re.test(v) ? v : null; });
  967. }
  968. function numSeriesVal(self, val) {
  969. return val == null ? "" : fmtNum(val);
  970. }
  971. var yAxisOpts = {
  972. show: true,
  973. scale: "y",
  974. space: 30,
  975. gap: 5,
  976. size: 50,
  977. labelSize: 30,
  978. labelFont: labelFont,
  979. side: 3,
  980. // class: "y-vals",
  981. // incrs: numIncrs,
  982. // values: (vals, space) => vals,
  983. // filter: retArg1,
  984. grid: grid,
  985. ticks: ticks,
  986. font: font,
  987. rotate: 0,
  988. };
  989. // takes stroke width
  990. function ptDia(width, mult) {
  991. var dia = 3 + (width || 1) * 2;
  992. return roundDec(dia * mult, 3);
  993. }
  994. function seriesPoints(self, si) {
  995. var s = self.series[si];
  996. var dia = ptDia(s.width, pxRatio);
  997. var maxPts = self.bbox.width / (s.points.space * pxRatio);
  998. var idxs = self.series[0].idxs;
  999. return idxs[1] - idxs[0] <= maxPts;
  1000. }
  1001. function seriesFillTo(self, seriesIdx, dataMin, dataMax) {
  1002. var scale = self.scales[self.series[seriesIdx].scale];
  1003. return scale.distr == 3 ? scale.min : 0;
  1004. }
  1005. var ySeriesOpts = {
  1006. scale: "y",
  1007. auto: true,
  1008. sorted: 0,
  1009. show: true,
  1010. band: false,
  1011. spanGaps: false,
  1012. isGap: function (self, seriesIdx, dataIdx) { return true; },
  1013. alpha: 1,
  1014. points: {
  1015. show: seriesPoints,
  1016. // stroke: "#000",
  1017. // fill: "#fff",
  1018. // width: 1,
  1019. // size: 10,
  1020. },
  1021. // label: "Value",
  1022. // value: v => v,
  1023. values: null,
  1024. // internal caches
  1025. min: inf,
  1026. max: -inf,
  1027. idxs: [],
  1028. path: null,
  1029. clip: null,
  1030. };
  1031. function clampScale(self, val, scaleMin, scaleMax, scaleKey) {
  1032. /*
  1033. if (val < 0) {
  1034. let cssHgt = self.bbox.height / pxRatio;
  1035. let absPos = self.valToPos(abs(val), scaleKey);
  1036. let fromBtm = cssHgt - absPos;
  1037. return self.posToVal(cssHgt + fromBtm, scaleKey);
  1038. }
  1039. */
  1040. return scaleMin / 10;
  1041. }
  1042. var xScaleOpts = {
  1043. time: FEAT_TIME,
  1044. auto: true,
  1045. distr: 1,
  1046. log: 10,
  1047. min: null,
  1048. max: null,
  1049. };
  1050. var yScaleOpts = assign({}, xScaleOpts, {
  1051. time: false,
  1052. });
  1053. var syncs = {};
  1054. function _sync(opts) {
  1055. var clients = [];
  1056. return {
  1057. sub: function sub(client) {
  1058. clients.push(client);
  1059. },
  1060. unsub: function unsub(client) {
  1061. clients = clients.filter(function (c) { return c != client; });
  1062. },
  1063. pub: function pub(type, self, x, y, w, h, i) {
  1064. if (clients.length > 1) {
  1065. clients.forEach(function (client) {
  1066. client != self && client.pub(type, self, x, y, w, h, i);
  1067. });
  1068. }
  1069. }
  1070. };
  1071. }
  1072. var props = Array(11);
  1073. function aliasProps(u, seriesIdx) {
  1074. var series = u.series[seriesIdx];
  1075. var scales = u.scales;
  1076. var bbox = u.bbox;
  1077. props[0] = series; // series
  1078. props[1] = u._data[0]; // dataX
  1079. props[2] = u._data[seriesIdx]; // dataY
  1080. props[3] = scales[u.series[0].scale]; // scaleX
  1081. props[4] = scales[series.scale]; // scaleY
  1082. props[5] = u.valToPosX; // valToPosX
  1083. props[6] = u.valToPosY; // valToPosY
  1084. props[7] = bbox.left; // plotLft
  1085. props[8] = bbox.top; // plotTop
  1086. props[9] = bbox.width; // plotWid
  1087. props[10] = bbox.height; // plotHgt
  1088. return props;
  1089. }
  1090. function clipGaps(gaps, ori, plotLft, plotTop, plotWid, plotHgt) {
  1091. var clip = null;
  1092. // create clip path (invert gaps and non-gaps)
  1093. if (gaps.length > 0) {
  1094. clip = new Path2D();
  1095. if (ori == 1) {
  1096. var prevGapEnd = plotLft;
  1097. for (var i = 0; i < gaps.length; i++) {
  1098. var g = gaps[i];
  1099. clip.rect(prevGapEnd, plotTop, g[0] - prevGapEnd, plotTop + plotHgt);
  1100. prevGapEnd = g[1];
  1101. }
  1102. clip.rect(prevGapEnd, plotTop, plotLft + plotWid - prevGapEnd, plotTop + plotHgt);
  1103. }
  1104. }
  1105. return clip;
  1106. }
  1107. function addGap(gaps, fromX, toX) {
  1108. if (toX > fromX) {
  1109. var prevGap = gaps[gaps.length - 1];
  1110. if (prevGap && prevGap[0] == fromX) // TODO: gaps must be encoded at stroke widths?
  1111. { prevGap[1] = toX; }
  1112. else
  1113. { gaps.push([fromX, toX]); }
  1114. }
  1115. }
  1116. var dir = 1;
  1117. function drawAcc(stroke, accX, minY, maxY, outY) {
  1118. stroke.lineTo(accX, minY);
  1119. stroke.lineTo(accX, maxY);
  1120. stroke.lineTo(accX, outY);
  1121. }
  1122. function linear() {
  1123. return function (u, seriesIdx, idx0, idx1) {
  1124. var ref = aliasProps(u, seriesIdx);
  1125. var series = ref[0];
  1126. var dataX = ref[1];
  1127. var dataY = ref[2];
  1128. var scaleX = ref[3];
  1129. var scaleY = ref[4];
  1130. var valToPosX = ref[5];
  1131. var valToPosY = ref[6];
  1132. var plotLft = ref[7];
  1133. var plotTop = ref[8];
  1134. var plotWid = ref[9];
  1135. var plotHgt = ref[10];
  1136. var isGap = series.isGap;
  1137. var _paths = dir == 1 ? {stroke: new Path2D(), fill: null, clip: null} : u.series[seriesIdx - 1]._paths;
  1138. var stroke = _paths.stroke;
  1139. var width = roundDec(series.width * pxRatio, 3);
  1140. var minY = inf,
  1141. maxY = -inf,
  1142. outY, outX, drawnAtX;
  1143. // todo: don't build gaps on dir = -1 pass
  1144. var gaps = [];
  1145. var accX = round(valToPosX(dataX[dir == 1 ? idx0 : idx1], scaleX, plotWid, plotLft));
  1146. var accGaps = false;
  1147. // data edges
  1148. var lftIdx = nonNullIdx(dataY, idx0, idx1, 1);
  1149. var rgtIdx = nonNullIdx(dataY, idx0, idx1, -1);
  1150. var lftX = incrRound(valToPosX(dataX[lftIdx], scaleX, plotWid, plotLft), 0.5);
  1151. var rgtX = incrRound(valToPosX(dataX[rgtIdx], scaleX, plotWid, plotLft), 0.5);
  1152. if (lftX > plotLft)
  1153. { addGap(gaps, plotLft, lftX); }
  1154. // the moves the shape edge outside the canvas so stroke doesnt bleed in
  1155. if (series.band && dir == 1)
  1156. { stroke.lineTo(lftX - width * 2, round(valToPosY(dataY[idx0], scaleY, plotHgt, plotTop))); }
  1157. for (var i = dir == 1 ? idx0 : idx1; i >= idx0 && i <= idx1; i += dir) {
  1158. var x = round(valToPosX(dataX[i], scaleX, plotWid, plotLft));
  1159. if (x == accX) {
  1160. if (dataY[i] != null) {
  1161. outY = round(valToPosY(dataY[i], scaleY, plotHgt, plotTop));
  1162. if (minY == inf)
  1163. { stroke.lineTo(x, outY); }
  1164. minY = min(outY, minY);
  1165. maxY = max(outY, maxY);
  1166. }
  1167. else if (!accGaps && isGap(u, seriesIdx, i))
  1168. { accGaps = true; }
  1169. }
  1170. else {
  1171. var _addGap = false;
  1172. if (minY != inf) {
  1173. drawAcc(stroke, accX, minY, maxY, outY);
  1174. outX = drawnAtX = accX;
  1175. }
  1176. else if (accGaps) {
  1177. _addGap = true;
  1178. accGaps = false;
  1179. }
  1180. if (dataY[i] != null) {
  1181. outY = round(valToPosY(dataY[i], scaleY, plotHgt, plotTop));
  1182. stroke.lineTo(x, outY);
  1183. minY = maxY = outY;
  1184. // prior pixel can have data but still start a gap if ends with null
  1185. if (x - accX > 1 && dataY[i - 1] == null && isGap(u, seriesIdx, i - 1))
  1186. { _addGap = true; }
  1187. }
  1188. else {
  1189. minY = inf;
  1190. maxY = -inf;
  1191. if (!accGaps && isGap(u, seriesIdx, i))
  1192. { accGaps = true; }
  1193. }
  1194. _addGap && addGap(gaps, outX, x);
  1195. accX = x;
  1196. }
  1197. }
  1198. if (minY != inf && minY != maxY && drawnAtX != accX)
  1199. { drawAcc(stroke, accX, minY, maxY, outY); }
  1200. if (rgtX < plotLft + plotWid)
  1201. { addGap(gaps, rgtX, plotLft + plotWid); }
  1202. if (series.band) {
  1203. var _x, _iy, _data = u._data, dataY2;
  1204. // the moves the shape edge outside the canvas so stroke doesnt bleed in
  1205. if (dir == 1) {
  1206. _x = rgtX + width * 2;
  1207. _iy = rgtIdx;
  1208. dataY2 = _data[seriesIdx + 1];
  1209. }
  1210. else {
  1211. _x = lftX - width * 2;
  1212. _iy = lftIdx;
  1213. dataY2 = _data[seriesIdx - 1];
  1214. }
  1215. stroke.lineTo(_x, round(valToPosY(dataY[_iy], scaleY, plotHgt, plotTop)));
  1216. stroke.lineTo(_x, round(valToPosY(dataY2[_iy], scaleY, plotHgt, plotTop)));
  1217. }
  1218. if (dir == 1) {
  1219. if (!series.spanGaps)
  1220. { _paths.clip = clipGaps(gaps, 1, plotLft, plotTop, plotWid, plotHgt); }
  1221. if (series.fill != null) {
  1222. var fill = _paths.fill = new Path2D(stroke);
  1223. var fillTo = round(valToPosY(series.fillTo(u, seriesIdx, series.min, series.max), scaleY, plotHgt, plotTop));
  1224. fill.lineTo(rgtX, fillTo);
  1225. fill.lineTo(lftX, fillTo);
  1226. }
  1227. }
  1228. if (series.band)
  1229. { dir *= -1; }
  1230. return _paths;
  1231. };
  1232. }
  1233. function spline(opts) {
  1234. return function (u, seriesIdx, idx0, idx1) {
  1235. var ref = aliasProps(u, seriesIdx);
  1236. var series = ref[0];
  1237. var dataX = ref[1];
  1238. var dataY = ref[2];
  1239. var scaleX = ref[3];
  1240. var scaleY = ref[4];
  1241. var valToPosX = ref[5];
  1242. var valToPosY = ref[6];
  1243. var plotLft = ref[7];
  1244. var plotTop = ref[8];
  1245. var plotWid = ref[9];
  1246. var plotHgt = ref[10];
  1247. idx0 = nonNullIdx(dataY, idx0, idx1, 1);
  1248. idx1 = nonNullIdx(dataY, idx0, idx1, -1);
  1249. var gaps = [];
  1250. var inGap = false;
  1251. var firstXPos = round(valToPosX(dataX[idx0], scaleX, plotWid, plotLft));
  1252. var prevXPos = firstXPos;
  1253. var xCoords = [];
  1254. var yCoords = [];
  1255. for (var i = idx0; i <= idx1; i++) {
  1256. var yVal = dataY[i];
  1257. var xVal = dataX[i];
  1258. var xPos = valToPosX(xVal, scaleX, plotWid, plotLft);
  1259. if (yVal == null) {
  1260. if (series.isGap(u, seriesIdx, i)) {
  1261. addGap(gaps, prevXPos, xPos);
  1262. inGap = true;
  1263. }
  1264. continue;
  1265. }
  1266. else {
  1267. if (inGap) {
  1268. addGap(gaps, prevXPos, xPos);
  1269. inGap = false;
  1270. }
  1271. xCoords.push((prevXPos = xPos));
  1272. yCoords.push(valToPosY(dataY[i], scaleY, plotHgt, plotTop));
  1273. }
  1274. }
  1275. var stroke = catmullRomFitting(xCoords, yCoords, 0.5);
  1276. var fill = new Path2D(stroke);
  1277. var fillTo = series.fillTo(u, seriesIdx, series.min, series.max);
  1278. var minY = round(valToPosY(fillTo, scaleY, plotHgt, plotTop));
  1279. fill.lineTo(prevXPos, minY);
  1280. fill.lineTo(firstXPos, minY);
  1281. var clip = !series.spanGaps ? clipGaps(gaps, 1, plotLft, plotTop, plotWid, plotHgt) : null;
  1282. return {
  1283. stroke: stroke,
  1284. fill: fill,
  1285. clip: clip,
  1286. };
  1287. // if FEAT_PATHS: false in rollup.config.js
  1288. // u.ctx.save();
  1289. // u.ctx.beginPath();
  1290. // u.ctx.rect(u.bbox.left, u.bbox.top, u.bbox.width, u.bbox.height);
  1291. // u.ctx.clip();
  1292. // u.ctx.strokeStyle = u.series[sidx].stroke;
  1293. // u.ctx.stroke(stroke);
  1294. // u.ctx.fillStyle = u.series[sidx].fill;
  1295. // u.ctx.fill(fill);
  1296. // u.ctx.restore();
  1297. // return null;
  1298. };
  1299. }
  1300. // adapted from https://gist.github.com/nicholaswmin/c2661eb11cad5671d816 (MIT)
  1301. function catmullRomFitting(xCoords, yCoords, alpha) {
  1302. var path = new Path2D();
  1303. var dataLen = xCoords.length;
  1304. var p0x,
  1305. p0y,
  1306. p1x,
  1307. p1y,
  1308. p2x,
  1309. p2y,
  1310. p3x,
  1311. p3y,
  1312. bp1x,
  1313. bp1y,
  1314. bp2x,
  1315. bp2y,
  1316. d1,
  1317. d2,
  1318. d3,
  1319. A,
  1320. B,
  1321. N,
  1322. M,
  1323. d3powA,
  1324. d2powA,
  1325. d3pow2A,
  1326. d2pow2A,
  1327. d1pow2A,
  1328. d1powA;
  1329. path.moveTo(round(xCoords[0]), round(yCoords[0]));
  1330. for (var i = 0; i < dataLen - 1; i++) {
  1331. var p0i = i == 0 ? 0 : i - 1;
  1332. p0x = xCoords[p0i];
  1333. p0y = yCoords[p0i];
  1334. p1x = xCoords[i];
  1335. p1y = yCoords[i];
  1336. p2x = xCoords[i + 1];
  1337. p2y = yCoords[i + 1];
  1338. if (i + 2 < dataLen) {
  1339. p3x = xCoords[i + 2];
  1340. p3y = yCoords[i + 2];
  1341. } else {
  1342. p3x = p2x;
  1343. p3y = p2y;
  1344. }
  1345. d1 = sqrt(pow(p0x - p1x, 2) + pow(p0y - p1y, 2));
  1346. d2 = sqrt(pow(p1x - p2x, 2) + pow(p1y - p2y, 2));
  1347. d3 = sqrt(pow(p2x - p3x, 2) + pow(p2y - p3y, 2));
  1348. // Catmull-Rom to Cubic Bezier conversion matrix
  1349. // A = 2d1^2a + 3d1^a * d2^a + d3^2a
  1350. // B = 2d3^2a + 3d3^a * d2^a + d2^2a
  1351. // [ 0 1 0 0 ]
  1352. // [ -d2^2a /N A/N d1^2a /N 0 ]
  1353. // [ 0 d3^2a /M B/M -d2^2a /M ]
  1354. // [ 0 0 1 0 ]
  1355. d3powA = pow(d3, alpha);
  1356. d3pow2A = pow(d3, alpha * 2);
  1357. d2powA = pow(d2, alpha);
  1358. d2pow2A = pow(d2, alpha * 2);
  1359. d1powA = pow(d1, alpha);
  1360. d1pow2A = pow(d1, alpha * 2);
  1361. A = 2 * d1pow2A + 3 * d1powA * d2powA + d2pow2A;
  1362. B = 2 * d3pow2A + 3 * d3powA * d2powA + d2pow2A;
  1363. N = 3 * d1powA * (d1powA + d2powA);
  1364. if (N > 0)
  1365. { N = 1 / N; }
  1366. M = 3 * d3powA * (d3powA + d2powA);
  1367. if (M > 0)
  1368. { M = 1 / M; }
  1369. bp1x = (-d2pow2A * p0x + A * p1x + d1pow2A * p2x) * N;
  1370. bp1y = (-d2pow2A * p0y + A * p1y + d1pow2A * p2y) * N;
  1371. bp2x = (d3pow2A * p1x + B * p2x - d2pow2A * p3x) * M;
  1372. bp2y = (d3pow2A * p1y + B * p2y - d2pow2A * p3y) * M;
  1373. if (bp1x == 0 && bp1y == 0) {
  1374. bp1x = p1x;
  1375. bp1y = p1y;
  1376. }
  1377. if (bp2x == 0 && bp2y == 0) {
  1378. bp2x = p2x;
  1379. bp2y = p2y;
  1380. }
  1381. path.bezierCurveTo(bp1x, bp1y, bp2x, bp2y, p2x, p2y);
  1382. }
  1383. return path;
  1384. }
  1385. function stepped(opts) {
  1386. var align = ifNull(opts.align, 1);
  1387. return function (u, seriesIdx, idx0, idx1) {
  1388. var ref = aliasProps(u, seriesIdx);
  1389. var series = ref[0];
  1390. var dataX = ref[1];
  1391. var dataY = ref[2];
  1392. var scaleX = ref[3];
  1393. var scaleY = ref[4];
  1394. var valToPosX = ref[5];
  1395. var valToPosY = ref[6];
  1396. var plotLft = ref[7];
  1397. var plotTop = ref[8];
  1398. var plotWid = ref[9];
  1399. var plotHgt = ref[10];
  1400. var stroke = new Path2D();
  1401. idx0 = nonNullIdx(dataY, idx0, idx1, 1);
  1402. idx1 = nonNullIdx(dataY, idx0, idx1, -1);
  1403. var gaps = [];
  1404. var inGap = false;
  1405. var prevYPos = round(valToPosY(dataY[idx0], scaleY, plotHgt, plotTop));
  1406. var firstXPos = round(valToPosX(dataX[idx0], scaleX, plotWid, plotLft));
  1407. var prevXPos = firstXPos;
  1408. stroke.moveTo(firstXPos, prevYPos);
  1409. for (var i = idx0 + 1; i <= idx1; i++) {
  1410. var yVal1 = dataY[i];
  1411. var x1 = round(valToPosX(dataX[i], scaleX, plotWid, plotLft));
  1412. if (yVal1 == null) {
  1413. if (series.isGap(u, seriesIdx, i)) {
  1414. addGap(gaps, prevXPos, x1);
  1415. inGap = true;
  1416. }
  1417. continue;
  1418. }
  1419. var y1 = round(valToPosY(yVal1, scaleY, plotHgt, plotTop));
  1420. if (inGap) {
  1421. addGap(gaps, prevXPos, x1);
  1422. // don't clip vertical extenders
  1423. if (prevYPos != y1) {
  1424. var halfStroke = (series.width * pxRatio) / 2;
  1425. var lastGap = gaps[gaps.length - 1];
  1426. lastGap[0] += halfStroke;
  1427. lastGap[1] -= halfStroke;
  1428. }
  1429. inGap = false;
  1430. }
  1431. if (align == 1)
  1432. { stroke.lineTo(x1, prevYPos); }
  1433. else
  1434. { stroke.lineTo(prevXPos, y1); }
  1435. stroke.lineTo(x1, y1);
  1436. prevYPos = y1;
  1437. prevXPos = x1;
  1438. }
  1439. var fill = new Path2D(stroke);
  1440. var fillTo = series.fillTo(u, seriesIdx, series.min, series.max);
  1441. var minY = round(valToPosY(fillTo, scaleY, plotHgt, plotTop));
  1442. fill.lineTo(prevXPos, minY);
  1443. fill.lineTo(firstXPos, minY);
  1444. var clip = !series.spanGaps ? clipGaps(gaps, 1, plotLft, plotTop, plotWid, plotHgt) : null;
  1445. return {
  1446. stroke: stroke,
  1447. fill: fill,
  1448. clip: clip,
  1449. };
  1450. };
  1451. }
  1452. function bars(opts) {
  1453. opts = opts || EMPTY_OBJ;
  1454. var size = ifNull(opts.size, [0.6, inf]);
  1455. var gapFactor = 1 - size[0];
  1456. var maxWidth = ifNull(size[1], inf) * pxRatio;
  1457. return function (u, seriesIdx, idx0, idx1) {
  1458. var ref = aliasProps(u, seriesIdx);
  1459. var series = ref[0];
  1460. var dataX = ref[1];
  1461. var dataY = ref[2];
  1462. var scaleX = ref[3];
  1463. var scaleY = ref[4];
  1464. var valToPosX = ref[5];
  1465. var valToPosY = ref[6];
  1466. var plotLft = ref[7];
  1467. var plotTop = ref[8];
  1468. var plotWid = ref[9];
  1469. var plotHgt = ref[10];
  1470. var colWid = valToPosX(dataX[1], scaleX, plotWid, plotLft) - valToPosX(dataX[0], scaleX, plotWid, plotLft);
  1471. var gapWid = colWid * gapFactor;
  1472. var fillToY = series.fillTo(u, seriesIdx, series.min, series.max);
  1473. var y0Pos = valToPosY(fillToY, scaleY, plotHgt, plotTop);
  1474. var strokeWidth = round(series.width * pxRatio);
  1475. var barWid = round(min(maxWidth, colWid - gapWid) - strokeWidth);
  1476. var stroke = new Path2D();
  1477. for (var i = idx0; i <= idx1; i++) {
  1478. var yVal = dataY[i];
  1479. if (yVal == null)
  1480. { continue; }
  1481. var xVal = scaleX.distr == 2 ? i : dataX[i];
  1482. // TODO: all xPos can be pre-computed once for all series in aligned set
  1483. var xPos = valToPosX(xVal, scaleX, plotWid, plotLft);
  1484. var yPos = valToPosY(yVal, scaleY, plotHgt, plotTop);
  1485. var lft = round(xPos - barWid / 2);
  1486. var btm = round(max(yPos, y0Pos));
  1487. var top = round(min(yPos, y0Pos));
  1488. var barHgt = btm - top;
  1489. stroke.rect(lft, top, barWid, barHgt);
  1490. }
  1491. var fill = series.fill != null ? new Path2D(stroke) : undefined;
  1492. return {
  1493. stroke: stroke,
  1494. fill: fill,
  1495. };
  1496. };
  1497. }
  1498. var linearPath = linear() ;
  1499. function setDefaults(d, xo, yo, initY) {
  1500. var d2 = initY ? [d[0], d[1]].concat(d.slice(2)) : [d[0]].concat(d.slice(1));
  1501. return d2.map(function (o, i) { return setDefault(o, i, xo, yo); });
  1502. }
  1503. function setDefault(o, i, xo, yo) {
  1504. return assign({}, (i == 0 || o && o.side % 2 == 0 ? xo : yo), o);
  1505. }
  1506. var nullMinMax = [null, null];
  1507. function snapNumX(self, dataMin, dataMax) {
  1508. return dataMin == null ? nullMinMax : [dataMin, dataMax];
  1509. }
  1510. var snapTimeX = snapNumX;
  1511. // this ensures that non-temporal/numeric y-axes get multiple-snapped padding added above/below
  1512. // TODO: also account for incrs when snapping to ensure top of axis gets a tick & value
  1513. function snapNumY(self, dataMin, dataMax) {
  1514. return dataMin == null ? nullMinMax : rangeNum(dataMin, dataMax, 0.1, true);
  1515. }
  1516. function snapLogY(self, dataMin, dataMax, scale) {
  1517. return dataMin == null ? nullMinMax : rangeLog(dataMin, dataMax, self.scales[scale].log, false);
  1518. }
  1519. var snapLogX = snapLogY;
  1520. // dim is logical (getClientBoundingRect) pixels, not canvas pixels
  1521. function findIncr(min, max, incrs, dim, minSpace) {
  1522. var pxPerUnit = dim / (max - min);
  1523. var minDec = (""+floor(min)).length;
  1524. for (var i = 0; i < incrs.length; i++) {
  1525. var space = incrs[i] * pxPerUnit;
  1526. var incrDec = incrs[i] < 10 ? fixedDec.get(incrs[i]) : 0;
  1527. if (space >= minSpace && minDec + incrDec < 17)
  1528. { return [incrs[i], space]; }
  1529. }
  1530. return [0, 0];
  1531. }
  1532. function pxRatioFont(font) {
  1533. var fontSize;
  1534. font = font.replace(/(\d+)px/, function (m, p1) { return (fontSize = round(p1 * pxRatio)) + 'px'; });
  1535. return [font, fontSize];
  1536. }
  1537. function uPlot(opts, data, then) {
  1538. var self = {};
  1539. function getValPct(val, scale) {
  1540. return (
  1541. scale.distr == 3
  1542. ? log10((val > 0 ? val : scale.clamp(self, val, scale.min, scale.max, scale.key)) / scale.min) / log10(scale.max / scale.min)
  1543. : (val - scale.min) / (scale.max - scale.min)
  1544. );
  1545. }
  1546. function getXPos(val, scale, wid, lft) {
  1547. var pctX = getValPct(val, scale);
  1548. return lft + pctX * wid;
  1549. }
  1550. function getYPos(val, scale, hgt, top) {
  1551. var pctY = getValPct(val, scale);
  1552. return top + (1 - pctY) * hgt;
  1553. }
  1554. self.valToPosX = getXPos;
  1555. self.valToPosY = getYPos;
  1556. var ready = false;
  1557. self.status = 0;
  1558. var root = self.root = placeDiv(UPLOT);
  1559. if (opts.id != null)
  1560. { root.id = opts.id; }
  1561. addClass(root, opts.class);
  1562. if (opts.title) {
  1563. var title = placeDiv(TITLE, root);
  1564. title.textContent = opts.title;
  1565. }
  1566. var can = placeTag("canvas");
  1567. var ctx = self.ctx = can.getContext("2d");
  1568. var wrap = placeDiv(WRAP, root);
  1569. var under = placeDiv(UNDER, wrap);
  1570. wrap.appendChild(can);
  1571. var over = placeDiv(OVER, wrap);
  1572. opts = copy(opts);
  1573. (opts.plugins || []).forEach(function (p) {
  1574. if (p.opts)
  1575. { opts = p.opts(self, opts) || opts; }
  1576. });
  1577. var ms = opts.ms || 1e-3;
  1578. var series = self.series = setDefaults(opts.series || [], xSeriesOpts, ySeriesOpts, false);
  1579. var axes = self.axes = setDefaults(opts.axes || [], xAxisOpts, yAxisOpts, true);
  1580. var scales = self.scales = {};
  1581. var xScaleKey = series[0].scale;
  1582. var drawOrderMap = {
  1583. axes: drawAxesGrid,
  1584. series: drawSeries,
  1585. };
  1586. var drawOrder = (opts.drawOrder || ["axes", "series"]).map(function (key) { return drawOrderMap[key]; });
  1587. function initScale(scaleKey) {
  1588. var sc = scales[scaleKey];
  1589. if (sc == null) {
  1590. var scaleOpts = (opts.scales || EMPTY_OBJ)[scaleKey] || EMPTY_OBJ;
  1591. if (scaleOpts.from != null) {
  1592. // ensure parent is initialized
  1593. initScale(scaleOpts.from);
  1594. // dependent scales inherit
  1595. scales[scaleKey] = assign({}, scales[scaleOpts.from], scaleOpts);
  1596. }
  1597. else {
  1598. sc = scales[scaleKey] = assign({}, (scaleKey == xScaleKey ? xScaleOpts : yScaleOpts), scaleOpts);
  1599. sc.key = scaleKey;
  1600. var isTime = sc.time;
  1601. var isLog = sc.distr == 3;
  1602. var rn = sc.range;
  1603. if (scaleKey != xScaleKey && !isArr(rn) && isObj(rn)) {
  1604. var cfg = rn;
  1605. // this is similar to snapNumY
  1606. rn = function (self, dataMin, dataMax) { return dataMin == null ? nullMinMax : rangeNum(dataMin, dataMax, cfg); };
  1607. }
  1608. sc.range = fnOrSelf(rn || (isTime ? snapTimeX : scaleKey == xScaleKey ? (isLog ? snapLogX : snapNumX) : (isLog ? snapLogY : snapNumY)));
  1609. sc.auto = fnOrSelf(sc.auto);
  1610. sc.clamp = fnOrSelf(sc.clamp || clampScale);
  1611. }
  1612. }
  1613. }
  1614. initScale("x");
  1615. initScale("y");
  1616. series.forEach(function (s, i) {
  1617. initScale(s.scale);
  1618. });
  1619. for (var k in opts.scales)
  1620. { initScale(k); }
  1621. var xScaleDistr = scales[xScaleKey].distr;
  1622. var pendScales = {};
  1623. // explicitly-set initial scales
  1624. for (var k$1 in scales) {
  1625. var sc = scales[k$1];
  1626. if (sc.min != null || sc.max != null)
  1627. { pendScales[k$1] = {min: sc.min, max: sc.max}; }
  1628. }
  1629. // self.tz = opts.tz || Intl.DateTimeFormat().resolvedOptions().timeZone;
  1630. var _tzDate = (opts.tzDate || (function (ts) { return new Date(ts / ms); }));
  1631. var _fmtDate = (opts.fmtDate || fmtDate);
  1632. var _timeAxisSplits = (ms == 1 ? timeAxisSplitsMs(_tzDate) : timeAxisSplitsS(_tzDate));
  1633. var _timeAxisVals = timeAxisVals(_tzDate, timeAxisStamps((ms == 1 ? _timeAxisStampsMs : _timeAxisStampsS), _fmtDate));
  1634. var _timeSeriesVal = timeSeriesVal(_tzDate, timeSeriesStamp(_timeSeriesStamp, _fmtDate));
  1635. var legend = assign({show: true, live: true}, opts.legend);
  1636. var showLegend = legend.show;
  1637. {
  1638. legend.stroke = fnOrSelf(legend.stroke || legendStroke);
  1639. legend.fill = fnOrSelf(legend.fill || legendFill);
  1640. }
  1641. var legendEl;
  1642. var legendRows = [];
  1643. var legendCols;
  1644. var multiValLegend = false;
  1645. if (showLegend) {
  1646. legendEl = placeTag("table", LEGEND, root);
  1647. var getMultiVals = series[1] ? series[1].values : null;
  1648. multiValLegend = getMultiVals != null;
  1649. if (multiValLegend) {
  1650. var head = placeTag("tr", LEGEND_THEAD, legendEl);
  1651. placeTag("th", null, head);
  1652. legendCols = getMultiVals(self, 1, 0);
  1653. for (var key in legendCols)
  1654. { placeTag("th", LEGEND_LABEL, head).textContent = key; }
  1655. }
  1656. else {
  1657. legendCols = {_: 0};
  1658. addClass(legendEl, LEGEND_INLINE);
  1659. legend.live && addClass(legendEl, LEGEND_LIVE);
  1660. }
  1661. }
  1662. function initLegendRow(s, i) {
  1663. if (i == 0 && (multiValLegend || !legend.live))
  1664. { return null; }
  1665. var _row = [];
  1666. var row = placeTag("tr", LEGEND_SERIES, legendEl, legendEl.childNodes[i]);
  1667. addClass(row, s.class);
  1668. if (!s.show)
  1669. { addClass(row, OFF); }
  1670. var label = placeTag("th", null, row);
  1671. var indic = placeDiv(LEGEND_MARKER, label);
  1672. if (i > 0) {
  1673. indic.style.borderColor = legend.stroke(self, i);
  1674. indic.style.backgroundColor = legend.fill(self, i);
  1675. }
  1676. var text = placeDiv(LEGEND_LABEL, label);
  1677. text.textContent = s.label;
  1678. if (i > 0) {
  1679. onMouse("click", label, function (e) {
  1680. if ( cursor._lock)
  1681. { return; }
  1682. setSeries(series.indexOf(s), {show: !s.show}, syncOpts.setSeries);
  1683. });
  1684. if (cursorFocus) {
  1685. onMouse(mouseenter, label, function (e) {
  1686. if (cursor._lock)
  1687. { return; }
  1688. setSeries(series.indexOf(s), {focus: true}, syncOpts.setSeries);
  1689. });
  1690. }
  1691. }
  1692. for (var key in legendCols) {
  1693. var v = placeTag("td", LEGEND_VALUE, row);
  1694. v.textContent = "--";
  1695. _row.push(v);
  1696. }
  1697. return _row;
  1698. }
  1699. var mouseListeners = new Map();
  1700. function onMouse(ev, targ, fn) {
  1701. var targListeners = mouseListeners.get(targ) || {};
  1702. var listener = cursor.bind[ev](self, targ, fn);
  1703. if (listener) {
  1704. on(ev, targ, targListeners[ev] = listener);
  1705. mouseListeners.set(targ, targListeners);
  1706. }
  1707. }
  1708. function offMouse(ev, targ, fn) {
  1709. var targListeners = mouseListeners.get(targ) || {};
  1710. off(ev, targ, targListeners[ev]);
  1711. targListeners[ev] = null;
  1712. }
  1713. var fullWidCss = 0;
  1714. var fullHgtCss = 0;
  1715. var plotWidCss = 0;
  1716. var plotHgtCss = 0;
  1717. // plot margins to account for axes
  1718. var plotLftCss = 0;
  1719. var plotTopCss = 0;
  1720. var plotLft = 0;
  1721. var plotTop = 0;
  1722. var plotWid = 0;
  1723. var plotHgt = 0;
  1724. self.bbox = {};
  1725. var shouldSetScales = false;
  1726. var shouldSetSize = false;
  1727. var shouldConvergeSize = false;
  1728. var shouldSetCursor = false;
  1729. var shouldSetLegend = false;
  1730. function _setSize(width, height) {
  1731. if (width != self.width || height != self.height)
  1732. { calcSize(width, height); }
  1733. resetYSeries(false);
  1734. shouldConvergeSize = true;
  1735. shouldSetSize = true;
  1736. shouldSetCursor = true;
  1737. shouldSetLegend = true;
  1738. commit();
  1739. }
  1740. function calcSize(width, height) {
  1741. // log("calcSize()", arguments);
  1742. self.width = fullWidCss = plotWidCss = width;
  1743. self.height = fullHgtCss = plotHgtCss = height;
  1744. plotLftCss = plotTopCss = 0;
  1745. calcPlotRect();
  1746. calcAxesRects();
  1747. var bb = self.bbox;
  1748. plotLft = bb.left = incrRound(plotLftCss * pxRatio, 0.5);
  1749. plotTop = bb.top = incrRound(plotTopCss * pxRatio, 0.5);
  1750. plotWid = bb.width = incrRound(plotWidCss * pxRatio, 0.5);
  1751. plotHgt = bb.height = incrRound(plotHgtCss * pxRatio, 0.5);
  1752. }
  1753. function convergeSize() {
  1754. var converged = false;
  1755. var cycleNum = 0;
  1756. while (!converged) {
  1757. cycleNum++;
  1758. var axesConverged = axesCalc(cycleNum);
  1759. var paddingConverged = paddingCalc(cycleNum);
  1760. converged = axesConverged && paddingConverged;
  1761. if (!converged) {
  1762. calcSize(self.width, self.height);
  1763. shouldSetSize = true;
  1764. }
  1765. }
  1766. }
  1767. function setSize(ref) {
  1768. var width = ref.width;
  1769. var height = ref.height;
  1770. _setSize(width, height);
  1771. }
  1772. self.setSize = setSize;
  1773. // accumulate axis offsets, reduce canvas width
  1774. function calcPlotRect() {
  1775. // easements for edge labels
  1776. var hasTopAxis = false;
  1777. var hasBtmAxis = false;
  1778. var hasRgtAxis = false;
  1779. var hasLftAxis = false;
  1780. axes.forEach(function (axis, i) {
  1781. if (axis.show && axis._show) {
  1782. var side = axis.side;
  1783. var _size = axis._size;
  1784. var isVt = side % 2;
  1785. var labelSize = axis.labelSize = (axis.label != null ? (axis.labelSize || 30) : 0);
  1786. var fullSize = _size + labelSize;
  1787. if (fullSize > 0) {
  1788. if (isVt) {
  1789. plotWidCss -= fullSize;
  1790. if (side == 3) {
  1791. plotLftCss += fullSize;
  1792. hasLftAxis = true;
  1793. }
  1794. else
  1795. { hasRgtAxis = true; }
  1796. }
  1797. else {
  1798. plotHgtCss -= fullSize;
  1799. if (side == 0) {
  1800. plotTopCss += fullSize;
  1801. hasTopAxis = true;
  1802. }
  1803. else
  1804. { hasBtmAxis = true; }
  1805. }
  1806. }
  1807. }
  1808. });
  1809. sidesWithAxes[0] = hasTopAxis;
  1810. sidesWithAxes[1] = hasRgtAxis;
  1811. sidesWithAxes[2] = hasBtmAxis;
  1812. sidesWithAxes[3] = hasLftAxis;
  1813. // hz padding
  1814. plotWidCss -= _padding[1] + _padding[3];
  1815. plotLftCss += _padding[3];
  1816. // vt padding
  1817. plotHgtCss -= _padding[2] + _padding[0];
  1818. plotTopCss += _padding[0];
  1819. }
  1820. function calcAxesRects() {
  1821. // will accum +
  1822. var off1 = plotLftCss + plotWidCss;
  1823. var off2 = plotTopCss + plotHgtCss;
  1824. // will accum -
  1825. var off3 = plotLftCss;
  1826. var off0 = plotTopCss;
  1827. function incrOffset(side, size) {
  1828. switch (side) {
  1829. case 1: off1 += size; return off1 - size;
  1830. case 2: off2 += size; return off2 - size;
  1831. case 3: off3 -= size; return off3 + size;
  1832. case 0: off0 -= size; return off0 + size;
  1833. }
  1834. }
  1835. axes.forEach(function (axis, i) {
  1836. if (axis.show && axis._show) {
  1837. var side = axis.side;
  1838. axis._pos = incrOffset(side, axis._size);
  1839. if (axis.label != null)
  1840. { axis._lpos = incrOffset(side, axis.labelSize); }
  1841. }
  1842. });
  1843. }
  1844. var cursor = (self.cursor = assign({}, cursorOpts, opts.cursor));
  1845. {
  1846. cursor._lock = false;
  1847. var points = cursor.points;
  1848. points.show = fnOrSelf(points.show);
  1849. points.size = fnOrSelf(points.size);
  1850. points.stroke = fnOrSelf(points.stroke);
  1851. points.width = fnOrSelf(points.width);
  1852. points.fill = fnOrSelf(points.fill);
  1853. }
  1854. var focus = self.focus = assign({}, opts.focus || {alpha: 0.3}, cursor.focus);
  1855. var cursorFocus = focus.prox >= 0;
  1856. // series-intersection markers
  1857. var cursorPts = [null];
  1858. function initCursorPt(s, si) {
  1859. if (si > 0) {
  1860. var pt = cursor.points.show(self, si);
  1861. if (pt) {
  1862. addClass(pt, CURSOR_PT);
  1863. addClass(pt, s.class);
  1864. trans(pt, -10, -10, plotWidCss, plotHgtCss);
  1865. over.insertBefore(pt, cursorPts[si]);
  1866. return pt;
  1867. }
  1868. }
  1869. }
  1870. function initSeries(s, i) {
  1871. var isTime = scales[s.scale].time;
  1872. var sv = s.value;
  1873. s.value = isTime ? (isStr(sv) ? timeSeriesVal(_tzDate, timeSeriesStamp(sv, _fmtDate)) : sv || _timeSeriesVal) : sv || numSeriesVal;
  1874. s.label = s.label || (isTime ? timeSeriesLabel : numSeriesLabel);
  1875. if (i > 0) {
  1876. s.width = s.width == null ? 1 : s.width;
  1877. s.paths = s.paths || linearPath || retNull;
  1878. s.fillTo = fnOrSelf(s.fillTo || seriesFillTo);
  1879. s.stroke = fnOrSelf(s.stroke || hexBlack);
  1880. s.fill = fnOrSelf(s.fill || null);
  1881. s._stroke = s._fill = s._paths = null;
  1882. var _ptDia = ptDia(s.width, 1);
  1883. var points = s.points = assign({}, {
  1884. size: _ptDia,
  1885. width: max(1, _ptDia * .2),
  1886. stroke: s.stroke,
  1887. space: _ptDia * 2,
  1888. _stroke: null,
  1889. _fill: null,
  1890. }, s.points);
  1891. points.show = fnOrSelf(points.show);
  1892. points.fill = fnOrSelf(points.fill);
  1893. points.stroke = fnOrSelf(points.stroke);
  1894. }
  1895. if (showLegend)
  1896. { legendRows.splice(i, 0, initLegendRow(s, i)); }
  1897. if ( cursor.show) {
  1898. var pt = initCursorPt(s, i);
  1899. pt && cursorPts.splice(i, 0, pt);
  1900. }
  1901. }
  1902. function addSeries(opts, si) {
  1903. si = si == null ? series.length : si;
  1904. opts = setDefault(opts, si, xSeriesOpts, ySeriesOpts);
  1905. series.splice(si, 0, opts);
  1906. initSeries(series[si], si);
  1907. }
  1908. self.addSeries = addSeries;
  1909. function delSeries(i) {
  1910. series.splice(i, 1);
  1911. showLegend && legendRows.splice(i, 1)[0][0].parentNode.remove();
  1912. cursorPts.length > 1 && cursorPts.splice(i, 1)[0].remove();
  1913. // TODO: de-init no-longer-needed scales?
  1914. }
  1915. self.delSeries = delSeries;
  1916. series.forEach(initSeries);
  1917. var sidesWithAxes = [false, false, false, false];
  1918. function initAxis(axis, i) {
  1919. axis._show = axis.show;
  1920. if (axis.show) {
  1921. var isVt = axis.side % 2;
  1922. var sc = scales[axis.scale];
  1923. // this can occur if all series specify non-default scales
  1924. if (sc == null) {
  1925. axis.scale = isVt ? series[1].scale : xScaleKey;
  1926. sc = scales[axis.scale];
  1927. }
  1928. // also set defaults for incrs & values based on axis distr
  1929. var isTime = sc.time;
  1930. axis.size = fnOrSelf(axis.size);
  1931. axis.space = fnOrSelf(axis.space);
  1932. axis.rotate = fnOrSelf(axis.rotate);
  1933. axis.incrs = fnOrSelf(axis.incrs || ( sc.distr == 2 ? wholeIncrs : (isTime ? (ms == 1 ? timeIncrsMs : timeIncrsS) : numIncrs)));
  1934. axis.splits = fnOrSelf(axis.splits || (isTime && sc.distr == 1 ? _timeAxisSplits : sc.distr == 3 ? logAxisSplits : numAxisSplits));
  1935. var av = axis.values;
  1936. axis.values = (
  1937. isTime ? (
  1938. isArr(av) ?
  1939. timeAxisVals(_tzDate, timeAxisStamps(av, _fmtDate)) :
  1940. isStr(av) ?
  1941. timeAxisVal(_tzDate, av) :
  1942. av || _timeAxisVals
  1943. ) : av || numAxisVals
  1944. );
  1945. axis.filter = fnOrSelf(axis.filter || ( sc.distr == 3 ? logAxisValsFilt : retArg1));
  1946. axis.font = pxRatioFont(axis.font);
  1947. axis.labelFont = pxRatioFont(axis.labelFont);
  1948. axis._size = axis.size(self, null, i, 0);
  1949. axis._space =
  1950. axis._rotate =
  1951. axis._incrs =
  1952. axis._found = // foundIncrSpace
  1953. axis._splits =
  1954. axis._values = null;
  1955. if (axis._size > 0)
  1956. { sidesWithAxes[i] = true; }
  1957. }
  1958. }
  1959. // set axis defaults
  1960. axes.forEach(initAxis);
  1961. function autoPadSide(self, side, sidesWithAxes, cycleNum) {
  1962. var hasTopAxis = sidesWithAxes[0];
  1963. var hasRgtAxis = sidesWithAxes[1];
  1964. var hasBtmAxis = sidesWithAxes[2];
  1965. var hasLftAxis = sidesWithAxes[3];
  1966. var ori = side % 2;
  1967. var size = 0;
  1968. if (ori == 0 && (hasLftAxis || hasRgtAxis))
  1969. { size = (side == 0 && !hasTopAxis || side == 2 && !hasBtmAxis ? round(xAxisOpts.size / 3) : 0); }
  1970. if (ori == 1 && (hasTopAxis || hasBtmAxis))
  1971. { size = (side == 1 && !hasRgtAxis || side == 3 && !hasLftAxis ? round(yAxisOpts.size / 2) : 0); }
  1972. return size;
  1973. }
  1974. var padding = self.padding = (opts.padding || [autoPadSide,autoPadSide,autoPadSide,autoPadSide]).map(function (p) { return fnOrSelf(ifNull(p, autoPadSide)); });
  1975. var _padding = self._padding = padding.map(function (p, i) { return p(self, i, sidesWithAxes, 0); });
  1976. var dataLen;
  1977. // rendered data window
  1978. var i0 = null;
  1979. var i1 = null;
  1980. var idxs = series[0].idxs;
  1981. var data0 = null;
  1982. var viaAutoScaleX = false;
  1983. function setData(_data, _resetScales) {
  1984. if (!isArr(_data) && isObj(_data)) {
  1985. _data.isGap && series.forEach(function (s) { s.isGap = _data.isGap; });
  1986. _data = _data.data;
  1987. }
  1988. _data = _data || [];
  1989. _data[0] = _data[0] || [];
  1990. self.data = _data;
  1991. data = _data.slice();
  1992. data0 = data[0];
  1993. dataLen = data0.length;
  1994. if (xScaleDistr == 2)
  1995. { data[0] = data0.map(function (v, i) { return i; }); }
  1996. self._data = data;
  1997. resetYSeries(true);
  1998. fire("setData");
  1999. if (_resetScales !== false) {
  2000. var xsc = scales[xScaleKey];
  2001. if (xsc.auto(self, viaAutoScaleX))
  2002. { autoScaleX(); }
  2003. else
  2004. { _setScale(xScaleKey, xsc.min, xsc.max); }
  2005. shouldSetCursor = true;
  2006. shouldSetLegend = true;
  2007. commit();
  2008. }
  2009. }
  2010. self.setData = setData;
  2011. function autoScaleX() {
  2012. var assign, assign$1;
  2013. viaAutoScaleX = true;
  2014. var _min, _max;
  2015. if (dataLen > 0) {
  2016. i0 = idxs[0] = 0;
  2017. i1 = idxs[1] = dataLen - 1;
  2018. _min = data[0][i0];
  2019. _max = data[0][i1];
  2020. if (xScaleDistr == 2) {
  2021. _min = i0;
  2022. _max = i1;
  2023. }
  2024. else if (dataLen == 1) {
  2025. if (xScaleDistr == 3)
  2026. { (assign = rangeLog(_min, _min, scales[xScaleKey].log, false), _min = assign[0], _max = assign[1]); }
  2027. else if (scales[xScaleKey].time)
  2028. { _max = _min + 86400 / ms; }
  2029. else
  2030. { (assign$1 = rangeNum(_min, _max, 0.1, true), _min = assign$1[0], _max = assign$1[1]); }
  2031. }
  2032. }
  2033. else {
  2034. i0 = idxs[0] = _min = null;
  2035. i1 = idxs[1] = _max = null;
  2036. }
  2037. _setScale(xScaleKey, _min, _max);
  2038. }
  2039. function setCtxStyle(stroke, width, dash, fill) {
  2040. ctx.strokeStyle = stroke || transparent;
  2041. ctx.lineWidth = width;
  2042. ctx.lineJoin = "round";
  2043. ctx.setLineDash(dash || []);
  2044. ctx.fillStyle = fill || transparent;
  2045. }
  2046. function setScales() {
  2047. // log("setScales()", arguments);
  2048. // wip scales
  2049. var wipScales = copy(scales);
  2050. for (var k in wipScales) {
  2051. var wsc = wipScales[k];
  2052. var psc = pendScales[k];
  2053. if (psc != null && psc.min != null) {
  2054. assign(wsc, psc);
  2055. // explicitly setting the x-scale invalidates everything (acts as redraw)
  2056. if (k == xScaleKey)
  2057. { resetYSeries(true); }
  2058. }
  2059. else if (k != xScaleKey) {
  2060. if (dataLen == 0 && wsc.from == null) {
  2061. var minMax = wsc.range(self, null, null, k);
  2062. wsc.min = minMax[0];
  2063. wsc.max = minMax[1];
  2064. }
  2065. else {
  2066. wsc.min = inf;
  2067. wsc.max = -inf;
  2068. }
  2069. }
  2070. }
  2071. if (dataLen > 0) {
  2072. // pre-range y-scales from y series' data values
  2073. series.forEach(function (s, i) {
  2074. var k = s.scale;
  2075. var wsc = wipScales[k];
  2076. var psc = pendScales[k];
  2077. if (i == 0) {
  2078. var minMax = wsc.range(self, wsc.min, wsc.max, k);
  2079. wsc.min = minMax[0];
  2080. wsc.max = minMax[1];
  2081. i0 = closestIdx(wsc.min, data[0]);
  2082. i1 = closestIdx(wsc.max, data[0]);
  2083. // closest indices can be outside of view
  2084. if (data[0][i0] < wsc.min)
  2085. { i0++; }
  2086. if (data[0][i1] > wsc.max)
  2087. { i1--; }
  2088. s.min = data0[i0];
  2089. s.max = data0[i1];
  2090. }
  2091. else if (s.show && s.auto && wsc.auto(self, viaAutoScaleX) && (psc == null || psc.min == null)) {
  2092. // only run getMinMax() for invalidated series data, else reuse
  2093. var minMax$1 = s.min == null ? (wsc.distr == 3 ? getMinMaxLog(data[i], i0, i1) : getMinMax(data[i], i0, i1, s.sorted)) : [s.min, s.max];
  2094. // initial min/max
  2095. wsc.min = min(wsc.min, s.min = minMax$1[0]);
  2096. wsc.max = max(wsc.max, s.max = minMax$1[1]);
  2097. }
  2098. s.idxs[0] = i0;
  2099. s.idxs[1] = i1;
  2100. });
  2101. // range independent scales
  2102. for (var k$1 in wipScales) {
  2103. var wsc$1 = wipScales[k$1];
  2104. var psc$1 = pendScales[k$1];
  2105. if (wsc$1.from == null && (psc$1 == null || psc$1.min == null)) {
  2106. var minMax$1 = wsc$1.range(
  2107. self,
  2108. wsc$1.min == inf ? null : wsc$1.min,
  2109. wsc$1.max == -inf ? null : wsc$1.max,
  2110. k$1
  2111. );
  2112. wsc$1.min = minMax$1[0];
  2113. wsc$1.max = minMax$1[1];
  2114. }
  2115. }
  2116. }
  2117. // range dependent scales
  2118. for (var k$2 in wipScales) {
  2119. var wsc$2 = wipScales[k$2];
  2120. if (wsc$2.from != null) {
  2121. var base = wipScales[wsc$2.from];
  2122. var minMax$2 = wsc$2.range(self, base.min, base.max, k$2);
  2123. wsc$2.min = minMax$2[0];
  2124. wsc$2.max = minMax$2[1];
  2125. }
  2126. }
  2127. var changed = {};
  2128. var anyChanged = false;
  2129. for (var k$3 in wipScales) {
  2130. var wsc$3 = wipScales[k$3];
  2131. var sc = scales[k$3];
  2132. if (sc.min != wsc$3.min || sc.max != wsc$3.max) {
  2133. sc.min = wsc$3.min;
  2134. sc.max = wsc$3.max;
  2135. changed[k$3] = anyChanged = true;
  2136. }
  2137. }
  2138. if (anyChanged) {
  2139. // invalidate paths of all series on changed scales
  2140. series.forEach(function (s) {
  2141. if (changed[s.scale])
  2142. { s._paths = null; }
  2143. });
  2144. for (var k$4 in changed) {
  2145. shouldConvergeSize = true;
  2146. fire("setScale", k$4);
  2147. }
  2148. if ( cursor.show)
  2149. { shouldSetCursor = true; }
  2150. }
  2151. for (var k$5 in pendScales)
  2152. { pendScales[k$5] = null; }
  2153. }
  2154. // TODO: drawWrap(si, drawPoints) (save, restore, translate, clip)
  2155. function drawPoints(si) {
  2156. // log("drawPoints()", arguments);
  2157. var s = series[si];
  2158. var p = s.points;
  2159. var width = roundDec(p.width * pxRatio, 3);
  2160. var offset = (width % 2) / 2;
  2161. var isStroked = p.width > 0;
  2162. var rad = (p.size - p.width) / 2 * pxRatio;
  2163. var dia = roundDec(rad * 2, 3);
  2164. ctx.translate(offset, offset);
  2165. ctx.save();
  2166. ctx.beginPath();
  2167. ctx.rect(
  2168. plotLft - dia,
  2169. plotTop - dia,
  2170. plotWid + dia * 2,
  2171. plotHgt + dia * 2
  2172. );
  2173. ctx.clip();
  2174. ctx.globalAlpha = s.alpha;
  2175. var path = new Path2D();
  2176. for (var pi = i0; pi <= i1; pi++) {
  2177. if (data[si][pi] != null) {
  2178. var x = round(getXPos(data[0][pi], scales[xScaleKey], plotWid, plotLft));
  2179. var y = round(getYPos(data[si][pi], scales[s.scale], plotHgt, plotTop));
  2180. path.moveTo(x + rad, y);
  2181. path.arc(x, y, rad, 0, PI * 2);
  2182. }
  2183. }
  2184. var _stroke = p._stroke = p.stroke(self, si);
  2185. var _fill = p._fill = p.fill(self, si);
  2186. setCtxStyle(
  2187. _stroke,
  2188. width,
  2189. p.dash,
  2190. _fill || (isStroked ? "#fff" : s._stroke)
  2191. );
  2192. ctx.fill(path);
  2193. isStroked && ctx.stroke(path);
  2194. ctx.globalAlpha = 1;
  2195. ctx.restore();
  2196. ctx.translate(-offset, -offset);
  2197. }
  2198. // grabs the nearest indices with y data outside of x-scale limits
  2199. function getOuterIdxs(ydata) {
  2200. var _i0 = clamp(i0 - 1, 0, dataLen - 1);
  2201. var _i1 = clamp(i1 + 1, 0, dataLen - 1);
  2202. while (ydata[_i0] == null && _i0 > 0)
  2203. { _i0--; }
  2204. while (ydata[_i1] == null && _i1 < dataLen - 1)
  2205. { _i1++; }
  2206. return [_i0, _i1];
  2207. }
  2208. var dir = 1;
  2209. function drawSeries() {
  2210. if (dataLen > 0) {
  2211. // path building loop must be before draw loop to ensure that all bands are fully constructed
  2212. series.forEach(function (s, i) {
  2213. if (i > 0 && s.show && s._paths == null) {
  2214. var _idxs = getOuterIdxs(data[i]);
  2215. s._paths = s.paths(self, i, _idxs[0], _idxs[1]);
  2216. }
  2217. });
  2218. series.forEach(function (s, i) {
  2219. if (i > 0 && s.show) {
  2220. if (s._paths)
  2221. { drawPath(i); }
  2222. if (s.points.show(self, i, i0, i1))
  2223. { drawPoints(i); }
  2224. fire("drawSeries", i);
  2225. }
  2226. });
  2227. }
  2228. }
  2229. function drawPath(si) {
  2230. var s = series[si];
  2231. if (dir == 1) {
  2232. var ref = s._paths;
  2233. var stroke = ref.stroke;
  2234. var fill = ref.fill;
  2235. var clip = ref.clip;
  2236. var width = roundDec(s.width * pxRatio, 3);
  2237. var offset = (width % 2) / 2;
  2238. var _stroke = s._stroke = s.stroke(self, si);
  2239. var _fill = s._fill = s.fill(self, si);
  2240. setCtxStyle(_stroke, width, s.dash, _fill);
  2241. ctx.globalAlpha = s.alpha;
  2242. ctx.translate(offset, offset);
  2243. ctx.save();
  2244. var lft = plotLft,
  2245. top = plotTop,
  2246. wid = plotWid,
  2247. hgt = plotHgt;
  2248. var halfWid = width * pxRatio / 2;
  2249. if (s.min == 0)
  2250. { hgt += halfWid; }
  2251. if (s.max == 0) {
  2252. top -= halfWid;
  2253. hgt += halfWid;
  2254. }
  2255. ctx.beginPath();
  2256. ctx.rect(lft, top, wid, hgt);
  2257. ctx.clip();
  2258. if (clip != null)
  2259. { ctx.clip(clip); }
  2260. if (s.band) {
  2261. ctx.fill(stroke);
  2262. width && ctx.stroke(stroke);
  2263. }
  2264. else {
  2265. if (_fill != null)
  2266. { ctx.fill(fill); }
  2267. width && ctx.stroke(stroke);
  2268. }
  2269. ctx.restore();
  2270. ctx.translate(-offset, -offset);
  2271. ctx.globalAlpha = 1;
  2272. }
  2273. if (s.band)
  2274. { dir *= -1; }
  2275. }
  2276. function getIncrSpace(axisIdx, min, max, fullDim) {
  2277. var axis = axes[axisIdx];
  2278. var incrSpace;
  2279. if (fullDim <= 0)
  2280. { incrSpace = [0, 0]; }
  2281. else {
  2282. var minSpace = axis._space = axis.space(self, axisIdx, min, max, fullDim);
  2283. var incrs = axis._incrs = axis.incrs(self, axisIdx, min, max, fullDim, minSpace);
  2284. incrSpace = axis._found = findIncr(min, max, incrs, fullDim, minSpace);
  2285. }
  2286. return incrSpace;
  2287. }
  2288. function drawOrthoLines(offs, filts, ori, side, pos0, len, width, stroke, dash) {
  2289. var offset = (width % 2) / 2;
  2290. ctx.translate(offset, offset);
  2291. setCtxStyle(stroke, width, dash);
  2292. ctx.beginPath();
  2293. var x0, y0, x1, y1, pos1 = pos0 + (side == 0 || side == 3 ? -len : len);
  2294. if (ori == 0) {
  2295. y0 = pos0;
  2296. y1 = pos1;
  2297. }
  2298. else {
  2299. x0 = pos0;
  2300. x1 = pos1;
  2301. }
  2302. offs.forEach(function (off, i) {
  2303. if (filts[i] == null)
  2304. { return; }
  2305. if (ori == 0)
  2306. { x0 = x1 = off; }
  2307. else
  2308. { y0 = y1 = off; }
  2309. ctx.moveTo(x0, y0);
  2310. ctx.lineTo(x1, y1);
  2311. });
  2312. ctx.stroke();
  2313. ctx.translate(-offset, -offset);
  2314. }
  2315. function axesCalc(cycleNum) {
  2316. // log("axesCalc()", arguments);
  2317. var converged = true;
  2318. axes.forEach(function (axis, i) {
  2319. if (!axis.show)
  2320. { return; }
  2321. var scale = scales[axis.scale];
  2322. if (scale.min == null) {
  2323. if (axis._show) {
  2324. converged = false;
  2325. axis._show = false;
  2326. resetYSeries(false);
  2327. }
  2328. return;
  2329. }
  2330. else {
  2331. if (!axis._show) {
  2332. converged = false;
  2333. axis._show = true;
  2334. resetYSeries(false);
  2335. }
  2336. }
  2337. var side = axis.side;
  2338. var ori = side % 2;
  2339. var min = scale.min;
  2340. var max = scale.max; // // should this toggle them ._show = false
  2341. var ref = getIncrSpace(i, min, max, ori == 0 ? plotWidCss : plotHgtCss);
  2342. var _incr = ref[0];
  2343. var _space = ref[1];
  2344. if (_space == 0)
  2345. { return; }
  2346. // if we're using index positions, force first tick to match passed index
  2347. var forceMin = scale.distr == 2;
  2348. var _splits = axis._splits = axis.splits(self, i, min, max, _incr, _space, forceMin);
  2349. // tick labels
  2350. // BOO this assumes a specific data/series
  2351. var splits = scale.distr == 2 ? _splits.map(function (i) { return data0[i]; }) : _splits;
  2352. var incr = scale.distr == 2 ? data0[_splits[1]] - data0[_splits[0]] : _incr;
  2353. var values = axis._values = axis.values(self, axis.filter(self, splits, i, _space, incr), i, _space, incr);
  2354. // rotating of labels only supported on bottom x axis
  2355. axis._rotate = side == 2 ? axis.rotate(self, values, i, _space) : 0;
  2356. var oldSize = axis._size;
  2357. axis._size = ceil(axis.size(self, values, i, cycleNum));
  2358. if (oldSize != null && axis._size != oldSize) // ready && ?
  2359. { converged = false; }
  2360. });
  2361. return converged;
  2362. }
  2363. function paddingCalc(cycleNum) {
  2364. var converged = true;
  2365. padding.forEach(function (p, i) {
  2366. var _p = p(self, i, sidesWithAxes, cycleNum);
  2367. if (_p != _padding[i])
  2368. { converged = false; }
  2369. _padding[i] = _p;
  2370. });
  2371. return converged;
  2372. }
  2373. function drawAxesGrid() {
  2374. axes.forEach(function (axis, i) {
  2375. if (!axis.show || !axis._show)
  2376. { return; }
  2377. var scale = scales[axis.scale];
  2378. var side = axis.side;
  2379. var ori = side % 2;
  2380. var getPos = ori == 0 ? getXPos : getYPos;
  2381. var plotDim = ori == 0 ? plotWid : plotHgt;
  2382. var plotOff = ori == 0 ? plotLft : plotTop;
  2383. var axisGap = round(axis.gap * pxRatio);
  2384. var ticks = axis.ticks;
  2385. var tickSize = ticks.show ? round(ticks.size * pxRatio) : 0;
  2386. var ref = axis._found;
  2387. var _incr = ref[0];
  2388. var _space = ref[1];
  2389. var _splits = axis._splits;
  2390. // tick labels
  2391. // BOO this assumes a specific data/series
  2392. var splits = scale.distr == 2 ? _splits.map(function (i) { return data0[i]; }) : _splits;
  2393. var incr = scale.distr == 2 ? data0[_splits[1]] - data0[_splits[0]] : _incr;
  2394. // rotating of labels only supported on bottom x axis
  2395. var angle = axis._rotate * -PI/180;
  2396. var basePos = round(axis._pos * pxRatio);
  2397. var shiftAmt = tickSize + axisGap;
  2398. var shiftDir = ori == 0 && side == 0 || ori == 1 && side == 3 ? -1 : 1;
  2399. var finalPos = basePos + shiftAmt * shiftDir;
  2400. var y = ori == 0 ? finalPos : 0;
  2401. var x = ori == 1 ? finalPos : 0;
  2402. ctx.font = axis.font[0];
  2403. ctx.fillStyle = axis.stroke || hexBlack; // rgba?
  2404. ctx.textAlign = axis.align == 1 ? LEFT :
  2405. axis.align == 2 ? RIGHT :
  2406. angle > 0 ? LEFT :
  2407. angle < 0 ? RIGHT :
  2408. ori == 0 ? "center" : side == 3 ? RIGHT : LEFT;
  2409. ctx.textBaseline = angle ||
  2410. ori == 1 ? "middle" : side == 2 ? TOP : BOTTOM;
  2411. var lineHeight = axis.font[1] * lineMult;
  2412. var canOffs = _splits.map(function (val) { return round(getPos(val, scale, plotDim, plotOff)); });
  2413. axis._values.forEach(function (val, i) {
  2414. if (val == null)
  2415. { return; }
  2416. if (ori == 0)
  2417. { x = canOffs[i]; }
  2418. else
  2419. { y = canOffs[i]; }
  2420. (""+val).split(/\n/gm).forEach(function (text, j) {
  2421. if (angle) {
  2422. ctx.save();
  2423. ctx.translate(x, y + j * lineHeight);
  2424. ctx.rotate(angle);
  2425. ctx.fillText(text, 0, 0);
  2426. ctx.restore();
  2427. }
  2428. else
  2429. { ctx.fillText(text, x, y + j * lineHeight); }
  2430. });
  2431. });
  2432. // axis label
  2433. if (axis.label) {
  2434. ctx.save();
  2435. var baseLpos = round(axis._lpos * pxRatio);
  2436. if (ori == 1) {
  2437. x = y = 0;
  2438. ctx.translate(
  2439. baseLpos,
  2440. round(plotTop + plotHgt / 2)
  2441. );
  2442. ctx.rotate((side == 3 ? -PI : PI) / 2);
  2443. }
  2444. else {
  2445. x = round(plotLft + plotWid / 2);
  2446. y = baseLpos;
  2447. }
  2448. ctx.font = axis.labelFont[0];
  2449. // ctx.fillStyle = axis.labelStroke || hexBlack; // rgba?
  2450. ctx.textAlign = "center";
  2451. ctx.textBaseline = side == 2 ? TOP : BOTTOM;
  2452. ctx.fillText(axis.label, x, y);
  2453. ctx.restore();
  2454. }
  2455. // ticks
  2456. if (ticks.show) {
  2457. drawOrthoLines(
  2458. canOffs,
  2459. ticks.filter(self, splits, i, _space, incr),
  2460. ori,
  2461. side,
  2462. basePos,
  2463. tickSize,
  2464. roundDec(ticks.width * pxRatio, 3),
  2465. ticks.stroke
  2466. );
  2467. }
  2468. // grid
  2469. var grid = axis.grid;
  2470. if (grid.show) {
  2471. drawOrthoLines(
  2472. canOffs,
  2473. grid.filter(self, splits, i, _space, incr),
  2474. ori,
  2475. ori == 0 ? 2 : 1,
  2476. ori == 0 ? plotTop : plotLft,
  2477. ori == 0 ? plotHgt : plotWid,
  2478. roundDec(grid.width * pxRatio, 3),
  2479. grid.stroke,
  2480. grid.dash
  2481. );
  2482. }
  2483. });
  2484. fire("drawAxes");
  2485. }
  2486. function resetYSeries(minMax) {
  2487. // log("resetYSeries()", arguments);
  2488. series.forEach(function (s, i) {
  2489. if (i > 0) {
  2490. s._paths = null;
  2491. if (minMax) {
  2492. s.min = null;
  2493. s.max = null;
  2494. }
  2495. }
  2496. });
  2497. }
  2498. var queuedCommit = false;
  2499. // could do rAF instead of microTask, or Promose.resolve().then()
  2500. function commit() {
  2501. if (!queuedCommit) {
  2502. microTask(_commit);
  2503. queuedCommit = true;
  2504. }
  2505. }
  2506. function _commit() {
  2507. // log("_commit()", arguments);
  2508. if (shouldSetScales) {
  2509. setScales();
  2510. shouldSetScales = false;
  2511. }
  2512. if (shouldConvergeSize) {
  2513. convergeSize();
  2514. shouldConvergeSize = false;
  2515. }
  2516. if (shouldSetSize) {
  2517. setStylePx(under, LEFT, plotLftCss);
  2518. setStylePx(under, TOP, plotTopCss);
  2519. setStylePx(under, WIDTH, plotWidCss);
  2520. setStylePx(under, HEIGHT, plotHgtCss);
  2521. setStylePx(over, LEFT, plotLftCss);
  2522. setStylePx(over, TOP, plotTopCss);
  2523. setStylePx(over, WIDTH, plotWidCss);
  2524. setStylePx(over, HEIGHT, plotHgtCss);
  2525. setStylePx(wrap, WIDTH, fullWidCss);
  2526. setStylePx(wrap, HEIGHT, fullHgtCss);
  2527. can.width = round(fullWidCss * pxRatio);
  2528. can.height = round(fullHgtCss * pxRatio);
  2529. syncRect();
  2530. fire("setSize");
  2531. shouldSetSize = false;
  2532. }
  2533. // if (shouldSetSelect) {
  2534. // TODO: update .u-select metrics (if visible)
  2535. // setStylePx(selectDiv, TOP, select.top = 0);
  2536. // setStylePx(selectDiv, LEFT, select.left = 0);
  2537. // setStylePx(selectDiv, WIDTH, select.width = 0);
  2538. // setStylePx(selectDiv, HEIGHT, select.height = 0);
  2539. // shouldSetSelect = false;
  2540. // }
  2541. if ( cursor.show && shouldSetCursor) {
  2542. updateCursor();
  2543. shouldSetCursor = false;
  2544. }
  2545. // if (FEAT_LEGEND && legend.show && legend.live && shouldSetLegend) {}
  2546. if (fullWidCss > 0 && fullHgtCss > 0) {
  2547. ctx.clearRect(0, 0, can.width, can.height);
  2548. fire("drawClear");
  2549. drawOrder.forEach(function (fn) { return fn(); });
  2550. fire("draw");
  2551. }
  2552. if (!ready) {
  2553. ready = true;
  2554. self.status = 1;
  2555. fire("ready");
  2556. }
  2557. viaAutoScaleX = false;
  2558. queuedCommit = false;
  2559. }
  2560. self.redraw = function (rebuildPaths) {
  2561. if (rebuildPaths !== false)
  2562. { _setScale(xScaleKey, scales[xScaleKey].min, scales[xScaleKey].max); }
  2563. else
  2564. { commit(); }
  2565. };
  2566. // redraw() => setScale('x', scales.x.min, scales.x.max);
  2567. // explicit, never re-ranged (is this actually true? for x and y)
  2568. function setScale(key, opts) {
  2569. var sc = scales[key];
  2570. if (sc.from == null) {
  2571. if (dataLen == 0) {
  2572. var minMax = sc.range(self, opts.min, opts.max, key);
  2573. opts.min = minMax[0];
  2574. opts.max = minMax[1];
  2575. }
  2576. if (dataLen > 1 && opts.min != null && opts.max != null && opts.max - opts.min < 1e-16)
  2577. { return; }
  2578. if (key == xScaleKey) {
  2579. if (sc.distr == 2 && dataLen > 0) {
  2580. opts.min = closestIdx(opts.min, data[0]);
  2581. opts.max = closestIdx(opts.max, data[0]);
  2582. }
  2583. }
  2584. // log("setScale()", arguments);
  2585. pendScales[key] = opts;
  2586. shouldSetScales = true;
  2587. commit();
  2588. }
  2589. }
  2590. self.setScale = setScale;
  2591. // INTERACTION
  2592. var vt;
  2593. var hz;
  2594. // starting position before cursor.move
  2595. var rawMouseLeft0;
  2596. var rawMouseTop0;
  2597. // starting position
  2598. var mouseLeft0;
  2599. var mouseTop0;
  2600. // current position before cursor.move
  2601. var rawMouseLeft1;
  2602. var rawMouseTop1;
  2603. // current position
  2604. var mouseLeft1;
  2605. var mouseTop1;
  2606. var dragging = false;
  2607. var drag = cursor.drag;
  2608. var dragX = drag.x;
  2609. var dragY = drag.y;
  2610. if ( cursor.show) {
  2611. if (cursor.x) {
  2612. mouseLeft1 = cursor.left;
  2613. vt = placeDiv(CURSOR_X, over);
  2614. }
  2615. if (cursor.y) {
  2616. mouseTop1 = cursor.top;
  2617. hz = placeDiv(CURSOR_Y, over);
  2618. }
  2619. }
  2620. var select = self.select = assign({
  2621. show: true,
  2622. over: true,
  2623. left: 0,
  2624. width: 0,
  2625. top: 0,
  2626. height: 0,
  2627. }, opts.select);
  2628. var selectDiv = select.show ? placeDiv(SELECT, select.over ? over : under) : null;
  2629. function setSelect(opts, _fire) {
  2630. if (select.show) {
  2631. for (var prop in opts)
  2632. { setStylePx(selectDiv, prop, select[prop] = opts[prop]); }
  2633. _fire !== false && fire("setSelect");
  2634. }
  2635. }
  2636. self.setSelect = setSelect;
  2637. function toggleDOM(i, onOff) {
  2638. var s = series[i];
  2639. var label = showLegend ? legendRows[i][0].parentNode : null;
  2640. if (s.show)
  2641. { label && remClass(label, OFF); }
  2642. else {
  2643. label && addClass(label, OFF);
  2644. cursorPts.length > 1 && trans(cursorPts[i], -10, -10, plotWidCss, plotHgtCss);
  2645. }
  2646. }
  2647. function _setScale(key, min, max) {
  2648. setScale(key, {min: min, max: max});
  2649. }
  2650. function setSeries(i, opts, pub) {
  2651. // log("setSeries()", arguments);
  2652. var s = series[i];
  2653. // will this cause redundant commit() if both show and focus are set?
  2654. if (opts.focus != null)
  2655. { setFocus(i); }
  2656. if (opts.show != null) {
  2657. s.show = opts.show;
  2658. toggleDOM(i, opts.show);
  2659. if (s.band) {
  2660. // not super robust, will break if two bands are adjacent
  2661. var ip = series[i+1] && series[i+1].band ? i+1 : i-1;
  2662. series[ip].show = s.show;
  2663. toggleDOM(ip, opts.show);
  2664. }
  2665. _setScale(s.scale, null, null);
  2666. commit();
  2667. }
  2668. fire("setSeries", i, opts);
  2669. pub && sync.pub("setSeries", self, i, opts);
  2670. }
  2671. self.setSeries = setSeries;
  2672. function _alpha(i, value) {
  2673. series[i].alpha = value;
  2674. if ( cursor.show && cursorPts[i])
  2675. { cursorPts[i].style.opacity = value; }
  2676. if ( showLegend && legendRows[i])
  2677. { legendRows[i][0].parentNode.style.opacity = value; }
  2678. }
  2679. function _setAlpha(i, value) {
  2680. var s = series[i];
  2681. _alpha(i, value);
  2682. if (s.band) {
  2683. // not super robust, will break if two bands are adjacent
  2684. var ip = series[i+1].band ? i+1 : i-1;
  2685. _alpha(ip, value);
  2686. }
  2687. }
  2688. // y-distance
  2689. var closestDist;
  2690. var closestSeries;
  2691. var focusedSeries;
  2692. function setFocus(i) {
  2693. if (i != focusedSeries) {
  2694. // log("setFocus()", arguments);
  2695. series.forEach(function (s, i2) {
  2696. _setAlpha(i2, i == null || i2 == 0 || i2 == i ? 1 : focus.alpha);
  2697. });
  2698. focusedSeries = i;
  2699. commit();
  2700. }
  2701. }
  2702. if (showLegend && cursorFocus) {
  2703. on(mouseleave, legendEl, function (e) {
  2704. if (cursor._lock)
  2705. { return; }
  2706. setSeries(null, {focus: false}, syncOpts.setSeries);
  2707. updateCursor();
  2708. });
  2709. }
  2710. function scaleValueAtPos(pos, scale) {
  2711. var dim = plotWidCss;
  2712. if (scale != xScaleKey) {
  2713. dim = plotHgtCss;
  2714. pos = dim - pos;
  2715. }
  2716. var pct = pos / dim;
  2717. var sc = scales[scale],
  2718. _min = sc.min,
  2719. _max = sc.max;
  2720. if (sc.distr == 3) {
  2721. _min = log10(_min);
  2722. _max = log10(_max);
  2723. return pow(10, _min + (_max - _min) * pct);
  2724. }
  2725. else
  2726. { return _min + (_max - _min) * pct; }
  2727. }
  2728. function closestIdxFromXpos(pos) {
  2729. var v = scaleValueAtPos(pos, xScaleKey);
  2730. return closestIdx(v, data[0], i0, i1);
  2731. }
  2732. self.valToIdx = function (val) { return closestIdx(val, data[0]); };
  2733. self.posToIdx = closestIdxFromXpos;
  2734. self.posToVal = scaleValueAtPos;
  2735. self.valToPos = function (val, scale, can) { return (
  2736. scale == xScaleKey ?
  2737. getXPos(val, scales[scale],
  2738. can ? plotWid : plotWidCss,
  2739. can ? plotLft : 0
  2740. ) :
  2741. getYPos(val, scales[scale],
  2742. can ? plotHgt : plotHgtCss,
  2743. can ? plotTop : 0
  2744. )
  2745. ); };
  2746. // defers calling expensive functions
  2747. function batch(fn) {
  2748. fn(self);
  2749. commit();
  2750. }
  2751. self.batch = batch;
  2752. (self.setCursor = function (opts) {
  2753. mouseLeft1 = opts.left;
  2754. mouseTop1 = opts.top;
  2755. // assign(cursor, opts);
  2756. updateCursor();
  2757. });
  2758. var cursorRaf = 0;
  2759. function updateCursor(ts, src) {
  2760. var assign;
  2761. // ts == null && log("updateCursor()", arguments);
  2762. cursorRaf = 0;
  2763. rawMouseLeft1 = mouseLeft1;
  2764. rawMouseTop1 = mouseTop1;
  2765. (assign = cursor.move(self, mouseLeft1, mouseTop1), mouseLeft1 = assign[0], mouseTop1 = assign[1]);
  2766. if (cursor.show) {
  2767. cursor.x && trans(vt, round(mouseLeft1), 0, plotWidCss, plotHgtCss);
  2768. cursor.y && trans(hz, 0, round(mouseTop1), plotWidCss, plotHgtCss);
  2769. }
  2770. var idx;
  2771. // when zooming to an x scale range between datapoints the binary search
  2772. // for nearest min/max indices results in this condition. cheap hack :D
  2773. var noDataInRange = i0 > i1;
  2774. closestDist = inf;
  2775. // if cursor hidden, hide points & clear legend vals
  2776. if (mouseLeft1 < 0 || dataLen == 0 || noDataInRange) {
  2777. idx = null;
  2778. for (var i = 0; i < series.length; i++) {
  2779. if (i > 0) {
  2780. cursorPts.length > 1 && trans(cursorPts[i], -10, -10, plotWidCss, plotHgtCss);
  2781. }
  2782. if (showLegend && legend.live) {
  2783. if (i == 0 && multiValLegend)
  2784. { continue; }
  2785. for (var j = 0; j < legendRows[i].length; j++)
  2786. { legendRows[i][j].firstChild.nodeValue = '--'; }
  2787. }
  2788. }
  2789. if (cursorFocus)
  2790. { setSeries(null, {focus: true}, syncOpts.setSeries); }
  2791. }
  2792. else {
  2793. // let pctY = 1 - (y / rect.height);
  2794. var valAtPos = scaleValueAtPos(mouseLeft1, xScaleKey);
  2795. idx = closestIdx(valAtPos, data[0], i0, i1);
  2796. var scX = scales[xScaleKey];
  2797. var xPos = incrRoundUp(getXPos(data[0][idx], scX, plotWidCss, 0), 0.5);
  2798. for (var i$1 = 0; i$1 < series.length; i$1++) {
  2799. var s = series[i$1];
  2800. var idx2 = cursor.dataIdx(self, i$1, idx, valAtPos);
  2801. var xPos2 = idx2 == idx ? xPos : incrRoundUp(getXPos(data[0][idx2], scX, plotWidCss, 0), 0.5);
  2802. if (i$1 > 0 && s.show) {
  2803. var valAtIdx = data[i$1][idx2];
  2804. var yPos = valAtIdx == null ? -10 : incrRoundUp(getYPos(valAtIdx, scales[s.scale], plotHgtCss, 0), 0.5);
  2805. if (yPos > 0) {
  2806. var dist = abs(yPos - mouseTop1);
  2807. if (dist <= closestDist) {
  2808. closestDist = dist;
  2809. closestSeries = i$1;
  2810. }
  2811. }
  2812. cursorPts.length > 1 && trans(cursorPts[i$1], xPos2, yPos, plotWidCss, plotHgtCss);
  2813. }
  2814. if (showLegend && legend.live) {
  2815. if ((idx2 == cursor.idx && !shouldSetLegend) || i$1 == 0 && multiValLegend)
  2816. { continue; }
  2817. var src$1 = i$1 == 0 && xScaleDistr == 2 ? data0 : data[i$1];
  2818. var vals = multiValLegend ? s.values(self, i$1, idx2) : {_: s.value(self, src$1[idx2], i$1, idx2)};
  2819. var j$1 = 0;
  2820. for (var k in vals)
  2821. { legendRows[i$1][j$1++].firstChild.nodeValue = vals[k]; }
  2822. }
  2823. }
  2824. shouldSetLegend = false;
  2825. }
  2826. // nit: cursor.drag.setSelect is assumed always true
  2827. if (select.show && dragging) {
  2828. if (src != null) {
  2829. var ref = syncOpts.scales;
  2830. var xKey = ref[0];
  2831. var yKey = ref[1];
  2832. // match the dragX/dragY implicitness/explicitness of src
  2833. var sdrag = src.cursor.drag;
  2834. dragX = sdrag._x;
  2835. dragY = sdrag._y;
  2836. if (xKey) {
  2837. var sc = scales[xKey];
  2838. var srcLeft = src.posToVal(src.select.left, xKey);
  2839. var srcRight = src.posToVal(src.select.left + src.select.width, xKey);
  2840. select.left = getXPos(srcLeft, sc, plotWidCss, 0);
  2841. select.width = abs(select.left - getXPos(srcRight, sc, plotWidCss, 0));
  2842. setStylePx(selectDiv, LEFT, select.left);
  2843. setStylePx(selectDiv, WIDTH, select.width);
  2844. if (!yKey) {
  2845. setStylePx(selectDiv, TOP, select.top = 0);
  2846. setStylePx(selectDiv, HEIGHT, select.height = plotHgtCss);
  2847. }
  2848. }
  2849. if (yKey) {
  2850. var sc$1 = scales[yKey];
  2851. var srcTop = src.posToVal(src.select.top, yKey);
  2852. var srcBottom = src.posToVal(src.select.top + src.select.height, yKey);
  2853. select.top = getYPos(srcTop, sc$1, plotHgtCss, 0);
  2854. select.height = abs(select.top - getYPos(srcBottom, sc$1, plotHgtCss, 0));
  2855. setStylePx(selectDiv, TOP, select.top);
  2856. setStylePx(selectDiv, HEIGHT, select.height);
  2857. if (!xKey) {
  2858. setStylePx(selectDiv, LEFT, select.left = 0);
  2859. setStylePx(selectDiv, WIDTH, select.width = plotWidCss);
  2860. }
  2861. }
  2862. }
  2863. else {
  2864. var rawDX = abs(rawMouseLeft1 - rawMouseLeft0);
  2865. var rawDY = abs(rawMouseTop1 - rawMouseTop0);
  2866. dragX = drag.x && rawDX >= drag.dist;
  2867. dragY = drag.y && rawDY >= drag.dist;
  2868. var uni = drag.uni;
  2869. if (uni != null) {
  2870. // only calc drag status if they pass the dist thresh
  2871. if (dragX && dragY) {
  2872. dragX = rawDX >= uni;
  2873. dragY = rawDY >= uni;
  2874. // force unidirectionality when both are under uni limit
  2875. if (!dragX && !dragY) {
  2876. if (rawDY > rawDX)
  2877. { dragY = true; }
  2878. else
  2879. { dragX = true; }
  2880. }
  2881. }
  2882. }
  2883. else if (drag.x && drag.y && (dragX || dragY))
  2884. // if omni with no uni then both dragX / dragY should be true if either is true
  2885. { dragX = dragY = true; }
  2886. if (dragX) {
  2887. var minX = min(mouseLeft0, mouseLeft1);
  2888. var dx = abs(mouseLeft1 - mouseLeft0);
  2889. setStylePx(selectDiv, LEFT, select.left = minX);
  2890. setStylePx(selectDiv, WIDTH, select.width = dx);
  2891. if (!dragY) {
  2892. setStylePx(selectDiv, TOP, select.top = 0);
  2893. setStylePx(selectDiv, HEIGHT, select.height = plotHgtCss);
  2894. }
  2895. }
  2896. if (dragY) {
  2897. var minY = min(mouseTop0, mouseTop1);
  2898. var dy = abs(mouseTop1 - mouseTop0);
  2899. setStylePx(selectDiv, TOP, select.top = minY);
  2900. setStylePx(selectDiv, HEIGHT, select.height = dy);
  2901. if (!dragX) {
  2902. setStylePx(selectDiv, LEFT, select.left = 0);
  2903. setStylePx(selectDiv, WIDTH, select.width = plotWidCss);
  2904. }
  2905. }
  2906. if (!dragX && !dragY) {
  2907. // the drag didn't pass the dist requirement
  2908. setStylePx(selectDiv, HEIGHT, select.height = 0);
  2909. setStylePx(selectDiv, WIDTH, select.width = 0);
  2910. }
  2911. }
  2912. }
  2913. cursor.idx = idx;
  2914. cursor.left = mouseLeft1;
  2915. cursor.top = mouseTop1;
  2916. drag._x = dragX;
  2917. drag._y = dragY;
  2918. // if ts is present, means we're implicitly syncing own cursor as a result of debounced rAF
  2919. if (ts != null) {
  2920. // this is not technically a "mousemove" event, since it's debounced, rename to setCursor?
  2921. // since this is internal, we can tweak it later
  2922. sync.pub(mousemove, self, mouseLeft1, mouseTop1, plotWidCss, plotHgtCss, idx);
  2923. if (cursorFocus) {
  2924. setSeries(closestDist <= focus.prox ? closestSeries : null, {focus: true}, syncOpts.setSeries);
  2925. }
  2926. }
  2927. ready && fire("setCursor");
  2928. }
  2929. var rect = null;
  2930. function syncRect() {
  2931. rect = over.getBoundingClientRect();
  2932. }
  2933. function mouseMove(e, src, _x, _y, _w, _h, _i) {
  2934. if (cursor._lock)
  2935. { return; }
  2936. cacheMouse(e, src, _x, _y, _w, _h, _i, false, e != null);
  2937. if (e != null) {
  2938. if (cursorRaf == 0)
  2939. { cursorRaf = rAF(updateCursor); }
  2940. }
  2941. else
  2942. { updateCursor(null, src); }
  2943. }
  2944. function cacheMouse(e, src, _x, _y, _w, _h, _i, initial, snap) {
  2945. var assign;
  2946. if (e != null) {
  2947. _x = e.clientX - rect.left;
  2948. _y = e.clientY - rect.top;
  2949. }
  2950. else {
  2951. if (_x < 0 || _y < 0) {
  2952. mouseLeft1 = -10;
  2953. mouseTop1 = -10;
  2954. return;
  2955. }
  2956. var ref = syncOpts.scales;
  2957. var xKey = ref[0];
  2958. var yKey = ref[1];
  2959. if (xKey != null)
  2960. { _x = getXPos(src.posToVal(_x, xKey), scales[xKey], plotWidCss, 0); }
  2961. else
  2962. { _x = plotWidCss * (_x/_w); }
  2963. if (yKey != null)
  2964. { _y = getYPos(src.posToVal(_y, yKey), scales[yKey], plotHgtCss, 0); }
  2965. else
  2966. { _y = plotHgtCss * (_y/_h); }
  2967. }
  2968. if (snap) {
  2969. if (_x <= 1 || _x >= plotWidCss - 1)
  2970. { _x = incrRound(_x, plotWidCss); }
  2971. if (_y <= 1 || _y >= plotHgtCss - 1)
  2972. { _y = incrRound(_y, plotHgtCss); }
  2973. }
  2974. if (initial) {
  2975. rawMouseLeft0 = _x;
  2976. rawMouseTop0 = _y;
  2977. (assign = cursor.move(self, _x, _y), mouseLeft0 = assign[0], mouseTop0 = assign[1]);
  2978. }
  2979. else {
  2980. mouseLeft1 = _x;
  2981. mouseTop1 = _y;
  2982. }
  2983. }
  2984. function hideSelect() {
  2985. setSelect({
  2986. width: 0,
  2987. height: 0,
  2988. }, false);
  2989. }
  2990. function mouseDown(e, src, _x, _y, _w, _h, _i) {
  2991. dragging = true;
  2992. dragX = dragY = drag._x = drag._y = false;
  2993. cacheMouse(e, src, _x, _y, _w, _h, _i, true, false);
  2994. if (e != null) {
  2995. onMouse(mouseup, doc, mouseUp);
  2996. sync.pub(mousedown, self, mouseLeft0, mouseTop0, plotWidCss, plotHgtCss, null);
  2997. }
  2998. }
  2999. function mouseUp(e, src, _x, _y, _w, _h, _i) {
  3000. dragging = drag._x = drag._y = false;
  3001. cacheMouse(e, src, _x, _y, _w, _h, _i, false, true);
  3002. var hasSelect = select.width > 0 || select.height > 0;
  3003. hasSelect && setSelect(select);
  3004. if (drag.setScale && hasSelect) {
  3005. // if (syncKey != null) {
  3006. // dragX = drag.x;
  3007. // dragY = drag.y;
  3008. // }
  3009. if (dragX) {
  3010. _setScale(xScaleKey,
  3011. scaleValueAtPos(select.left, xScaleKey),
  3012. scaleValueAtPos(select.left + select.width, xScaleKey)
  3013. );
  3014. }
  3015. if (dragY) {
  3016. for (var k in scales) {
  3017. var sc = scales[k];
  3018. if (k != xScaleKey && sc.from == null && sc.min != inf) {
  3019. _setScale(k,
  3020. scaleValueAtPos(select.top + select.height, k),
  3021. scaleValueAtPos(select.top, k)
  3022. );
  3023. }
  3024. }
  3025. }
  3026. hideSelect();
  3027. }
  3028. else if (cursor.lock) {
  3029. cursor._lock = !cursor._lock;
  3030. if (!cursor._lock)
  3031. { updateCursor(); }
  3032. }
  3033. if (e != null) {
  3034. offMouse(mouseup, doc);
  3035. sync.pub(mouseup, self, mouseLeft1, mouseTop1, plotWidCss, plotHgtCss, null);
  3036. }
  3037. }
  3038. function mouseLeave(e, src, _x, _y, _w, _h, _i) {
  3039. if (!cursor._lock) {
  3040. var _dragging = dragging;
  3041. if (dragging) {
  3042. // handle case when mousemove aren't fired all the way to edges by browser
  3043. var snapX = true;
  3044. var snapY = true;
  3045. var snapProx = 10;
  3046. if (dragX && dragY) {
  3047. // maybe omni corner snap
  3048. snapX = mouseLeft1 <= snapProx || mouseLeft1 >= plotWidCss - snapProx;
  3049. snapY = mouseTop1 <= snapProx || mouseTop1 >= plotHgtCss - snapProx;
  3050. }
  3051. if (dragX && snapX) {
  3052. var dLft = mouseLeft1;
  3053. var dRgt = plotWidCss - mouseLeft1;
  3054. var xMin = min(dLft, dRgt);
  3055. if (xMin == dLft)
  3056. { mouseLeft1 = 0; }
  3057. if (xMin == dRgt)
  3058. { mouseLeft1 = plotWidCss; }
  3059. }
  3060. if (dragY && snapY) {
  3061. var dTop = mouseTop1;
  3062. var dBtm = plotHgtCss - mouseTop1;
  3063. var yMin = min(dTop, dBtm);
  3064. if (yMin == dTop)
  3065. { mouseTop1 = 0; }
  3066. if (yMin == dBtm)
  3067. { mouseTop1 = plotHgtCss; }
  3068. }
  3069. updateCursor(1);
  3070. dragging = false;
  3071. }
  3072. mouseLeft1 = -10;
  3073. mouseTop1 = -10;
  3074. // passing a non-null timestamp to force sync/mousemove event
  3075. updateCursor(1);
  3076. if (_dragging)
  3077. { dragging = _dragging; }
  3078. }
  3079. }
  3080. function dblClick(e, src, _x, _y, _w, _h, _i) {
  3081. autoScaleX();
  3082. hideSelect();
  3083. if (e != null)
  3084. { sync.pub(dblclick, self, mouseLeft1, mouseTop1, plotWidCss, plotHgtCss, null); }
  3085. }
  3086. // internal pub/sub
  3087. var events = {};
  3088. events.mousedown = mouseDown;
  3089. events.mousemove = mouseMove;
  3090. events.mouseup = mouseUp;
  3091. events.dblclick = dblClick;
  3092. events["setSeries"] = function (e, src, idx, opts) {
  3093. setSeries(idx, opts);
  3094. };
  3095. var deb;
  3096. if ( cursor.show) {
  3097. onMouse(mousedown, over, mouseDown);
  3098. onMouse(mousemove, over, mouseMove);
  3099. onMouse(mouseenter, over, syncRect);
  3100. // this has to be rAF'd so it always fires after the last queued/rAF'd updateCursor
  3101. onMouse(mouseleave, over, function (e) { rAF(mouseLeave); });
  3102. onMouse(dblclick, over, dblClick);
  3103. deb = debounce(syncRect, 100);
  3104. on(resize, win, deb);
  3105. on(scroll, win, deb);
  3106. self.syncRect = syncRect;
  3107. }
  3108. // external on/off
  3109. var hooks = self.hooks = opts.hooks || {};
  3110. function fire(evName, a1, a2) {
  3111. if (evName in hooks) {
  3112. hooks[evName].forEach(function (fn) {
  3113. fn.call(null, self, a1, a2);
  3114. });
  3115. }
  3116. }
  3117. (opts.plugins || []).forEach(function (p) {
  3118. for (var evName in p.hooks)
  3119. { hooks[evName] = (hooks[evName] || []).concat(p.hooks[evName]); }
  3120. });
  3121. var syncOpts = assign({
  3122. key: null,
  3123. setSeries: false,
  3124. scales: [xScaleKey, null]
  3125. }, cursor.sync);
  3126. var syncKey = syncOpts.key;
  3127. var sync = (syncKey != null ? (syncs[syncKey] = syncs[syncKey] || _sync()) : _sync());
  3128. sync.sub(self);
  3129. function pub(type, src, x, y, w, h, i) {
  3130. events[type](null, src, x, y, w, h, i);
  3131. }
  3132. (self.pub = pub);
  3133. function destroy() {
  3134. sync.unsub(self);
  3135. off(resize, win, deb);
  3136. off(scroll, win, deb);
  3137. root.remove();
  3138. fire("destroy");
  3139. }
  3140. self.destroy = destroy;
  3141. function _init() {
  3142. fire("init", opts, data);
  3143. setData(data || opts.data, false);
  3144. if (pendScales[xScaleKey])
  3145. { setScale(xScaleKey, pendScales[xScaleKey]); }
  3146. else
  3147. { autoScaleX(); }
  3148. _setSize(opts.width, opts.height);
  3149. setSelect(select, false);
  3150. }
  3151. if (then) {
  3152. if (then instanceof HTMLElement) {
  3153. then.appendChild(root);
  3154. _init();
  3155. }
  3156. else
  3157. { then(self, _init); }
  3158. }
  3159. else
  3160. { _init(); }
  3161. return self;
  3162. }
  3163. uPlot.assign = assign;
  3164. uPlot.fmtNum = fmtNum;
  3165. uPlot.rangeNum = rangeNum;
  3166. uPlot.rangeLog = rangeLog;
  3167. {
  3168. uPlot.join = join;
  3169. }
  3170. {
  3171. uPlot.fmtDate = fmtDate;
  3172. uPlot.tzDate = tzDate;
  3173. }
  3174. {
  3175. uPlot.addGap = addGap;
  3176. uPlot.clipGaps = clipGaps;
  3177. var paths = uPlot.paths = {};
  3178. (paths.linear = linear);
  3179. (paths.spline = spline);
  3180. (paths.stepped = stepped);
  3181. (paths.bars = bars);
  3182. }
  3183. return uPlot;
  3184. }());