uPlot.cjs.js 92 KB

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