Merge remote-tracking branch 'upstream/master'
diff --git a/web/ons-demo/RELEASE_NOTES.txt b/web/ons-demo/RELEASE_NOTES.txt
index bfe6059..369e9c4 100644
--- a/web/ons-demo/RELEASE_NOTES.txt
+++ b/web/ons-demo/RELEASE_NOTES.txt
@@ -1,3 +1,7 @@
+** April 9, 2013 **
+- display number of flows for each core<->core link
+- graphics tweaks
+
 ** April 8, 2013 **
 - map view
 - onos nodes at top
diff --git a/web/ons-demo/css/skin.default.css b/web/ons-demo/css/skin.default.css
index f6086a3..f9aa830 100644
--- a/web/ons-demo/css/skin.default.css
+++ b/web/ons-demo/css/skin.default.css
@@ -106,6 +106,34 @@
 	border-bottom: 1px solid #AAA;;
 }
 
+#onos {
+	background-color: #333;
+}
+
+#cluster-label {
+	font-size: 22px;
+	display: -webkit-box;
+	-webkit-box-align: center;
+	padding-left: .5em;
+	padding-right: .5em;
+	color: #EEE;
+}
+
+#actions {
+	padding-right: .25em;
+	padding-left: .25em;
+	border-left: 1px solid white;
+	display: -webkit-box;
+	-webkit-box-align: center;
+}
+
+#controllers {
+	padding: .25em;
+	background-color: black;
+	margin: .25em;
+	border-radius: 8px;
+}
+
 #flowChooser .selectedFlow {
 	background-color: rgba(255, 255, 255, .75);
 	color: black;
@@ -208,6 +236,7 @@
 	border: 1px solid #444;
 	color: white;
 	position: relative;
+	border-radius: 8px;
 }
 
 .controller:hover {
@@ -395,19 +424,21 @@
 
 .action {
 	margin: .25em;
-	padding: .25em;
-	padding-left: 1em;
-	padding-right: 1em;
-	border: 1px solid #AAA;
+	border: 2px solid #AAA;
+	height: 2em;
+	width: 2em;
 	background-color: #444;
 	display: -webkit-box;
 	-webkit-box-pack: center;
 	-webkit-box-align: center;
-	color: white;
+	color: #AAA;
+	border-radius: 50%;
+	-webkit-box-sizing: border-box;
 }
 
 .action:hover {
-	border: 1px solid #FFF;
+	border: 2px solid #FFF;
+	color: white;
 }
 
 .action:active {
@@ -437,3 +468,8 @@
 	stroke: black;
 }
 
+.flowCount {
+	font-size: 20px;
+	fill: rgba(255, 255, 255, .75);
+}
+
diff --git a/web/ons-demo/index.html b/web/ons-demo/index.html
index ca930ec..12ac5f9 100644
--- a/web/ons-demo/index.html
+++ b/web/ons-demo/index.html
@@ -20,11 +20,13 @@
 	</div>
 
 	<div id='onos'>
+		<div id='cluster-label'>ONOS Node Cluster</div>
 		<div id='controllers'></div>
 		<div id='actions'>
-			<div id='action-all' class='action'>ALL</div>
-			<div id='action-local' class='action'>LOCAL</div>
-			<div id='action-scale' class='action'>SCALE</div>
+			<div id='action-all' class='action'>A</div>
+			<div id='action-local' class='action'>1</div>
+			<div id='action-scale' class='action'>S</div>
+			<div id='action-reset' class='action'>R</div>
 		</div>
 	</div>
 
diff --git a/web/ons-demo/js/init.js b/web/ons-demo/js/init.js
index dd11bf8..a412440 100644
--- a/web/ons-demo/js/init.js
+++ b/web/ons-demo/js/init.js
@@ -25,5 +25,9 @@
 		alert('scale')
 	});
 
+	d3.select('#action-reset').on('click', function () {
+		alert('reset')
+	});
+
 	createTopologyView(cb);
 }
diff --git a/web/ons-demo/js/map.js b/web/ons-demo/js/map.js
index 7662aaf..3849b8d 100644
--- a/web/ons-demo/js/map.js
+++ b/web/ons-demo/js/map.js
@@ -120,6 +120,64 @@
 	});
 }
 
+function drawCoreFlowCounts() {
+	var links = {};
+	model.links.forEach(function (l) {
+		links[makeLinkKey(l)] = l;
+	});
+
+	var flowCounts = [];
+	countCoreLinkFlows().forEach(function (count) {
+		var l = links[count.key];
+		if (l) {
+			var src = d3.select(document.getElementById(l['src-switch']));
+			var dst = d3.select(document.getElementById(l['dst-switch']));
+
+			if (!src.empty() && !dst.empty()) {
+				var x1 = parseFloat(src.attr('x'));
+				var x2 = parseFloat(dst.attr('x'));
+				var y1 = parseFloat(src.attr('y'));
+				var y2 = parseFloat(dst.attr('y'));
+
+				var slope = (y2 - y1)/(x2 - x1);
+
+				var offset = 15;
+				var xOffset = offset;
+				var yOffset = slope*offset;
+
+				var d = Math.sqrt(xOffset*xOffset + yOffset*yOffset);
+				var scaler = offset/d;
+
+				count.pt = {
+					x: x1 + (x2 - x1)/2 + xOffset*scaler,
+					y: y1 + (y2 - y1)/2 + yOffset*scaler
+				}
+			}
+			flowCounts.push(count);
+		}
+	});
+
+
+	var counts = linksLayer.selectAll('.flowCount').data(flowCounts, function (d) {
+		return d.key;
+	});
+
+	counts.enter().append('svg:text')
+		.attr('class', 'flowCount')
+		.attr('x', function (d) {
+			return d.pt.x;
+		})
+		.attr('y', function (d) {
+			return d.pt.y;
+		});
+
+	counts.text(function (d) {
+		return d.value;
+	});
+
+	counts.exit().remove();
+}
+
 function drawLinkLines() {
 
 	// key on link dpids since these will come/go during demo
@@ -163,6 +221,7 @@
 	linkLines.exit().remove();
 }
 
+
 var fanOutAngles = {
 	aggregation: 100,
 	edge: 5
@@ -252,9 +311,13 @@
 		g.append('svg:text')
 			.classed('label', true)
 			.text(d.label)
-			.attr("text-anchor", "end")
-			.attr('x', d.x - width)
-			.attr('y', d.y - width);
+			.attr("text-anchor", function (d) {
+				return d.x > 500 ? "end" : "start";
+			})
+			.attr('x', function (d) {
+				return d.x > 500 ? d.x - width*.8 : d.x + width*.8;
+			})
+			.attr('y', d.y - width*.8);
 	}
 }
 
@@ -333,6 +396,8 @@
 
 	drawLinkLines();
 
+	drawCoreFlowCounts();
+
 	labelsEnter(switches);
 }
 
diff --git a/web/ons-demo/js/model.js b/web/ons-demo/js/model.js
index f251c87..df4a751 100644
--- a/web/ons-demo/js/model.js
+++ b/web/ons-demo/js/model.js
@@ -5,13 +5,20 @@
 		edgeSwitches: [],
 		aggregationSwitches: [],
 		coreSwitches: [],
-		flows: results.flows,
+		flows: [],
 		controllers: results.controllers,
 		activeControllers: results.activeControllers,
 		links: results.links,
 		configuration: results.configuration
 	};
 
+	// remove bad flows;
+	results.flows.forEach(function (f) {
+		if (f.dataPath && f.dataPath.flowEntries && f.dataPath.flowEntries.length > 1) {
+			model.flows.push(f);
+		}
+	})
+
 	// sort the switches
 	results.switches.sort(function (a, b) {
 		var aA = a.dpid.split(':');
diff --git a/web/ons-demo/js/utils.js b/web/ons-demo/js/utils.js
index be7cf53..65117f5 100644
--- a/web/ons-demo/js/utils.js
+++ b/web/ons-demo/js/utils.js
@@ -81,24 +81,27 @@
 function updateHeader() {
 	d3.select('#lastUpdate').text(new Date());
 
-	var count = 0;
+	var activeSwitchCount = 0;
 	model.edgeSwitches.forEach(function (s) {
 		if (s.state === 'ACTIVE') {
-			count += 1;
+			activeSwitchCount += 1;
 		}
 	});
 	model.aggregationSwitches.forEach(function (s) {
 		if (s.state === 'ACTIVE') {
-			count += 1;
+			activeSwitchCount += 1;
 		}
 	});
 	model.coreSwitches.forEach(function (s) {
 		if (s.state === 'ACTIVE') {
-			count += 1;
+			activeSwitchCount += 1;
 		}
 	});
 
-	d3.select('#activeSwitches').text(count);
+	d3.select('#activeSwitches').text(activeSwitchCount);
+
+
+
 	d3.select('#activeFlows').text(model.flows.length);
 }
 
@@ -153,4 +156,51 @@
 	return svg;
 }
 
+/***************************************************************************************************
+counts the number of flows which pass through each core<->core link
+***************************************************************************************************/
+function countCoreLinkFlows() {
+	var allCounts = {};
+	model.flows.forEach(function (f) {
+		if (f.dataPath && f.dataPath.flowEntries && f.dataPath.flowEntries.length > 1) {
+			var flowEntries = f.dataPath.flowEntries;
+			var i;
+
+			for (i = 0; i < flowEntries.length - 1; i += 1) {
+				var linkKey = flowEntries[i].dpid.value + '=>' + flowEntries[i+1].dpid.value;
+				if (!allCounts[linkKey]) {
+					allCounts[linkKey] = 1;
+				} else {
+					allCounts[linkKey] += 1;
+				}
+			}
+		}
+	});
+
+	var coreCounts = {};
+	var i, j;
+	for (i = 0; i < model.coreSwitches.length - 1; i += 1) {
+		for (j = i + 1; j < model.coreSwitches.length; j += 1) {
+			var si = model.coreSwitches[i];
+			var sj = model.coreSwitches[j];
+			var key1 =  si.dpid + '=>' + sj.dpid;
+			var key2 =  sj.dpid + '=>' + si.dpid;
+			var linkCount = 0;
+			if (allCounts[key1]) {
+				linkCount += allCounts[key1];
+			}
+			if (allCounts[key2]) {
+				linkCount += allCounts[key2];
+			}
+
+			coreCounts[key1] = linkCount;
+		}
+	}
+
+	return d3.entries(coreCounts);
+}
+
+
+
+