diff options
Diffstat (limited to 'web')
| -rw-r--r-- | web/nms.gathering.org/index.html | 196 | ||||
| -rw-r--r-- | web/nms.gathering.org/js/nms-color-util.js | 22 | ||||
| -rw-r--r-- | web/nms.gathering.org/js/nms-map-handlers.js | 71 | ||||
| -rw-r--r-- | web/nms.gathering.org/js/nms-map.js | 265 | ||||
| -rw-r--r-- | web/nms.gathering.org/js/nms.js | 510 | 
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">×</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">×</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">×</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">×</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() | 
