mirror of
https://github.com/zaphar/Heracles.git
synced 2025-07-23 20:49:50 -04:00
maint: JSDoc type annotations and tsserver configuration
This commit is contained in:
parent
464f5db99c
commit
fb48c6900c
11
jsconfig.json
Normal file
11
jsconfig.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2022",
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"allowJs": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"static/*.js"
|
||||||
|
]
|
||||||
|
}
|
147
static/lib.js
147
static/lib.js
@ -12,25 +12,79 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef PlotList
|
||||||
|
* @type {object}
|
||||||
|
* @property {?Array} Series
|
||||||
|
* @property {?Array} Scalar
|
||||||
|
* @property {?Array} StreamInstant
|
||||||
|
* @property {?Array} Stream
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef QueryData
|
||||||
|
* @type {object}
|
||||||
|
* @property {object} yaxes
|
||||||
|
* @property {?string} legend_orientation
|
||||||
|
* @property {Array<PlotList>} plots
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef PlotTrace
|
||||||
|
* @type {object}
|
||||||
|
* @property {string=} name
|
||||||
|
* @property type {string}
|
||||||
|
* @property {string=} mode
|
||||||
|
* @property {Array} x
|
||||||
|
* @property {Array} y
|
||||||
|
* @peroperty {string=} xaxis
|
||||||
|
* @peroperty {string=} yaxis
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get's a css variable's value from the document.
|
||||||
|
* @param {string} variableName - Name of the variable to get `--var-name`
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
function getCssVariableValue(variableName) {
|
function getCssVariableValue(variableName) {
|
||||||
return getComputedStyle(document.documentElement).getPropertyValue(variableName);
|
return getComputedStyle(document.documentElement).getPropertyValue(variableName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom element for showing a plotly graph.
|
||||||
|
*
|
||||||
|
* @extends HTMLElement
|
||||||
|
*/
|
||||||
class GraphPlot extends HTMLElement {
|
class GraphPlot extends HTMLElement {
|
||||||
|
/** @type {?string} */
|
||||||
#uri;
|
#uri;
|
||||||
|
/** @type {?number} */
|
||||||
#width;
|
#width;
|
||||||
|
/** @type {?number} */
|
||||||
#height;
|
#height;
|
||||||
|
/** @type {?number} */
|
||||||
#intervalId;
|
#intervalId;
|
||||||
|
/** @type {?number} */
|
||||||
#pollSeconds;
|
#pollSeconds;
|
||||||
|
/** @type {?string} */
|
||||||
#end;
|
#end;
|
||||||
|
/** @type {?number} */
|
||||||
#duration;
|
#duration;
|
||||||
|
/** @type {?string} */
|
||||||
#step_duration;
|
#step_duration;
|
||||||
|
/** @type {?string} */
|
||||||
#d3TickFormat = "~s";
|
#d3TickFormat = "~s";
|
||||||
|
/** @type {?HTMLDivElement} */
|
||||||
#targetNode = null;
|
#targetNode = null;
|
||||||
|
/** @type {?HTMLElement} */
|
||||||
#menuContainer = null;
|
#menuContainer = null;
|
||||||
|
/** @type {Object<string, HTMLSelectElement>} */
|
||||||
#filterSelectElements = {};
|
#filterSelectElements = {};
|
||||||
|
/** @type {Object<string, Array<string>>} */
|
||||||
#filterLabels = {};
|
#filterLabels = {};
|
||||||
|
/** @type {Object<string, Array<string>>} */
|
||||||
#filteredLabelSets = {};
|
#filteredLabelSets = {};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.#width = 800;
|
this.#width = 800;
|
||||||
@ -45,25 +99,32 @@ class GraphPlot extends HTMLElement {
|
|||||||
|
|
||||||
static observedAttributes = ['uri', 'width', 'height', 'poll-seconds', 'end', 'duration', 'step-duration', 'd3-tick-format'];
|
static observedAttributes = ['uri', 'width', 'height', 'poll-seconds', 'end', 'duration', 'step-duration', 'd3-tick-format'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for attributes changes.
|
||||||
|
*
|
||||||
|
* @param {string} name - The name of the attribute.
|
||||||
|
* @param {?string} _oldValue - The old value for the attribute
|
||||||
|
* @param {?string} newValue - The new value for the attribute
|
||||||
|
*/
|
||||||
attributeChangedCallback(name, _oldValue, newValue) {
|
attributeChangedCallback(name, _oldValue, newValue) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'uri':
|
case 'uri':
|
||||||
this.#uri = newValue;
|
this.#uri = newValue;
|
||||||
break;
|
break;
|
||||||
case 'width':
|
case 'width':
|
||||||
this.#width = newValue;
|
this.#width = Number(newValue);
|
||||||
break;
|
break;
|
||||||
case 'height':
|
case 'height':
|
||||||
this.#height = newValue;
|
this.#height = Number(newValue);
|
||||||
break;
|
break;
|
||||||
case 'poll-seconds':
|
case 'poll-seconds':
|
||||||
this.#pollSeconds = newValue;
|
this.#pollSeconds = Number(newValue);
|
||||||
break;
|
break;
|
||||||
case 'end':
|
case 'end':
|
||||||
this.#end = newValue;
|
this.#end = newValue;
|
||||||
break;
|
break;
|
||||||
case 'duration':
|
case 'duration':
|
||||||
this.#duration = newValue;
|
this.#duration = Number(newValue);
|
||||||
break;
|
break;
|
||||||
case 'step-duration':
|
case 'step-duration':
|
||||||
this.#step_duration = newValue;
|
this.#step_duration = newValue;
|
||||||
@ -79,11 +140,11 @@ class GraphPlot extends HTMLElement {
|
|||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.#uri = this.getAttribute('uri') || this.#uri;
|
this.#uri = this.getAttribute('uri') || this.#uri;
|
||||||
this.#width = this.getAttribute('width') || this.#width;
|
this.#width = Number(this.getAttribute('width') || this.#width);
|
||||||
this.#height = this.getAttribute('height') || this.#height;
|
this.#height = Number(this.getAttribute('height') || this.#height);
|
||||||
this.#pollSeconds = this.getAttribute('poll-seconds') || this.#pollSeconds;
|
this.#pollSeconds = Number(this.getAttribute('poll-seconds') || this.#pollSeconds);
|
||||||
this.#end = this.getAttribute('end') || null;
|
this.#end = this.getAttribute('end') || null;
|
||||||
this.#duration = this.getAttribute('duration') || null;
|
this.#duration = Number(this.getAttribute('duration')) || null;
|
||||||
this.#step_duration = this.getAttribute('step-duration') || null;
|
this.#step_duration = this.getAttribute('step-duration') || null;
|
||||||
this.#d3TickFormat = this.getAttribute('d3-tick-format') || this.#d3TickFormat;
|
this.#d3TickFormat = this.getAttribute('d3-tick-format') || this.#d3TickFormat;
|
||||||
this.reset();
|
this.reset();
|
||||||
@ -95,10 +156,17 @@ class GraphPlot extends HTMLElement {
|
|||||||
|
|
||||||
static elementName = "graph-plot";
|
static elementName = "graph-plot";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get's the target node for placing the plotly graph.
|
||||||
|
*
|
||||||
|
* @returns {?HTMLDivElement}
|
||||||
|
*/
|
||||||
getTargetNode() {
|
getTargetNode() {
|
||||||
return this.#targetNode;
|
return this.#targetNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
stopInterval() {
|
stopInterval() {
|
||||||
if (this.#intervalId) {
|
if (this.#intervalId) {
|
||||||
clearInterval(this.#intervalId);
|
clearInterval(this.#intervalId);
|
||||||
@ -106,6 +174,10 @@ class GraphPlot extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the entire graph and then restarts polling.
|
||||||
|
* @param {boolean=} updateOnly
|
||||||
|
*/
|
||||||
reset(updateOnly) {
|
reset(updateOnly) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.stopInterval()
|
self.stopInterval()
|
||||||
@ -120,12 +192,18 @@ class GraphPlot extends HTMLElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Registers the custom element if it doesn't already exist */
|
||||||
static registerElement() {
|
static registerElement() {
|
||||||
if (!customElements.get(GraphPlot.elementName)) {
|
if (!customElements.get(GraphPlot.elementName)) {
|
||||||
customElements.define(GraphPlot.elementName, GraphPlot);
|
customElements.define(GraphPlot.elementName, GraphPlot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the uri formatted with any query strings if necessary.
|
||||||
|
*
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
getUri() {
|
getUri() {
|
||||||
if (this.#end && this.#duration && this.#step_duration) {
|
if (this.#end && this.#duration && this.#step_duration) {
|
||||||
return this.#uri + "?end=" + this.#end + "&duration=" + this.#duration + "&step_duration=" + this.#step_duration;
|
return this.#uri + "?end=" + this.#end + "&duration=" + this.#duration + "&step_duration=" + this.#step_duration;
|
||||||
@ -134,6 +212,11 @@ class GraphPlot extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data from an api call.
|
||||||
|
*
|
||||||
|
* @return {Promise<QueryData>}
|
||||||
|
*/
|
||||||
async fetchData() {
|
async fetchData() {
|
||||||
// TODO(zaphar): Can we do some massaging on these
|
// TODO(zaphar): Can we do some massaging on these
|
||||||
// to get the full set of labels and possible values?
|
// to get the full set of labels and possible values?
|
||||||
@ -142,6 +225,12 @@ class GraphPlot extends HTMLElement {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the name for the plot trace.
|
||||||
|
* @param {{name_format: ?string}} meta
|
||||||
|
* @param {Map<string, string>} labels
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
formatName(meta, labels) {
|
formatName(meta, labels) {
|
||||||
var name = "";
|
var name = "";
|
||||||
const formatter = meta.name_format
|
const formatter = meta.name_format
|
||||||
@ -157,6 +246,9 @@ class GraphPlot extends HTMLElement {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object<string, string>} labels
|
||||||
|
*/
|
||||||
populateFilterData(labels) {
|
populateFilterData(labels) {
|
||||||
for (var key in labels) {
|
for (var key in labels) {
|
||||||
const label = this.#filterLabels[key];
|
const label = this.#filterLabels[key];
|
||||||
@ -170,13 +262,18 @@ class GraphPlot extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
* @returns {HTMLDivElement}
|
||||||
|
*/
|
||||||
buildSelectElement(key) {
|
buildSelectElement(key) {
|
||||||
// TODO(jwall): Should we have a select all?
|
// TODO(jwall): Should we have a select all?
|
||||||
var id = key + "-select" + Math.random();
|
var id = key + "-select" + Math.random();
|
||||||
const element = document.createElement("div");
|
const element = document.createElement("div");
|
||||||
const select = document.createElement("select");
|
const select = document.createElement("select");
|
||||||
select.setAttribute("name", id);
|
select.setAttribute("name", id);
|
||||||
select.setAttribute("multiple", true);
|
// TODO(jwall): This is how you set boolean attributes. Use the attribute named... :-(
|
||||||
|
select.setAttribute("multiple", "multiple");
|
||||||
const optElement = document.createElement("option");
|
const optElement = document.createElement("option");
|
||||||
const optValue = "Select " + key;
|
const optValue = "Select " + key;
|
||||||
optElement.innerText = optValue;
|
optElement.innerText = optValue;
|
||||||
@ -184,7 +281,7 @@ class GraphPlot extends HTMLElement {
|
|||||||
for (var opt of this.#filterLabels[key]) {
|
for (var opt of this.#filterLabels[key]) {
|
||||||
const optElement = document.createElement("option");
|
const optElement = document.createElement("option");
|
||||||
optElement.setAttribute("value", opt);
|
optElement.setAttribute("value", opt);
|
||||||
optElement.setAttribute("selected", true);
|
optElement.setAttribute("selected", "selected");
|
||||||
optElement.innerText = opt;
|
optElement.innerText = opt;
|
||||||
select.appendChild(optElement);
|
select.appendChild(optElement);
|
||||||
}
|
}
|
||||||
@ -193,7 +290,7 @@ class GraphPlot extends HTMLElement {
|
|||||||
select.onchange = function(evt) {
|
select.onchange = function(evt) {
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
var filteredValues = [];
|
var filteredValues = [];
|
||||||
for (var opt of evt.target.selectedOptions) {
|
for (var opt of /** @type {HTMLSelectElement} */(evt.target).selectedOptions) {
|
||||||
filteredValues.push(opt.getAttribute("value"));
|
filteredValues.push(opt.getAttribute("value"));
|
||||||
}
|
}
|
||||||
self.#filteredLabelSets[key] = filteredValues;
|
self.#filteredLabelSets[key] = filteredValues;
|
||||||
@ -217,6 +314,9 @@ class GraphPlot extends HTMLElement {
|
|||||||
this.#menuContainer.replaceChildren(...children);
|
this.#menuContainer.replaceChildren(...children);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {QueryData} graph
|
||||||
|
*/
|
||||||
getLabelsForData(graph) {
|
getLabelsForData(graph) {
|
||||||
const data = graph.plots;
|
const data = graph.plots;
|
||||||
for (var subplot of data) {
|
for (var subplot of data) {
|
||||||
@ -247,6 +347,11 @@ class GraphPlot extends HTMLElement {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the graph with new data.
|
||||||
|
*
|
||||||
|
* @param {?QueryData=} maybeGraph
|
||||||
|
*/
|
||||||
async updateGraph(maybeGraph) {
|
async updateGraph(maybeGraph) {
|
||||||
var graph = maybeGraph;
|
var graph = maybeGraph;
|
||||||
if (!graph) {
|
if (!graph) {
|
||||||
@ -278,10 +383,9 @@ class GraphPlot extends HTMLElement {
|
|||||||
yaxis.gridColor = getCssVariableValue("--accent-color");
|
yaxis.gridColor = getCssVariableValue("--accent-color");
|
||||||
layout[nextYaxis()] = yaxis;
|
layout[nextYaxis()] = yaxis;
|
||||||
}
|
}
|
||||||
var traces = [];
|
var traces = /** @type {Array<PlotTrace>} */ ([]);
|
||||||
for (var subplot_idx in data) {
|
for (var subplot_idx in data) {
|
||||||
const subplot = data[subplot_idx];
|
const subplot = data[subplot_idx];
|
||||||
const subplotCount = Number(subplot_idx) + 1;
|
|
||||||
var nextYaxis = this.yaxisNameGenerator();
|
var nextYaxis = this.yaxisNameGenerator();
|
||||||
if (subplot.Series) {
|
if (subplot.Series) {
|
||||||
// https://plotly.com/javascript/reference/scatter/
|
// https://plotly.com/javascript/reference/scatter/
|
||||||
@ -297,7 +401,7 @@ class GraphPlot extends HTMLElement {
|
|||||||
var yaxis = meta.yaxis || "y";
|
var yaxis = meta.yaxis || "y";
|
||||||
// https://plotly.com/javascript/reference/layout/yaxis/
|
// https://plotly.com/javascript/reference/layout/yaxis/
|
||||||
const series = triple[2];
|
const series = triple[2];
|
||||||
var trace = {
|
const trace = {
|
||||||
type: "scatter",
|
type: "scatter",
|
||||||
mode: "lines+text",
|
mode: "lines+text",
|
||||||
x: [],
|
x: [],
|
||||||
@ -330,32 +434,39 @@ class GraphPlot extends HTMLElement {
|
|||||||
}
|
}
|
||||||
const meta = triple[1];
|
const meta = triple[1];
|
||||||
const series = triple[2];
|
const series = triple[2];
|
||||||
var trace = {
|
const trace = /** @type PlotTrace */({
|
||||||
type: "bar",
|
type: "bar",
|
||||||
x: [],
|
x: [],
|
||||||
y: [],
|
y: [],
|
||||||
yhoverformat: meta["d3_tick_format"],
|
yhoverformat: meta["d3_tick_format"],
|
||||||
};
|
});
|
||||||
var name = this.formatName(meta, labels);
|
var name = this.formatName(meta, labels);
|
||||||
if (name) { trace.name = name; }
|
if (name) { trace.name = name; }
|
||||||
trace.y.push(series.value);
|
trace.y.push(series.value);
|
||||||
trace.x.push(trace.name);
|
trace.x.push(trace.name);
|
||||||
traces.push(trace);
|
traces.push(trace);
|
||||||
}
|
}
|
||||||
}
|
} // TODO(zaphar): subplot.Stream // log lines!!!
|
||||||
}
|
}
|
||||||
// https://plotly.com/javascript/plotlyjs-function-reference/#plotlyreact
|
// https://plotly.com/javascript/plotlyjs-function-reference/#plotlyreact
|
||||||
|
// @ts-ignore
|
||||||
Plotly.react(this.getTargetNode(), traces, layout, null);
|
Plotly.react(this.getTargetNode(), traces, layout, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphPlot.registerElement();
|
GraphPlot.registerElement();
|
||||||
|
|
||||||
|
/** Custom Element for selecting a timespan for the dashboard. */
|
||||||
class SpanSelector extends HTMLElement {
|
class SpanSelector extends HTMLElement {
|
||||||
|
/** @type {HTMLElement} */
|
||||||
#targetNode = null;
|
#targetNode = null;
|
||||||
|
/** @type {HTMLInputElement} */
|
||||||
#endInput = null;
|
#endInput = null;
|
||||||
|
/** @type {HTMLInputElement} */
|
||||||
#durationInput = null;
|
#durationInput = null;
|
||||||
|
/** @type {HTMLInputElement} */
|
||||||
#stepDurationInput = null;
|
#stepDurationInput = null;
|
||||||
|
/** @type {HTMLButtonElement} */
|
||||||
#updateInput = null
|
#updateInput = null
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -387,6 +498,7 @@ class SpanSelector extends HTMLElement {
|
|||||||
this.#updateInput.onclick = undefined;
|
this.#updateInput.onclick = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Updates all the graphs on the dashboard with the new timespan. */
|
||||||
updateGraphs() {
|
updateGraphs() {
|
||||||
for (var node of document.getElementsByTagName(GraphPlot.elementName)) {
|
for (var node of document.getElementsByTagName(GraphPlot.elementName)) {
|
||||||
node.setAttribute('end', this.#endInput.value);
|
node.setAttribute('end', this.#endInput.value);
|
||||||
@ -397,6 +509,7 @@ class SpanSelector extends HTMLElement {
|
|||||||
|
|
||||||
static elementName = "span-selector";
|
static elementName = "span-selector";
|
||||||
|
|
||||||
|
/** Register the element if it doesn't exist */
|
||||||
static registerElement() {
|
static registerElement() {
|
||||||
if (!customElements.get(SpanSelector.elementName)) {
|
if (!customElements.get(SpanSelector.elementName)) {
|
||||||
customElements.define(SpanSelector.elementName, SpanSelector);
|
customElements.define(SpanSelector.elementName, SpanSelector);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user