function isObject(value) { return ( value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Element) ); } function attrTrans(key) { switch (key) { case "class": return "className"; case "for": return "htmlFor"; default: return key; } } var tagRe = /([^\s.#]+)(?:#([^\s.#]+))?(?:.([^\s#]+))?/; function domTag(infoTag) { var _db1 = infoTag.match(tagRe); var _ = _db1[0]; var tag = _db1[1]; var id = _db1[2]; var className = _db1[3]; var el = document.createElement(tag); if (id) { el.id = id; } if (className) { el.className = className.replaceAll(".", " "); } __PS_MV_REG = []; return el; } function fillAttrs(el, attrs) { if (isObject(attrs)) { for (var key in attrs) { if (key.startsWith("data-")) { el.setAttribute(key, attrs[key]); } else if (key === "style") { Object.assign(el[key], attrs[key]); } else { el[attrTrans(key)] = attrs[key]; } } __PS_MV_REG = []; return true; } } function domEl() { var params = Array.prototype.slice.call(arguments, 0); if (Array.isArray(params[0])) { __PS_MV_REG = []; return domEl.apply(this, params[0]); } else { var el = domTag(params[0]); var contents = params.slice(fillAttrs(el, params[1]) ? 2 : 1); for (const arg of contents) { if (Array.isArray(arg)) { el.appendChild(domEl(arg)); } else if (arg instanceof Element) { el.appendChild(arg); } else if (arg && typeof arg === "string") { el.appendChild(document.createTextNode(arg)); } } __PS_MV_REG = []; return el; } } function logger(level, obj) { var objects = Array.prototype.slice.call(arguments, 2); console[level].apply(console, [obj].concat(objects)); __PS_MV_REG = []; return obj; } function responseToJson(response) { if (response.ok) { __PS_MV_REG = []; return response.json().then(function (value) { if (value["error"]) { throw new Error(value["error"]); } else { __PS_MV_REG = []; return value; } }); } else { throw new Error("not 2XX resp"); } } function csvFloat(data) { let headers = data[0]; let series = headers.map((_, idx) => data.slice(1).map((row) => parseFloat(row[idx])), ); return [headers, series]; } function responseParseCSV(response) { if (response.ok) return response.text().then((data) => data .split(/\n/) .filter((x) => x) .map((row) => row.split(/,/)), ); throw new Error("not 2XX resp"); } function withSuffix(val, suffix) { return val.toFixed(1).replace(/.?0+$/, "").concat("", suffix); } function siScaling(value) { var v = Math.abs(value); return 0 === v ? [0, ""] : v >= 1000000000000000.0 ? [value / 1000000000000000.0, "P"] : v >= 1000000000000.0 ? [value / 1000000000000.0, "T"] : v >= 1000000000.0 ? [value / 1000000000.0, "G"] : v >= 1000000.0 ? [value / 1000000.0, "M"] : v >= 1000.0 ? [value / 1000.0, "K"] : v >= 0.6 ? [value, ""] : v >= 0.001 ? [value / 0.001, "m"] : v >= 0.000001 ? [value / 0.000001, "μ"] : v >= 0.000000001 ? [value / 0.000000001, "n"] : v >= 0.000000000001 ? [value / 0.000000000001, "p"] : null; } function scaling(val) { return withSuffix.apply(this, siScaling(val)); } function spacedColor(idx, alpha) { if (alpha === undefined) { alpha = "/ 1"; } return "hsl(" + 137.506 * idx + " 70% 55% " + alpha + ")"; } function makeChart(data, container) { const opts = { title: "Server Events", width: 920, height: 600, // ms: 1, // cursor: { // x: false, // y: false, // }, series: [ {}, { label: "hits", stroke: "red", // scale: "%", value: (u, v) => scaling(v), // width: 1 / devicePixelRatio, }, // { // label: "visits", // stroke: "blue", // // scale: "%", // // value: (u, v) => (v == null ? null : v.toFixed(1) + "%"), // // width: 1 / devicePixelRatio, // }, { label: "tx", scale: "mb", stroke: "green", value: (u, v) => (v == null ? null : scaling(v) + "B"), width: 1 / devicePixelRatio, }, ], axes: [ {}, { values: (u, vals, space) => vals.map(scaling), }, { side: 1, scale: "mb", size: 60, values: (u, vals, space) => vals.map((v) => scaling(v) + "B"), grid: { show: false }, stroke: "green", }, ], }; let uplot = new uPlot(opts, data, container); container["uobj"] = uplot; } function agentChart(header, series, container) { const opts = { width: 920, height: 600, hooks: { setSeries: [ (u, seriesIdx, opts) => { if (opts.focus != null) { u.series.forEach((s, i) => { s.width = i == seriesIdx ? 3 : 1; }); } }, ], }, focus: { alpha: 0.5 }, cursor: { focus: { prox: 1e6, bias: 0, dist: (self, seriesIdx, dataIdx, valPos, curPos) => { return valPos - curPos; }, }, }, series: [ {}, { label: header[1], stroke: "black", dash: [10, 5], value: (u, v) => scaling(v), scale: "hits", }, ].concat( header.slice(2).map((name, idx) => ({ label: name, stroke: spacedColor(idx), fill: spacedColor(idx, "/ 0.1"), value: (u, v) => scaling(v) + "B", })), ), axes: [ {}, { values: (u, vals, space) => vals.map((v) => (v ? scaling(v) + "B" : v)), size: 60, label: "Bandwidth", labelSize: 50, }, { side: 1, scale: "hits", label: "Requests", grid: { show: false }, values: (u, vals, space) => vals.map((v) => (v ? scaling(v) : v)), labelSize: 50, }, ], }; let uplot = new uPlot(opts, series, container); container["uobj"] = uplot; } function decodeURIOrPass(str) { try { return decodeURI(str); } catch (e) { console.error("error with", str); return str; } } addEventListener("load", () => { fetch("/top_agent_traffic.csv") .then(responseParseCSV) .then(csvFloat) .then(([headers, series]) => { let cont = document.querySelector("#agent-traffic").parentNode; cont.innerHTML = ""; agentChart(headers, series, cont); }); document.querySelectorAll("table.fail-pages tr").forEach((tr, idx) => { if (idx !== 0) { tr.children[0].textContent = decodeURIOrPass(tr.children[0].textContent); } }); });