iperf display implemented
diff --git a/web/ons-demo/RELEASE_NOTES.txt b/web/ons-demo/RELEASE_NOTES.txt
index 916a965..90af6d1 100644
--- a/web/ons-demo/RELEASE_NOTES.txt
+++ b/web/ons-demo/RELEASE_NOTES.txt
@@ -1,11 +1,20 @@
 ** April 4, 2013 **
+iperf display implemented
+- scaled to 50,000,000
+- update rate is every 2s
+- the display does not draw until receiving 2 buffers of data (this way if there is a stale buffer it doesn't get displayed)
+- duration is 10000 seconds. seems like there is no need for a button to restart?
+- displaying 10s data
+- if the data underruns (either because the server response is too slow or because the iperf data stops being updated) the display draws 0s
+- seeing the data stall a lot (timestamp and end-time remain the same through many fetches)
+
+** April 4, 2013 **
 Fix issues:
 	305 - "close x" now unselects flow. double click to delete a flow
 	323 - gui now recovers on timeout errors and polls again
 	324 - fixed problem with added flows not displaying
 	325 - fixed logic displaying flows in topology view
 
-
 ** March 28, 2013 **
 - add and delete flow implemented
 - to add flow
diff --git a/web/ons-demo/js/app.js b/web/ons-demo/js/app.js
index 5573b1b..d6fa983 100644
--- a/web/ons-demo/js/app.js
+++ b/web/ons-demo/js/app.js
@@ -96,7 +96,7 @@
 				return;
 			}
 			var pts = [];
-			if (!d.dataPath.flowEntries || !d.dataPath.flowEntries.length) {
+			if (!d.dataPath.flowEntries) {
 				// create a temporary vector to indicate the pending flow
 				var s1 = d3.select(document.getElementById(d.dataPath.srcPort.dpid.value));
 				var s2 = d3.select(document.getElementById(d.dataPath.dstPort.dpid.value));
@@ -128,7 +128,11 @@
 					}
 				});
 			}
-			return line(pts);
+			if (pts.length) {
+				return line(pts);
+			} else {
+				return "M0,0";
+			}
 		})
 		.attr('id', function (d) {
 			if (d) {
@@ -185,7 +189,7 @@
 			}
 		});
 
-		if (d && !d.iperfData) {
+		if (!d || !hasIPerf(d)) {
 			row.select('.iperfdata')
 				.attr('d', 'M0,0');
 		}
@@ -256,7 +260,7 @@
 function startIPerfForFlow(flow) {
 	var duration = 10000; // seconds
 	var interval = 100; // ms. this is defined by the server
-	var updateRate = 1000; // ms
+	var updateRate = 2000; // ms
 
 	function makePoints() {
 		var pts = [];
@@ -267,7 +271,7 @@
 			if (height > 32)
 				height = 32;
 			pts.push({
-				x: i * 10,
+				x: i * 1000/99,
 				y: 32 - height
 			})
 		}
@@ -279,11 +283,8 @@
 		startIPerf(flow, duration, updateRate/interval);
 		flow.iperfDisplayInterval = setInterval(function () {
 			if (flow.iperfData) {
-				if (flow.iperfData.samples.length < 100) {
-					var repeatValue = flow.iperfData.samples[flow.iperfData.samples.length-1];
-					while (flow.iperfData.samples.length < 100) {
-						flow.iperfData.samples.push(repeatValue);
-					}
+				while (flow.iperfData.samples.length < 100) {
+					flow.iperfData.samples.push(0);
 				}
 				var iperfPath = d3.select(document.getElementById(makeSelectedFlowKey(flow))).select('path');
 				iperfPath.attr('d', line(makePoints()));
@@ -307,7 +308,7 @@
 
 					var iperfData = JSON.parse(data);
 					// if the data is fresh
-					if (iperfData.timestamp != flow.iperfData.timestamp) {
+					if (flow.iperfData.timestamp && iperfData.timestamp != flow.iperfData.timestamp) {
 						iperfData.samples.forEach(function (s) {
 							flow.iperfData.samples.push(s);
 						});
@@ -318,7 +319,7 @@
 				}
 //				console.log(data);
 			});
-		}, updateRate);
+		}, updateRate/2); // over sample to avoid gaps
 	}
 }
 
@@ -343,13 +344,15 @@
 					liveFlow.iperfDisplayInterval = flow.iperfDisplayInterval;
 				} else if (flow.createPending) {
 					newSelectedFlows.push(flow);
+				} else if (hasIPerf(flow)) {
+					clearIPerf(flow);
 				}
 			}
 		});
 		selectedFlows = newSelectedFlows;
 	}
 	selectedFlows.forEach(function (flow) {
-		if (!flow.iperfFetchInterval) {
+		if (!hasIPerf(flow)) {
 			startIPerfForFlow(flow);
 		}
 	});
@@ -377,6 +380,19 @@
 	}
 }
 
+function hasIPerf(flow) {
+	return flow && flow.iperfFetchInterval;
+}
+
+function clearIPerf(flow) {
+	console.log('clearing iperf interval for: ' + flow.flowId.value);
+	clearInterval(flow.iperfFetchInterval);
+	delete flow.iperfFetchInterval;
+	clearInterval(flow.iperfDisplayInterval);
+	delete flow.iperfDisplayInterval;
+	delete flow.iperfData;
+}
+
 function deselectFlow(flow, ifCreatePending) {
 	var flowKey = makeFlowKey(flow);
 	var newSelectedFlows = [];
@@ -386,10 +402,8 @@
 				flowKey === makeFlowKey(flow) && ifCreatePending && !flow.createPending ) {
 			newSelectedFlows.push(flow);
 		} else {
-			if (flow && flow.iperfFetchInterval) {
-				console.log('clearing iperf interval for: ' + flow.flowId.value);
-				clearInterval(flow.iperfFetchInterval);
-				clearInterval(flow.iperfDisplayInterval);
+			if (hasIPerf(flow)) {
+				clearIPerf(flow);
 			}
 		}
 	});