diff options
Diffstat (limited to 'toolkit/content/aboutwebrtc/graph.mjs')
-rw-r--r-- | toolkit/content/aboutwebrtc/graph.mjs | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/toolkit/content/aboutwebrtc/graph.mjs b/toolkit/content/aboutwebrtc/graph.mjs new file mode 100644 index 0000000000..f2c93f0709 --- /dev/null +++ b/toolkit/content/aboutwebrtc/graph.mjs @@ -0,0 +1,186 @@ +/* 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(width, height) { + this.width = width; + this.height = height; + } + + // 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; + + // 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"; + + // Returns an SVG element that needs to be inserted into the DOM for display + drawSparseValues = (dataSet, title, config) => { + const { width, height } = this; + // Clear the canvas + const bgColor = this.bgColor(); + const mkSvgElem = type => + document.createElementNS("http://www.w3.org/2000/svg", type); + const svgText = (x, y, text, color, subclass) => { + const txt = mkSvgElem("text"); + txt.setAttribute("x", x); + txt.setAttribute("y", y); + txt.setAttribute("stroke", bgColor); + txt.setAttribute("fill", color); + txt.setAttribute("paint-order", "stroke"); + txt.textContent = text; + txt.classList.add(["graph-text", ...[subclass]].join("-")); + return txt; + }; + const svg = mkSvgElem("svg"); + svg.setAttribute("viewBox", `0 0 ${width} ${height}`); + svg.setAttribute("version", "1.1"); + svg.setAttribute("width", width); + svg.setAttribute("height", height); + svg.classList.add("svg-graph"); + const rect = mkSvgElem("rect"); + rect.setAttribute("fill", bgColor); + rect.setAttribute("width", width); + rect.setAttribute("height", height); + svg.appendChild(rect); + + if (config.toRate) { + dataSet = dataSet.toRateDataSet(); + } + + 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 svg; + } + + let range = filtered.dataRange(); + if (range === undefined) { + return svg; + } + 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; + + const toPathStr = dataPoints => + [...dataPoints] + .map( + (datum, index) => `${index ? "L" : "M"}${xPos(datum)} ${yPos(datum)}` + ) + .join(" "); + const valuePath = mkSvgElem("path"); + valuePath.setAttribute("d", toPathStr(filtered.dataPoints)); + valuePath.setAttribute("stroke", this.valueLineColor()); + valuePath.setAttribute("fill", "none"); + svg.appendChild(valuePath); + + const avgPath = mkSvgElem("path"); + avgPath.setAttribute("d", toPathStr(avgDataSet.dataPoints)); + avgPath.setAttribute("stroke", this.averageLineColor()); + avgPath.setAttribute("fill", "none"); + svg.appendChild(avgPath); + const fixed = num => num.toFixed(config.fixedPointDecimals); + const formatValue = value => + config.toHuman + ? toHumanReadable(value, config.fixedPointDecimals).join("") + : fixed(value); + + // Draw rolling average text + avgDataSet.dataPoints.slice(-1).forEach(({ value }) => { + svg.appendChild( + svgText( + 5, + height - 4, + `AVG: ${formatValue(value)}`, + this.averageLineColor(), + "avg" + ) + ); + }); + + // Draw title text + if (title) { + svg.appendChild( + svgText( + 5, + 12, + `${title}${config.toRate ? "/s" : ""}`, + this.titleColor(this), + "title" + ) + ); + } + + // Draw max value text + const maxText = svgText( + width - 5, + 12, + `Max: ${formatValue(range.max)}`, + this.maxColor(range.max), + "max" + ); + maxText.setAttribute("text-anchor", "end"); + svg.appendChild(maxText); + + // Draw min value text + const minText = svgText( + width - 5, + height - 4, + `Min: ${formatValue(range.min)}`, + this.minColor(range.min), + "min" + ); + minText.setAttribute("text-anchor", "end"); + svg.appendChild(minText); + return svg; + }; +} + +export { GraphImpl }; |