summaryrefslogtreecommitdiffstats
path: root/toolkit/content/aboutwebrtc/graph.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/content/aboutwebrtc/graph.mjs')
-rw-r--r--toolkit/content/aboutwebrtc/graph.mjs150
1 files changed, 150 insertions, 0 deletions
diff --git a/toolkit/content/aboutwebrtc/graph.mjs b/toolkit/content/aboutwebrtc/graph.mjs
new file mode 100644
index 0000000000..fefb564a38
--- /dev/null
+++ b/toolkit/content/aboutwebrtc/graph.mjs
@@ -0,0 +1,150 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function compStyle(property) {
+ return getComputedStyle(window.document.body).getPropertyValue(property);
+}
+
+function toHumanReadable(num, fpDecimals) {
+ const prefixes = [..." kMGTPEYZYRQ"];
+ const inner = (curr, remainingPrefixes) => {
+ return Math.abs(curr >= 1000)
+ ? inner(curr / 1000, remainingPrefixes.slice(1, -1))
+ : [curr.toFixed(fpDecimals), remainingPrefixes[0].trimEnd()];
+ };
+ return inner(num, prefixes);
+}
+
+class GraphImpl {
+ constructor(canvas, width, height) {
+ this.canvas = canvas;
+ this.width = width;
+ this.height = height;
+ this.drawCtx = canvas.getContext("2d");
+ }
+
+ // The returns the earliest time to graph
+ startTime = dataSet => (dataSet.earliest() || { time: 0 }).time;
+
+ // Returns the latest time to graph
+ stopTime = dataSet => (dataSet.latest() || { time: 0 }).time;
+
+ /** @type {HTMLCanvasElement} */
+ canvas = {};
+ /** @type {CanvasRenderingContext2D} */
+ drawCtx = {};
+ // The default background color
+ bgColor = () => compStyle("--in-content-page-background");
+ // The color to use for value graph lines
+ valueLineColor = () => "grey";
+ // The color to use for average graph lines and text
+ averageLineColor = () => "green";
+ // The color to use for the max value
+ maxColor = ({ time, value }) => "grey";
+ // The color to use for the min value
+ minColor = ({ time, value }) => "grey";
+ // Title color
+ titleColor = title => compStyle("--in-content-page-color");
+ // The color to use for a data point at a time.
+ // The destination x coordinate and graph width are also provided.
+ datumColor = ({ time, value, x, width }) => "red";
+
+ drawSparseValues = (dataSet, title, config) => {
+ if (config.toRate) {
+ dataSet = dataSet.toRateDataSet();
+ }
+ const { drawCtx: ctx, width, height } = this;
+ // Clear the canvas
+ const bgColor = this.bgColor();
+ ctx.fillStyle = bgColor;
+ ctx.clearRect(0, 0, width, height);
+ ctx.fillRect(0, 0, width, height);
+
+ const startTime = this.startTime(dataSet);
+ const stopTime = this.stopTime(dataSet);
+ let timeFilter = ({ time }) => time >= startTime && time <= stopTime;
+
+ let avgDataSet = { dataPoints: [] };
+ if (!config.noAvg) {
+ avgDataSet = dataSet.toRollingAverageDataSet(config.avgPoints);
+ }
+
+ let filtered = dataSet.filter(timeFilter);
+ if (filtered.dataPoints == []) {
+ return;
+ }
+
+ let range = filtered.dataRange();
+ if (range === undefined) {
+ return;
+ }
+ let { min: rangeMin, max: rangeMax } = range;
+
+ // Adjust the _display_ range to lift flat lines towards the center
+ if (rangeMin == rangeMax) {
+ rangeMin = rangeMin - 1;
+ rangeMax = rangeMax + 1;
+ }
+ const yFactor = (height - 26) / (1 + rangeMax - rangeMin);
+ const yPos = ({ value }) =>
+ this.height - 1 - (value - rangeMin) * yFactor - 13;
+ const xFactor = width / (1 + stopTime - startTime);
+ const xPos = ({ time }) => (time - startTime) * xFactor;
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.strokeStyle = this.valueLineColor();
+ [...filtered.dataPoints].forEach((datum, index) => {
+ if (!index) {
+ ctx.moveTo(xPos(datum), yPos(datum));
+ } else {
+ ctx.lineTo(xPos(datum), yPos(datum));
+ }
+ // Draw data point
+ ctx.fillStyle = this.datumColor({ ...datum, width: this.width });
+ ctx.fillRect(xPos(datum), yPos(datum), 1, 1);
+ });
+ ctx.stroke();
+
+ // Rolling average
+ ctx.lineWidth = 2;
+ ctx.strokeStyle = this.averageLineColor();
+ ctx.beginPath();
+ const rollingAverage = avgDataSet.dataPoints;
+ [...rollingAverage].forEach((datum, index) => {
+ if (!index) {
+ ctx.moveTo(xPos(datum), yPos(datum));
+ } else {
+ ctx.lineTo(xPos(datum), yPos(datum));
+ }
+ });
+ const fixed = num => num.toFixed(config.fixedPointDecimals);
+ const formatValue = value =>
+ config.toHuman
+ ? toHumanReadable(value, config.fixedPointDecimals).join("")
+ : fixed(value);
+ rollingAverage.slice(-1).forEach(({ value }) => {
+ ctx.stroke();
+ ctx.fillStyle = this.averageLineColor();
+ ctx.font = "12px Arial";
+ ctx.textAlign = "left";
+ ctx.fillText(formatValue(value), 5, this.height - 4);
+ });
+
+ // Draw the title
+ if (title) {
+ ctx.fillStyle = this.titleColor(this);
+ ctx.font = "12px Arial";
+ ctx.textAlign = "left";
+ ctx.fillText(`${title}${config.toRate ? "/s" : ""}`, 5, 12);
+ }
+ ctx.font = "12px Arial";
+ ctx.fillStyle = this.maxColor(range.max);
+ ctx.textAlign = "right";
+ ctx.fillText(formatValue(range.max), this.width - 5, 12);
+ ctx.fillStyle = this.minColor(range.min);
+ ctx.fillText(formatValue(range.min), this.width - 5, this.height - 4);
+ };
+}
+
+export { GraphImpl };