diff options
-rw-r--r-- | web/css/nms.css | 5 | ||||
-rw-r--r-- | web/index.html | 7 | ||||
-rw-r--r-- | web/js/nms-oplog.js | 44 | ||||
-rw-r--r-- | web/js/nms-ui-boxes.js | 191 | ||||
-rw-r--r-- | web/js/nms-ui-switch.js | 126 |
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) + } +} |