aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKristian Lyngstol <kly@kly.no>2019-01-06 01:51:17 +0100
committerKristian Lyngstol <kly@kly.no>2019-01-06 01:51:17 +0100
commit8d63fa675264ae0d809f86ee036e081519b88299 (patch)
tree433b6e01951e2fbbeab306739660d25666779f7d
parent4558d453f06a233a3df020223786071f007f9e2c (diff)
Introduce nms-ui-boxes, a big step for GUI
nmsBox is a new class for generic HTML-based elements, and will eventually be used to replace nms-info-box. The idea is simple: a generic way to deal with containers that are usually represented in HTML too. To test it, I've re-implemented the GUI for the oplog. This rewrite didn't really utilize the new benefits of the framework, but was a small step. I've also added nms-ui-switch, which isn't exposed anywhere yet and isn't done, but is a good start. nms-ui-switch will be the new way to add and edit switches in the future, it will probably be made more generic over time and thus can be reused for networks too. Note how x = nmsUiSwitch() will allow you to do x.row["community"].value to both get and set the value, and setting will visually update and run any verifier that will be relevant, and alert the parent. This can then be used for simple stuff like json-verification, but also for stuff like auto-complete or whatnot. God only knows. Obviously I will continue to work on this over the next few days...
-rw-r--r--web/css/nms.css5
-rw-r--r--web/index.html7
-rw-r--r--web/js/nms-oplog.js44
-rw-r--r--web/js/nms-ui-boxes.js191
-rw-r--r--web/js/nms-ui-switch.js126
5 files changed, 349 insertions, 24 deletions
diff --git a/web/css/nms.css b/web/css/nms.css
index 9c783af..6bf2890 100644
--- a/web/css/nms.css
+++ b/web/css/nms.css
@@ -129,3 +129,8 @@ div.map-mode-legend button {
width: 100%;
height: 240px;
}
+.genericBox {
+ position: absolute;
+ z-index: 120;
+}
+
diff --git a/web/index.html b/web/index.html
index 447772f..3e92dc8 100644
--- a/web/index.html
+++ b/web/index.html
@@ -152,7 +152,7 @@
</div>
<div class="container-fluid" id="map">
<div class="row-fluid">
- <div class="span12">
+ <div class="span12" id="metaContainer">
<div id="aboutKeybindings" class="col-md-4" style="position: absolute; display:none; z-index: 130;">
<div class="panel panel-default">
<div class="panel-heading">
@@ -306,8 +306,7 @@
<canvas id="textCanvas" width="1920" height="1032" style="position: absolute; z-index: 40;"> </canvas>
<canvas id="textInfoCanvas" width="1920" height="1032" style="position: absolute; z-index: 45;"> </canvas>
<canvas id="topCanvas" width="1920" height="1032" style="position: absolute; z-index: 50;"> </canvas>
- <canvas id="inputCanvas" width="1920" height="1032" style="position: absolute; z-index: 60; cursor: pointer;" onmousedown="nmsMap.canvasClick(event)">
- </canvas>
+ <canvas id="inputCanvas" width="1920" height="1032" style="position: absolute; z-index: 60; cursor: pointer;" onmousedown="nmsMap.canvasClick(event)"> </canvas>
<canvas id="hiddenCanvas" width="1000" height="10" style="display: none; position: absolute; z-index: 1000 "></canvas>
<div class="logbook logbook-mini gondul-is-private" style="position: absolute; right: 10px; width: 20%; z-index: 70; float: right;">
<div id="oplog-parent-mini" class="logbook" style="border-color: transparent;">
@@ -337,6 +336,8 @@
<script type="text/javascript" src="js/nms-dhcp.js"></script>
<script type="text/javascript" src="js/nms-template.js"></script>
<script type="text/javascript" src="js/nms-draw-chart.js"></script>
+ <script type="text/javascript" src="js/nms-ui-boxes.js"></script>
+ <script type="text/javascript" src="js/nms-ui-switch.js"></script>
<script src="js/jquery.datetimepicker.full.js" type="text/javascript"></script>
<script type="text/javascript">
initNMS();
diff --git a/web/js/nms-oplog.js b/web/js/nms-oplog.js
index 7ac11c4..cea75ca 100644
--- a/web/js/nms-oplog.js
+++ b/web/js/nms-oplog.js
@@ -1,7 +1,7 @@
"use strict";
var nmsOplog = nmsOplog || {
-
+ table: {}
}
nmsOplog.init = function() {
@@ -78,42 +78,43 @@ nmsOplog.getSwitchLogs = function(sw) {
return logs;
}
+/*
+ * This can be re-written now that it uses nmsBox... That rewrite was just a short
+ * test of nmsBox...
+ */
nmsOplog._updateComments = function(limit,prefix,timefield,cutoff) {
- var table = document.createElement("table");
- var tr;
- var td1;
- var td2;
- var td3;
- table.className = "table";
- table.classList.add("table");
- table.classList.add("table-condensed");
+ var table = new nmsTable([]);
var i = 0;
for (var v in nmsData['oplog']['oplog']) {
if (cutoff && nmsData.oplog.oplog[v]['username'] == "system") {
continue;
}
- tr = table.insertRow(-1);
- td1 = tr.insertCell(0);
- td2 = tr.insertCell(1);
+ var col1;
var date = new Date(nmsData.oplog.oplog[v]['timestamp'].replace(" ","T").replace("+00",""));
if (timefield == "time") {
- td1.textContent = date.toTimeString().replace(/:\d\d .*$/,"");
+ col1 = date.toTimeString().replace(/:\d\d .*$/,"");
} else {
var month = date.getMonth() + 1;
var day = date.getDate();
var tmp = (date.getYear() + 1900) + "-" + (month < 10 ? "0": "") + month + "-" + (day < 10 ? "0" : "") + day + " " + date.toTimeString().replace(/:\d\d .*$/,"");
- td1.textContent = tmp;
+ col1 = tmp;
}
- td1.classList.add("left");
var data = nmsData['oplog']['oplog'][v]['log'];
+ var col2 = new nmsBox("p");
if (cutoff && data.length > cutoff) {
- td2.title = data;
+ col2.html.title = data;
data = data.slice(0,cutoff);
data = data + "(...)";
}
- td2.textContent = nmsData['oplog']['oplog'][v]['systems'] + " [" + nmsData['oplog']['oplog'][v]['username'] + "] " + data;
- td2.hiddenthing = v;
- td2.onclick = function(e){ var x = document.getElementById("searchbox"); var v = e.path[0].hiddenthing; x.value = nmsData['oplog']['oplog'][v]['systems']; x.oninput(); }
+ col2.html.textContent = nmsData['oplog']['oplog'][v]['systems'] + " [" + nmsData['oplog']['oplog'][v]['username'] + "] " + data;
+ col2.html.hiddenthing = v;
+ col2.html.onclick = function(e) {
+ var x = document.getElementById("searchbox");
+ var v = e.path[0].hiddenthing;
+ x.value = nmsData['oplog']['oplog'][v]['systems'];
+ x.oninput();
+ }
+ table.add([col1,col2]);
if (++i == limit)
break;
}
@@ -122,7 +123,8 @@ nmsOplog._updateComments = function(limit,prefix,timefield,cutoff) {
old.parentElement.removeChild(old);
} catch(e) {}
var par = document.getElementById("oplog-parent" + prefix);
- table.id = "oplog-table" + prefix;
- par.appendChild(table);
+ table.html.id = "oplog-table" + prefix;
+ par.appendChild(table.html);
+ nmsOplog.table["x" + prefix] = table;
};
diff --git a/web/js/nms-ui-boxes.js b/web/js/nms-ui-boxes.js
new file mode 100644
index 0000000..3ed65cd
--- /dev/null
+++ b/web/js/nms-ui-boxes.js
@@ -0,0 +1,191 @@
+"use strict";
+
+/*
+ * An attempt at breaking up nms-info-box into something that works better.
+ * The idea is to retain the ability to fiddle with html, but solve common
+ * problems such as "I need to show a box that looks good" and "I need to
+ * update the content of this field in this box when something happens".
+ *
+ * Seeing as how it's 2019, I figured I'd try to use this whole "class"
+ * thing :D
+ *
+ * Eventually, everything in nms-info-box.js should use this, but more over,
+ * if a need to write some code that needs a UI element arises, the general
+ * problem should be solved here, while the implementation: elsewhere.
+ *
+ * Example: Adding a new switch or network should not be closely tied in
+ * here.
+ *
+ * I expect this code to change dramatically as I start using it.
+ */
+
+class nmsBox {
+ constructor(type,settings) {
+ this.type = type;
+ this._boxes = []
+ this.html = document.createElement(type);
+ this.html.nmsBox = this;
+ if (settings) {
+ this.applySettings(settings);
+ }
+ }
+ get value() {
+ var values = [];
+ for (var x in this._boxes) {
+ var tmp = this._boxes[x].value;
+ if (tmp != undefined && !((tmp instanceof Array) && tmp.length == 0)) {
+ values.push(tmp)
+ }
+ }
+ return values;
+ }
+
+ applySettings(settings) {
+ if (settings.html) {
+ for (var x in settings.html) {
+ if (settings.html[x] instanceof Array) {
+ /* If you just sett classList = array it will end up being
+ * classList = "panel,panel-default,foo", instead of
+ * classList = ["panel","panel-default","foo"] ...
+ * Not sure if this applies to all arrays in a html
+ * object, but we'll see.
+ */
+ for (var y in settings.html[x]) {
+ this.html[x].add(settings.html[x][y]);
+ }
+ } else {
+ this.html[x] = settings.html[x];
+ }
+ }
+ }
+ }
+ add(box) {
+ this._boxes.push(box);
+ this.html.appendChild(box.html);
+ }
+ close() {
+ for (var x in this._boxes) {
+ this._boxes[x].close();
+ }
+ if (this.html.parentElement != null) {
+ this.html.parentElement.removeChild(this.html);
+ }
+ }
+ update() {
+ for (var x in this._boxes) {
+ this._boxes[x].update();
+ }
+ }
+};
+
+
+class nmsString extends nmsBox {
+ constructor(content,type) {
+ type = type ? type : "p";
+ super(type,{ html: { textContent: content }})
+ }
+ set value(input) {
+ this.html.textContent = input;
+ }
+ get value() {
+ return this.html.textContent;
+ }
+}
+/*
+ * A general-purpose table. It can be created in one go, or
+ * as-you-go.
+ */
+class nmsTable extends nmsBox {
+ constructor(content, caption) {
+ super("table");
+ this.html.className = "table";
+ this.html.classList.add("table");
+ this.html.classList.add("table-condensed");
+ if (caption != undefined) {
+ var cap = new nmsBox("caption");
+ cap.html.textContent = caption;
+ super.add(cap);
+ }
+ this._tbody = new nmsBox("tbody");
+ super.add(this._tbody);
+ for (var v in content) {
+ this.add(content[v]);
+ }
+ }
+ /* Can take either a single nmsBox-object that will be added as-is,
+ * or an array of items that will be added as individual cells
+ */
+ add(content) {
+ if (content instanceof nmsBox) {
+ this._tbody.add(content)
+ return;
+ }
+ var tr;
+ var td1;
+ var td2;
+ tr = new nmsBox("tr");
+ tr.html.className = content[0].toLowerCase().replace(/[^a-z0-9_]/g,"");
+ for (var x in content) {
+ var td = new nmsBox("td");
+ var child = content[x];
+ if (x == 0) {
+ td.html.classList.add("left");
+ }
+ if (child instanceof nmsBox) {
+ td.add(child);
+ } else {
+ td.add(new nmsString(child));
+ }
+ tr.add(td);
+ }
+ this._tbody.add(tr);
+ }
+}
+
+/*
+ * Basic panel. Rooted at the 'metaContainer' as of now, but that MIGHT CHANGE.
+ * Draws a nice box with a background, title, close-button and whatnot.
+ * Usage:
+ * var myThing = new nmsPanel("initial title");
+ * myThing.add(nmsString("whatever"))
+ * myThing.title = "blah...."
+ */
+class nmsPanel extends nmsBox{
+ constructor(title){
+ super("div",{html: { classList: ["col-sm-8","col-md-6","col-lg-5","genericBox"]}});
+ this._topBox = new nmsBox("div",{ html: { classList: ["panel","panel-default"]}});
+ this._body = new nmsBox("div",{html:{classList: ["panel-body"]}});
+ this.nav = new nmsBox("div",{html:{classList: ["panel-body"]}});
+ this._topBox.add(this.makeHeading(title));
+ this._topBox.add(this.nav);
+ this._topBox.add(this._body);
+ super.add(this._topBox);
+ this._root = document.getElementById("metaContainer");
+ this._root.appendChild(this.html);
+ }
+ /* Mainly just to make the constructor more readable. */
+ makeHeading(title) {
+ var titleObject = new nmsBox("div",{html:{classList: ["panel-heading"]}});
+ this._titleText = new nmsBox("p",{html:{textContent: title}});
+ var closeButton = new nmsBox("button");
+ closeButton.html.className = "close";
+ closeButton.panel = this;
+ closeButton.html.onclick = function() {
+ this.nmsBox.panel.close();
+ }
+ closeButton.html.style = "float: right;";
+ closeButton.add(new nmsString("X","span"));
+ titleObject.add(closeButton);
+ titleObject.add(this._titleText);
+ return titleObject;
+ }
+ add(item) {
+ this._body.add(item)
+ }
+ set title(input) {
+ this._titleText.html.textContent = input;
+ }
+ get title() {
+ this._titleText.html.textContent;
+ }
+}
diff --git a/web/js/nms-ui-switch.js b/web/js/nms-ui-switch.js
new file mode 100644
index 0000000..fe4bbe9
--- /dev/null
+++ b/web/js/nms-ui-switch.js
@@ -0,0 +1,126 @@
+"use strict";
+
+class nmsUiSwitch extends nmsPanel {
+ constructor(sw) {
+ var title;
+ if (sw == undefined) {
+ title = "Add new switch"
+ } else {
+ title = "Edit " + sw;
+ }
+ super(title)
+ this._sw = sw;
+ this.populate()
+ }
+ /*
+ * We really should base this on a backend-API exposing relevant fields...
+ */
+ getTemplate(sw) {
+ if (sw == undefined) {
+ return {
+ mgmt_v4_addr: null,
+ mgmt_v6_addr: null,
+ community: null,
+ placement: null,
+ mgmt_vlan: null,
+ poll_frequency: null,
+ tags: null
+ };
+ }
+ var swi = [];
+ var swm = [];
+ try {
+ swi = nmsData.switches["switches"][this._sw];
+ } catch(e) {}
+ try {
+ swm = nmsData.smanagement.switches[this._sw];
+ } catch(e) {}
+
+ var template = {}
+ for (var v in swi) {
+ template[v] = swi[v];
+ }
+ for (var v in swm) {
+ if (v == "last_updated") {
+ continue;
+ }
+ template[v] = swm[v];
+ }
+ return template;
+ }
+ populate() {
+ var template = this.getTemplate(this._sw);
+ this.table = new nmsTable();
+ var first = new Array("sysname","distro_name","distro_phy_port","traffic_vlan")
+ var sorted = new Array();
+ for (var v in template) {
+ if (!first.includes(v)) {
+ sorted.push(v);
+ }
+ }
+ sorted.sort();
+ var finals = first.concat(sorted);
+ this.rows = {}
+ for (var i in finals) {
+ var v = finals[i];
+ this.rows[v] = new nmsEditRow(v, nmsInfoBox._nullBlank(template[v]));
+ this.rows[v].parent = this;
+ this.table.add(this.rows[v]);
+ }
+ this.add(this.table);
+ }
+ changed(row) {
+ this.title = "saw row change on " + row.name + " to " + row.value;
+ }
+ get value() {
+ return this.table.value;
+ }
+}
+
+class nmsEditRow extends nmsBox {
+ constructor(text,value) {
+ super("tr")
+ // This should/could be smarter in the future.
+ if (value instanceof Object) {
+ value = JSON.stringify(value);
+ }
+ this.name = text;
+ this._value = value;
+ this.original = value;
+ var td1 = new nmsBox("td")
+ td1.add(new nmsString(text))
+ this.add(td1);
+
+ var td2 = new nmsBox("td")
+ var input = new nmsBox("input")
+ input.html.value = value;
+ input.html.className = "form-control";
+ input.html.type = "text";
+ input.row = this;
+ input.html.onchange = function() {
+ this.nmsBox.row.value = this.value
+ }
+ input.html.oninput = function() {
+ this.nmsBox.row.value = this.value
+ }
+ this._input = input;
+ this._td2 = td2;
+ td2.add(input)
+ this.add(td2)
+ }
+ get value() {
+ return this._value;
+ }
+ set value(value) {
+ this._value = value;
+ if (this._input.html.value != value) {
+ this._input.html.value = value
+ }
+ if (this._value != this.original) {
+ this._td2.html.classList.add("has-warning");
+ } else {
+ this._td2.html.classList.remove("has-warning");
+ }
+ this.parent.changed(this)
+ }
+}