ring topology completely factored out
diff --git a/web/ons-demo/js/constants.js b/web/ons-demo/js/constants.js
index 7431bba..0ef0882 100644
--- a/web/ons-demo/js/constants.js
+++ b/web/ons-demo/js/constants.js
@@ -18,4 +18,13 @@
 //	'color11',
 	'color12'
 ];
-colors.reverse();
\ No newline at end of file
+colors.reverse();
+
+/***************************************************************************************************
+Widths of each switch type
+***************************************************************************************************/
+var widths = {
+	edge: 6,
+	aggregation: 12,
+	core: 18
+}
\ No newline at end of file
diff --git a/web/ons-demo/js/forward.js b/web/ons-demo/js/forward.js
index 9c4d03f..5e854ee 100644
--- a/web/ons-demo/js/forward.js
+++ b/web/ons-demo/js/forward.js
@@ -3,3 +3,10 @@
 ***************************************************************************************************/
 
 var updateTopology;
+
+
+/***************************************************************************************************
+defined by whichever topology module is included
+***************************************************************************************************/
+var createTopologyView;
+var drawTopology;
diff --git a/web/ons-demo/js/globals.js b/web/ons-demo/js/globals.js
index c7cf5bb..f0922a6 100644
--- a/web/ons-demo/js/globals.js
+++ b/web/ons-demo/js/globals.js
@@ -13,7 +13,6 @@
 ***************************************************************************************************/
 var modelString;
 
-
 /***************************************************************************************************
 the svg element for the topology view
 ***************************************************************************************************/
diff --git a/web/ons-demo/js/rings.js b/web/ons-demo/js/rings.js
index 5141195..ec6d59f 100644
--- a/web/ons-demo/js/rings.js
+++ b/web/ons-demo/js/rings.js
@@ -1,4 +1,6 @@
-function createTopologyView() {
+(function () {
+
+createTopologyView = function () {
 
 	window.addEventListener('resize', function () {
 		// this is too slow. instead detect first resize event and hide the paths that have explicit matrix applied
@@ -22,13 +24,7 @@
 			attr('id', 'viewbox').append('svg:g').attr('transform', 'translate(500 500)');
 }
 
-var widths = {
-	edge: 6,
-	aggregation: 12,
-	core: 18
-}
-
-function createTopologyModel(model) {
+function createRingTopologyModel(model) {
 	var rings = [{
 		radius: 3,
 		width: widths.edge,
@@ -123,4 +119,164 @@
 
 //	return rings;
 	return testRings;
-}
\ No newline at end of file
+}
+
+drawTopology = function () {
+	// DRAW THE SWITCHES
+	var rings = svg.selectAll('.ring').data(createRingTopologyModel(model));
+
+	function ringEnter(data, i) {
+		if (!data.length) {
+			return;
+		}
+
+		// create the nodes
+		var nodes = d3.select(this).selectAll("g")
+			.data(data, function (data) {
+				return data.dpid;
+			})
+			.enter().append("svg:g")
+			.attr("id", function (data, i) {
+				return data.dpid;
+			})
+			.attr("transform", function(data, i) {
+				return "rotate(" + data.angle+ ")translate(" + data.radius * 150 + ")rotate(" + (-data.angle) + ")";
+			});
+
+		// add the cirles representing the switches
+		nodes.append("svg:circle")
+			.attr("transform", function(data, i) {
+				var m = document.querySelector('#viewbox').getTransformToElement().inverse();
+				if (data.scale) {
+					m = m.scale(data.scale);
+				}
+				return "matrix( " + m.a + " " + m.b + " " + m.c + " " + m.d + " " + m.e + " " + m.f + " )";
+			})
+			.attr("x", function (data) {
+				return -data.width / 2;
+			})
+			.attr("y", function (data) {
+				return -data.width / 2;
+			})
+			.attr("r", function (data) {
+				return data.width;
+			});
+	}
+
+	// append switches
+	rings.enter().append("svg:g")
+		.attr("class", "ring")
+		.each(ringEnter);
+
+
+	function ringUpdate(data, i) {
+		var nodes = d3.select(this).selectAll("g")
+			.data(data, function (data) {
+				return data.dpid;
+			});
+		nodes.select('circle')
+			.each(function (data) {
+				// if there's a pending state changed and then the state changes, clear the pending class
+				var circle = d3.select(this);
+				if (data.state === 'ACTIVE' && circle.classed('inactive') ||
+					data.state === 'INACTIVE' && circle.classed('active')) {
+					circle.classed('pending', false);
+				}
+			})
+			.attr('class', function (data)  {
+				if (data.state === 'ACTIVE' && data.controller) {
+					return data.className + ' active ' + controllerColorMap[data.controller];
+				} else {
+					return data.className + ' inactive ' + 'colorInactive';
+				}
+			});
+	}
+
+	// update  switches
+	rings.each(ringUpdate);
+
+
+	// Now setup the labels
+	// This is done separately because SVG draws in node order and we want the labels
+	// always on top
+	var labelRings = svg.selectAll('.labelRing').data(createRingTopologyModel(model));
+
+	function labelRingEnter(data) {
+		if (!data.length) {
+			return;
+		}
+
+		// create the nodes
+		var nodes = d3.select(this).selectAll("g")
+			.data(data, function (data) {
+				return data.dpid;
+			})
+			.enter().append("svg:g")
+			.classed('nolabel', true)
+			.attr("id", function (data) {
+				return data.dpid + '-label';
+			})
+			.attr("transform", function(data, i) {
+				return "rotate(" + data.angle+ ")translate(" + data.radius * 150 + ")rotate(" + (-data.angle) + ")";
+			})
+
+		// add the text nodes which show on mouse over
+		nodes.append("svg:text")
+				.text(function (data) {return data.dpid;})
+				.attr("x", function (data) {
+					if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
+						if (data.className == 'edge') {
+							return - data.width*3 - 4;
+						} else {
+							return - data.width - 4;
+						}
+					} else {
+						if (data.className == 'edge') {
+							return data.width*3 + 4;
+						} else {
+							return data.width + 4;
+						}
+					}
+				})
+				.attr("y", function (data) {
+					var y;
+					if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
+						if (data.className == 'edge') {
+							y = data.width*3/2 + 4;
+						} else {
+							y = data.width/2 + 4;
+						}
+					} else {
+						if (data.className == 'edge') {
+							y = data.width*3/2 + 4;
+						} else {
+							y = data.width/2 + 4;
+						}
+					}
+					return y - 6;
+				})
+				.attr("text-anchor", function (data) {
+					if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
+						return "end";
+					} else {
+						return "start";
+					}
+				})
+				.attr("transform", function(data) {
+					var m = document.querySelector('#viewbox').getTransformToElement().inverse();
+					if (data.scale) {
+						m = m.scale(data.scale);
+					}
+					return "matrix( " + m.a + " " + m.b + " " + m.c + " " + m.d + " " + m.e + " " + m.f + " )";
+				})
+	}
+
+	labelRings.enter().append("svg:g")
+		.attr("class", "textRing")
+		.each(labelRingEnter);
+
+	// switches should not change during operation of the ui so no
+	// rings.exit()
+}
+
+})();
diff --git a/web/ons-demo/js/topology.js b/web/ons-demo/js/topology.js
index e934972..a2a69ae 100644
--- a/web/ons-demo/js/topology.js
+++ b/web/ons-demo/js/topology.js
@@ -6,233 +6,19 @@
 
 (function () {
 
-updateTopology = function() {
-
-	// DRAW THE SWITCHES
-	var rings = svg.selectAll('.ring').data(createTopologyModel(model));
-
-	function ringEnter(data, i) {
-		if (!data.length) {
-			return;
-		}
-
-		// create the nodes
-		var nodes = d3.select(this).selectAll("g")
-			.data(data, function (data) {
-				return data.dpid;
-			})
-			.enter().append("svg:g")
-			.attr("id", function (data, i) {
-				return data.dpid;
-			})
-			.attr("transform", function(data, i) {
-				return "rotate(" + data.angle+ ")translate(" + data.radius * 150 + ")rotate(" + (-data.angle) + ")";
-			});
-
-		// add the cirles representing the switches
-		nodes.append("svg:circle")
-			.attr("transform", function(data, i) {
-				var m = document.querySelector('#viewbox').getTransformToElement().inverse();
-				if (data.scale) {
-					m = m.scale(data.scale);
-				}
-				return "matrix( " + m.a + " " + m.b + " " + m.c + " " + m.d + " " + m.e + " " + m.f + " )";
-			})
-			.attr("x", function (data) {
-				return -data.width / 2;
-			})
-			.attr("y", function (data) {
-				return -data.width / 2;
-			})
-			.attr("r", function (data) {
-				return data.width;
-			});
-
-		// setup the mouseover behaviors
-		nodes.on('mouseover', mouseOverSwitch);
-		nodes.on('mouseout', mouseOutSwitch);
-		nodes.on('mouseup', mouseUpSwitch);
-		nodes.on('mousedown', mouseDownSwitch);
-
-		// only do switch up/down for core switches
-		if (i == 2) {
-			nodes.on('dblclick', doubleClickSwitch);
-		}
-	}
-
-	// append switches
-	rings.enter().append("svg:g")
-		.attr("class", "ring")
-		.each(ringEnter);
-
-
-	function ringUpdate(data, i) {
-		var nodes = d3.select(this).selectAll("g")
-			.data(data, function (data) {
-				return data.dpid;
-			});
-		nodes.select('circle')
-			.each(function (data) {
-				// if there's a pending state changed and then the state changes, clear the pending class
-				var circle = d3.select(this);
-				if (data.state === 'ACTIVE' && circle.classed('inactive') ||
-					data.state === 'INACTIVE' && circle.classed('active')) {
-					circle.classed('pending', false);
-				}
-			})
-			.attr('class', function (data)  {
-				if (data.state === 'ACTIVE' && data.controller) {
-					return data.className + ' active ' + controllerColorMap[data.controller];
-				} else {
-					return data.className + ' inactive ' + 'colorInactive';
-				}
-			});
-	}
-
-	// update  switches
-	rings.each(ringUpdate);
-
-
-	// Now setup the labels
-	// This is done separately because SVG draws in node order and we want the labels
-	// always on top
-	var labelRings = svg.selectAll('.labelRing').data(createTopologyModel(model));
-
-	d3.select(document.body).on('mousemove', function () {
-		if (!d3.select('#topology').classed('linking')) {
-			return;
-		}
-		var linkVector = document.getElementById('linkVector');
-		if (!linkVector) {
-			return;
-		}
-		linkVector = d3.select(linkVector);
-
-		var highlighted = svg.selectAll('.highlight')[0];
-		var s1 = null, s2 = null;
-		if (highlighted.length > 1) {
-			var s1 = d3.select(highlighted[0]);
-			var s2 = d3.select(highlighted[1]);
-
-		} else if (highlighted.length > 0) {
-			var s1 = d3.select(highlighted[0]);
-		}
-		var src = s1;
-		if (s2 && !s2.data()[0].target) {
-			src = s2;
-		}
-		if (src) {
-			linkVector.attr('d', function () {
-					var srcPt = document.querySelector('svg').createSVGPoint();
-					srcPt.x = src.attr('x');
-					srcPt.y = src.attr('y');
-					srcPt = srcPt.matrixTransform(src[0][0].getCTM());
-
-					var svg = document.getElementById('topology');
-					var mouse = d3.mouse(viewbox);
-					var dstPt = document.querySelector('svg').createSVGPoint();
-					dstPt.x = mouse[0];
-					dstPt.y = mouse[1];
-					dstPt = dstPt.matrixTransform(viewbox.getCTM());
-
-					return line([srcPt, dstPt]);
-				});
-		}
-	});
-
-	function labelRingEnter(data) {
-		if (!data.length) {
-			return;
-		}
-
-		// create the nodes
-		var nodes = d3.select(this).selectAll("g")
-			.data(data, function (data) {
-				return data.dpid;
-			})
-			.enter().append("svg:g")
-			.classed('nolabel', true)
-			.attr("id", function (data) {
-				return data.dpid + '-label';
-			})
-			.attr("transform", function(data, i) {
-				return "rotate(" + data.angle+ ")translate(" + data.radius * 150 + ")rotate(" + (-data.angle) + ")";
-			})
-
-		// add the text nodes which show on mouse over
-		nodes.append("svg:text")
-				.text(function (data) {return data.dpid;})
-				.attr("x", function (data) {
-					if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
-						if (data.className == 'edge') {
-							return - data.width*3 - 4;
-						} else {
-							return - data.width - 4;
-						}
-					} else {
-						if (data.className == 'edge') {
-							return data.width*3 + 4;
-						} else {
-							return data.width + 4;
-						}
-					}
-				})
-				.attr("y", function (data) {
-					var y;
-					if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
-						if (data.className == 'edge') {
-							y = data.width*3/2 + 4;
-						} else {
-							y = data.width/2 + 4;
-						}
-					} else {
-						if (data.className == 'edge') {
-							y = data.width*3/2 + 4;
-						} else {
-							y = data.width/2 + 4;
-						}
-					}
-					return y - 6;
-				})
-				.attr("text-anchor", function (data) {
-					if (data.angle <= 90 || data.angle >= 270 && data.angle <= 360) {
-						return "end";
-					} else {
-						return "start";
-					}
-				})
-				.attr("transform", function(data) {
-					var m = document.querySelector('#viewbox').getTransformToElement().inverse();
-					if (data.scale) {
-						m = m.scale(data.scale);
-					}
-					return "matrix( " + m.a + " " + m.b + " " + m.c + " " + m.d + " " + m.e + " " + m.f + " )";
-				})
-	}
-
-	labelRings.enter().append("svg:g")
-		.attr("class", "textRing")
-		.each(labelRingEnter);
-
-	// switches should not change during operation of the ui so no
-	// rings.exit()
-
-
-	// DRAW THE LINKS
+function updateLinkLines() {
 
 	// key on link dpids since these will come/go during demo
 	var linkLines = d3.select('svg').selectAll('.link').data(links, function (d) {
-			return d['src-switch']+'->'+d['dst-switch'];
+		return d['src-switch']+'->'+d['dst-switch'];
 	});
 
 	// add new links
-	linkLines.enter().append("svg:path")
-	.attr("class", "link");
+	linkLines.enter().append("svg:path").attr("class", "link");
 
 	linkLines.attr('id', function (d) {
 			return makeLinkKey(d);
-		})
-		.attr("d", function (d) {
+		}).attr("d", function (d) {
 			var src = d3.select(document.getElementById(d['src-switch']));
 			var dst = d3.select(document.getElementById(d['dst-switch']));
 
@@ -257,9 +43,32 @@
 			return d.pending;
 		});
 
-
 	// remove old links
 	linkLines.exit().remove();
 }
 
+updateTopology = function() {
+
+	/* currently rings.js and map.js can be included to define the topology display */
+
+	drawTopology();
+
+
+
+	/* the remainder should work regardless of the topology display */
+
+	updateLinkLines();
+
+	// setup the mouseover behaviors
+	var allSwitches = d3.selectAll('.edge, .core, .aggregation');
+
+	allSwitches.on('mouseover', mouseOverSwitch);
+	allSwitches.on('mouseout', mouseOutSwitch);
+	allSwitches.on('mouseup', mouseUpSwitch);
+	allSwitches.on('mousedown', mouseDownSwitch);
+
+	// only do switch up/down for core switches
+	d3.selectAll('.core').on('dblclick', doubleClickSwitch);
+}
+
 })();
diff --git a/web/ons-demo/js/topologyactions.js b/web/ons-demo/js/topologyactions.js
index a7ceaaa..4ba68f6 100644
--- a/web/ons-demo/js/topologyactions.js
+++ b/web/ons-demo/js/topologyactions.js
@@ -226,4 +226,46 @@
 	} else {
 		clearHighlight();
 	}
-});
\ No newline at end of file
+});
+
+d3.select(document.body).on('mousemove', function () {
+	if (!d3.select('#topology').classed('linking')) {
+		return;
+	}
+	var linkVector = document.getElementById('linkVector');
+	if (!linkVector) {
+		return;
+	}
+	linkVector = d3.select(linkVector);
+
+	var highlighted = svg.selectAll('.highlight')[0];
+	var s1 = null, s2 = null;
+	if (highlighted.length > 1) {
+		var s1 = d3.select(highlighted[0]);
+		var s2 = d3.select(highlighted[1]);
+
+	} else if (highlighted.length > 0) {
+		var s1 = d3.select(highlighted[0]);
+	}
+	var src = s1;
+	if (s2 && !s2.data()[0].target) {
+		src = s2;
+	}
+	if (src) {
+		linkVector.attr('d', function () {
+				var srcPt = document.querySelector('svg').createSVGPoint();
+				srcPt.x = src.attr('x');
+				srcPt.y = src.attr('y');
+				srcPt = srcPt.matrixTransform(src[0][0].getCTM());
+
+				var svg = document.getElementById('topology');
+				var mouse = d3.mouse(viewbox);
+				var dstPt = document.querySelector('svg').createSVGPoint();
+				dstPt.x = mouse[0];
+				dstPt.y = mouse[1];
+				dstPt = dstPt.matrixTransform(viewbox.getCTM());
+
+				return line([srcPt, dstPt]);
+			});
+	}
+});