update uplot

pull/3170/head
REJack 2020-11-29 03:57:24 +01:00
parent 2c98813cc1
commit 5fc36f6176
6 changed files with 627 additions and 572 deletions

6
package-lock.json generated
View File

@ -11348,9 +11348,9 @@
} }
}, },
"uplot": { "uplot": {
"version": "1.4.4", "version": "1.4.6",
"resolved": "https://registry.npmjs.org/uplot/-/uplot-1.4.4.tgz", "resolved": "https://registry.npmjs.org/uplot/-/uplot-1.4.6.tgz",
"integrity": "sha512-vgV84+by3fGTU4bdpffSvA9FX8ide6MsmlBzOASPDdZCquXmCA+T2qodeNdnBen+7YOeqD9H91epVnF0dQgVKw==" "integrity": "sha512-nw3LdjLFhAqKQF/Rv7QjZICVnjJemOQVj2L3b7889gHBrnC1LwltkzTcawDcbMe6pxo++0KVev9xSC0YCw+m5w=="
}, },
"upper-case": { "upper-case": {
"version": "1.1.3", "version": "1.1.3",

View File

@ -126,7 +126,7 @@
"sweetalert2": "^10.10.2", "sweetalert2": "^10.10.2",
"tempusdominus-bootstrap-4": "^5.39.0", "tempusdominus-bootstrap-4": "^5.39.0",
"toastr": "^2.1.4", "toastr": "^2.1.4",
"uplot": "^1.4.4" "uplot": "^1.4.6"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.11.6", "@babel/core": "^7.11.6",

View File

@ -4,7 +4,7 @@
* *
* uPlot.js (μPlot) * uPlot.js (μPlot)
* A small, fast chart for time series, lines, areas, ohlc & bars * A small, fast chart for time series, lines, areas, ohlc & bars
* https://github.com/leeoniya/uPlot (v1.4.4) * https://github.com/leeoniya/uPlot (v1.4.6)
*/ */
'use strict'; 'use strict';
@ -551,8 +551,6 @@ var onlyWhole = function (v) { return v % 1 == 0; };
var allMults = [1,2,2.5,5]; var allMults = [1,2,2.5,5];
var wholeMults = allMults.filter(onlyWhole);
// ...0.01, 0.02, 0.025, 0.05, 0.1, 0.2, 0.25, 0.5 // ...0.01, 0.02, 0.025, 0.05, 0.1, 0.2, 0.25, 0.5
var decIncrs = genIncrs(10, -16, 0, allMults); var decIncrs = genIncrs(10, -16, 0, allMults);
@ -564,61 +562,196 @@ var wholeIncrs = oneIncrs.filter(onlyWhole);
var numIncrs = decIncrs.concat(oneIncrs); var numIncrs = decIncrs.concat(oneIncrs);
var s = 1, var NL = "\n";
m = 60,
h = m * m,
d = h * 24,
mo = d * 30,
y = d * 365;
// min of 1e-3 prevents setting a temporal x ticks too small since Date objects cannot advance ticks smaller than 1ms var yyyy = "{YYYY}";
var timeIncrs = genIncrs(10, -3, 0, wholeMults).concat([ var NLyyyy = NL + yyyy;
// minute divisors (# of secs) var md = "{M}/{D}";
1, var NLmd = NL + md;
5, var NLmdyy = NLmd + "/{YY}";
10,
15, var aa = "{aa}";
30, var hmm = "{h}:{mm}";
// hour divisors (# of mins) var hmmaa = hmm + aa;
m, var NLhmmaa = NL + hmmaa;
m * 5, var ss = ":{ss}";
m * 10,
m * 15, var _ = null;
m * 30,
// day divisors (# of hrs) function genTimeStuffs(ms) {
h, var s = ms * 1e3,
h * 2, m = s * 60,
h * 3, h = m * 60,
h * 4, d = h * 24,
h * 6, mo = d * 30,
h * 8, y = d * 365;
h * 12,
// month divisors TODO: need more? // min of 1e-3 prevents setting a temporal x ticks too small since Date objects cannot advance ticks smaller than 1ms
d, var subSecIncrs = ms == 1 ? genIncrs(10, 0, 3, allMults).filter(onlyWhole) : genIncrs(10, -3, 0, allMults);
d * 2,
d * 3, var timeIncrs = subSecIncrs.concat([
d * 4, // minute divisors (# of secs)
d * 5, s,
d * 6, s * 5,
d * 7, s * 10,
d * 8, s * 15,
d * 9, s * 30,
d * 10, // hour divisors (# of mins)
d * 15, m,
// year divisors (# months, approx) m * 5,
mo, m * 10,
mo * 2, m * 15,
mo * 3, m * 30,
mo * 4, // day divisors (# of hrs)
mo * 6, h,
// century divisors h * 2,
y, h * 3,
y * 2, h * 4,
y * 5, h * 6,
y * 10, h * 8,
y * 25, h * 12,
y * 50, // month divisors TODO: need more?
y * 100 ]); d,
d * 2,
d * 3,
d * 4,
d * 5,
d * 6,
d * 7,
d * 8,
d * 9,
d * 10,
d * 15,
// year divisors (# months, approx)
mo,
mo * 2,
mo * 3,
mo * 4,
mo * 6,
// century divisors
y,
y * 2,
y * 5,
y * 10,
y * 25,
y * 50,
y * 100 ]);
// [0]: minimum num secs in the tick incr
// [1]: default tick format
// [2-7]: rollover tick formats
// [8]: mode: 0: replace [1] -> [2-7], 1: concat [1] + [2-7]
var _timeAxisStamps = [
// tick incr default year month day hour min sec mode
[y, yyyy, _, _, _, _, _, _, 1],
[d * 28, "{MMM}", NLyyyy, _, _, _, _, _, 1],
[d, md, NLyyyy, _, _, _, _, _, 1],
[h, "{h}" + aa, NLmdyy, _, NLmd, _, _, _, 1],
[m, hmmaa, NLmdyy, _, NLmd, _, _, _, 1],
[s, ss, NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1],
[ms, ss + ".{fff}", NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1] ];
// the ensures that axis ticks, values & grid are aligned to logical temporal breakpoints and not an arbitrary timestamp
// https://www.timeanddate.com/time/dst/
// https://www.timeanddate.com/time/dst/2019.html
// https://www.epochconverter.com/timezones
function timeAxisSplits(tzDate) {
return function (self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace) {
var splits = [];
var isYr = foundIncr >= y;
var isMo = foundIncr >= mo && foundIncr < y;
// get the timezone-adjusted date
var minDate = tzDate(scaleMin);
var minDateTs = minDate * ms;
// get ts of 12am (this lands us at or before the original scaleMin)
var minMin = mkDate(minDate[getFullYear](), isYr ? 0 : minDate[getMonth](), isMo || isYr ? 1 : minDate[getDate]());
var minMinTs = minMin * ms;
if (isMo || isYr) {
var moIncr = isMo ? foundIncr / mo : 0;
var yrIncr = isYr ? foundIncr / y : 0;
// let tzOffset = scaleMin - minDateTs; // needed?
var split = minDateTs == minMinTs ? minDateTs : mkDate(minMin[getFullYear]() + yrIncr, minMin[getMonth]() + moIncr, 1) * ms;
var splitDate = new Date(split / ms);
var baseYear = splitDate[getFullYear]();
var baseMonth = splitDate[getMonth]();
for (var i = 0; split <= scaleMax; i++) {
var next = mkDate(baseYear + yrIncr * i, baseMonth + moIncr * i, 1);
var offs = next - tzDate(next * ms);
split = (+next + offs) * ms;
if (split <= scaleMax)
{ splits.push(split); }
}
}
else {
var incr0 = foundIncr >= d ? d : foundIncr;
var tzOffset = floor(scaleMin) - floor(minDateTs);
var split$1 = minMinTs + tzOffset + incrRoundUp(minDateTs - minMinTs, incr0);
splits.push(split$1);
var date0 = tzDate(split$1);
var prevHour = date0[getHours]() + (date0[getMinutes]() / m) + (date0[getSeconds]() / h);
var incrHours = foundIncr / h;
var minSpace = self.axes[axisIdx]._space;
var pctSpace = foundSpace / minSpace;
while (1) {
split$1 = roundDec(split$1 + foundIncr, ms == 1 ? 0 : 3);
if (split$1 > scaleMax)
{ break; }
if (incrHours > 1) {
var expectedHour = floor(roundDec(prevHour + incrHours, 6)) % 24;
var splitDate$1 = tzDate(split$1);
var actualHour = splitDate$1.getHours();
var dstShift = actualHour - expectedHour;
if (dstShift > 1)
{ dstShift = -1; }
split$1 -= dstShift * h;
prevHour = (prevHour + incrHours) % 24;
// add a tick only if it's further than 70% of the min allowed label spacing
var prevSplit = splits[splits.length - 1];
var pctIncr = roundDec((split$1 - prevSplit) / foundIncr, 3);
if (pctIncr * pctSpace >= .7)
{ splits.push(split$1); }
}
else
{ splits.push(split$1); }
}
}
return splits;
}
}
return [
timeIncrs,
_timeAxisStamps,
timeAxisSplits ];
}
var ref = genTimeStuffs(1);
var timeIncrsMs = ref[0];
var _timeAxisStampsMs = ref[1];
var timeAxisSplitsMs = ref[2];
var ref$1 = genTimeStuffs(1e-3);
var timeIncrsS = ref$1[0];
var _timeAxisStampsS = ref$1[1];
var timeAxisSplitsS = ref$1[2];
// base 2 // base 2
var binIncrs = genIncrs(2, -53, 53, [1]); var binIncrs = genIncrs(2, -53, 53, [1]);
@ -639,36 +772,6 @@ function timeAxisStamps(stampCfg, fmtDate) {
); }); ); });
} }
var NL = "\n";
var yyyy = "{YYYY}";
var NLyyyy = NL + yyyy;
var md = "{M}/{D}";
var NLmd = NL + md;
var NLmdyy = NLmd + "/{YY}";
var aa = "{aa}";
var hmm = "{h}:{mm}";
var hmmaa = hmm + aa;
var NLhmmaa = NL + hmmaa;
var ss = ":{ss}";
var _ = null;
// [0]: minimum num secs in the tick incr
// [1]: default tick format
// [2-7]: rollover tick formats
// [8]: mode: 0: replace [1] -> [2-7], 1: concat [1] + [2-7]
var _timeAxisStamps = [
// tick incr default year month day hour min sec mode
[y, yyyy, _, _, _, _, _, _, 1],
[d * 28, "{MMM}", NLyyyy, _, _, _, _, _, 1],
[d, md, NLyyyy, _, _, _, _, _, 1],
[h, "{h}" + aa, NLmdyy, _, NLmd, _, _, _, 1],
[m, hmmaa, NLmdyy, _, NLmd, _, _, _, 1],
[s, ss, NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1],
[1e-3, ss + ".{fff}", NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1] ];
// TODO: will need to accept spaces[] and pull incr into the loop when grid will be non-uniform, eg for log scales. // TODO: will need to accept spaces[] and pull incr into the loop when grid will be non-uniform, eg for log scales.
// currently we ignore this for months since they're *nearly* uniform and the added complexity is not worth it // currently we ignore this for months since they're *nearly* uniform and the added complexity is not worth it
function timeAxisVals(tzDate, stamps) { function timeAxisVals(tzDate, stamps) {
@ -725,93 +828,6 @@ function mkDate(y, m, d) {
return new Date(y, m, d); return new Date(y, m, d);
} }
// the ensures that axis ticks, values & grid are aligned to logical temporal breakpoints and not an arbitrary timestamp
// https://www.timeanddate.com/time/dst/
// https://www.timeanddate.com/time/dst/2019.html
// https://www.epochconverter.com/timezones
function timeAxisSplits(tzDate) {
return function (self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace) {
var splits = [];
var isYr = foundIncr >= y;
var isMo = foundIncr >= mo && foundIncr < y;
// get the timezone-adjusted date
var minDate = tzDate(scaleMin);
var minDateTs = minDate / 1e3;
// get ts of 12am (this lands us at or before the original scaleMin)
var minMin = mkDate(minDate[getFullYear](), isYr ? 0 : minDate[getMonth](), isMo || isYr ? 1 : minDate[getDate]());
var minMinTs = minMin / 1e3;
if (isMo || isYr) {
var moIncr = isMo ? foundIncr / mo : 0;
var yrIncr = isYr ? foundIncr / y : 0;
// let tzOffset = scaleMin - minDateTs; // needed?
var split = minDateTs == minMinTs ? minDateTs : mkDate(minMin[getFullYear]() + yrIncr, minMin[getMonth]() + moIncr, 1) / 1e3;
var splitDate = new Date(split * 1e3);
var baseYear = splitDate[getFullYear]();
var baseMonth = splitDate[getMonth]();
for (var i = 0; split <= scaleMax; i++) {
var next = mkDate(baseYear + yrIncr * i, baseMonth + moIncr * i, 1);
var offs = next - tzDate(next / 1e3);
split = (+next + offs) / 1e3;
if (split <= scaleMax)
{ splits.push(split); }
}
}
else {
var incr0 = foundIncr >= d ? d : foundIncr;
var tzOffset = floor(scaleMin) - floor(minDateTs);
var split$1 = minMinTs + tzOffset + incrRoundUp(minDateTs - minMinTs, incr0);
splits.push(split$1);
var date0 = tzDate(split$1);
var prevHour = date0[getHours]() + (date0[getMinutes]() / m) + (date0[getSeconds]() / h);
var incrHours = foundIncr / h;
var minSpace = self.axes[axisIdx]._space;
var pctSpace = foundSpace / minSpace;
while (1) {
split$1 = roundDec(split$1 + foundIncr, 3);
if (split$1 > scaleMax)
{ break; }
if (incrHours > 1) {
var expectedHour = floor(roundDec(prevHour + incrHours, 6)) % 24;
var splitDate$1 = tzDate(split$1);
var actualHour = splitDate$1.getHours();
var dstShift = actualHour - expectedHour;
if (dstShift > 1)
{ dstShift = -1; }
split$1 -= dstShift * h;
prevHour = (prevHour + incrHours) % 24;
// add a tick only if it's further than 70% of the min allowed label spacing
var prevSplit = splits[splits.length - 1];
var pctIncr = roundDec((split$1 - prevSplit) / foundIncr, 3);
if (pctIncr * pctSpace >= .7)
{ splits.push(split$1); }
}
else
{ splits.push(split$1); }
}
}
return splits;
}
}
function timeSeriesStamp(stampCfg, fmtDate) { function timeSeriesStamp(stampCfg, fmtDate) {
return fmtDate(stampCfg); return fmtDate(stampCfg);
} }
@ -1235,7 +1251,7 @@ function uPlot(opts, data, then) {
{ opts = p.opts(self, opts) || opts; } { opts = p.opts(self, opts) || opts; }
}); });
var ms = opts.ms || 1e-3;
var series = self.series = setDefaults(opts.series || [], xSeriesOpts, ySeriesOpts, false); var series = self.series = setDefaults(opts.series || [], xSeriesOpts, ySeriesOpts, false);
var axes = self.axes = setDefaults(opts.axes || [], xAxisOpts, yAxisOpts, true); var axes = self.axes = setDefaults(opts.axes || [], xAxisOpts, yAxisOpts, true);
@ -1311,11 +1327,11 @@ function uPlot(opts, data, then) {
gutters._y = gutters.y(self); gutters._y = gutters.y(self);
// self.tz = opts.tz || Intl.DateTimeFormat().resolvedOptions().timeZone; // self.tz = opts.tz || Intl.DateTimeFormat().resolvedOptions().timeZone;
var _tzDate = (opts.tzDate || (function (ts) { return new Date(ts * 1e3); })); var _tzDate = (opts.tzDate || (function (ts) { return new Date(ts / ms); }));
var _fmtDate = (opts.fmtDate || fmtDate); var _fmtDate = (opts.fmtDate || fmtDate);
var _timeAxisSplits = timeAxisSplits(_tzDate); var _timeAxisSplits = (ms == 1 ? timeAxisSplitsMs(_tzDate) : timeAxisSplitsS(_tzDate));
var _timeAxisVals = timeAxisVals(_tzDate, timeAxisStamps(_timeAxisStamps, _fmtDate)); var _timeAxisVals = timeAxisVals(_tzDate, timeAxisStamps((ms == 1 ? _timeAxisStampsMs : _timeAxisStampsS), _fmtDate));
var _timeSeriesVal = timeSeriesVal(_tzDate, timeSeriesStamp(_timeSeriesStamp, _fmtDate)); var _timeSeriesVal = timeSeriesVal(_tzDate, timeSeriesStamp(_timeSeriesStamp, _fmtDate));
var legend = assign({show: true, live: true}, opts.legend); var legend = assign({show: true, live: true}, opts.legend);
@ -1471,9 +1487,13 @@ function uPlot(opts, data, then) {
function convergeSize() { function convergeSize() {
var converged = false; var converged = false;
var cycleNum = 0;
while (!converged) { while (!converged) {
var axesConverged = axesCalc(); cycleNum++;
var guttersConverged = guttersCalc();
var axesConverged = axesCalc(cycleNum);
var guttersConverged = guttersCalc(cycleNum);
converged = axesConverged && guttersConverged; converged = axesConverged && guttersConverged;
@ -1685,7 +1705,7 @@ function uPlot(opts, data, then) {
axis.size = fnOrSelf(axis.size); axis.size = fnOrSelf(axis.size);
axis.space = fnOrSelf(axis.space); axis.space = fnOrSelf(axis.space);
axis.rotate = fnOrSelf(axis.rotate); axis.rotate = fnOrSelf(axis.rotate);
axis.incrs = fnOrSelf(axis.incrs || ( sc.distr == 2 ? wholeIncrs : (isTime ? timeIncrs : numIncrs))); axis.incrs = fnOrSelf(axis.incrs || ( sc.distr == 2 ? wholeIncrs : (isTime ? (ms == 1 ? timeIncrsMs : timeIncrsS) : numIncrs)));
axis.splits = fnOrSelf(axis.splits || (isTime && sc.distr == 1 ? _timeAxisSplits : sc.distr == 3 ? logAxisSplits : numAxisSplits)); axis.splits = fnOrSelf(axis.splits || (isTime && sc.distr == 1 ? _timeAxisSplits : sc.distr == 3 ? logAxisSplits : numAxisSplits));
var av = axis.values; var av = axis.values;
@ -1704,7 +1724,7 @@ function uPlot(opts, data, then) {
axis.font = pxRatioFont(axis.font); axis.font = pxRatioFont(axis.font);
axis.labelFont = pxRatioFont(axis.labelFont); axis.labelFont = pxRatioFont(axis.labelFont);
axis._size = axis.size(self, null, i); axis._size = axis.size(self, null, i, 0);
axis._space = axis._space =
axis._rotate = axis._rotate =
@ -1789,7 +1809,7 @@ function uPlot(opts, data, then) {
if (xScaleDistr == 3) if (xScaleDistr == 3)
{ (assign = rangeLog(_min, _min, scales[xScaleKey].log, false), _min = assign[0], _max = assign[1]); } { (assign = rangeLog(_min, _min, scales[xScaleKey].log, false), _min = assign[0], _max = assign[1]); }
else if (scales[xScaleKey].time) else if (scales[xScaleKey].time)
{ _max = _min + 86400; } { _max = _min + 86400 / ms; }
else else
{ (assign$1 = rangeNum(_min, _max, 0.1, true), _min = assign$1[0], _max = assign$1[1]); } { (assign$1 = rangeNum(_min, _max, 0.1, true), _min = assign$1[0], _max = assign$1[1]); }
} }
@ -2330,7 +2350,7 @@ function uPlot(opts, data, then) {
ctx.translate(-offset, -offset); ctx.translate(-offset, -offset);
} }
function axesCalc() { function axesCalc(cycleNum) {
// log("axesCalc()", arguments); // log("axesCalc()", arguments);
var converged = true; var converged = true;
@ -2387,7 +2407,7 @@ function uPlot(opts, data, then) {
var oldSize = axis._size; var oldSize = axis._size;
axis._size = axis.size(self, values, i); axis._size = ceil(axis.size(self, values, i, cycleNum));
if (oldSize != null && axis._size != oldSize) // ready && ? if (oldSize != null && axis._size != oldSize) // ready && ?
{ converged = false; } { converged = false; }
@ -2396,14 +2416,14 @@ function uPlot(opts, data, then) {
return converged; return converged;
} }
function guttersCalc() { function guttersCalc(cycleNum) {
var converged = true; var converged = true;
var _x = gutters._x; var _x = gutters._x;
var _y = gutters._y; var _y = gutters._y;
gutters._x = gutters.x(self); gutters._x = ceil(gutters.x(self, cycleNum));
gutters._y = gutters.y(self); gutters._y = ceil(gutters.y(self, cycleNum));
if (gutters._x != _x || gutters._y != _y) if (gutters._x != _x || gutters._y != _y)
{ converged = false; } { converged = false; }

View File

@ -4,7 +4,7 @@
* *
* uPlot.js (μPlot) * uPlot.js (μPlot)
* A small, fast chart for time series, lines, areas, ohlc & bars * A small, fast chart for time series, lines, areas, ohlc & bars
* https://github.com/leeoniya/uPlot (v1.4.4) * https://github.com/leeoniya/uPlot (v1.4.6)
*/ */
function debounce(fn, time) { function debounce(fn, time) {
@ -551,8 +551,6 @@ const onlyWhole = v => v % 1 == 0;
const allMults = [1,2,2.5,5]; const allMults = [1,2,2.5,5];
const wholeMults = allMults.filter(onlyWhole);
// ...0.01, 0.02, 0.025, 0.05, 0.1, 0.2, 0.25, 0.5 // ...0.01, 0.02, 0.025, 0.05, 0.1, 0.2, 0.25, 0.5
const decIncrs = genIncrs(10, -16, 0, allMults); const decIncrs = genIncrs(10, -16, 0, allMults);
@ -564,62 +562,193 @@ const wholeIncrs = oneIncrs.filter(onlyWhole);
const numIncrs = decIncrs.concat(oneIncrs); const numIncrs = decIncrs.concat(oneIncrs);
let s = 1, const NL = "\n";
m = 60,
h = m * m,
d = h * 24,
mo = d * 30,
y = d * 365;
// min of 1e-3 prevents setting a temporal x ticks too small since Date objects cannot advance ticks smaller than 1ms const yyyy = "{YYYY}";
const timeIncrs = genIncrs(10, -3, 0, wholeMults).concat([ const NLyyyy = NL + yyyy;
// minute divisors (# of secs) const md = "{M}/{D}";
1, const NLmd = NL + md;
5, const NLmdyy = NLmd + "/{YY}";
10,
15, const aa = "{aa}";
30, const hmm = "{h}:{mm}";
// hour divisors (# of mins) const hmmaa = hmm + aa;
m, const NLhmmaa = NL + hmmaa;
m * 5, const ss = ":{ss}";
m * 10,
m * 15, const _ = null;
m * 30,
// day divisors (# of hrs) function genTimeStuffs(ms) {
h, let s = ms * 1e3,
h * 2, m = s * 60,
h * 3, h = m * 60,
h * 4, d = h * 24,
h * 6, mo = d * 30,
h * 8, y = d * 365;
h * 12,
// month divisors TODO: need more? // min of 1e-3 prevents setting a temporal x ticks too small since Date objects cannot advance ticks smaller than 1ms
d, let subSecIncrs = ms == 1 ? genIncrs(10, 0, 3, allMults).filter(onlyWhole) : genIncrs(10, -3, 0, allMults);
d * 2,
d * 3, let timeIncrs = subSecIncrs.concat([
d * 4, // minute divisors (# of secs)
d * 5, s,
d * 6, s * 5,
d * 7, s * 10,
d * 8, s * 15,
d * 9, s * 30,
d * 10, // hour divisors (# of mins)
d * 15, m,
// year divisors (# months, approx) m * 5,
mo, m * 10,
mo * 2, m * 15,
mo * 3, m * 30,
mo * 4, // day divisors (# of hrs)
mo * 6, h,
// century divisors h * 2,
y, h * 3,
y * 2, h * 4,
y * 5, h * 6,
y * 10, h * 8,
y * 25, h * 12,
y * 50, // month divisors TODO: need more?
y * 100, d,
]); d * 2,
d * 3,
d * 4,
d * 5,
d * 6,
d * 7,
d * 8,
d * 9,
d * 10,
d * 15,
// year divisors (# months, approx)
mo,
mo * 2,
mo * 3,
mo * 4,
mo * 6,
// century divisors
y,
y * 2,
y * 5,
y * 10,
y * 25,
y * 50,
y * 100,
]);
// [0]: minimum num secs in the tick incr
// [1]: default tick format
// [2-7]: rollover tick formats
// [8]: mode: 0: replace [1] -> [2-7], 1: concat [1] + [2-7]
const _timeAxisStamps = [
// tick incr default year month day hour min sec mode
[y, yyyy, _, _, _, _, _, _, 1],
[d * 28, "{MMM}", NLyyyy, _, _, _, _, _, 1],
[d, md, NLyyyy, _, _, _, _, _, 1],
[h, "{h}" + aa, NLmdyy, _, NLmd, _, _, _, 1],
[m, hmmaa, NLmdyy, _, NLmd, _, _, _, 1],
[s, ss, NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1],
[ms, ss + ".{fff}", NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1],
];
// the ensures that axis ticks, values & grid are aligned to logical temporal breakpoints and not an arbitrary timestamp
// https://www.timeanddate.com/time/dst/
// https://www.timeanddate.com/time/dst/2019.html
// https://www.epochconverter.com/timezones
function timeAxisSplits(tzDate) {
return (self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace) => {
let splits = [];
let isYr = foundIncr >= y;
let isMo = foundIncr >= mo && foundIncr < y;
// get the timezone-adjusted date
let minDate = tzDate(scaleMin);
let minDateTs = minDate * ms;
// get ts of 12am (this lands us at or before the original scaleMin)
let minMin = mkDate(minDate[getFullYear](), isYr ? 0 : minDate[getMonth](), isMo || isYr ? 1 : minDate[getDate]());
let minMinTs = minMin * ms;
if (isMo || isYr) {
let moIncr = isMo ? foundIncr / mo : 0;
let yrIncr = isYr ? foundIncr / y : 0;
// let tzOffset = scaleMin - minDateTs; // needed?
let split = minDateTs == minMinTs ? minDateTs : mkDate(minMin[getFullYear]() + yrIncr, minMin[getMonth]() + moIncr, 1) * ms;
let splitDate = new Date(split / ms);
let baseYear = splitDate[getFullYear]();
let baseMonth = splitDate[getMonth]();
for (let i = 0; split <= scaleMax; i++) {
let next = mkDate(baseYear + yrIncr * i, baseMonth + moIncr * i, 1);
let offs = next - tzDate(next * ms);
split = (+next + offs) * ms;
if (split <= scaleMax)
splits.push(split);
}
}
else {
let incr0 = foundIncr >= d ? d : foundIncr;
let tzOffset = floor(scaleMin) - floor(minDateTs);
let split = minMinTs + tzOffset + incrRoundUp(minDateTs - minMinTs, incr0);
splits.push(split);
let date0 = tzDate(split);
let prevHour = date0[getHours]() + (date0[getMinutes]() / m) + (date0[getSeconds]() / h);
let incrHours = foundIncr / h;
let minSpace = self.axes[axisIdx]._space;
let pctSpace = foundSpace / minSpace;
while (1) {
split = roundDec(split + foundIncr, ms == 1 ? 0 : 3);
if (split > scaleMax)
break;
if (incrHours > 1) {
let expectedHour = floor(roundDec(prevHour + incrHours, 6)) % 24;
let splitDate = tzDate(split);
let actualHour = splitDate.getHours();
let dstShift = actualHour - expectedHour;
if (dstShift > 1)
dstShift = -1;
split -= dstShift * h;
prevHour = (prevHour + incrHours) % 24;
// add a tick only if it's further than 70% of the min allowed label spacing
let prevSplit = splits[splits.length - 1];
let pctIncr = roundDec((split - prevSplit) / foundIncr, 3);
if (pctIncr * pctSpace >= .7)
splits.push(split);
}
else
splits.push(split);
}
}
return splits;
}
}
return [
timeIncrs,
_timeAxisStamps,
timeAxisSplits,
];
}
const [ timeIncrsMs, _timeAxisStampsMs, timeAxisSplitsMs ] = genTimeStuffs(1);
const [ timeIncrsS, _timeAxisStampsS, timeAxisSplitsS ] = genTimeStuffs(1e-3);
// base 2 // base 2
const binIncrs = genIncrs(2, -53, 53, [1]); const binIncrs = genIncrs(2, -53, 53, [1]);
@ -641,37 +770,6 @@ function timeAxisStamps(stampCfg, fmtDate) {
)); ));
} }
const NL = "\n";
const yyyy = "{YYYY}";
const NLyyyy = NL + yyyy;
const md = "{M}/{D}";
const NLmd = NL + md;
const NLmdyy = NLmd + "/{YY}";
const aa = "{aa}";
const hmm = "{h}:{mm}";
const hmmaa = hmm + aa;
const NLhmmaa = NL + hmmaa;
const ss = ":{ss}";
const _ = null;
// [0]: minimum num secs in the tick incr
// [1]: default tick format
// [2-7]: rollover tick formats
// [8]: mode: 0: replace [1] -> [2-7], 1: concat [1] + [2-7]
const _timeAxisStamps = [
// tick incr default year month day hour min sec mode
[y, yyyy, _, _, _, _, _, _, 1],
[d * 28, "{MMM}", NLyyyy, _, _, _, _, _, 1],
[d, md, NLyyyy, _, _, _, _, _, 1],
[h, "{h}" + aa, NLmdyy, _, NLmd, _, _, _, 1],
[m, hmmaa, NLmdyy, _, NLmd, _, _, _, 1],
[s, ss, NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1],
[1e-3, ss + ".{fff}", NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1],
];
// TODO: will need to accept spaces[] and pull incr into the loop when grid will be non-uniform, eg for log scales. // TODO: will need to accept spaces[] and pull incr into the loop when grid will be non-uniform, eg for log scales.
// currently we ignore this for months since they're *nearly* uniform and the added complexity is not worth it // currently we ignore this for months since they're *nearly* uniform and the added complexity is not worth it
function timeAxisVals(tzDate, stamps) { function timeAxisVals(tzDate, stamps) {
@ -728,93 +826,6 @@ function mkDate(y, m, d) {
return new Date(y, m, d); return new Date(y, m, d);
} }
// the ensures that axis ticks, values & grid are aligned to logical temporal breakpoints and not an arbitrary timestamp
// https://www.timeanddate.com/time/dst/
// https://www.timeanddate.com/time/dst/2019.html
// https://www.epochconverter.com/timezones
function timeAxisSplits(tzDate) {
return (self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace) => {
let splits = [];
let isYr = foundIncr >= y;
let isMo = foundIncr >= mo && foundIncr < y;
// get the timezone-adjusted date
let minDate = tzDate(scaleMin);
let minDateTs = minDate / 1e3;
// get ts of 12am (this lands us at or before the original scaleMin)
let minMin = mkDate(minDate[getFullYear](), isYr ? 0 : minDate[getMonth](), isMo || isYr ? 1 : minDate[getDate]());
let minMinTs = minMin / 1e3;
if (isMo || isYr) {
let moIncr = isMo ? foundIncr / mo : 0;
let yrIncr = isYr ? foundIncr / y : 0;
// let tzOffset = scaleMin - minDateTs; // needed?
let split = minDateTs == minMinTs ? minDateTs : mkDate(minMin[getFullYear]() + yrIncr, minMin[getMonth]() + moIncr, 1) / 1e3;
let splitDate = new Date(split * 1e3);
let baseYear = splitDate[getFullYear]();
let baseMonth = splitDate[getMonth]();
for (let i = 0; split <= scaleMax; i++) {
let next = mkDate(baseYear + yrIncr * i, baseMonth + moIncr * i, 1);
let offs = next - tzDate(next / 1e3);
split = (+next + offs) / 1e3;
if (split <= scaleMax)
splits.push(split);
}
}
else {
let incr0 = foundIncr >= d ? d : foundIncr;
let tzOffset = floor(scaleMin) - floor(minDateTs);
let split = minMinTs + tzOffset + incrRoundUp(minDateTs - minMinTs, incr0);
splits.push(split);
let date0 = tzDate(split);
let prevHour = date0[getHours]() + (date0[getMinutes]() / m) + (date0[getSeconds]() / h);
let incrHours = foundIncr / h;
let minSpace = self.axes[axisIdx]._space;
let pctSpace = foundSpace / minSpace;
while (1) {
split = roundDec(split + foundIncr, 3);
if (split > scaleMax)
break;
if (incrHours > 1) {
let expectedHour = floor(roundDec(prevHour + incrHours, 6)) % 24;
let splitDate = tzDate(split);
let actualHour = splitDate.getHours();
let dstShift = actualHour - expectedHour;
if (dstShift > 1)
dstShift = -1;
split -= dstShift * h;
prevHour = (prevHour + incrHours) % 24;
// add a tick only if it's further than 70% of the min allowed label spacing
let prevSplit = splits[splits.length - 1];
let pctIncr = roundDec((split - prevSplit) / foundIncr, 3);
if (pctIncr * pctSpace >= .7)
splits.push(split);
}
else
splits.push(split);
}
}
return splits;
}
}
function timeSeriesStamp(stampCfg, fmtDate) { function timeSeriesStamp(stampCfg, fmtDate) {
return fmtDate(stampCfg); return fmtDate(stampCfg);
} }
@ -1238,7 +1249,7 @@ function uPlot(opts, data, then) {
opts = p.opts(self, opts) || opts; opts = p.opts(self, opts) || opts;
}); });
const ms = opts.ms || 1e-3;
const series = self.series = setDefaults(opts.series || [], xSeriesOpts, ySeriesOpts, false); const series = self.series = setDefaults(opts.series || [], xSeriesOpts, ySeriesOpts, false);
const axes = self.axes = setDefaults(opts.axes || [], xAxisOpts, yAxisOpts, true); const axes = self.axes = setDefaults(opts.axes || [], xAxisOpts, yAxisOpts, true);
@ -1314,11 +1325,11 @@ function uPlot(opts, data, then) {
gutters._y = gutters.y(self); gutters._y = gutters.y(self);
// self.tz = opts.tz || Intl.DateTimeFormat().resolvedOptions().timeZone; // self.tz = opts.tz || Intl.DateTimeFormat().resolvedOptions().timeZone;
const _tzDate = (opts.tzDate || (ts => new Date(ts * 1e3))); const _tzDate = (opts.tzDate || (ts => new Date(ts / ms)));
const _fmtDate = (opts.fmtDate || fmtDate); const _fmtDate = (opts.fmtDate || fmtDate);
const _timeAxisSplits = timeAxisSplits(_tzDate); const _timeAxisSplits = (ms == 1 ? timeAxisSplitsMs(_tzDate) : timeAxisSplitsS(_tzDate));
const _timeAxisVals = timeAxisVals(_tzDate, timeAxisStamps(_timeAxisStamps, _fmtDate)); const _timeAxisVals = timeAxisVals(_tzDate, timeAxisStamps((ms == 1 ? _timeAxisStampsMs : _timeAxisStampsS), _fmtDate));
const _timeSeriesVal = timeSeriesVal(_tzDate, timeSeriesStamp(_timeSeriesStamp, _fmtDate)); const _timeSeriesVal = timeSeriesVal(_tzDate, timeSeriesStamp(_timeSeriesStamp, _fmtDate));
const legend = assign({show: true, live: true}, opts.legend); const legend = assign({show: true, live: true}, opts.legend);
@ -1474,9 +1485,13 @@ function uPlot(opts, data, then) {
function convergeSize() { function convergeSize() {
let converged = false; let converged = false;
let cycleNum = 0;
while (!converged) { while (!converged) {
let axesConverged = axesCalc(); cycleNum++;
let guttersConverged = guttersCalc();
let axesConverged = axesCalc(cycleNum);
let guttersConverged = guttersCalc(cycleNum);
converged = axesConverged && guttersConverged; converged = axesConverged && guttersConverged;
@ -1684,7 +1699,7 @@ function uPlot(opts, data, then) {
axis.size = fnOrSelf(axis.size); axis.size = fnOrSelf(axis.size);
axis.space = fnOrSelf(axis.space); axis.space = fnOrSelf(axis.space);
axis.rotate = fnOrSelf(axis.rotate); axis.rotate = fnOrSelf(axis.rotate);
axis.incrs = fnOrSelf(axis.incrs || ( sc.distr == 2 ? wholeIncrs : (isTime ? timeIncrs : numIncrs))); axis.incrs = fnOrSelf(axis.incrs || ( sc.distr == 2 ? wholeIncrs : (isTime ? (ms == 1 ? timeIncrsMs : timeIncrsS) : numIncrs)));
axis.splits = fnOrSelf(axis.splits || (isTime && sc.distr == 1 ? _timeAxisSplits : sc.distr == 3 ? logAxisSplits : numAxisSplits)); axis.splits = fnOrSelf(axis.splits || (isTime && sc.distr == 1 ? _timeAxisSplits : sc.distr == 3 ? logAxisSplits : numAxisSplits));
let av = axis.values; let av = axis.values;
@ -1703,7 +1718,7 @@ function uPlot(opts, data, then) {
axis.font = pxRatioFont(axis.font); axis.font = pxRatioFont(axis.font);
axis.labelFont = pxRatioFont(axis.labelFont); axis.labelFont = pxRatioFont(axis.labelFont);
axis._size = axis.size(self, null, i); axis._size = axis.size(self, null, i, 0);
axis._space = axis._space =
axis._rotate = axis._rotate =
@ -1786,7 +1801,7 @@ function uPlot(opts, data, then) {
if (xScaleDistr == 3) if (xScaleDistr == 3)
[_min, _max] = rangeLog(_min, _min, scales[xScaleKey].log, false); [_min, _max] = rangeLog(_min, _min, scales[xScaleKey].log, false);
else if (scales[xScaleKey].time) else if (scales[xScaleKey].time)
_max = _min + 86400; _max = _min + 86400 / ms;
else else
[_min, _max] = rangeNum(_min, _max, 0.1, true); [_min, _max] = rangeNum(_min, _max, 0.1, true);
} }
@ -2324,7 +2339,7 @@ function uPlot(opts, data, then) {
ctx.translate(-offset, -offset); ctx.translate(-offset, -offset);
} }
function axesCalc() { function axesCalc(cycleNum) {
// log("axesCalc()", arguments); // log("axesCalc()", arguments);
let converged = true; let converged = true;
@ -2378,7 +2393,7 @@ function uPlot(opts, data, then) {
let oldSize = axis._size; let oldSize = axis._size;
axis._size = axis.size(self, values, i); axis._size = ceil(axis.size(self, values, i, cycleNum));
if (oldSize != null && axis._size != oldSize) // ready && ? if (oldSize != null && axis._size != oldSize) // ready && ?
converged = false; converged = false;
@ -2387,13 +2402,13 @@ function uPlot(opts, data, then) {
return converged; return converged;
} }
function guttersCalc() { function guttersCalc(cycleNum) {
let converged = true; let converged = true;
let {_x, _y} = gutters; let {_x, _y} = gutters;
gutters._x = gutters.x(self); gutters._x = ceil(gutters.x(self, cycleNum));
gutters._y = gutters.y(self); gutters._y = ceil(gutters.y(self, cycleNum));
if (gutters._x != _x || gutters._y != _y) if (gutters._x != _x || gutters._y != _y)
converged = false; converged = false;

View File

@ -4,7 +4,7 @@
* *
* uPlot.js (μPlot) * uPlot.js (μPlot)
* A small, fast chart for time series, lines, areas, ohlc & bars * A small, fast chart for time series, lines, areas, ohlc & bars
* https://github.com/leeoniya/uPlot (v1.4.4) * https://github.com/leeoniya/uPlot (v1.4.6)
*/ */
var uPlot = (function () { var uPlot = (function () {
@ -552,8 +552,6 @@ var uPlot = (function () {
var allMults = [1,2,2.5,5]; var allMults = [1,2,2.5,5];
var wholeMults = allMults.filter(onlyWhole);
// ...0.01, 0.02, 0.025, 0.05, 0.1, 0.2, 0.25, 0.5 // ...0.01, 0.02, 0.025, 0.05, 0.1, 0.2, 0.25, 0.5
var decIncrs = genIncrs(10, -16, 0, allMults); var decIncrs = genIncrs(10, -16, 0, allMults);
@ -565,61 +563,196 @@ var uPlot = (function () {
var numIncrs = decIncrs.concat(oneIncrs); var numIncrs = decIncrs.concat(oneIncrs);
var s = 1, var NL = "\n";
m = 60,
h = m * m,
d = h * 24,
mo = d * 30,
y = d * 365;
// min of 1e-3 prevents setting a temporal x ticks too small since Date objects cannot advance ticks smaller than 1ms var yyyy = "{YYYY}";
var timeIncrs = genIncrs(10, -3, 0, wholeMults).concat([ var NLyyyy = NL + yyyy;
// minute divisors (# of secs) var md = "{M}/{D}";
1, var NLmd = NL + md;
5, var NLmdyy = NLmd + "/{YY}";
10,
15, var aa = "{aa}";
30, var hmm = "{h}:{mm}";
// hour divisors (# of mins) var hmmaa = hmm + aa;
m, var NLhmmaa = NL + hmmaa;
m * 5, var ss = ":{ss}";
m * 10,
m * 15, var _ = null;
m * 30,
// day divisors (# of hrs) function genTimeStuffs(ms) {
h, var s = ms * 1e3,
h * 2, m = s * 60,
h * 3, h = m * 60,
h * 4, d = h * 24,
h * 6, mo = d * 30,
h * 8, y = d * 365;
h * 12,
// month divisors TODO: need more? // min of 1e-3 prevents setting a temporal x ticks too small since Date objects cannot advance ticks smaller than 1ms
d, var subSecIncrs = ms == 1 ? genIncrs(10, 0, 3, allMults).filter(onlyWhole) : genIncrs(10, -3, 0, allMults);
d * 2,
d * 3, var timeIncrs = subSecIncrs.concat([
d * 4, // minute divisors (# of secs)
d * 5, s,
d * 6, s * 5,
d * 7, s * 10,
d * 8, s * 15,
d * 9, s * 30,
d * 10, // hour divisors (# of mins)
d * 15, m,
// year divisors (# months, approx) m * 5,
mo, m * 10,
mo * 2, m * 15,
mo * 3, m * 30,
mo * 4, // day divisors (# of hrs)
mo * 6, h,
// century divisors h * 2,
y, h * 3,
y * 2, h * 4,
y * 5, h * 6,
y * 10, h * 8,
y * 25, h * 12,
y * 50, // month divisors TODO: need more?
y * 100 ]); d,
d * 2,
d * 3,
d * 4,
d * 5,
d * 6,
d * 7,
d * 8,
d * 9,
d * 10,
d * 15,
// year divisors (# months, approx)
mo,
mo * 2,
mo * 3,
mo * 4,
mo * 6,
// century divisors
y,
y * 2,
y * 5,
y * 10,
y * 25,
y * 50,
y * 100 ]);
// [0]: minimum num secs in the tick incr
// [1]: default tick format
// [2-7]: rollover tick formats
// [8]: mode: 0: replace [1] -> [2-7], 1: concat [1] + [2-7]
var _timeAxisStamps = [
// tick incr default year month day hour min sec mode
[y, yyyy, _, _, _, _, _, _, 1],
[d * 28, "{MMM}", NLyyyy, _, _, _, _, _, 1],
[d, md, NLyyyy, _, _, _, _, _, 1],
[h, "{h}" + aa, NLmdyy, _, NLmd, _, _, _, 1],
[m, hmmaa, NLmdyy, _, NLmd, _, _, _, 1],
[s, ss, NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1],
[ms, ss + ".{fff}", NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1] ];
// the ensures that axis ticks, values & grid are aligned to logical temporal breakpoints and not an arbitrary timestamp
// https://www.timeanddate.com/time/dst/
// https://www.timeanddate.com/time/dst/2019.html
// https://www.epochconverter.com/timezones
function timeAxisSplits(tzDate) {
return function (self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace) {
var splits = [];
var isYr = foundIncr >= y;
var isMo = foundIncr >= mo && foundIncr < y;
// get the timezone-adjusted date
var minDate = tzDate(scaleMin);
var minDateTs = minDate * ms;
// get ts of 12am (this lands us at or before the original scaleMin)
var minMin = mkDate(minDate[getFullYear](), isYr ? 0 : minDate[getMonth](), isMo || isYr ? 1 : minDate[getDate]());
var minMinTs = minMin * ms;
if (isMo || isYr) {
var moIncr = isMo ? foundIncr / mo : 0;
var yrIncr = isYr ? foundIncr / y : 0;
// let tzOffset = scaleMin - minDateTs; // needed?
var split = minDateTs == minMinTs ? minDateTs : mkDate(minMin[getFullYear]() + yrIncr, minMin[getMonth]() + moIncr, 1) * ms;
var splitDate = new Date(split / ms);
var baseYear = splitDate[getFullYear]();
var baseMonth = splitDate[getMonth]();
for (var i = 0; split <= scaleMax; i++) {
var next = mkDate(baseYear + yrIncr * i, baseMonth + moIncr * i, 1);
var offs = next - tzDate(next * ms);
split = (+next + offs) * ms;
if (split <= scaleMax)
{ splits.push(split); }
}
}
else {
var incr0 = foundIncr >= d ? d : foundIncr;
var tzOffset = floor(scaleMin) - floor(minDateTs);
var split$1 = minMinTs + tzOffset + incrRoundUp(minDateTs - minMinTs, incr0);
splits.push(split$1);
var date0 = tzDate(split$1);
var prevHour = date0[getHours]() + (date0[getMinutes]() / m) + (date0[getSeconds]() / h);
var incrHours = foundIncr / h;
var minSpace = self.axes[axisIdx]._space;
var pctSpace = foundSpace / minSpace;
while (1) {
split$1 = roundDec(split$1 + foundIncr, ms == 1 ? 0 : 3);
if (split$1 > scaleMax)
{ break; }
if (incrHours > 1) {
var expectedHour = floor(roundDec(prevHour + incrHours, 6)) % 24;
var splitDate$1 = tzDate(split$1);
var actualHour = splitDate$1.getHours();
var dstShift = actualHour - expectedHour;
if (dstShift > 1)
{ dstShift = -1; }
split$1 -= dstShift * h;
prevHour = (prevHour + incrHours) % 24;
// add a tick only if it's further than 70% of the min allowed label spacing
var prevSplit = splits[splits.length - 1];
var pctIncr = roundDec((split$1 - prevSplit) / foundIncr, 3);
if (pctIncr * pctSpace >= .7)
{ splits.push(split$1); }
}
else
{ splits.push(split$1); }
}
}
return splits;
}
}
return [
timeIncrs,
_timeAxisStamps,
timeAxisSplits ];
}
var ref = genTimeStuffs(1);
var timeIncrsMs = ref[0];
var _timeAxisStampsMs = ref[1];
var timeAxisSplitsMs = ref[2];
var ref$1 = genTimeStuffs(1e-3);
var timeIncrsS = ref$1[0];
var _timeAxisStampsS = ref$1[1];
var timeAxisSplitsS = ref$1[2];
// base 2 // base 2
var binIncrs = genIncrs(2, -53, 53, [1]); var binIncrs = genIncrs(2, -53, 53, [1]);
@ -640,36 +773,6 @@ var uPlot = (function () {
); }); ); });
} }
var NL = "\n";
var yyyy = "{YYYY}";
var NLyyyy = NL + yyyy;
var md = "{M}/{D}";
var NLmd = NL + md;
var NLmdyy = NLmd + "/{YY}";
var aa = "{aa}";
var hmm = "{h}:{mm}";
var hmmaa = hmm + aa;
var NLhmmaa = NL + hmmaa;
var ss = ":{ss}";
var _ = null;
// [0]: minimum num secs in the tick incr
// [1]: default tick format
// [2-7]: rollover tick formats
// [8]: mode: 0: replace [1] -> [2-7], 1: concat [1] + [2-7]
var _timeAxisStamps = [
// tick incr default year month day hour min sec mode
[y, yyyy, _, _, _, _, _, _, 1],
[d * 28, "{MMM}", NLyyyy, _, _, _, _, _, 1],
[d, md, NLyyyy, _, _, _, _, _, 1],
[h, "{h}" + aa, NLmdyy, _, NLmd, _, _, _, 1],
[m, hmmaa, NLmdyy, _, NLmd, _, _, _, 1],
[s, ss, NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1],
[1e-3, ss + ".{fff}", NLmdyy + " " + hmmaa, _, NLmd + " " + hmmaa, _, NLhmmaa, _, 1] ];
// TODO: will need to accept spaces[] and pull incr into the loop when grid will be non-uniform, eg for log scales. // TODO: will need to accept spaces[] and pull incr into the loop when grid will be non-uniform, eg for log scales.
// currently we ignore this for months since they're *nearly* uniform and the added complexity is not worth it // currently we ignore this for months since they're *nearly* uniform and the added complexity is not worth it
function timeAxisVals(tzDate, stamps) { function timeAxisVals(tzDate, stamps) {
@ -726,93 +829,6 @@ var uPlot = (function () {
return new Date(y, m, d); return new Date(y, m, d);
} }
// the ensures that axis ticks, values & grid are aligned to logical temporal breakpoints and not an arbitrary timestamp
// https://www.timeanddate.com/time/dst/
// https://www.timeanddate.com/time/dst/2019.html
// https://www.epochconverter.com/timezones
function timeAxisSplits(tzDate) {
return function (self, axisIdx, scaleMin, scaleMax, foundIncr, foundSpace) {
var splits = [];
var isYr = foundIncr >= y;
var isMo = foundIncr >= mo && foundIncr < y;
// get the timezone-adjusted date
var minDate = tzDate(scaleMin);
var minDateTs = minDate / 1e3;
// get ts of 12am (this lands us at or before the original scaleMin)
var minMin = mkDate(minDate[getFullYear](), isYr ? 0 : minDate[getMonth](), isMo || isYr ? 1 : minDate[getDate]());
var minMinTs = minMin / 1e3;
if (isMo || isYr) {
var moIncr = isMo ? foundIncr / mo : 0;
var yrIncr = isYr ? foundIncr / y : 0;
// let tzOffset = scaleMin - minDateTs; // needed?
var split = minDateTs == minMinTs ? minDateTs : mkDate(minMin[getFullYear]() + yrIncr, minMin[getMonth]() + moIncr, 1) / 1e3;
var splitDate = new Date(split * 1e3);
var baseYear = splitDate[getFullYear]();
var baseMonth = splitDate[getMonth]();
for (var i = 0; split <= scaleMax; i++) {
var next = mkDate(baseYear + yrIncr * i, baseMonth + moIncr * i, 1);
var offs = next - tzDate(next / 1e3);
split = (+next + offs) / 1e3;
if (split <= scaleMax)
{ splits.push(split); }
}
}
else {
var incr0 = foundIncr >= d ? d : foundIncr;
var tzOffset = floor(scaleMin) - floor(minDateTs);
var split$1 = minMinTs + tzOffset + incrRoundUp(minDateTs - minMinTs, incr0);
splits.push(split$1);
var date0 = tzDate(split$1);
var prevHour = date0[getHours]() + (date0[getMinutes]() / m) + (date0[getSeconds]() / h);
var incrHours = foundIncr / h;
var minSpace = self.axes[axisIdx]._space;
var pctSpace = foundSpace / minSpace;
while (1) {
split$1 = roundDec(split$1 + foundIncr, 3);
if (split$1 > scaleMax)
{ break; }
if (incrHours > 1) {
var expectedHour = floor(roundDec(prevHour + incrHours, 6)) % 24;
var splitDate$1 = tzDate(split$1);
var actualHour = splitDate$1.getHours();
var dstShift = actualHour - expectedHour;
if (dstShift > 1)
{ dstShift = -1; }
split$1 -= dstShift * h;
prevHour = (prevHour + incrHours) % 24;
// add a tick only if it's further than 70% of the min allowed label spacing
var prevSplit = splits[splits.length - 1];
var pctIncr = roundDec((split$1 - prevSplit) / foundIncr, 3);
if (pctIncr * pctSpace >= .7)
{ splits.push(split$1); }
}
else
{ splits.push(split$1); }
}
}
return splits;
}
}
function timeSeriesStamp(stampCfg, fmtDate) { function timeSeriesStamp(stampCfg, fmtDate) {
return fmtDate(stampCfg); return fmtDate(stampCfg);
} }
@ -1236,7 +1252,7 @@ var uPlot = (function () {
{ opts = p.opts(self, opts) || opts; } { opts = p.opts(self, opts) || opts; }
}); });
var ms = opts.ms || 1e-3;
var series = self.series = setDefaults(opts.series || [], xSeriesOpts, ySeriesOpts, false); var series = self.series = setDefaults(opts.series || [], xSeriesOpts, ySeriesOpts, false);
var axes = self.axes = setDefaults(opts.axes || [], xAxisOpts, yAxisOpts, true); var axes = self.axes = setDefaults(opts.axes || [], xAxisOpts, yAxisOpts, true);
@ -1312,11 +1328,11 @@ var uPlot = (function () {
gutters._y = gutters.y(self); gutters._y = gutters.y(self);
// self.tz = opts.tz || Intl.DateTimeFormat().resolvedOptions().timeZone; // self.tz = opts.tz || Intl.DateTimeFormat().resolvedOptions().timeZone;
var _tzDate = (opts.tzDate || (function (ts) { return new Date(ts * 1e3); })); var _tzDate = (opts.tzDate || (function (ts) { return new Date(ts / ms); }));
var _fmtDate = (opts.fmtDate || fmtDate); var _fmtDate = (opts.fmtDate || fmtDate);
var _timeAxisSplits = timeAxisSplits(_tzDate); var _timeAxisSplits = (ms == 1 ? timeAxisSplitsMs(_tzDate) : timeAxisSplitsS(_tzDate));
var _timeAxisVals = timeAxisVals(_tzDate, timeAxisStamps(_timeAxisStamps, _fmtDate)); var _timeAxisVals = timeAxisVals(_tzDate, timeAxisStamps((ms == 1 ? _timeAxisStampsMs : _timeAxisStampsS), _fmtDate));
var _timeSeriesVal = timeSeriesVal(_tzDate, timeSeriesStamp(_timeSeriesStamp, _fmtDate)); var _timeSeriesVal = timeSeriesVal(_tzDate, timeSeriesStamp(_timeSeriesStamp, _fmtDate));
var legend = assign({show: true, live: true}, opts.legend); var legend = assign({show: true, live: true}, opts.legend);
@ -1472,9 +1488,13 @@ var uPlot = (function () {
function convergeSize() { function convergeSize() {
var converged = false; var converged = false;
var cycleNum = 0;
while (!converged) { while (!converged) {
var axesConverged = axesCalc(); cycleNum++;
var guttersConverged = guttersCalc();
var axesConverged = axesCalc(cycleNum);
var guttersConverged = guttersCalc(cycleNum);
converged = axesConverged && guttersConverged; converged = axesConverged && guttersConverged;
@ -1686,7 +1706,7 @@ var uPlot = (function () {
axis.size = fnOrSelf(axis.size); axis.size = fnOrSelf(axis.size);
axis.space = fnOrSelf(axis.space); axis.space = fnOrSelf(axis.space);
axis.rotate = fnOrSelf(axis.rotate); axis.rotate = fnOrSelf(axis.rotate);
axis.incrs = fnOrSelf(axis.incrs || ( sc.distr == 2 ? wholeIncrs : (isTime ? timeIncrs : numIncrs))); axis.incrs = fnOrSelf(axis.incrs || ( sc.distr == 2 ? wholeIncrs : (isTime ? (ms == 1 ? timeIncrsMs : timeIncrsS) : numIncrs)));
axis.splits = fnOrSelf(axis.splits || (isTime && sc.distr == 1 ? _timeAxisSplits : sc.distr == 3 ? logAxisSplits : numAxisSplits)); axis.splits = fnOrSelf(axis.splits || (isTime && sc.distr == 1 ? _timeAxisSplits : sc.distr == 3 ? logAxisSplits : numAxisSplits));
var av = axis.values; var av = axis.values;
@ -1705,7 +1725,7 @@ var uPlot = (function () {
axis.font = pxRatioFont(axis.font); axis.font = pxRatioFont(axis.font);
axis.labelFont = pxRatioFont(axis.labelFont); axis.labelFont = pxRatioFont(axis.labelFont);
axis._size = axis.size(self, null, i); axis._size = axis.size(self, null, i, 0);
axis._space = axis._space =
axis._rotate = axis._rotate =
@ -1790,7 +1810,7 @@ var uPlot = (function () {
if (xScaleDistr == 3) if (xScaleDistr == 3)
{ (assign = rangeLog(_min, _min, scales[xScaleKey].log, false), _min = assign[0], _max = assign[1]); } { (assign = rangeLog(_min, _min, scales[xScaleKey].log, false), _min = assign[0], _max = assign[1]); }
else if (scales[xScaleKey].time) else if (scales[xScaleKey].time)
{ _max = _min + 86400; } { _max = _min + 86400 / ms; }
else else
{ (assign$1 = rangeNum(_min, _max, 0.1, true), _min = assign$1[0], _max = assign$1[1]); } { (assign$1 = rangeNum(_min, _max, 0.1, true), _min = assign$1[0], _max = assign$1[1]); }
} }
@ -2331,7 +2351,7 @@ var uPlot = (function () {
ctx.translate(-offset, -offset); ctx.translate(-offset, -offset);
} }
function axesCalc() { function axesCalc(cycleNum) {
// log("axesCalc()", arguments); // log("axesCalc()", arguments);
var converged = true; var converged = true;
@ -2388,7 +2408,7 @@ var uPlot = (function () {
var oldSize = axis._size; var oldSize = axis._size;
axis._size = axis.size(self, values, i); axis._size = ceil(axis.size(self, values, i, cycleNum));
if (oldSize != null && axis._size != oldSize) // ready && ? if (oldSize != null && axis._size != oldSize) // ready && ?
{ converged = false; } { converged = false; }
@ -2397,14 +2417,14 @@ var uPlot = (function () {
return converged; return converged;
} }
function guttersCalc() { function guttersCalc(cycleNum) {
var converged = true; var converged = true;
var _x = gutters._x; var _x = gutters._x;
var _y = gutters._y; var _y = gutters._y;
gutters._x = gutters.x(self); gutters._x = ceil(gutters.x(self, cycleNum));
gutters._y = gutters.y(self); gutters._y = ceil(gutters.y(self, cycleNum));
if (gutters._x != _x || gutters._y != _y) if (gutters._x != _x || gutters._y != _y)
{ converged = false; } { converged = false; }

File diff suppressed because one or more lines are too long