diff --git a/theonionbox/scripts/cc.js b/theonionbox/scripts/cc.js
index 1a99e72..bf4d2e5 100644
--- a/theonionbox/scripts/cc.js
+++ b/theonionbox/scripts/cc.js
@@ -3868,7 +3868,24 @@ var str = ρσ_str, repr = ρσ_repr;;
})();
(function(){
- var __name__ = "RapydSmoothie.util";
+//####
+ //
+ // Part of RapydSmoothie
+ // A RapydScript-NG clone of SmoothieChart
+ //
+ // SmoothieChart Copyright (c) 2010-2013, Joe Walnes
+ // 2013-2018, Drew Noakes
+ // Details: www.smoothiechart.org / https://github.com/joewalnes/smoothie
+ // License: MIT @ https://github.com/joewalnes/smoothie/blob/master/LICENSE.txt
+ //
+ // RapydSmoothie Copyright (c) 2018, Ralph Wetzel
+ // License: MIT
+ // Language: RapydScript-NG Transpiler
+ // https://github.com/kovidgoyal/rapydscript-ng
+ // Coverage: SmoothieChart 1.36 ("Add title option, by @mesca")
+ //
+ //####
+ var __name__ = "RapydSmoothie.util";
function DefaultOptionsBase() {
if (this.ρσ_object_id === undefined) Object.defineProperty(this, "ρσ_object_id", {"value":++ρσ_object_counter});
DefaultOptionsBase.prototype.__init__.apply(this, arguments);
@@ -3879,6 +3896,7 @@ var str = ρσ_str, repr = ρσ_repr;;
var self = this;
var rv;
rv = !ρσ_in(key, ρσ_list_decorate([ "constructor", "__init__", "__repr__", "__str__", "hasOwnProperty" ]));
+ // console.log('hOP: ' + key + " => " + rv)
return rv;
};
if (!DefaultOptionsBase.prototype.hasOwnProperty.__argnames__) Object.defineProperties(DefaultOptionsBase.prototype.hasOwnProperty, {
@@ -3916,6 +3934,7 @@ var str = ρσ_str, repr = ρσ_repr;;
for (var ρσ_Index0 = 0; ρσ_Index0 < ρσ_Iter0.length; ρσ_Index0++) {
key = ρσ_Iter0[ρσ_Index0];
if (args[(typeof i === "number" && i < 0) ? args.length + i : i].hasOwnProperty(key)) {
+ // second test necessary as typeof(null) == 'object'
if (typeof(args[i][key]) === 'object' && ρσ_exists.n((ρσ_expr_temp = args[(typeof i === "number" && i < 0) ? args.length + i : i])[(typeof key === "number" && key < 0) ? ρσ_expr_temp.length + key : key])) {
if (Array.isArray((ρσ_expr_temp = args[(typeof i === "number" && i < 0) ? args.length + i : i])[(typeof key === "number" && key < 0) ? ρσ_expr_temp.length + key : key]) === true) {
(ρσ_expr_temp = args[0])[(typeof key === "number" && key < 0) ? ρσ_expr_temp.length + key : key] = (ρσ_expr_temp = args[(typeof i === "number" && i < 0) ? args.length + i : i])[(typeof key === "number" && key < 0) ? ρσ_expr_temp.length + key : key];
@@ -3959,7 +3978,22 @@ var str = ρσ_str, repr = ρσ_repr;;
})();
(function(){
- var __name__ = "RapydSmoothie.timeseries";
+//
+ // Part of RapydSmoothie
+ // A RapydScript-NG clone of SmoothieChart
+ //
+ // SmoothieChart Copyright (c) 2010-2013, Joe Walnes
+ // 2013-2018, Drew Noakes
+ // Details: www.smoothiechart.org / https://github.com/joewalnes/smoothie
+ // License: MIT @ https://github.com/joewalnes/smoothie/blob/master/LICENSE.txt
+ //
+ // RapydSmoothie Copyright (c) 2018, Ralph Wetzel
+ // License: MIT
+ // Language: RapydScript-NG Transpiler
+ // https://github.com/kovidgoyal/rapydscript-ng
+ // Coverage: SmoothieChart 1.36 ("Add title option, by @mesca")
+ //
+ var __name__ = "RapydSmoothie.timeseries";
var extend = ρσ_modules["RapydSmoothie.util"].extend;
function TimeSeries() {
@@ -3998,9 +4032,9 @@ var str = ρσ_str, repr = ρσ_repr;;
if (len(self.data) > 0) {
self.minValue = self.data[0][1];
self.maxValue = self.data[0][1];
- var ρσ_Iter0 = ρσ_Iterable(self.data);
- for (var ρσ_Index0 = 0; ρσ_Index0 < ρσ_Iter0.length; ρσ_Index0++) {
- p = ρσ_Iter0[ρσ_Index0];
+ var ρσ_Iter1 = ρσ_Iterable(self.data);
+ for (var ρσ_Index1 = 0; ρσ_Index1 < ρσ_Iter1.length; ρσ_Index1++) {
+ p = ρσ_Iter1[ρσ_Index1];
pv = p[1];
if (pv < self.minValue) {
self.minValue = pv;
@@ -4029,16 +4063,22 @@ var str = ρσ_str, repr = ρσ_repr;;
i -= 1;
}
if ((i === -1 || typeof i === "object" && ρσ_equals(i, -1))) {
+ // This new item is the oldest data
self.data.insert(0, ρσ_list_decorate([ timestamp, value ]));
} else if (len(self.data) > 0 && ((ρσ_expr_temp = self.data)[(typeof i === "number" && i < 0) ? ρσ_expr_temp.length + i : i][0] === timestamp || typeof (ρσ_expr_temp = self.data)[(typeof i === "number" && i < 0) ? ρσ_expr_temp.length + i : i][0] === "object" && ρσ_equals((ρσ_expr_temp = self.data)[(typeof i === "number" && i < 0) ? ρσ_expr_temp.length + i : i][0], timestamp))) {
+ // Update existing values in the array
if (sumRepeatedTimeStampValues === true) {
+ // Sum this value into the existing 'bucket'
(ρσ_expr_temp = self.data)[(typeof i === "number" && i < 0) ? ρσ_expr_temp.length + i : i][1] += value;
} else {
+ // Replace the previous value
(ρσ_expr_temp = self.data)[(typeof i === "number" && i < 0) ? ρσ_expr_temp.length + i : i][1] = value;
}
} else if (i < len(self.data) - 1) {
+ // Splice into the correct position to keep timestamps in order
self.data.insert(i + 1, ρσ_list_decorate([ timestamp, value ]));
} else {
+ // Add to the end of the array
self.data.push(ρσ_list_decorate([ timestamp, value ]));
}
self.maxValue = (self.maxValue === null) ? value : max(self.maxValue, value);
@@ -4052,6 +4092,8 @@ var str = ρσ_str, repr = ρσ_repr;;
TimeSeries.prototype.dropOldData = function dropOldData(oldestValidTime, maxDataSetLength) {
var self = this;
var removeCount, lsd;
+ // We must always keep one expired data point as we need this to draw the
+ // line that comes into the chart from the left, but any points prior to that can be removed.
removeCount = 0;
lsd = len(self.data);
while (lsd - removeCount >= maxDataSetLength && (ρσ_expr_temp = self.data)[ρσ_bound_index(removeCount + 1, ρσ_expr_temp)][0] < oldestValidTime) {
@@ -4083,12 +4125,28 @@ var str = ρσ_str, repr = ρσ_repr;;
})();
(function(){
- var __name__ = "RapydSmoothie.smoothie";
+//
+ // RapydSmoothie
+ // A RapydScript-NG clone of SmoothieChart
+ //
+ // SmoothieChart Copyright (c) 2010-2013, Joe Walnes
+ // 2013-2018, Drew Noakes
+ // Details: www.smoothiechart.org / https://github.com/joewalnes/smoothie
+ // License: MIT @ https://github.com/joewalnes/smoothie/blob/master/LICENSE.txt
+ //
+ // RapydSmoothie Copyright (c) 2018, Ralph Wetzel
+ // License: MIT
+ // Language: RapydScript-NG Python to Javascript Transpiler
+ // https://github.com/kovidgoyal/rapydscript-ng
+ // Coverage: SmoothieChart 1.36 ("Add title option, by @mesca")
+ //
+ var __name__ = "RapydSmoothie.smoothie";
var TimeSeries = ρσ_modules["RapydSmoothie.timeseries"].TimeSeries;
var extend = ρσ_modules["RapydSmoothie.util"].extend;
var DefaultOptionsBase = ρσ_modules["RapydSmoothie.util"].DefaultOptionsBase;
+ // Date.now polyfill
Date.now = Date.now || function () {
return (new Date).getTime();
};
@@ -4147,6 +4205,7 @@ return this.__repr__();
DefaultChartOptions.prototype.maxDataSetLength = 2;
DefaultChartOptions.prototype.interpolation = "bezier";
DefaultChartOptions.prototype.timestampFormatter = null;
+ // will become RapydSmoothie.timeFormatter at __init__
DefaultChartOptions.prototype.scrollBackwards = false;
DefaultChartOptions.prototype.horizontalLines = ρσ_list_decorate([]);
DefaultChartOptions.prototype.grid = (function(){
@@ -4258,11 +4317,12 @@ return this.__repr__();
RapydSmoothie.__handles_kwarg_interpolation__ = RapydSmoothie.prototype.__init__.__handles_kwarg_interpolation__;
RapydSmoothie.tooltipFormatter = function tooltipFormatter(timestamp, data) {
var timestampFormatter, lines, item;
+ // tooltipFormatter = def(timestamp, data):
timestampFormatter = this.options.timestampFormatter || RapydSmoothie.timeFormatter;
lines = ρσ_list_decorate([ timestampFormatter(new Date(timestamp)) ]);
- var ρσ_Iter0 = ρσ_Iterable(data);
- for (var ρσ_Index0 = 0; ρσ_Index0 < ρσ_Iter0.length; ρσ_Index0++) {
- item = ρσ_Iter0[ρσ_Index0];
+ var ρσ_Iter2 = ρσ_Iterable(data);
+ for (var ρσ_Index2 = 0; ρσ_Index2 < ρσ_Iter2.length; ρσ_Index2++) {
+ item = ρσ_Iter2[ρσ_Index2];
lines.push("" + this.options.yMaxFormatter(item.value, this.options.labels.precision) + "");
}
return lines.join("
");
@@ -4302,9 +4362,9 @@ return this.__repr__();
RapydSmoothie.prototype.removeTimeSeries = function removeTimeSeries(ts) {
var self = this;
var ρσ_unpack, index, serie;
- var ρσ_Iter1 = ρσ_Iterable(enumerate(self.seriesSet));
- for (var ρσ_Index1 = 0; ρσ_Index1 < ρσ_Iter1.length; ρσ_Index1++) {
- ρσ_unpack = ρσ_Iter1[ρσ_Index1];
+ var ρσ_Iter3 = ρσ_Iterable(enumerate(self.seriesSet));
+ for (var ρσ_Index3 = 0; ρσ_Index3 < ρσ_Iter3.length; ρσ_Index3++) {
+ ρσ_unpack = ρσ_Iter3[ρσ_Index3];
index = ρσ_unpack[0];
serie = ρσ_unpack[1];
if (serie.timeSeries === ts) {
@@ -4323,9 +4383,9 @@ return this.__repr__();
RapydSmoothie.prototype.getTimeSeriesOptions = function getTimeSeriesOptions(ts) {
var self = this;
var serie;
- var ρσ_Iter2 = ρσ_Iterable(self.seriesSet);
- for (var ρσ_Index2 = 0; ρσ_Index2 < ρσ_Iter2.length; ρσ_Index2++) {
- serie = ρσ_Iter2[ρσ_Index2];
+ var ρσ_Iter4 = ρσ_Iterable(self.seriesSet);
+ for (var ρσ_Index4 = 0; ρσ_Index4 < ρσ_Iter4.length; ρσ_Index4++) {
+ serie = ρσ_Iter4[ρσ_Index4];
if (serie.timeSeries === ts) {
return serie.options;
}
@@ -4338,9 +4398,9 @@ return this.__repr__();
RapydSmoothie.prototype.bringToFront = function bringToFront(ts) {
var self = this;
var ρσ_unpack, index, serie;
- var ρσ_Iter3 = ρσ_Iterable(enumerate(self.seriesSet));
- for (var ρσ_Index3 = 0; ρσ_Index3 < ρσ_Iter3.length; ρσ_Index3++) {
- ρσ_unpack = ρσ_Iter3[ρσ_Index3];
+ var ρσ_Iter5 = ρσ_Iterable(enumerate(self.seriesSet));
+ for (var ρσ_Index5 = 0; ρσ_Index5 < ρσ_Iter5.length; ρσ_Index5++) {
+ ρσ_unpack = ρσ_Iter5[ρσ_Index5];
index = ρσ_unpack[0];
serie = ρσ_unpack[1];
if (serie.timeSeries === ts) {
@@ -4391,9 +4451,9 @@ return this.__repr__();
t = time - (self.canvas.offsetWidth - self.mouseX) * self.options.millisPerPixel;
}
data = ρσ_list_decorate([]);
- var ρσ_Iter4 = ρσ_Iterable(self.seriesSet);
- for (var ρσ_Index4 = 0; ρσ_Index4 < ρσ_Iter4.length; ρσ_Index4++) {
- serie = ρσ_Iter4[ρσ_Index4];
+ var ρσ_Iter6 = ρσ_Iterable(self.seriesSet);
+ for (var ρσ_Index6 = 0; ρσ_Index6 < ρσ_Iter6.length; ρσ_Index6++) {
+ serie = ρσ_Iter6[ρσ_Index6];
ts = serie.timeSeries;
if ((ts.disabled === true || typeof ts.disabled === "object" && ρσ_equals(ts.disabled, true))) {
continue;
@@ -4489,6 +4549,18 @@ return this.__repr__();
animate = function () {
this.frame = this.AnimateCompatibility.requestAnimationFrame(function () {
var dateZero, maxTimeStamp;
+ // reducer = def(max, series):
+ // dataSet = series.timeSeries.data
+ // lds = len(dataSet)
+ // indexToCheck = Math.round(self.options.displayDataFromPercentile * lds) - 1
+ // indexToCheck = max(indexToCheck, 0)
+ // indexToCheck = min(indexToCheck, lds - 1)
+ // if dataSet is not None and lds > 0:
+ // lastDataTimeStamp = dataSet[indexToCheck][0]
+ // max = max(max, lastDataTimeStamp)
+ // return max
+ // .bind(this)
+ // reducer = reduce_f.bind(this)
if (this.options.nonRealtimeData) {
dateZero = new Date(0);
maxTimeStamp = this.seriesSet.reduce((function() {
@@ -4534,9 +4606,9 @@ return this.__repr__();
chartOptions = self.options;
chartMaxValue = Number.NaN;
chartMinValue = Number.NaN;
- var ρσ_Iter5 = ρσ_Iterable(self.seriesSet);
- for (var ρσ_Index5 = 0; ρσ_Index5 < ρσ_Iter5.length; ρσ_Index5++) {
- serie = ρσ_Iter5[ρσ_Index5];
+ var ρσ_Iter7 = ρσ_Iterable(self.seriesSet);
+ for (var ρσ_Index7 = 0; ρσ_Index7 < ρσ_Iter7.length; ρσ_Index7++) {
+ serie = ρσ_Iter7[ρσ_Index7];
ts = serie.timeSeries;
if (ts.disabled === true) {
continue;
@@ -4603,6 +4675,7 @@ return this.__repr__();
self.lastRenderTimeMillis = nowMillis;
canvas = canvas || self.canvas;
time = time || nowMillis - (self.delay || 0);
+ // Round time down to pixel granularity, so motion appears smoother.
time -= time % self.options.millisPerPixel;
self.lastChartTimestamp = time;
context = canvas.getContext("2d");
@@ -4615,6 +4688,7 @@ return this.__repr__();
ρσ_d["height"] = canvas.clientHeight;
return ρσ_d;
}).call(this);
+ // Calculate the threshold time for the oldest data points.
oldestValidTime = time - dimensions.width * chartOptions.millisPerPixel;
valueToYPixel = (function() {
var ρσ_anonfunc = function (value) {
@@ -4642,13 +4716,21 @@ return this.__repr__();
self.updateValueRange();
context.font = chartOptions.labels.fontSize + "px " + chartOptions.labels.fontFamily;
+ // Move origin, create clipping rectangle, clear area
self.render_10(context, chartOptions, dimensions);
+ // Grid lines Vertical & Horizontal, Bounding Rect
self.render_20(context, chartOptions, dimensions, time, oldestValidTime, timeToXPixel);
+ // Draw any horizontal lines...
self.render_30(context, chartOptions, dimensions, valueToYPixel);
+ // Draw the TimeSeries
self.render_40(context, chartOptions, dimensions, oldestValidTime, timeToXPixel, valueToYPixel);
+ // Draw vertical bar to show tooltip position
self.render_50(context, chartOptions, dimensions);
+ // Draw the axis values on the chart.
self.render_60(context, chartOptions, dimensions);
+ // Display intermediate y axis labels along y-axis to the left of the chart
self.render_70(context, chartOptions, dimensions);
+ // Display timestamps along x-axis at the bottom of the chart.
self.render_80(context, chartOptions, dimensions, time, oldestValidTime, timeToXPixel);
};
if (!RapydSmoothie.prototype.render.__argnames__) Object.defineProperties(RapydSmoothie.prototype.render, {
@@ -4656,11 +4738,18 @@ return this.__repr__();
});
RapydSmoothie.prototype.render_10 = function render_10(context, chartOptions, dimensions) {
var self = this;
+ // Save the state of the canvas context, any transformations applied in this method
+ // will get removed from the stack at the end of this method when .restore() is called.
context.save();
+ // Move the origin.
context.translate(dimensions.left, dimensions.top);
+ // Create a clipped rectangle - anything we draw will be constrained to this rectangle.
+ // This prevents the occasional pixels from curves near the edges overrunning and creating
+ // screen cheese (that phrase should need no explanation).
context.beginPath();
context.rect(0, 0, dimensions.width, dimensions.height);
context.clip();
+ // Clear the working area.
context.save();
context.fillStyle = chartOptions.grid.fillStyle;
context.clearRect(0, 0, dimensions.width, dimensions.height);
@@ -4673,9 +4762,12 @@ return this.__repr__();
RapydSmoothie.prototype.render_20 = function render_20(context, chartOptions, dimensions, time, oldestValidTime, timeToXPixel) {
var self = this;
var t, gx, v, gy;
+ // Grid lines...
context.save();
context.lineWidth = chartOptions.grid.lineWidth;
context.strokeStyle = chartOptions.grid.strokeStyle;
+ // print(chartOptions.grid.strokeStyle)
+ // Vertical (time) dividers.
if (chartOptions.grid.millisPerLine > 0) {
context.beginPath();
t = time - time % chartOptions.grid.millisPerLine;
@@ -4717,10 +4809,11 @@ return this.__repr__();
RapydSmoothie.prototype.render_30 = function render_30(context, chartOptions, dimensions, valueToYPixel) {
var self = this;
var hly, line;
+ // Draw any horizontal lines...
context.save();
- var ρσ_Iter6 = ρσ_Iterable(chartOptions.horizontalLines);
- for (var ρσ_Index6 = 0; ρσ_Index6 < ρσ_Iter6.length; ρσ_Index6++) {
- line = ρσ_Iter6[ρσ_Index6];
+ var ρσ_Iter8 = ρσ_Iterable(chartOptions.horizontalLines);
+ for (var ρσ_Index8 = 0; ρσ_Index8 < ρσ_Iter8.length; ρσ_Index8++) {
+ line = ρσ_Iter8[ρσ_Index8];
hly = Math.round(valueToYPixel(line.value)) - .5;
context.strokeStyle = line.color || "#ffffff";
context.lineWidth = line.lineWidth || 1;
@@ -4738,9 +4831,10 @@ return this.__repr__();
RapydSmoothie.prototype.render_40 = function render_40(context, chartOptions, dimensions, oldestValidTime, timeToXPixel, valueToYPixel) {
var self = this;
var ts, dataSet, seriesOptions, firstX, lastX, lastY, x, y, ρσ_unpack, index, set, serie;
- var ρσ_Iter7 = ρσ_Iterable(self.seriesSet);
- for (var ρσ_Index7 = 0; ρσ_Index7 < ρσ_Iter7.length; ρσ_Index7++) {
- serie = ρσ_Iter7[ρσ_Index7];
+ // For each data set...
+ var ρσ_Iter9 = ρσ_Iterable(self.seriesSet);
+ for (var ρσ_Index9 = 0; ρσ_Index9 < ρσ_Iter9.length; ρσ_Index9++) {
+ serie = ρσ_Iter9[ρσ_Index9];
context.save();
ts = serie.timeSeries;
if (ts.disabled === true) {
@@ -4748,17 +4842,21 @@ return this.__repr__();
}
dataSet = ts.data;
seriesOptions = serie.options;
+ // Delete old data that's moved off the left of the chart.
ts.dropOldData(oldestValidTime, chartOptions.maxDataSetLength);
+ // Set style for this dataSet.
context.lineWidth = seriesOptions.lineWidth;
context.strokeStyle = seriesOptions.strokeStyle;
+ // Draw the line...
context.beginPath();
+ // Retain lastX, lastY for calculating the control points of bezier curves.
firstX = 0;
lastX = 0;
lastY = 0;
if (len(dataSet) > 1) {
- var ρσ_Iter8 = ρσ_Iterable(enumerate(dataSet));
- for (var ρσ_Index8 = 0; ρσ_Index8 < ρσ_Iter8.length; ρσ_Index8++) {
- ρσ_unpack = ρσ_Iter8[ρσ_Index8];
+ var ρσ_Iter10 = ρσ_Iterable(enumerate(dataSet));
+ for (var ρσ_Index10 = 0; ρσ_Index10 < ρσ_Iter10.length; ρσ_Index10++) {
+ ρσ_unpack = ρσ_Iter10[ρσ_Index10];
index = ρσ_unpack[0];
set = ρσ_unpack[1];
x = timeToXPixel(set[0]);
@@ -4773,7 +4871,24 @@ return this.__repr__();
context.lineTo(x, lastY);
context.lineTo(x, y);
} else {
- context.bezierCurveTo(Math.round((lastX + x) / 2), lastY, Math.round(lastX + x) / 2, y, x, y);
+ // Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves
+ //
+ // Assuming A was the last point in the line plotted and B is the new point,
+ // we draw a curve with control points P and Q as below.
+ //
+ // A---P
+ // |
+ // |
+ // |
+ // Q---B
+ //
+ // Importantly, A and P are at the same y coordinate, as are B and Q. This is
+ // so adjacent curves appear to flow as one.
+ //
+ context.bezierCurveTo(// startPoint (A) is implicit from last iteration of loop
+ Math.round((lastX + x) / 2), lastY, // controlPoint1 (P)
+ Math.round(lastX + x) / 2, y, // controlPoint2 (Q)
+ x, y);
}
}
lastX = x;
@@ -4782,6 +4897,7 @@ return this.__repr__();
}
if (len(dataSet) > 1) {
if (seriesOptions.fillStyle) {
+ // Close up the fill region.
context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, lastY);
context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, dimensions.height + seriesOptions.lineWidth + 1);
context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth);
@@ -4801,6 +4917,7 @@ return this.__repr__();
});
RapydSmoothie.prototype.render_50 = function render_50(context, chartOptions, dimensions) {
var self = this;
+ // Draw vertical bar to show tooltip position
if (chartOptions.tooltip && self.mouseX >= 0) {
context.save();
context.lineWidth = chartOptions.tooltipLine.lineWidth;
@@ -4820,6 +4937,7 @@ return this.__repr__();
RapydSmoothie.prototype.render_60 = function render_60(context, chartOptions, dimensions) {
var self = this;
var maxValueString, minValueString, maxLabelPos, minLabelPos;
+ // Draw the axis values on the chart.
if (chartOptions.labels.disabled === false) {
if (self.valueRange.min === self.valueRange.min) {
if (self.valueRange.max === self.valueRange.max) {
@@ -4847,9 +4965,11 @@ return this.__repr__();
RapydSmoothie.prototype.render_70 = function render_70(context, chartOptions, dimensions) {
var self = this;
var step, stepPixels, v, gy, yValue, intermediateLabelPos;
+ // Display intermediate y axis labels along y-axis to the left of the chart
if (chartOptions.labels.showIntermediateLabels === true) {
if (self.valueRange.min === self.valueRange.min && self.valueRange.max === self.valueRange.max) {
if (chartOptions.grid.verticalSections > 0) {
+ // show a label above every vertical section divider
context.save();
step = (self.valueRange.max - self.valueRange.min) / chartOptions.grid.verticalSections;
stepPixels = dimensions.height / chartOptions.grid.verticalSections;
@@ -4860,6 +4980,7 @@ return this.__repr__();
gy -= .5;
}
yValue = chartOptions.yIntermediateFormatter(self.valueRange.min + v * step, chartOptions.labels.precision);
+ // left of right axis?
if (chartOptions.labels.intermediateLabelSameAxis === true) {
if (chartOptions.scrollBackwards === true) {
intermediateLabelPos = 0;
@@ -4887,6 +5008,7 @@ return this.__repr__();
RapydSmoothie.prototype.render_80 = function render_80(context, chartOptions, dimensions, time, oldestValidTime, timeToXPixel) {
var self = this;
var minValueString, textUntilX, t, gx, tx, tsf, tsWidth;
+ // Display timestamps along x-axis at the bottom of the chart.
if (ρσ_exists.n(chartOptions.timestampFormatter) && chartOptions.grid.millisPerLine > 0) {
context.save();
if (self.valueRange.min === self.valueRange.min) {
@@ -4902,7 +5024,10 @@ return this.__repr__();
t = time - time % chartOptions.grid.millisPerLine;
while (t >= oldestValidTime) {
gx = timeToXPixel(t);
+ // Only draw the timestamp if it won't overlap with the previously drawn one.
if (chartOptions.scrollBackwards === false && gx < textUntilX || chartOptions.scrollBackwards === true && gx > textUntilX) {
+ // Formats the timestamp based on user specified formatting function
+ // SmoothieChart.timeFormatter function above is one such formatting option
tx = new Date(t);
tsf = chartOptions.timestampFormatter(tx);
tsWidth = context.measureText(tsf).width;
diff --git a/theonionbox/stamp.py b/theonionbox/stamp.py
index 2dd85d0..a2eeb90 100644
--- a/theonionbox/stamp.py
+++ b/theonionbox/stamp.py
@@ -1,4 +1,4 @@
__title__ = 'The Onion Box'
__description__ = 'Dashboard to monitor Tor node operations.'
-__version__ = '19.1b1'
-__stamp__ = '20191018|223636'
\ No newline at end of file
+__version__ = '19.1b2'
+__stamp__ = '20191018|230820'
\ No newline at end of file
diff --git a/theonionbox/tob/apps/controlcenter.py b/theonionbox/tob/apps/controlcenter.py
index 74cc3b2..24635aa 100644
--- a/theonionbox/tob/apps/controlcenter.py
+++ b/theonionbox/tob/apps/controlcenter.py
@@ -330,7 +330,7 @@ def post_save_node(self, session):
status = session['status']
- if status in ['ok', 'auto']:
+ if status in ['frame']:
config = self.cc.add_node() # This returns None if readonly!
if config is None:
raise bottle.HTTPError(403)