"use strict";
/*
 * NMS info-window controller
 *
 * Interface: nmsInfoBox.showWindow(windowType,optionalParameter), nmsInfoBox.hide()
 *
 * Uses a basic hierarchy of window > views > panels, where each panel can work
 * independently, but gets loaded/unloaded when needed by window or view.
 *
 */
/*
 *
 * Currently broken or needs reimplementing:
 * - Handler unloading is not working correctly, and many are never removed
 * - SSH-management link, this should propably be a custom "view" of sorts
 *
 * General TODO:
 * - Move inventory into a separate tab.
 * - Add external windows (timetravel, etc)
 * - Take a critical look at what methods/variables should be marked as "_"
 * - Currently argument is assumed to be a switch, this should not be the case
 * - Add some basic styling to separate panels visually when in the same view
 *
 */
/*
 * Basic configuration
 */
var nmsInfoBox = nmsInfoBox || {
	stats: {},
	_container: false, //Container window
	_windowHandler: false, //Window handler
	_sw: false, //Name of last switch opened, used for toggle-click
	_windowTypes: [
	{
		'id': 'switchInfo',
		'title': 'Switch info',
		'views': {
			'initial': {
				'name': 'Summary',
				'panels': ['switchSummary','switchComments']
			},
			'ports': {
				'name': 'Ports',
				'panels': ['switchPorts']
			},
			'misc': {
				'name': 'SNMP',
				'panels': ['switchSNMP:misc']
			},
			'details': {
				'name': 'Settings',
				'panels': ['switchDetails']
			},
			'edit': {
				'name': 'Edit',
				'panels': ['switchEdit']
			}
		}
	},
	{
		'id': 'addSwitch',
		'title': 'Add new switch(es)',
		'views': {
			'initial': {
				'name': 'Add switch',
				'panels': ['switchAdd']
			}
		}
	},
	{
		'id': 'searchHelp',
		'title': 'Search help',
		'views': {
			'initial': {
				'name': 'Search help',
				'panels': ['searchHelp']
			}
		}
	},
	{
		'id': 'inventoryListing',
		'title': 'Inventory listing',
		'views': {
			'initial': {
				'name': 'Distro names',
				'panels': ['inventoryListing:distro_name']
			},
			'sysDescr': {
				'name': 'System description',
				'panels': ['inventoryListing:sysDescr']
			},
			'jnxBoxSerialNo': {
				'name': 'Serial numbers',
				'panels': ['inventoryListing:jnxBoxSerialNo']
			}
		}
	}
	],
	_panelTypes: {} //Populate by using the nmsInfoBox.addPanelType method
};
/*
 * Shows a window, and triggers initial load if needed
 */
nmsInfoBox.showWindow = function (windowName,argument) {
	if(windowName == "switchInfo" && argument != '' && argument == this._sw) {
		nmsInfoBox.hide();
		return;
	}
	if(!this._container)
		this._load();
	if(!windowName)
		windowName = 'switchInfo';
	this._sw = argument;
	this._windowHandler.showWindow(windowName,argument)
	this._container.style.display = "block";
	$(this._windowHandler.getTitleObj()).on('mousedown', function(e) {
		var cont = $(nmsInfoBox._container);
		var dimensions = nmsInfoBox._container.getBoundingClientRect();
		var startLeft = dimensions.left;
		var startTop = dimensions.top;
		nmsInfoBox.startDrag(e.screenX, e.screenY, startLeft, startTop);
	});
	$(this._windowHandler.getTitleObj()).on('mouseup',nmsInfoBox.stopDrag);
};
/*
 * Internal function to load and register the initial html objects
 */
nmsInfoBox._load = function() {
	var infoBox = document.createElement("div");
	infoBox.classList.add("panel", "panel-default");
	var title = document.createElement("div");
	title.id = "info-box-title";
	title.classList.add("panel-heading");
	var nav = document.createElement("div");
	nav.id = "info-box-nav";
	nav.classList.add("panel-body");
	var body = document.createElement("div");
	body.id = "info-box-body";
	body.classList.add("panel-body");
	infoBox.appendChild(title);
	infoBox.appendChild(nav);
	infoBox.appendChild(body);
	this._container = document.getElementById("info-box-container");
	this._container.appendChild(infoBox);
	this._windowHandler = new windowHandler();
	this._windowHandler.setContainerObj(document.getElementById("info-box-container"));
	this._windowHandler.setTitleObj(document.getElementById("info-box-title"));
	this._windowHandler.setBodyObj(document.getElementById("info-box-body"));
	this._windowHandler.setNavObj(document.getElementById("info-box-nav"));
	this._windowHandler.setPanelTypes(this._panelTypes);
	this._windowHandler.setWindowTypes(this._windowTypes);
};
/*
 * Adds a panel type to _panelTypes for usage in windows and views
 */
nmsInfoBox.addPanelType = function (id, obj) {
	this._panelTypes[id] = obj;
};
/*
 * Hide the active window and tell it to unload
 */
nmsInfoBox.hide = function() {
	this._sw = false;
	if (this._windowHandler != undefined && this._windowHandler.hide != undefined) {
		this._windowHandler.hide();
		this._windowHandler.unloadWindow();
	}
	nmsInfoBox.resetDrag();
};
/*
 * Start window drag
 */
nmsInfoBox.startDrag = function(mouseStartX, mouseStartY, startLeft, startTop) {
	document.onmousemove = function(e) {
		var mouseOffsetX = e.screenX - mouseStartX;
		var mouseOffsetY = e.screenY - mouseStartY;
		$(nmsInfoBox._container).css('left', startLeft + mouseOffsetX + 'px');
		$(nmsInfoBox._container).css('top', startTop + mouseOffsetY + 'px');
	};
};
/*
 * Reset drag
 */
nmsInfoBox.resetDrag = function() {
	nmsInfoBox.stopDrag();
	$(nmsInfoBox._container).css('left','');
	$(nmsInfoBox._container).css('top','');
};
/*
 * Stop window drag
 */
nmsInfoBox.stopDrag = function() {
	document.onmousemove = null;
};
/*
 * Click a switch and display it.
 */
nmsInfoBox.click = function(sw)
{
	this.showWindow("switchInfo",sw);
};
/*
 * Window handler
 *
 * Is based on a hierarchy of objects: Window (itself) > Views > Panels. Where
 * any object should not interact with any other directly. Panels are special
 * nmsInfoPanel-objects that handle their own rendering, refreshing, etc. The
 * window handler only makes sure these panels are loaded and unloaded when
 * needed in a window or view.
 *
 * Does primarily rely on an imported list of panel types and window types to
 * display stuff, but has methods for manual overrides if needed.
 *
 * Panels can use the doInPanel(panelId,functionName,arguments) method to pass
 * actions to themselves if needed.
 *
 */
var windowHandler = function () {
	this.containerObj = false;
	this.titleObj = false;
	this.navObj = false;
	this.bodyObj = false;
	this._panels = {};
	this._view = "";
	this._viewId = {};
	this._window = {};
	this.windowTypes = false;
	this.panelTypes = false;
	this.argument = false;
	this.show = function () {
		this.containerObj.classList.remove("hidden");
	};
	this.hide = function () {
		this.containerObj.classList.add("hidden");
	};
	this.setContainerObj = function (containerObj) {
		this.containerObj = containerObj;
	};
	this.setTitleObj = function (titleObj) {
		this.titleObj = titleObj;
	};
	this.getTitleObj = function (titleObj) {
		return this.titleObj;
	};
	this.setNavObj = function (navObj) {
		this.navObj = navObj;
	};
	this.setBodyObj = function (bodyObj) {
		this.bodyObj = bodyObj;
	};
	this.setPanelTypes = function (panelTypes) {
		this.panelTypes = panelTypes;
	};
	this.setWindowTypes = function (windowTypes) {
		this.windowTypes = {};
		for(var i in windowTypes) {
			this.windowTypes[windowTypes[i].id] = windowTypes[i];
		}
	};
	this.setArgument = function (argument) {
		if(this.argument != argument) {
			this.argument = argument;
			this.showView(this._view);
		}
	};
	this.showPanel = function (panelName) {
		var panelArray = panelName.split(":");
		var panelName = panelArray[0];
		var panelMode = panelArray[1];
		if(this.panelTypes[panelName]) {
			var id = (Math.random().toString(36)+'00000000000000000').slice(2, 10+2);
			var panel = new this.panelTypes[panelName];
			panel.setId(id);
			if(!!panelMode)
				panel.setMode(panelMode);
			panel.load(this.bodyObj,this.argument);
			this._panels[id] = panel;
		}
	};
	this.showTitle = function (title) {
		this.titleObj.innerHTML = '× 
' + title + ' ';
	};
	this.showNav = function () {
		if(!this._window.views)
			this.navObj.innerHTML = '';
		var output = '';
		var i = 0;
		for(var view in this._window.views) {
			var viewObj = this._window.views[view];
			var active = '';
			if(this._view == view)
				active = ' class="active" ';
			output += '' + viewObj.name + '   ';
			i++;
		}
		output += ' ';
		if(i < 2) {
			this.navObj.innerHTML = '';
		} else {
			this.navObj.innerHTML = output;
		}
	};
	this.addWindow = function (windowObj) {
		this.windowTypes[windowObj.id] = windowObj;
	};
	this.showWindow = function (windowName,argument) {
		if(!this.windowTypes[windowName])
			return;
		this.unloadWindow();
		this.argument = argument;
		this._window = this.windowTypes[windowName];
		this.showTitle(this._window.title + " " + (argument ? argument : ""));
		this.showView();
		this.show();
	};
	this.showView = function (viewId) {
		if(!viewId || viewId == '') {
			if (this._viewId[this._window.id] == undefined)
				viewId = "initial";
			else
				viewId = this._viewId[this._window.id];
		} else {
			this._viewId[this._window.id] = viewId;
		}
		if(!this._window.views || !this._window.views[viewId])
			return;
		this.unloadView();
		for(var panel in this._window.views[viewId].panels) {
			this.showPanel(this._window.views[viewId].panels[panel]);
		}
		this._view = viewId;
		this.showNav();
	};
	this.removePanel = function (panelId) {
		if(!!panelId)
			this.unloadPanel(panelId);
	};
	this.unloadView = function () {
		this.unloadPanel();
	};
	this.unloadWindow = function() {
		this.hide();
		this.unloadPanel();
	};
	this.unloadPanel = function (panelId) {
		if(!panelId) {
			for(var i in this._panels) {
				this._panels[i].unload();
			}
			this._panels = {};
		} else {
			try {
				this._panels[panelId].unload();
			} catch (e) {}
			delete this._panels[panelId];
		}
	};
	//Method for triggering a function in an active panel
	this.doInPanel = function (panelId, action, argument) {
		if(!this._panels[panelId])
			return;
		if(typeof this._panels[panelId][action] === "function") {
			if(!argument) {
				this._panels[panelId][action]();
			} else {
				this._panels[panelId][action](argument);
			}
		}
	};
};
/*
 * Basic info-panel object
 *
 * Has a basic model of a stand alone panel which is intended to be extended
 * upon to implement specific panels:
 *
 * var myNewPanel = function () {
 *	nmsInfoPanel.call(this,"myNewPanelId");
 *	this.refresh = function (reason) {
 *		//My custom window function
 *		this._render(htmlObj);
 *	};
 * };
 *
 */
var nmsInfoPanel = function nmsInfoPanel(name,id) {
	this.name = name;
	this.id = id;
	this.sw = false;
	this.container = false;
	this.classList = ['info-panel'];
	this.me = false;
	this.handlers = [];
	this.mode = "initial";
	if(!this.id)
		this.id = (Math.random().toString(36)+'00000000000000000').slice(2, 10+2);
	//Method for loading the general panel properties
	this.load = function (container,argument) {
		this.container = container;
		this.sw = argument;
		this.me = document.createElement("div");
		this.me.id = this.id;
		for(var i in this.classList)
			this.me.classList.add(this.classList[i]);
		this.container.appendChild(this.me);
		this.init(argument);
	};
	//Method for making this specific panel-instance ready for first refresh
	//Override in children when any custom init-functionality is needed
	this.init = function (argument) {
		this.refresh("init");
	};
	//Methods for getting and setting panel id
	this.setId = function (id) {
		this.id = id;
	};
	this.getId = function () {
		return this.id;
	};
	//Methods for setting and getting mode (default "initial")
	this.setMode = function (mode) {
		this.mode = mode;
	};
	this.getMode = function () {
		return this.mode;
	};
	//Internal method for rendering content
	this._render = function (newContent) {
		if(!newContent || !this.me)
			return;
		this.me.innerHTML = newContent.outerHTML;
	};
	//Helper method for rendering error messages in a unified way
	this._renderError = function (message) {
		var error = document.createElement("div");
		error.className = "alert alert-info";
		error.innerHTML = message;
		this._render(error);
	};
	//Method for unloading any local data
	this.unload = function () {
		if(!this.me)
			return;
		this.me.remove();
		this.removeHandlers();
		this.sw = false;
		this.container = false;
		this.me = false;
		this.id = false;
	};
	//Method for loading new data and triggering a _render if needed
	//Implemented in children only
	this.refresh = function (reason) {};
	//Methods for adding and removing classes
	this.addClass = function (className) {
		if(this.classList.indexOf(className) == -1) {
			this.classList.push(className);
			this.me.classList.add(className);
		}
	};
	this.removeClass = function (className) {
		var index = this.classList.indexOf(className);
		if(index != -1) {
			this.classList.splice(index,1);
			this.me.classList.remove(className);
		}
	};
	//Method for registering a data handler (lets us clean them up later)
	this.addHandler = function (dataType,targetFunction) {
		if(!targetFunction)
			targetFunction = "refresh";
		nmsData.addHandler(dataType,this.id,(this[targetFunction]).bind(this),"handler-"+dataType);
		this.handlers.push(dataType);
	};
	//Method for removing all handlers we have registered
	this.removeHandlers = function () {
		for(var i in this.handlers) {
			nmsData.unregisterHandler(this.handlers[i],this.id);
		}
	};
	//Method for checking if we have handlers registered
	this.hasHandler = function (dataType) {
		for(var i in this.handlers) {
			if(this.handlers[i] == dataType)
				return true;
		}
		return false;
	};
};
/*
 * Panel type: Switch SNMP information
 *
 * Displays a list of available SNMP data from a set SNMP-group (Ex. "Ports", "Misc")
 *
 * TODO: Clean up html-generator code.
 *
 */
var switchSNMPPanel = function () {
	nmsInfoPanel.call(this,"switchSNMP");
	this.init = function() {
		this.refresh();
	};
	this.refresh = function(reason) {
		var domObj = document.createElement("div");
		domObj.classList.add("panel-group");
		try {
			var snmpJson = nmsData.snmp.snmp[this.sw][this.mode];
		} catch(e) {
			this._renderError("Waiting for data.");
			return;
		}
		for(var obj in snmpJson) {
			var cleanObj = obj.replace(/\W+/g, "");
			var groupObj = document.createElement("div");
			groupObj.classList.add("panel","panel-default","nms-panel-small");
			groupObj.innerHTML = '' + obj + ' ';
			var groupObjCollapse = document.createElement("div");
			groupObjCollapse.id = cleanObj + "-group";
			groupObjCollapse.classList.add("collapse");
			var panelBodyObj = document.createElement("div");
			panelBodyObj.classList.add("panel-body");
			var tableObj = document.createElement("table");
			tableObj.classList.add("table","table-condensed");
			var tbody = document.createElement("tbody");
			for(var prop in snmpJson[obj]) {
				var propObj = document.createElement("tr");
				propObj.innerHTML = '' + prop + ' ' + snmpJson[obj][prop] + ' ';
				tbody.appendChild(propObj);
			}
			tableObj.appendChild(tbody);
			panelBodyObj.appendChild(tableObj);
			groupObjCollapse.appendChild(panelBodyObj);
			groupObj.appendChild(groupObjCollapse);
			domObj.appendChild(groupObj);
		}
		this._render(domObj);
	};
};
nmsInfoBox.addPanelType("switchSNMP",switchSNMPPanel);
var switchPortsPanel = function () {
	nmsInfoPanel.call(this,"switchPorts");
	this.init = function() {
		this.refresh();
	};
	this.refresh = function(reason) {
		var domObj = document.createElement("div");
		domObj.classList.add("panel-group");
		try {
			var snmpOldJson;
			var snmpJson = nmsData.snmp.snmp[this.sw]['ports'];
			if (nmsData.old.snmp != undefined) {
				snmpOldJson = nmsData.old.snmp.snmp[this.sw]['ports'];
			}
		} catch(e) {
			this._renderError("Waiting for data.");
			return;
		}
		var img = document.createElement("img");
		var i = "totals";
		img.src = '/render/?title=' + this.sw + ' totals&width=600&from=-12h&target=cactiStyle(group(aliasByMetric(perSecond(sum(snmp.' + this.sw + '.*.ifHCOutOctets))),aliasByMetric(perSecond(sum(snmp.' + this.sw + '.*.ifHCInOctets)))),"binary")' + nmsInfoBox._graphDefaults();
		img.classList.add("graph");
		var expanderButton = document.createElement("a");
		expanderButton.innerHTML = "Toggle all";
		expanderButton.setAttribute("onclick","$('.collapse-top').collapse('toggle');");
		expanderButton.setAttribute("role","button");
		domObj.appendChild(img);
		domObj.appendChild(expanderButton);
		var indicies = [];
		for (var obj in snmpJson) {
			indicies.push(obj);
		}
		indicies.sort(function(a,b) {
			return snmpJson[a].ifIndex - snmpJson[b].ifIndex;
		});
		for(var obji in indicies) {
			var obj = indicies[obji];
			var cleanObj = obj.replace(/\W+/g, "");
			var groupObj = document.createElement("div");
			groupObj.classList.add("panel","panel-default","nms-panel-small");
			var glyphicon = "glyphicon-remove";
			var button = "btn-danger";
			var title = "link down";
			if (snmpJson[obj].ifOperStatus == "up") {
				glyphicon = "glyphicon-ok";
				button = "btn-success";
				title = "link up";
			}
			if (snmpJson[obj].ifAdminStatus == "down") {
				glyphicon = "glyphicon-ban-circle";
				title = "admin down";
				button = "btn-info";
			}
			var traffic = "";
			try {
				var nowin = parseInt(snmpJson[obj].ifHCInOctets);
				var nowout = parseInt(snmpJson[obj].ifHCOutOctets);
				if (!isNaN(nowin) && !isNaN(nowout)) {
					traffic = "" + byteCount(nowin) + "B in | " + byteCount(nowout) + "B out  ";
				}
			} catch(e) {};
			groupObj.innerHTML = '' + snmpJson[obj].ifDescr + '  ' + snmpJson[obj].ifAlias + ' ' + traffic + '  ';
			var groupObjCollapse = document.createElement("div");
			groupObjCollapse.id = cleanObj + "-group";
			groupObjCollapse.classList.add("collapse");
			groupObjCollapse.classList.add("collapse-top");
			var panelBodyObj = document.createElement("div");
			panelBodyObj.classList.add("panel-body");
			var tableObj = document.createElement("table");
			tableObj.classList.add("table","table-condensed");
			var tbody = document.createElement("tbody");
			var props = [];
			for (var prop in snmpJson[obj]) {
				props.push(prop);
			}
			props.sort();
			for(var index in props) {
				var prop = props[index];
				var propObj = document.createElement("tr");
				var append = "";
				var value = snmpJson[obj][prop];
				if (!isNaN(parseInt(value))) {
					append = byteCount(value, 2);
					if (append != value)
						append = " (" + append + ")";
					else
						append = "";
				}
				propObj.innerHTML = '' + prop + ' ' + value + append + ' ';
				tbody.appendChild(propObj);
			}
			tableObj.appendChild(tbody);
			if (snmpJson[obj].ifHCInOctets != 0 
			 || snmpJson[obj].ifHCOutOctets != 0) {
				var img = document.createElement("img");
				img.src = '/render/?width=600&from=-12h&target=cactiStyle(aliasByMetric(perSecond(snmp.' + this.sw + '.' + obj + '.{ifHCOutOctets,ifHCInOctets})),"binary")&target=cactiStyle(aliasByMetric(secondYAxis(snmp.' + this.sw + '.' + obj + '.{ifInDiscards,ifInErrors,ifOutDiscards,ifOutErrors})),"binary")' + nmsInfoBox._graphDefaults();
				img.classList.add("graph");
				panelBodyObj.appendChild(img);
			}
			var tableTopObj = document.createElement("div");
			tableTopObj.innerHTML = 'Details  ';
			var tableTopObjCollapse = document.createElement("div");
			tableTopObjCollapse.id = cleanObj + "-table-group";
			tableTopObjCollapse.classList.add("collapse");
			tableTopObjCollapse.classList.add("collapse-detail");
			tableTopObjCollapse.appendChild(tableObj);
			tableTopObj.appendChild(tableTopObjCollapse);
			panelBodyObj.appendChild(tableTopObj);
			//panelBodyObj.appendChild(tableObj);
			groupObjCollapse.appendChild(panelBodyObj);
			groupObj.appendChild(groupObjCollapse);
			domObj.appendChild(groupObj);
		}
		this._render(domObj);
	};
};
nmsInfoBox.addPanelType("switchPorts",switchPortsPanel);
/*
 * Panel type: Switch details
 *
 * Displays a table of switch information
 *
 */
var switchDetailsPanel = function() {
	nmsInfoPanel.call(this,"switchDetails");
	this.refresh = function(reason) {
		var swi = [];
		var swm = [];
		try {
			swi = nmsData.switches["switches"][this.sw];
		} catch(e) {}
		try {
			swm = nmsData.smanagement.switches[this.sw];
		} catch(e) {}
		var content = [];
		for (var v in swi) {
			if (v == "placement") {
				var place = JSON.stringify(swi[v]);
				content.push([v,place]);
				continue;
			}
			content.push([v, swi[v]]);
		}
		for (var v in swm) {
			content.push([v, swm[v]]);
		}
		content.sort();
		var infotable = nmsInfoBox._makeTable(content);
		this._render(infotable);
	};
};
nmsInfoBox.addPanelType("switchDetails",switchDetailsPanel);
var searchHelpPanel = function() {
	nmsInfoPanel.call(this,"searchHelp");
	this.refresh = function(reason) {
		var x = document.createElement("div");
		var searchHelp = nmsSearch.helpText;
		for (var a in searchHelp) {
			var c = document.createElement("p");
			c.innerText = searchHelp[a];
			x.appendChild(c);
		}
		this._render(x);
	};
};
nmsInfoBox.addPanelType("searchHelp",searchHelpPanel);
/*
 * Panel type: Add switch
 *
 * Lets you add a new switch using the switch-add api
 *
 */
var switchAddPanel = function() {
	nmsInfoPanel.call(this,"switchAdd");
	this.refresh = function(reason) {
		var domObj = document.createElement("div");
		domObj.innerHTML = 'Add switch '
		this._render(domObj);
	};
	this.save = function () {
		var sysname = document.getElementById('create-sysname').value.split(" ");
		var myData = [];
		for (var v in sysname) {
			myData.push({"sysname":sysname[v]});
		}
		var myData = JSON.stringify(myData);
		$.ajax({
			type: "POST",
			url: "/api/write/switch-add",
			dataType: "text",
			data:myData,
			success: function (data, textStatus, jqXHR) {
				var result = JSON.parse(data);
				if(result.switches_addded.length > 0) { // FIXME unresolved variable switches_addded
					nmsInfoBox.hide();
				}
				nmsData.invalidate("switches");
				nmsData.invalidate("smanagement");
			}
		});
	};
};
nmsInfoBox.addPanelType("switchAdd",switchAddPanel);
/*
 * Panel type: Inventory listing
 *
 * Displays a filterable table with switch data, based on a selected mode
 *
 * TODO:
 * - Add support for multiple columns with data
 * - Add sorting
 * - Add live filtering
 * - Add export options?
 *
 */
var inventoryListingPanel = function() {
	nmsInfoPanel.call(this,"inventoryListing");
	this.filter = "";
	this.init = function (mode) {
		if(!nmsData.snmp || !nmsData.snmp.snmp) {
			if(!this.hasHandler("snmp")) {
				this.addHandler("snmp","init");
				this._renderError("Waiting for SNMP data.");
			}
			return;
		} else {
			this.removeHandlers();
			if(!!mode && this.mode == "initial")
				this.setMode(mode);
			this.refresh("init");
		}
	};
	this.setFilter = function (filter) {
		this.filter = filter;
		this.refresh();
	};
	this.refresh = function (reason) {
		var targetArray = [];
		var listTitle = '';
		var contentObj = document.createElement("div");
		var inputObj = document.createElement("div");
		inputObj.innerHTML = 'Filtrer 
';
		contentObj.appendChild(inputObj);
		switch (this.mode) {
			case 'distro_name':
				listTitle = 'Distro names';
				break;
			case 'sysDescr':
				listTitle = 'System description';
				break;
			case 'jnxBoxSerialNo':
				listTitle = 'Serial Numbers';
				break;
			default:
				listTitle = 'Distro names';
		}
		var resultArray = [];
		for(var sw in nmsData.switches.switches) {
			var value = '';
			if(this.filter != '') {
				if(sw.toLowerCase().indexOf(this.filter) == -1 && !nmsSearch.searchTest(this.filter,sw))
					continue;
			}
			try {
				switch (this.mode) {
					case 'distro_name':
						value = nmsData.switches.switches[sw]["distro_name"];
						break;
					case 'sysDescr':
						value = nmsData.snmp.snmp[sw]["misc"]["sysDescr"][0];
						break;
					case 'jnxBoxSerialNo':
						value = nmsData.snmp.snmp[sw]["misc"]["jnxBoxSerialNo"][0];
						break;
				}
			} catch (e) {}
			resultArray.push([sw, value]);
		}
		resultArray.sort();
		var infotable = nmsInfoBox._makeTable(resultArray,listTitle);
		infotable.id = "inventory-table";
		contentObj.appendChild(infotable);
		this._render(contentObj);
	};
};
nmsInfoBox.addPanelType("inventoryListing",inventoryListingPanel);
/*
 * Panel type: Edit switch
 *
 * Lets you edit basic switch and switch management data through the switch-update api
 *
 */
var switchEditPanel = function () {
	nmsInfoPanel.call(this,"switchEdit");
	this.refresh = function (reason) {
		var swi = [];
		var swm = [];
		try {
			swi = nmsData.switches["switches"][this.sw];
		} catch(e) {}
		try {
			swm = nmsData.smanagement.switches[this.sw];
		} catch(e) {}
		var domObj = document.createElement("div");
		var template = {};
		nmsInfoBox._editValues = {};
		var place;
		var tags;
		for (var v in swi) {
			if (v == "placement") {
				place = JSON.stringify(swi[v]);
				template[v] = place;
				continue;
			}
			if (v == "tags") {
				tags = JSON.stringify(swi[v]);
				template[v] = tags;
				continue;
			}
			template[v] = nmsInfoBox._nullBlank(swi[v]);
		}
		for (var v in swm) {
			template[v] = nmsInfoBox._nullBlank(swm[v]);
		}
		var content = [];
		for (v in template) {
			var tmpsw = '\'' + this.sw + '\'';
			var tmpv = '\'' + v + '\'';
			var tmphandler = '"nmsInfoBox._editChange(' + tmpsw + ',' + tmpv + ');"';
			var html = ' ';
			content.push([v, html]);
		}
		content.sort();
		var table = nmsInfoBox._makeTable(content);
		domObj.appendChild(table);
		var outputCont = document.createElement("div");
		outputCont.id = "edit-output-cont";
		outputCont.classList.add("collapse");
		outputCont.innerHTML = "Request preview ";
		var output = document.createElement("output");
		output.id = "edit-output";
		outputCont.appendChild(output);
		domObj.appendChild(outputCont);
		var nav = document.createElement("nav");
		nav.classList.add("nav","nav-pills");
		var submit = document.createElement("button");
		submit.innerHTML = "Save changes";
		submit.classList.add("btn", "btn-primary");
		submit.id = "edit-submit-" + this.sw;
		submit.setAttribute("onclick","nmsInfoBox._windowHandler.doInPanel('" + this.id + "','save');");
		nav.appendChild(submit);
		var toggleDetails = document.createElement("button");
		toggleDetails.innerHTML = '';
		toggleDetails.classList.add("btn", "btn-default", "pull-right");
		toggleDetails.dataset.toggle = "collapse";
		toggleDetails.dataset.target = "#edit-output-cont";
		toggleDetails.title = "Show request preview";
		toggleDetails.id = "edit-toggle-details-" + this.sw;
		nav.appendChild(toggleDetails);
		domObj.appendChild(nav);
		this._render(domObj);
		if (place) {
			var pval = document.getElementById("edit-" + this.sw + "-placement");
			if (pval) {
				pval.value = place;
			}
		}
		if (tags) {
			var ptags = document.getElementById("edit-" + this.sw + "-tags");
			if (ptags) {
				ptags.value = tags;
			}
		}
	};
	this.save = function () {
		var myData = nmsInfoBox._editStringify(this.sw);
		$.ajax({
			type: "POST",
			url: "/api/write/switch-update",
			dataType: "text",
			data:myData,
			success: function (data, textStatus, jqXHR) {
				var result = JSON.parse(data);
				if(result.switches_updated.length > 0) { // FIXME unresolved variable switches_addded
					nmsInfoBox.hide();
				}
				nmsData.invalidate("switches");
				nmsData.invalidate("smanagement");
			}
		});
	};
};
nmsInfoBox.addPanelType("switchEdit",switchEditPanel);
/*
 * Panel type: Switch comments
 *
 * Displays the current comments and lets you interact with them or add new ones
 *
 */
var switchCommentsPanel = function () {
	nmsInfoPanel.call(this,"switchComments");
	this.commentsHash = false;
	this.refresh = function (reason) {
		var domObj = document.createElement("div");
		var comments = [];
		var logs = nmsOplog.getSwitchLogs(this.sw);
		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 cap = document.createElement("caption");
		cap.textContent = "Relevant log entries";
		table.appendChild(cap);
		for (var v in logs) {
			tr = table.insertRow(-1);
			tr.className = 
				td1 = tr.insertCell(0);
			td2 = tr.insertCell(1);
			var date = new Date(logs[v]['timestamp']);
			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;
			td1.classList.add("left");
			td2.textContent = "[" + logs[v]['username'] + "] " + logs[v]['log'];
		}
		domObj.appendChild(table);
		this._render(domObj);
	};
};
nmsInfoBox.addPanelType("switchComments",switchCommentsPanel);
/*
 * Panel type: Switch summary
 *
 * Display a live summary of key metrics
 *
 */
var switchSummaryPanel = function() {
	nmsInfoPanel.call(this,"switchSummary");
	this.init = function() {
		this.addHandler("ticker");
		this.refresh();
	};
	this.refresh = function(reason) {
		var content = [];
		if (this.sw == false) {
			console.log("ugh, cleanup failed?");
			return;
		}
		var topper = document.createElement("div");
		for ( var h in handlers ) {
			if (handlers[h].getInfo != undefined) {
				var tmp = handlers[h].getInfo(this.sw);
				for (var x in tmp.data) {
					if (tmp.data[x].value != undefined) {
						var d = "" + tmp.data[x].value + '
';
						content.push([tmp.data[x].description, d]);
					}
				}
			}
		}
		var contentCleaned = [];
		for(var i in content) {
			if(content[i][1] == '' || content[i][1] == null)
				continue;
			contentCleaned.push(content[i]);
		}
		var table = nmsInfoBox._makeTable(contentCleaned);
		var latency = document.createElement("img");
		latency.src = '/render/?height=240&width=600&target=alias(movingAverage(ping.' + this.sw + '.ipv4,60),"Latency (ms)")&target=alias(secondYAxis(perSecond(sum(snmp.' + this.sw + '.*.{ifInDiscards,ifInErrors}))),"Input errors and discards")&target=alias(secondYAxis(perSecond(sum(snmp.' + this.sw + '.*.{ifOutDiscards,ifOutErrors}))),"Output errors and discards")' + nmsInfoBox._graphDefaults("Click to cycle");
		latency.classList.add("graph");
		
		latency.setAttribute("onclick","nmsInfoBox._graphZoom();");
		latency.setAttribute("role","button");
		topper.appendChild(latency);
		topper.appendChild(table);
		this._render(topper);
	};
};
nmsInfoBox.setLegendPick = function(tag,id) {
	if (nms.legendPick != undefined) {
		if (nms.legendPick.handler == tag && nms.legendPick.idx == id) {
			nms.legendPick = undefined;
			return;
		}
	}
	nms.legendPick = {handler: tag, idx: id};
}
nmsInfoBox.addPanelType("switchSummary",switchSummaryPanel);
/*
 * General-purpose table-maker?
 *
 * Takes an array of arrays as input, and an optional caption.
 *
 * E.g.: _makeTable([["name","Kjell"],["Age","five"]], "Age list");
 */
nmsInfoBox._makeTable = function(content, caption) {
	var table = document.createElement("table");
	var tr;
	var td1;
	var td2;
	table.className = "table";
	table.classList.add("table");
	table.classList.add("table-condensed");
	if (caption != undefined) {
		var cap = document.createElement("caption");
		cap.textContent = caption;
		table.appendChild(cap);
	}
	for (var v in content) {
		tr = table.insertRow(-1);
		tr.className = content[v][0].toLowerCase().replace(/[^a-z0-9_]/g,"");
		td1 = tr.insertCell(0);
		td1.classList.add("left");
		td2 = tr.insertCell(1);
		td1.innerHTML = content[v][0];
		td2.innerHTML = content[v][1];
	}
	return table;
};
nmsInfoBox._nullBlank = function(x) {
	if (x == null || x == false || x == undefined)
		return "";
	return x;
};
/*
 * Provide common defaults for graph renders.
 *
 * Kept on the URL to avoid having to manage templates since we need to
 * manage night mode anyway for now.
 */
nmsInfoBox._graphZoom = function() {
	if (nmsInfoBox._graphFrom == "-60min") {
		nmsInfoBox._graphFrom = "-6h";
	} else if (nmsInfoBox._graphFrom == "-6h") {
		nmsInfoBox._graphFrom = "-24h";
	} else if (nmsInfoBox._graphFrom == "-24h") {
		nmsInfoBox._graphFrom = "-7days";
	} else {
		nmsInfoBox._graphFrom = "-60min";
	}
}
nmsInfoBox._graphFrom = "-60min";
nmsInfoBox._graphUntil = "now";
nmsInfoBox._graphDefaults = function(title) {
	// Copied from nms-color-util.js. Could do with expanding.
	var colorlist = "337ab7,5cb85c,5bc0de,f0ad4e,d9534f,ffffff";
	if (title != undefined) {
		title = "From " + nmsInfoBox._graphFrom + " until " + nmsInfoBox._graphUntil + " (" + title + ")";
	} else {
		title = "From " + nmsInfoBox._graphFrom + " until " + nmsInfoBox._graphUntil;
	}
	var base = "&yUnitSystem=binary&format=svg&colorList=" + colorlist + "&from=" + nmsInfoBox._graphFrom + "&until=" + nmsInfoBox._graphUntil + '&title=' + title;
	if (nms.nightMode) {
		return "&bgcolor=222222&fgcolor=dddddd&minorGridLineColor=%233d3d3d&majorGridLineColor=%23666666" + base;
	} else {
		return base;
	}
}
nmsInfoBox._editChange = function(sw, v) {
	var el = document.getElementById("edit-" + sw + "-" + v);
	var val = el.value;
	if (v == "placement") {
		try {
			val = JSON.parse(val);
			el.parentElement.classList.remove("has-error");
			el.parentElement.classList.add("has-success");
		} catch (e) {
			el.parentElement.classList.add("has-error");
			return;
		}
	}
	nmsInfoBox._editValues[v] = val;
	el.classList.add("has-warning");
	var myData = nmsInfoBox._editStringify(sw);
	var out = document.getElementById("edit-output");
	out.value = myData;
};
nmsInfoBox._editStringify = function(sw) {
	nmsInfoBox._editValues['sysname'] = sw;
	return JSON.stringify([nmsInfoBox._editValues]);
};