aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKristian Lyngstol <kly@kly.no>2016-03-12 21:42:55 +0000
committerKristian Lyngstol <kly@kly.no>2016-03-12 21:42:55 +0000
commit7f35a9a4593d946f626cea8c56e4568a7a7fe0d7 (patch)
tree72d11d15ec8737da7881b0e67a9a696acb26c4c3
parentd38e6ba26931cffd6c09128dfeb7321bc224ede3 (diff)
NMSjs: Massive rework of map drawing
Still half-way done, but it's looking better. Confirmed working right now: Comment spotter and disco.
-rw-r--r--web/nms.gathering.org/index.html196
-rw-r--r--web/nms.gathering.org/js/nms-color-util.js22
-rw-r--r--web/nms.gathering.org/js/nms-map-handlers.js71
-rw-r--r--web/nms.gathering.org/js/nms-map.js265
-rw-r--r--web/nms.gathering.org/js/nms.js510
5 files changed, 333 insertions, 731 deletions
diff --git a/web/nms.gathering.org/index.html b/web/nms.gathering.org/index.html
index b5ef88e..f2776e8 100644
--- a/web/nms.gathering.org/index.html
+++ b/web/nms.gathering.org/index.html
@@ -70,16 +70,12 @@
<li class="divider"> </li>
<li class="dropdown-header">View</li>
<li><a href="#" onclick="toggleNightMode()">Toggle Night Mode</a></li>
- <li><a href="#" onclick="showBlurBox()">Tweak Night Mode blur</a></li>
- <li><a href="#" onclick='showLayer("layerVisibility");'>Set layer visibility</a></li>
<li class="divider"> </li>
<li class="dropdown-header">Map scale</li>
<li><a href="#"><label id="scaler-text" for='scaler'></label><input type="range" id="scaler" name="points" min="0.2" max="3" step="0.01" onchange="scaleChange()" /></a></li>
<li class="divider"> </li>
<li class="dropdown-header">Help</li>
- <li><a href="#" onclick="toggleLayer('aboutData');">About TG15 data</a></li>
<li><a href="#" onclick="toggleLayer('aboutBox');" >About NMS</a></li>
- <li><a href="#" onclick="toggleLayer('aboutPerformance');" >About Performance</a></li>
<li><a href="#" onclick="toggleLayer('aboutKeybindings');" >Keyboard Shortcuts</a></li>
<li><a onclick="showTimerDebug(); hideSwitch();" style="cursor: pointer;" >Debug timers</a></li>
</ul>
@@ -107,113 +103,6 @@
<div class="row-fluid">
<div class="span12">
- <div id="aboutData" class="col-md-4" style="position: absolute; display:none; z-index: 130;">
- <div id="abotData" class="panel panel-default">
- <div class="panel-heading">
- <h3 class="panel-title">About the TG15 data
- <button type="button" class="close" aria-label="Close" onclick="document.getElementById('aboutData').style.display = 'none';" style="float: right">
- <span aria-hidden="true">&times;</span>
- </button>
- </h3>
- </div>
- <div class="panel-body">
- <p>The data you see from The Gathering 2015 will seem
- "broken up". This is not because we don't have data from
- the first day, but because the backend was re-written on
- day 1/2 and this web app only uses the new API.</p>
- <p>NMS was set up on March 30th (Monday). Data started
- pouring in on the same day. </p>
- <p>Ping data is available for the entire event with 1
- second resolution. We "lost" data from the 30th because we
- re-inserted the switches (We have the ping data, but not
- the mapping between switch ID number and actual
- switch).</p>
- <p>DHCP data is available only for the last detected DHCP
- ack (no history, except extensive text-based logs)</p>
- <p>Uplink status is available for most of the event, but
- not exposed here. We only expose traffic-based uplink state
- here, which, again, is based on the new API.</p>
- <p>Traffic status was temporarily bugged, but is available
- from late on day 2.</p>
- <p>Temperature data is available from day 2.</p>
- <p>Plans are being made to ensure that we don't have gaps
- like these in the future.</p>
- <p>It is also worth mentioning that things like switch
- positions are not logged historically, so you see the final
- position on the map.</p>
- </div>
- </div>
- </div>
- <div id="aboutPerformance" class="col-md-4" style="position: absolute; display:none; z-index: 130;">
- <div class="panel panel-default">
- <div class="panel-heading">
- <h3 class="panel-title">Performance
- <button type="button" class="close" aria-label="Close" onclick="document.getElementById('aboutPerformance').style.display = 'none';" style="float: right">
- <span aria-hidden="true">&times;</span>
- </button>
- </h3>
- </div>
- <table class="table">
- <tr>
- <td>Outstanding AJAX requests</td>
- <td id="outstandingAJAX"></td>
- </tr>
- <tr>
- <td>Overflowed AJAX requests</td>
- <td id="overflowAJAX"></td>
- </tr>
- </table>
- <div class="panel-body">
- <p>NMS performance is surprisingly complex. It's split into
- several parts and dealt with differently.</p>
- <p>Poller performance is a matter of efficiently collecting
- data and is mostly handled in the Perl code (and ensuring
- we use sensible database schemas).</p>
- <p>Backend performance for the GUI is mostly about not
- killing the database server. We do NOT try to protect
- against malicious clients directly, since this is a
- management system not public-facing, but Varnish is used to
- cache requests. To be able to do that properly, we need use
- absolute time when reviewing past events (so "2015-04-02
- 17:30:00", not "2 hours ago"). We've also tried to minimize
- the stupidity in the queries. There's still work to be done
- here, though, as we need to split up a few large backend
- requests (port-state.pl).</p>
- <p>Front-end performance is mostly about drawing things
- sensibly and not completely bombing the memory usage. And
- about gracefully handling slow backends This will affect
- you. For example, if you are reviewing past events and the
- DB is struggling, we'll simply skip a backend request if we
- have too many outstanding requests, that means you may jump
- from "17:00" to "18:30" instead of going through
- "17:30" and "18:00" too. This is working as intended. It
- also means that you can happily spam the forward/backward
- keyboard bindings to jump 18 hours forward: You'll overflow
- the extra AJAX requests for individual requests, but you'll
- land at the right time when you let go. But there could be
- a 1 second delay (or more if the backend really struggles)
- since you'll have to rely on the periodic backend requests
- instead of the explicit ones triggered on hitting a
- button.</p>
- <p>Note that the counters on top are updated on a timer,
- but this timer is set up at the same time as everything
- else, which means that it's likely to update at the same
- time as we fire off AJAX requests, so the 'outstanding ajax
- requests' counter might either show almost constantly 3 or
- 0 depending on what timer happens to fire first. This does
- NOT mean that NMS has 3 requests all the time, just that
- we're checking right after we fire off AJAX requests every
- time.</p>
- <p>NMS also tries to handle drawing OK, which is why things
- are split into different HTML5 canvases. Blur and text are
- particularly expensive, but there's no reason to re-paint
- that all the time, etc).</p>
- <p>The basic performance experiments are done on TG15 data
- using a laptop and a VM with 6GB of memory, so it should
- hold up quite well on "proper" hardware.</p>
- </div>
- </div>
- </div>
<div id="aboutKeybindings" class="col-md-4" style="position: absolute; display:none; z-index: 130;">
<div class="panel panel-default">
<div class="panel-heading">
@@ -408,28 +297,6 @@
</div>
</div>
</div>
- <div id="blurManic" class="panel panel-default" style="display: none; position: absolute; z-index: 100;">
- <div class="panel-heading">
- <h1 class="panel-title">Blur tweaks
- <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('blurManic').style.display = 'none';" style="float: right;">
- <span aria-hidden="true">&times;</span>
- </button>
- </h1>
- </div>
- <div class="panel-body">
- <div class="form">
- <div class="form-group">
- <label for="shadowBlur">Blur strength</label>
- <input type="number" id="shadowBlur" class="form-control">
- </div>
- <div class="form-group">
- <label for="shadowColor">Blur color</label>
- <input type="color" id="shadowColor" class="form-control">
- </div>
- <button type="button" class="btn btn-default" onclick="applyBlur();">Apply</button>
- </div>
- </div>
- </div>
</div>
<div id="debugTimers" class="panel panel-default" style="display: none; position: absolute; z-index: 100;">
<div class="panel-heading">
@@ -447,68 +314,6 @@
</div>
<table id="timerTable"> </table>
</div>
- <div id="layerVisibility" class="panel panel-default" style="display: none; position: absolute; z-index: 100;">
- <div class="panel-heading">
- <h1 class="panel-title">Set layer visibility
- <button type="button" class="close" aria-labe="Close" onclick="document.getElementById('layerVisibility').style.display = 'none';" style="float: right;">
- <span aria-hidden="true">&times;</span>
- </button>
- </h1>
- </div>
- <div class="panel-body">
- <table id="visibilityTable" class="table">
- <tr>
- <td>Background</td>
- <td>
- <button onclick='hideLayer("bgCanvas");'>Hide</button>
- <button onclick='showLayer("bgCanvas");'>Show</button>
- </td>
- </tr>
- <tr>
- <td>Linknets</td>
- <td>
- <button onclick='hideLayer("linkCanvas");'>Hide</button>
- <button onclick='showLayer("linkCanvas");'>Show</button>
- </td>
- </tr>
- <tr>
- <td>Blur</td>
- <td>
- <button onclick='hideLayer("blurCanvas");'>Hide</button>
- <button onclick='showLayer("blurCanvas");'>Show</button>
- </td>
- </tr>
- <tr>
- <td>Switches</td>
- <td>
- <button onclick='hideLayer("switchCanvas");'>Hide</button>
- <button onclick='showLayer("switchCanvas");'>Show</button>
- </td>
- </tr>
- <tr>
- <td>Text</td>
- <td>
- <button onclick='hideLayer("textCanvas");'>Hide</button>
- <button onclick='showLayer("textCanvas");'>Show</button>
- </td>
- </tr>
- <tr>
- <td>TextInfo</td>
- <td>
- <button onclick='hideLayer("textInfoCanvas");'>Hide</button>
- <button onclick='showLayer("textInfoCanvas");'>Show</button>
- </td>
- </tr>
- <tr>
- <td>Timestamp</td>
- <td>
- <button onclick='hideLayer("topCanvas");'>Hide</button>
- <button onclick='showLayer("topCanvas");'>Show</button>
- </td>
- </tr>
- </table>
- </div>
- </div>
<canvas id="bgCanvas" width="1920" height="1032" style="position: absolute; z-index: 1;"> </canvas>
<canvas id="linkCanvas" width="1920" height="1032" style="position: absolute; z-index: 10;"> </canvas>
@@ -527,6 +332,7 @@
<script src="js/jquery.min.js" type="text/javascript"></script>
<script src="js/bootstrap.min.js" type="text/javascript"></script>
<script type="text/javascript" src="js/nms-data.js"></script>
+ <script type="text/javascript" src="js/nms-map.js"></script>
<script type="text/javascript" src="js/nms.js"></script>
<script type="text/javascript" src="js/nms-color-util.js"></script>
<script type="text/javascript" src="js/nms-map-handlers.js"></script>
diff --git a/web/nms.gathering.org/js/nms-color-util.js b/web/nms.gathering.org/js/nms-color-util.js
index 965a10e..f50ee04 100644
--- a/web/nms.gathering.org/js/nms-color-util.js
+++ b/web/nms.gathering.org/js/nms-color-util.js
@@ -49,21 +49,22 @@ function getRandomColor()
*/
function drawGradient(gradients)
{
- var gradient = dr.hidden.ctx.createLinearGradient(0,0,1000,0);
+ var ctx = nmsMap._c.hidden.ctx; // FIXME: Move it away...
+ var gradient = ctx.createLinearGradient(0,0,1000,0);
var stops = gradients.length - 1;
nms.gradients = gradients;
for (var color in gradients) {
var i = color / stops;
gradient.addColorStop(i, gradients[color]);
}
- dr.hidden.ctx.beginPath();
- dr.hidden.ctx.strokeStyle = gradient;
- dr.hidden.ctx.moveTo(0,0);
- dr.hidden.ctx.lineTo(1000,0);
- dr.hidden.ctx.lineWidth = 10;
- dr.hidden.ctx.closePath();
- dr.hidden.ctx.stroke();
- dr.hidden.ctx.moveTo(0,0);
+ ctx.beginPath();
+ ctx.strokeStyle = gradient;
+ ctx.moveTo(0,0);
+ ctx.lineTo(1000,0);
+ ctx.lineWidth = 10;
+ ctx.closePath();
+ ctx.stroke();
+ ctx.moveTo(0,0);
}
/*
@@ -83,7 +84,8 @@ function getColorStop(x) {
* made generic.
*/
function getColor(x,y) {
- var imageData = dr.hidden.ctx.getImageData(x, y, 1, 1);
+ var ctx = nmsMap._c.hidden.ctx; // FIXME: Move it away...
+ var imageData = ctx.getImageData(x, y, 1, 1);
var data = imageData.data;
if (data.length < 4)
return false;
diff --git a/web/nms.gathering.org/js/nms-map-handlers.js b/web/nms.gathering.org/js/nms-map-handlers.js
index eaa62fd..ce1d6db 100644
--- a/web/nms.gathering.org/js/nms-map-handlers.js
+++ b/web/nms.gathering.org/js/nms-map-handlers.js
@@ -2,8 +2,7 @@
* Map handlers/updaters for NMS.
*
* These are functions used to determine how the map should look in NMS.
- * They represent vastly different information, but in a uniform way. I
- * suppose this is the c++-type of object orientation...
+ * They represent vastly different information, but in a uniform way.
*
* The idea is that these updaters only parse information that's fetched by
* NMS - they do not request additional information. E.g., ping data is
@@ -11,6 +10,10 @@
* displayed. This might seem redundant, but it means any handler can
* utilize information from any aspect of NMS, and thus opens NMS up to the
* world of intelligent maps base don multiple data sources.
+ *
+ * Warning: This paradigm will change. Handlers will be expected to
+ * register their own callbacks for nmsData. Work in progress.
+ *
*/
/*
@@ -257,7 +260,6 @@ function tempInit()
function pingUpdater()
{
if (!nms.ping_data || !nms.ping_data["switches"]) {
- resetColors();
return;
}
for (var sw in nms.switches_now["switches"]) {
@@ -291,35 +293,36 @@ function commentUpdater()
{
var realnow = Date.now();
var now = Math.floor(realnow / 1000);
- for (var sw in nms.switches_now["switches"]) {
+ if (nmsData.comments == undefined || nmsData.comments.comments == undefined) {
+ return
+ }
+ for (var sw in nmsData.comments.comments) {
var c = "white";
- var s = nms.switches_now["switches"][sw];
- if (s["comments"] && s["comments"].length > 0) {
- var then = 0;
- var active = 0;
- var persist = 0;
- c = "yellow";
- for (var v in s["comments"]) {
- var then_test = parseInt(s["comments"][v]["time"]);
- if (then_test > then && s["comments"][v]["state"] != "inactive")
- then = then_test;
- if (s["comments"][v]["state"] == "active") {
- active++;
- }
- if (s["comments"][v]["state"] == "persist")
- persist++;
- }
- if (then > (now - (60*15))) {
- c = red;
- } else if (active > 0) {
- c = orange;
- } else if (persist > 0) {
- c = blue;
- } else {
- c = green;
+ var s = nmsData.comments.comments[sw];
+ var then = 0;
+ var active = 0;
+ var persist = 0;
+ c = "yellow";
+ for (var v in s["comments"]) {
+ var then_test = parseInt(s["comments"][v]["time"]);
+ if (then_test > then && s["comments"][v]["state"] != "inactive")
+ then = then_test;
+ if (s["comments"][v]["state"] == "active") {
+ active++;
}
+ if (s["comments"][v]["state"] == "persist")
+ persist++;
}
- setSwitchColor(sw, c);
+ if (then > (now - (60*15))) {
+ c = red;
+ } else if (active > 0) {
+ c = orange;
+ } else if (persist > 0) {
+ c = blue;
+ } else {
+ c = green;
+ }
+ nmsMap.setSwitchColor(sw, c);
}
}
@@ -337,11 +340,15 @@ function commentInit()
*/
function randomizeColors()
{
- for (var i in nms.switches_now.linknets) {
+/* for (var i in nms.switches_now.linknets) {
setLinknetColors(i, getRandomColor(), getRandomColor());
}
- for (var sw in nms.switches_now.switches) {
- setSwitchColor(sw, getRandomColor());
+*/
+ if (nmsData.switches == undefined || nmsData.switches.switches == undefined) {
+ return;
+ }
+ for (var sw in nmsData.switches.switches) {
+ nmsMap.setSwitchColor(sw, getRandomColor());
}
}
diff --git a/web/nms.gathering.org/js/nms-map.js b/web/nms.gathering.org/js/nms-map.js
new file mode 100644
index 0000000..69a76e0
--- /dev/null
+++ b/web/nms.gathering.org/js/nms-map.js
@@ -0,0 +1,265 @@
+"use strict";
+
+/* WORK
+ * IN
+ * PROGRESS
+ *
+ * Interface:
+ *
+ * nmsMap.init() - start things up
+ * nmsMap.setSwitchColor(switch,color)
+ * nmsMap.setSwitchInfo(switch,info)
+ */
+
+
+var nmsMap = nmsMap || {
+ stats: {
+ earlyDrawAll:0,
+ colorChange:0,
+ colorSame:0,
+ resizeEvents:0,
+ switchInfoUpdate:0,
+ switchInfoSame:0
+ },
+ contexts: ["bg","link","blur","switch","text","textInfo","top","input","hidden"],
+ _info: {},
+ _settings: {
+ fontLineFactor: 3,
+ textMargin: 3,
+ xMargin: 10,
+ yMargin: 20
+ },
+ scale: 1,
+ _orig: { width:1920, height:1032 },
+ _canvas: {
+ get width() { return nmsMap.scale * nmsMap._orig.width; },
+ get height() { return nmsMap.scale * nmsMap._orig.height; }
+ },
+
+ _color: { },
+ _c: {}
+}
+
+nmsMap.init = function() {
+ this._initContexts();
+ this._drawBG();
+ nmsData.registerSource("switches","/api/public/switches",function(){nmsMap._drawAllSwitches();});
+ window.addEventListener('resize',nmsMap._resizeEvent,true);
+ document.addEventListener('load',nmsMap._resizeEvent,true);
+ this._drawAllSwitches();
+}
+
+nmsMap.setSwitchColor = function(sw, color) {
+ if (this._color[sw] != color) {
+ this._color[sw] = color;
+ this._drawSwitch(sw);
+ this.stats.colorChange++;
+ } else {
+ this.stats.colorSame++;
+ }
+}
+
+nmsMap.reset = function() {
+ for (var sw in this._color) {
+ nmsMap.setSwitchColor(sw, undefined);
+ }
+ for (var sw in this._info) {
+ nmsMap.setSwitchInfo(sw, undefined);
+ }
+}
+
+nmsMap.setSwitchInfo = function(sw,info) {
+ if (this._info[sw] != info) {
+ this._info[sw] = info;
+ this._drawSwitchInfo(sw);
+ this.stats.switchInfoUpdate++;
+ } else {
+ this.stats.switchInfoSame++;
+ }
+}
+
+nmsMap._initContext = function(name) {
+ this._c[name] = {};
+ this._c[name].c = document.getElementById(name + "Canvas");
+ this._c[name].ctx = this._c[name].c.getContext('2d');
+}
+
+nmsMap._initContexts = function() {
+ for (var context in this.contexts) {
+ this._initContext(this.contexts[context]);
+ }
+}
+
+nmsMap._resizeEvent = function() {
+ var width = window.innerWidth - nmsMap._c.bg.c.offsetLeft;
+ var height = window.innerHeight - nmsMap._c.bg.c.offsetTop;
+
+ var xScale = (width / (nmsMap._orig.width + nmsMap._settings.xMargin));
+ var yScale = (height / (nmsMap._orig.height + nmsMap._settings.yMargin));
+
+ if (xScale > yScale) {
+ nmsMap.scale = yScale;
+ } else {
+ nmsMap.scale = xScale;
+ }
+ for (var a in nmsMap._c) {
+ /*
+ * Resizing this to a too small size breaks gradients on smaller screens.
+ */
+ if (a == 'hidden')
+ continue;
+ nmsMap._c[a].c.height = nmsMap._canvas.height;
+ nmsMap._c[a].c.width = nmsMap._canvas.width;
+ }
+ nmsMap._drawBG();
+ nmsMap._drawAllSwitches();
+ nmsMap.stats.resizeEvents++;
+}
+
+nmsMap.setNightMode = function(toggle) {
+ if (this._nightmode == toggle)
+ return;
+ this._nightmode = toggle;
+ nmsMap._drawBG();
+}
+
+nmsMap._drawBG = function() {
+ var imageObj = document.getElementById('source');
+ this._c.bg.ctx.drawImage(imageObj, 0, 0, nmsMap._canvas.width, nmsMap._canvas.height);
+ if(this._nightmode)
+ nmsMap._invertBG();
+}
+
+nmsMap._invertBG = function() {
+ var imageData = this._c.bg.ctx.getImageData(0, 0, nmsMap._canvas.width, nmsMap._canvas.height);
+ var data = imageData.data;
+
+ for(var i = 0; i < data.length; i += 4) {
+ data[i] = 255 - data[i];
+ data[i + 1] = 255 - data[i + 1];
+ data[i + 2] = 255 - data[i + 2];
+ }
+ this._c.bg.ctx.putImageData(imageData, 0, 0);
+}
+
+nmsMap._getBox = function(sw) {
+ var box = nmsData.switches.switches[sw]['placement'];
+ box.x = parseInt(box.x);
+ box.y = parseInt(box.y);
+ box.width = parseInt(box.width);
+ box.height = parseInt(box.height);
+ return box;
+}
+
+nmsMap._drawSwitch = function(sw)
+{
+ var box = this._getBox(sw);
+ var color = nmsMap._color[sw];
+ if (color == undefined) {
+ color = blue;
+ }
+ this._c.switch.ctx.fillStyle = color;
+ this._drawBox(this._c.switch.ctx, box['x'],box['y'],box['width'],box['height']);
+ this._c.switch.ctx.shadowBlur = 0;
+ this._drawSidewaysText(this._c.text.ctx, sw,box);
+ /*
+ if ((box['width'] + 10 )< box['height']) {
+ //
+ } else {
+ //drawRegular(dr.text.ctx,sw,box['x'],box['y'],box['width'],box['height']);
+ }
+ */
+}
+
+nmsMap._drawSwitchInfo = function(sw) {
+ var box = this._getBox(sw);
+ if (this._info[sw] == undefined) {
+ this._clearBox(this._c.textInfo.ctx, box);
+ } else {
+ this._drawSidewaysText(this._c.textInfo.ctx, this._info[sw], box, "right");
+ }
+}
+
+nmsMap._clearBox = function(ctx,box) {
+ ctx.save();
+ ctx.scale(this.scale,this.scale);
+ ctx.clearRect(box['x'], box['y'], box['width'], box['height']);
+ ctx.restore();
+}
+
+nmsMap._drawSidewaysText = function(ctx, text, box, align) {
+ this._clearBox(ctx,box);
+ ctx.save();
+ ctx.scale(this.scale, this.scale); // FIXME: Do it everywhere?
+ ctx.lineWidth = Math.floor(nmsMap._settings.fontLineFactor);
+ if (ctx.lineWidth == 0) {
+ ctx.lineWidth = Math.round(nms._settings.fontLineFactor);
+ }
+ ctx.fillStyle = "white";
+ ctx.strokeStyle = "black";
+ ctx.translate(box.x + box.width - this._settings.textMargin, box.y + box.height - this._settings.textMargin);
+ ctx.rotate(Math.PI * 3/2);
+ if (align == "right") {
+ ctx.textAlign = "right";
+ /*
+ * Margin*2 is to compensate for the margin above.
+ */
+ ctx.translate(box.height - this._settings.textMargin*2,0);
+ }
+ ctx.strokeText(text, 0, 0);
+ ctx.fillText(text, 0, 0);
+ ctx.restore();
+}
+
+nmsMap._drawAllSwitches = function() {
+ if (nmsData.switches == undefined) {
+ this.stats.earlyDrawAll++;
+ return;
+ }
+ for (var sw in nmsData.switches.switches) {
+ this._drawSwitch(sw);
+ }
+}
+
+nmsMap._drawBox = function(ctx, x, y, boxw, boxh) {
+ ctx.save();
+ ctx.scale(this.scale, this.scale); // FIXME
+ ctx.fillRect(x,y, boxw, boxh);
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = "#000000";
+ ctx.strokeRect(x,y, boxw, boxh);
+ ctx.restore();
+
+}
+
+nmsMap._connectSwitches = function(sw1, sw2, color1, color2) {
+ nmsMap._connectBoxes(this._getBox(sw1), this._getBox(sw2),
+ color1, color2);
+}
+
+/*
+ * Draw a line between two boxes, with a gradient going from color1 to
+ * color2.
+ */
+nmsMap._connectBoxes = function(box1, box2,color1, color2) {
+ var ctx = nmsMap._c.link.ctx;
+ if (color1 == undefined)
+ color1 = blue;
+ if (color2 == undefined)
+ color2 = blue;
+ var x0 = Math.floor(box1.x + box1.width/2);
+ var y0 = Math.floor(box1.y + box1.height/2);
+ var x1 = Math.floor(box2.x + box2.width/2);
+ var y1 = Math.floor(box2.y + box2.height/2);
+ ctx.save();
+ ctx.scale(nmsMap.scale, nmsMap.scale);
+ var gradient = ctx.createLinearGradient(x1,y1,x0,y0);
+ gradient.addColorStop(0, color1);
+ gradient.addColorStop(1, color2);
+ ctx.strokeStyle = gradient;
+ ctx.moveTo(x0,y0);
+ ctx.lineTo(x1,y1);
+ ctx.lineWidth = 5;
+ ctx.stroke();
+ ctx.restore();
+}
diff --git a/web/nms.gathering.org/js/nms.js b/web/nms.gathering.org/js/nms.js
index b47b131..cd62579 100644
--- a/web/nms.gathering.org/js/nms.js
+++ b/web/nms.gathering.org/js/nms.js
@@ -3,30 +3,14 @@ var nms = {
stats:{}, // Various internal stats
updater:undefined, // Active updater
speed:0, // Current aggregated speed
- drawn:false, // Set to 'true' when switches are drawn
switch_showing:"", // Which switch we are displaying (if any).
switchInfo:{},
- switchInfoDrawn:{},
repop_switch:false, // True if we need to repopulate the switch info when port state updates (e.g.: added comments);
repop_time:false, // Timestamp in case we get a cached result
nightMode:false,
- /*
- * Switch-specific variables. These are currently separate from
- * "switches_now" because switches_now is reset every time we get
- * new data.
- */
- nightBlur:{}, // Have we blurred this switch or not?
- shadowBlur:10,
- shadowColor:"#EEEEEE",
- switch_color:{}, // Color for switch
- linknet_color:{}, // color for linknet
- textDrawn:{}, // Have we drawn text for this switch?
_now: false,
get now() { return this._now },
set now(v) { this._now = n; nmsData.now = n; },
- fontSize:16, // This is scaled too, but 16 seems to make sense.
- fontFace:"Verdana",
- fontLineFactor:3,
/*
* Various setInterval() handlers. See nmsTimer() for how they are
* used.
@@ -36,8 +20,6 @@ var nms = {
*/
timers: {
playback:false,
- ports:false,
- ping:false
},
menuShowing:true,
/*
@@ -67,8 +49,7 @@ var nms = {
'k':moveTimeFromKey,
'l':moveTimeFromKey,
'p':moveTimeFromKey,
- 'r':moveTimeFromKey,
- 'not-default':keyDebug
+ 'r':moveTimeFromKey
},
/*
* Playback controllers and variables
@@ -117,90 +98,6 @@ function nmsTimer(handler, interval, name, description) {
/*
- * Drawing primitives.
- *
- * This contains both canvas and context for drawing layers. It's on a
- * top-level namespace to reduce SLIGHTLY the ridiculously long names
- * (e.g.: dr.bg.ctx.drawImage() is long enough....).
- *
- * Only initialized once (for now).
- */
-var dr = {};
-
-/*
- * Original scale. This is just used to define the coordinate system.
- * 1920x1032 was chosen for tg15 by coincidence: We scaled the underlying
- * map down to "full hd" and these are the bounds we got. There's no
- * particular reason this couldn't change, except it means re-aligning all
- * switches.
- */
-var orig = {
- width:1920,
- height:1032
- };
-
-/*
- * Canvas dimensions, and scale factor.
- *
- * We could derive scale factor from canvas.width / orig.width, but it's
- * used so frequently that this makes much more sense.
- *
- * Width and height are rarely used.
- */
-var canvas = {
- width:0,
- height:0,
- scale:1
-};
-
-/*
- * Various margins at the sides.
- *
- * Not really used much, except for "text", which is really more of a
- * padding than margin...
- */
-var margin = {
- x:10,
- y:20,
- text:3
-};
-
-/*
- * Convenience-function to populate the 'dr' structure.
- *
- * Only run once.
- */
-function initDrawing() {
- dr['bg'] = {};
- dr['bg']['c'] = document.getElementById("bgCanvas");
- dr['bg']['ctx'] = dr['bg']['c'].getContext('2d');
- dr['link'] = {};
- dr['link']['c'] = document.getElementById("linkCanvas");
- dr['link']['ctx'] = dr['link']['c'].getContext('2d');
- dr['blur'] = {};
- dr['blur']['c'] = document.getElementById("blurCanvas");
- dr['blur']['ctx'] = dr['blur']['c'].getContext('2d');
- dr['switch'] = {};
- dr['switch']['c'] = document.getElementById("switchCanvas");
- dr['switch']['ctx'] = dr['switch']['c'].getContext('2d');
- dr['text'] = {};
- dr['text']['c'] = document.getElementById("textCanvas");
- dr['text']['ctx'] = dr['text']['c'].getContext('2d');
- dr['textInfo'] = {};
- dr['textInfo']['c'] = document.getElementById("textInfoCanvas");
- dr['textInfo']['ctx'] = dr['textInfo']['c'].getContext('2d');
- dr['top'] = {};
- dr['top']['c'] = document.getElementById("topCanvas");
- dr['top']['ctx'] = dr['top']['c'].getContext('2d');
- dr['input'] = {};
- dr['input']['c'] = document.getElementById("inputCanvas");
- dr['input']['ctx'] = dr['top']['c'].getContext('2d');
- dr['hidden'] = {};
- dr['hidden']['c'] = document.getElementById("hiddenCanvas");
- dr['hidden']['ctx'] = dr['hidden']['c'].getContext('2d');
-}
-
-/*
* Convenience function that doesn't support huge numbers, and it's easier
* to comment than to fix. But not really, but I'm not fixing it anyway.
*/
@@ -619,12 +516,6 @@ function updateMap()
}
if (!newerSwitches())
return;
- if (!nms.drawn)
- setScale();
- if (!nms.drawn)
- return;
- if (!nms.ping_data)
- return;
if (nms.updater != undefined && nmsData.switches && nms.switches_then) {
nms.updater.updater();
@@ -638,14 +529,12 @@ function updateMap()
function setUpdater(fo)
{
nms.updater = undefined;
- resetColors();
- resetTextInfo();
+ nmsMap.reset();
fo.init();
nms.updater = fo;
var foo = document.getElementById("updater_name");
foo.innerHTML = fo.name + " ";
document.location.hash = fo.tag;
- initialUpdate();
if (nms.ping_data && nms.switches_then && nmsData.switches) {
nms.updater.updater();
}
@@ -697,31 +586,6 @@ function resetSwitchStates() {
}
}
-/*
- * Convenience function to avoid waiting for pollers when data is available
- * for the first time.
- */
-function initialUpdate()
-{
- return;
- if (nms.ping_data && nms.switches_then && nmsData.switches && nms.updater != undefined && nms.did_update == false ) {
- resizeEvent();
- if (!nms.drawn) {
- drawSwitches();
- drawLinknets();
- }
- nms.updater.updater();
- nms.did_update = true;
- }
-}
-
-function resetBlur()
-{
- nms.nightBlur = {};
- dr.blur.ctx.clearRect(0,0,canvas.width,canvas.height);
- drawSwitches();
-}
-
function applyBlur()
{
var blur = document.getElementById("shadowBlur");
@@ -732,6 +596,14 @@ function applyBlur()
saveSettings();
}
+function toggleLayer(layer) {
+ var l = document.getElementById(layer);
+ if (l.style.display == 'none')
+ l.style.display = '';
+ else
+ l.style.display = 'none';
+}
+
function showBlurBox()
{
var blur = document.getElementById("shadowBlur");
@@ -864,54 +736,6 @@ function setLinknetColors(i,c1,c2)
}
}
-/*
- * (Re)draw a switch 'sw'.
- *
- * Color defaults to 'blue' if it's not set in the data structure.
- */
-function drawSwitch(sw)
-{
- var box = nmsData.switches.switches[sw]['placement'];
- var color = nms.switch_color[sw];
- if (color == undefined) {
- color = blue;
- }
- dr.switch.ctx.fillStyle = color;
- /*
- * XXX: This is a left-over from before NMS did separate
- * canvases, and might be done better elsewhere.
- */
- if (nms.nightMode && nms.nightBlur[sw] != true) {
- dr.blur.ctx.shadowBlur = nms.shadowBlur;
- dr.blur.ctx.shadowColor = nms.shadowColor;
- drawBoxBlur(box['x'],box['y'],box['width'],box['height']);
- nms.nightBlur[sw] = true;
- }
- drawBox(box['x'],box['y'],box['width'],box['height']);
- dr.switch.ctx.shadowBlur = 0;
- if (!nms.textDrawn[sw]) {
- if ((box['width'] + 10 )< box['height']) {
- drawSideways(dr.text.ctx, sw,box['x'],box['y'],box['width'],box['height']);
- } else {
- drawRegular(dr.text.ctx,sw,box['x'],box['y'],box['width'],box['height']);
- }
-
- nms.textDrawn[sw] = true;
- }
-}
-
-/*
- * Draw all switches
- */
-function drawSwitches()
-{
- if (!nmsData.switches || !nmsData.switches.switches)
- return;
- for (var sw in nmsData.switches.switches) {
- drawSwitch(sw);
- }
- nms.drawn = true;
-}
function drawSwitchInfo()
{
if (!nmsData.switches || !nmsData.switches.switches)
@@ -957,44 +781,13 @@ function drawNow()
*/
function drawScene()
{
- dr.text.ctx.font = Math.floor(nms.fontSize * canvas.scale) + "px " + nms.fontFace;
+ //dr.text.ctx.font = Math.floor(nms.fontSize * canvas.scale) + "px " + nms.fontFace;
dr.textInfo.ctx.font = Math.floor(nms.fontSize * canvas.scale) + "px " + nms.fontFace;
drawLinknets();
- drawSwitches();
drawSwitchInfo();
}
/*
- * Set the scale factor and (re)draw the scene and background.
- * Uses canvas.scale and updates canvas.height and canvas.width.
- */
-function setScale()
-{
- canvas.height = orig.height * canvas.scale ;
- canvas.width = orig.width * canvas.scale ;
- for (var a in dr) {
- /*
- * Resizing this to a too small size breaks gradients on smaller screens.
- */
- if (a == 'hidden')
- continue;
- dr[a].c.height = canvas.height;
- dr[a].c.width = canvas.width;
- }
- nms.nightBlur = {};
- nms.textDrawn = {};
- nms.switchInfoDrawn = {};
- if (nms.gradients)
- drawGradient(nms.gradients);
- drawBG();
- drawScene();
- drawNow();
-
- document.getElementById("scaler").value = canvas.scale;
- document.getElementById("scaler-text").innerHTML = (parseFloat(canvas.scale)).toPrecision(3);
-}
-
-/*
* Returns true if the coordinates (x,y) is inside the box defined by
* box.{x,y,w.h} (e.g.: placement of a switch).
*/
@@ -1012,8 +805,8 @@ function isIn(box, x, y)
* if none is found.
*/
function findSwitch(x,y) {
- x = parseInt(parseInt(x) / canvas.scale);
- y = parseInt(parseInt(y) / canvas.scale);
+ x = parseInt(parseInt(x) / nmsMap.scale);
+ y = parseInt(parseInt(y) / nmsMap.scale);
for (var v in nmsData.switches.switches) {
if(isIn(nmsData.switches.switches[v]['placement'],x,y)) {
@@ -1024,27 +817,6 @@ function findSwitch(x,y) {
}
/*
- * Set switch color of 'sw' to 'c', then re-draw the switch.
- */
-function setSwitchColor(sw, c)
-{
- if(!nms.switch_color || !nms.switch_color[sw] || nms.switch_color[sw] != c) {
- nms.switch_color[sw] = c;
- drawSwitch(sw);
- }
-}
-
-/*
- * Event handler for the front-end drag bar to change scale
- */
-function scaleChange()
-{
- var scaler = document.getElementById("scaler").value;
- canvas.scale = scaler;
- setScale();
-}
-
-/*
* Called when a switch is clicked
*/
function switchClick(sw)
@@ -1056,35 +828,6 @@ function switchClick(sw)
}
/*
- * Resets the colors of linknets and switches.
- *
- * Useful when mode changes so we don't re-use colors from previous modes
- * due to lack of data or bugs.
- */
-function resetColors()
-{
- if (!nms.sswitches_now)
- return;
- if (nmsData.switches.linknets) {
- for (var i in nmsData.switches.linknets) {
- setLinknetColors(i, blue,blue);
- }
- }
- for (var sw in nmsData.switches.switches) {
- setSwitchColor(sw, blue);
- }
-}
-
-function resetTextInfo()
-{
- if (!nmsData.switches)
- return;
- for (var sw in nmsData.switches.switches) {
- switchInfoText(sw, undefined);
- }
-
-}
-/*
* onclick handler for the canvas.
*
* Currently just shows info for a switch.
@@ -1098,40 +841,6 @@ function canvasClick(e)
}
/*
- * Resize event-handler.
- *
- * Recomputes the scale and applies it.
- *
- * Has to use c.offset* since we are just scaling the canvas, not
- * everything else.
- *
- */
-function resizeEvent()
-{
- var width = window.innerWidth - dr.bg.c.offsetLeft;
- var height = window.innerHeight - dr.bg.c.offsetTop;
- if (width / (orig.width + margin.x) > height / (orig.height + margin.y)) {
- canvas.scale = height / (orig.height + margin.y);
- } else {
- canvas.scale = width / (orig.width + margin.x);
- }
- setScale();
-}
-
-/*
- * Draws the background image (scaled).
- */
-function drawBG()
-{
- if (nms.nightMode) {
- invertCanvas();
- } else {
- var image = document.getElementById('source');
- dr.bg.ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
- }
-}
-
-/*
* Set night mode to whatever 'toggle' is.
*
* XXX: setScale() is a bit of a hack, but it really is the same stuff we
@@ -1143,50 +852,11 @@ function setNightMode(toggle) {
body.style.background = toggle ? "black" : "white";
var nav = document.getElementsByTagName("nav")[0];
if (toggle) {
- dr.blur.c.style.display = '';
nav.classList.add('navbar-inverse');
} else {
- dr.blur.c.style.display = 'none';
nav.classList.remove('navbar-inverse');
}
- setScale();
-}
-
-function switchInfoText(sw, text)
-{
- var box = nmsData.switches.switches.[sw]['placement'];
- var c = canvas.scale;
- if (nms.switchInfo[sw] == text && nms.switchInfoDrawn[sw]) {
- return;
- }
- nms.switchInfo[sw] = text;
- nms.switchInfoDrawn[sw] = true;
- dr.textInfo.ctx.clearRect(c* box['x'], c*box['y'], c*box['width'], c*box['height']);
- if (text != undefined && text != "") {
- if ((box['width'] + 10 )< box['height']) {
- drawSideways(dr.textInfo.ctx, text,box['x'],box['y'],box['width'],box['height'],"end");
- } else {
- drawRegular(dr.textInfo.ctx, text,box['x'],box['y'],box['width'],box['height'],"end");
- }
- }
-}
-
-/*
- * Draw a box (e.g.: switch).
- */
-function drawBox(x,y,boxw,boxh)
-{
- var myX = Math.floor(x * canvas.scale);
- var myY = Math.floor(y * canvas.scale);
- var myX2 = Math.floor((boxw) * canvas.scale);
- var myY2 = Math.floor((boxh) * canvas.scale);
- dr.switch.ctx.fillRect(myX,myY, myX2, myY2);
- dr.switch.ctx.lineWidth = Math.floor(1.0 * canvas.scale);
- if (canvas.scale < 1.0) {
- dr.switch.ctx.lineWidth = 1.0;
- }
- dr.switch.ctx.strokeStyle = "#000000";
- dr.switch.ctx.strokeRect(myX,myY, myX2, myY2);
+ nmsMap.setNightMode(toggle);
}
/*
@@ -1200,108 +870,6 @@ function drawBoxBlur(x,y,boxw,boxh)
var myY2 = Math.floor((boxh) * canvas.scale);
dr.blur.ctx.fillRect(myX,myY, myX2, myY2);
}
-/*
- * Draw text on a box - sideways!
- *
- * XXX: This is pretty nasty and should also probably take a box as input.
- */
-function drawSideways(ctx,text,x,y,w,h,align)
-{
- ctx.rotate(Math.PI * 3 / 2);
- ctx.fillStyle = "white";
- ctx.strokeStyle = "black";
- if (align == "end") {
- ctx.textAlign = align;
- y = y-h + margin.text*2;
- } else {
- ctx.textAlign = "start";
- }
- ctx.lineWidth = Math.floor(nms.fontLineFactor * canvas.scale);
- if (ctx.lineWidth == 0) {
- ctx.lineWidth = Math.round(nms.fontLineFactor * canvas.scale);
- }
- ctx.strokeText(text, - canvas.scale * (y + h - margin.text),canvas.scale * (x + w - margin.text) );
- ctx.fillText(text, - canvas.scale * (y + h - margin.text),canvas.scale * (x + w - margin.text) );
-
- ctx.rotate(Math.PI / 2);
-}
-
-/*
- * Draw background inverted (wooo)
- *
- * XXX: This is broken for chromium on local file system (e.g.: file:///)
- * Seems like a chromium bug?
- */
-function invertCanvas() {
- var imageObj = document.getElementById('source');
- dr.bg.ctx.drawImage(imageObj, 0, 0, canvas.width, canvas.height);
-
- var imageData = dr.bg.ctx.getImageData(0, 0, canvas.width, canvas.height);
- var data = imageData.data;
-
- for(var i = 0; i < data.length; i += 4) {
- data[i] = 255 - data[i];
- data[i + 1] = 255 - data[i + 1];
- data[i + 2] = 255 - data[i + 2];
- }
- dr.bg.ctx.putImageData(imageData, 0, 0);
-}
-
-/*
- * Draw regular text on a box.
- *
- * Should take the same format as drawSideways()
- *
- * XXX: Both should be renamed to have 'text' or something in them
- */
-function drawRegular(ctx,text,x,y,w,h,align) {
-
- ctx.fillStyle = "white";
- ctx.strokeStyle = "black";
- ctx.lineWidth = Math.floor(nms.fontLineFactor * canvas.scale);
- if (align == "end") {
- ctx.textAlign = align;
- x = x+w - margin.text*2;
- } else {
- ctx.textAlign = "start";
- }
- if (ctx.lineWidth == 0) {
- ctx.lineWidth = Math.round(nms.fontLineFactor * canvas.scale);
- }
- ctx.strokeText(text, (x + margin.text) * canvas.scale, (y + h - margin.text) * canvas.scale);
- ctx.fillText(text, (x + margin.text) * canvas.scale, (y + h - margin.text) * canvas.scale);
-}
-
-/*
- * Draw a line between switch "insw1" and "insw2", using a gradiant going
- * from color1 to color2.
- *
- * XXX: beginPath() and closePath() is needed to avoid re-using the
- * gradient/color
- */
-function connectSwitches(insw1, insw2,color1, color2) {
- var sw1 = nmsData.switches.switches[insw1].placement;
- var sw2 = nmsData.switches.switches[insw2].placement;
- if (color1 == undefined)
- color1 = blue;
- if (color2 == undefined)
- color2 = blue;
- var x0 = Math.floor((sw1.x + sw1.width/2) * canvas.scale);
- var y0 = Math.floor((sw1.y + sw1.height/2) * canvas.scale);
- var x1 = Math.floor((sw2.x + sw2.width/2) * canvas.scale);
- var y1 = Math.floor((sw2.y + sw2.height/2) * canvas.scale);
- var gradient = dr.link.ctx.createLinearGradient(x1,y1,x0,y0);
- gradient.addColorStop(0, color1);
- gradient.addColorStop(1, color2);
- dr.link.ctx.beginPath();
- dr.link.ctx.strokeStyle = gradient;
- dr.link.ctx.moveTo(x0,y0);
- dr.link.ctx.lineTo(x1,y1);
- dr.link.ctx.lineWidth = Math.floor(5 * canvas.scale);
- dr.link.ctx.closePath();
- dr.link.ctx.stroke();
- dr.link.ctx.moveTo(0,0);
-}
/*
* Boot up "fully fledged" NMS.
@@ -1311,16 +879,13 @@ function connectSwitches(insw1, insw2,color1, color2) {
* drawing, etc).
*/
function initNMS() {
- initDrawing();
- window.addEventListener('resize',resizeEvent,true);
- document.addEventListener('load',resizeEvent,true);
nms.timers.playback = new nmsTimer(nms.playback.tick, 1000, "Playback ticker", "Handler used to advance time");
// Public
nmsData.registerSource("ping", "/api/public/ping");
- nmsData.registerSource("switches","/api/public/switches", drawSwitches);
+ nmsData.registerSource("switches","/api/public/switches");
nmsData.registerSource("switchstate","/api/public/switch-state");
// Private
@@ -1328,6 +893,7 @@ function initNMS() {
nmsData.registerSource("comments", "/api/private/comments");
nmsData.registerSource("smanagement","/api/private/switches-management");
+ nmsMap.init();
detectHandler();
nms.playback.play();
setupKeyhandler();
@@ -1390,45 +956,10 @@ function showTimerDebug() {
document.getElementById('debugTimers').style.display = 'block';
}
-function hideLayer(layer) {
- var l = document.getElementById(layer);
- l.style.display = "none";
- if (layer != "layerVisibility")
- nms.layerVisibility[layer] = false;
- saveSettings();
-}
-
-function showLayer(layer) {
- var l = document.getElementById(layer);
- l.style.display = "";
- if (layer != "layerVisibility")
- nms.layerVisibility[layer] = true;
- saveSettings();
-}
-
-function toggleLayer(layer) {
- var l = document.getElementById(layer);
- if (l.style.display == 'none')
- l.style.display = '';
- else
- l.style.display = 'none';
-}
-
-function applyLayerVis()
-{
- for (var l in nms.layerVisibility) {
- var layer = document.getElementById(l);
- if (layer)
- layer.style.display = nms.layerVisibility[l] ? '' : 'none';
- }
-}
-
function setMenu()
{
var nav = document.getElementsByTagName("nav")[0];
nav.style.display = nms.menuShowing ? '' : 'none';
- resizeEvent();
-
}
function toggleMenu()
{
@@ -1491,13 +1022,6 @@ function moveTimeFromKey(e,key)
return true;
}
-var debugEvent;
-function keyDebug(e)
-{
- console.log(e);
- debugEvent = e;
-}
-
function keyPressed(e)
{
if (e.target.nodeName == "INPUT") {
@@ -1551,10 +1075,8 @@ function restoreSettings()
for (var v in retrieve) {
nms[v] = retrieve[v];
}
- setScale();
setMenu();
setNightMode(nms.nightMode);
- applyLayerVis();
}
function forgetSettings()