setup logic for adding/deleting flows
diff --git a/web/ons-demo/css/skin.default.css b/web/ons-demo/css/skin.default.css
index aebf68b..5607190 100644
--- a/web/ons-demo/css/skin.default.css
+++ b/web/ons-demo/css/skin.default.css
@@ -155,10 +155,14 @@
 }
 
 
-.flowId, .srcDPID, .dstDPID {
+.srcDPID, .dstDPID {
 	border-right: 1px solid #AAA;
 }
 
+.srcDPID {
+	border-left: 1px solid #AAA;
+}
+
 
 #controllers {
 	border-right: 1px solid #AAA;
diff --git a/web/ons-demo/js/app.js b/web/ons-demo/js/app.js
index 0fa5fa3..63048bd 100644
--- a/web/ons-demo/js/app.js
+++ b/web/ons-demo/js/app.js
@@ -18,6 +18,7 @@
 var svg;
 var updateTopology;
 var pendingLinks = {};
+var pendingFlows = {};
 
 var colors = [
 	'color1',
@@ -67,11 +68,9 @@
 
 var selectedFlows = [null, null, null];
 
-function drawFlows() {
+function updateSelectedFlowsTopology() {
 	// DRAW THE FLOWS
-	var flows = d3.select('svg').selectAll('.flow').data(selectedFlows, function (d) {
-		return d ? d.flowId.value : null;
-	});
+	var flows = d3.select('svg').selectAll('.flow').data(selectedFlows);
 
 	flows.enter().append("svg:path").attr('class', 'flow')
 		.attr('stroke-dasharray', '4, 10')
@@ -85,27 +84,159 @@
 
 
 	flows.attr('d', function (d) {
-		if (!d) {
-			return;
-		}
-		var pts = [];
-		d.dataPath.flowEntries.forEach(function (flowEntry) {
-			var s = d3.select(document.getElementById(flowEntry.dpid.value));
-			var pt = document.querySelector('svg').createSVGPoint();
-			pt.x = s.attr('x');
-			pt.y = s.attr('y');
-			pt = pt.matrixTransform(s[0][0].getCTM());
-			pts.push(pt);
+			if (!d) {
+				return;
+			}
+			var pts = [];
+			if (d.pending) {
+				// 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));
+
+				var pt1 = document.querySelector('svg').createSVGPoint();
+				pt1.x = s1.attr('x');
+				pt1.y = s1.attr('y');
+				pt1 = pt1.matrixTransform(s1[0][0].getCTM());
+				pts.push(pt1);
+
+				var pt2 = document.querySelector('svg').createSVGPoint();
+				pt2.x = s2.attr('x');
+				pt2.y = s2.attr('y');
+				pt2 = pt2.matrixTransform(s2[0][0].getCTM());
+				pts.push(pt2);
+
+			} else {
+				d.dataPath.flowEntries.forEach(function (flowEntry) {
+					var s = d3.select(document.getElementById(flowEntry.dpid.value));
+					// s[0] is null if the flow entry refers to a non-existent switch
+					if (s[0][0]) {
+						var pt = document.querySelector('svg').createSVGPoint();
+						pt.x = s.attr('x');
+						pt.y = s.attr('y');
+						pt = pt.matrixTransform(s[0][0].getCTM());
+						pts.push(pt);
+					} else {
+						console.log('flow refers to non-existent switch: ' + flowEntry.dpid.value);
+					}
+				});
+			}
+			return line(pts);
+		})
+		.classed('pending', function (d) {
+			return d && d.pending
 		});
-		return line(pts);
-	})
 
 	// "marching ants"
 	flows.select('animate').attr('from', 500);
 
+}
+
+function updateSelectedFlowsTable() {
+	function rowEnter(d) {
+		var row = d3.select(this);
+		row.append('div').classed('flowId', true);
+		row.append('div').classed('srcDPID', true);
+		row.append('div').classed('dstDPID', true);
+		row.append('div').classed('iperf', true);
+	}
+
+	function rowUpdate(d) {
+		var row = d3.select(this);
+		row.select('.flowId')
+			.text(function (d) {
+				if (d) {
+					if (d.flowId) {
+						return d.flowId.value;
+					} else {
+						return '0x--';
+					}
+				}
+			})
+			.classed('pending', d && d.pending);
+
+		row.select('.srcDPID')
+			.text(function (d) {
+				if (d) {
+					return d.dataPath.srcPort.dpid.value;
+				}
+			});
+
+		row.select('.dstDPID')
+			.text(function (d) {
+				if (d) {
+					return d.dataPath.dstPort.dpid.value;
+				}
+			});
+	}
+
+	var flows = d3.select('#selectedFlows')
+		.selectAll('.selectedFlow')
+		.data(selectedFlows);
+
+	flows.enter()
+		.append('div')
+		.classed('selectedFlow', true)
+		.each(rowEnter);
+
+	flows.each(rowUpdate);
+
 	flows.exit().remove();
 }
 
+function updateSelectedFlows() {
+	// make sure that all of the selected flows are either
+	// 1) valid (meaning they are in the latest list of flows)
+	// 2) pending
+	if (model) {
+		var flowMap = {};
+		model.flows.forEach(function (flow) {
+			flowMap[makeFlowKey(flow)] = flow;
+		});
+
+		var newSelectedFlows = [];
+		selectedFlows.forEach(function (flow) {
+			if (flow) {
+				if (flow.pending) {
+					newSelectedFlows.push(flow);
+				} else {
+					var liveFlow = flowMap[makeFlowKey(flow)];
+					if (liveFlow) {
+						newSelectedFlows.push(liveFlow);
+					}
+				}
+			} else {
+				newSelectedFlows.push(null);
+			}
+		});
+		selectedFlows = newSelectedFlows;
+	}
+
+	updateSelectedFlowsTable();
+	updateSelectedFlowsTopology();
+}
+
+function selectFlow(flow) {
+	selectedFlows.unshift(flow);
+	selectedFlows = selectedFlows.slice(0, 3);
+	updateSelectedFlows();
+}
+
+function deselectFlow(flow) {
+	var flowKey = makeFlowKey(flow);
+	var newSelectedFlows = [];
+	selectedFlows.forEach(function (flow) {
+		if (!flow || flowKey !== makeFlowKey(flow)) {
+			newSelectedFlows.push(flow);
+		}
+	});
+	selectedFlows = newSelectedFlows;
+	while (selectedFlows.length < 3) {
+		selectedFlows.push(null);
+	}
+
+	updateSelectedFlows();
+}
+
 function showFlowChooser() {
 	function rowEnter(d) {
 		var row = d3.select(this);
@@ -113,11 +244,7 @@
 		row.append('div')
 			.classed('black-eye', true).
 			on('click', function () {
-				selectedFlows.unshift(d);
-				selectedFlows = selectedFlows.slice(0, 3);
-
-				updateSelectedFlows();
-				updateTopology();
+				selectFlow(d);
 			});
 
 		row.append('div')
@@ -159,54 +286,7 @@
 	}, 0);
 }
 
-function updateSelectedFlows() {
-	function rowEnter(d) {
-		var row = d3.select(this);
-		row.append('div').classed('flowId', true);
-		row.append('div').classed('srcDPID', true);
-		row.append('div').classed('dstDPID', true);
-		row.append('div').classed('iperf', true);
-	}
 
-	function rowUpdate(d) {
-		var row = d3.select(this);
-		row.select('.flowId')
-			.text(function (d) {
-				if (d) {
-					return d.flowId.value;
-				}
-			});
-
-		row.select('.srcDPID')
-			.text(function (d) {
-				if (d) {
-					return d.dataPath.srcPort.dpid.value;
-				}
-			});
-
-		row.select('.dstDPID')
-			.text(function (d) {
-				if (d) {
-					return d.dataPath.dstPort.dpid.value;
-				}
-			});
-	}
-
-	var flows = d3.select('#selectedFlows')
-		.selectAll('.selectedFlow')
-		.data(selectedFlows);
-
-	flows.enter()
-		.append('div')
-		.classed('selectedFlow', true)
-		.each(rowEnter);
-
-	flows.each(rowUpdate);
-
-	flows.exit().remove();
-
-	return flows;
-}
 
 function updateHeader(model) {
 	d3.select('#lastUpdate').text(new Date());
@@ -325,6 +405,10 @@
 	return link['src-switch'] + '=>' + link['dst-switch'];
 }
 
+function makeFlowKey(flow) {
+	return flow.dataPath.srcPort.dpid.value + '=>' + flow.dataPath.dstPort.dpid.value;
+}
+
 function createLinkMap(links) {
 	var linkMap = {};
 	links.forEach(function (link) {
@@ -361,7 +445,6 @@
 
 	var links = reconcilePendingLinks(model);
 	var linkMap = createLinkMap(links);
-//	var flowMap = createFlowMap(model);
 
 	function mouseOverSwitch(data) {
 
@@ -650,9 +733,29 @@
 			if (s1Data.className == 'edge' && s2Data.className == 'edge') {
 				var prompt = 'Create flow from ' + srcData.dpid + ' to ' + dstData.dpid + '?';
 				if (confirm(prompt)) {
-					alert('do create flow');
-				} else {
-					alert('do not create flow');
+					addFlow(srcData, dstData);
+
+					var flow = {
+						dataPath: {
+							srcPort: {
+								dpid: {
+									value: srcData.dpid
+								}
+							},
+							dstPort: {
+								dpid: {
+									value: dstData.dpid
+								}
+							}
+						},
+						pending: true
+					};
+
+					selectFlow(flow);
+
+					setTimeout(function () {
+						deselectFlow(flow);
+					}, 10000);
 				}
 			} else {
 				var map = linkMap[srcData.dpid];
@@ -830,9 +933,6 @@
 
 	// remove old links
 	links.exit().remove();
-
-
-	drawFlows();
 }
 
 function updateControllers() {
diff --git a/web/ons-demo/js/controller.js b/web/ons-demo/js/controller.js
index a36ae1c..a6726bb 100644
--- a/web/ons-demo/js/controller.js
+++ b/web/ons-demo/js/controller.js
@@ -12,55 +12,58 @@
 
 
 var controllerFunctions = {
-	l: function (cmd, link) {
+	linkCmd: function (cmd, link) {
 		var url = '/proxy/gui/link/' + [cmd, link['src-switch'], link['src-port'], link['dst-switch'], link['dst-port']].join('/');
 		callURL(url);
 
 	},
-	s: function (cmd, s) {
+	switchCmd: function (cmd, s) {
 		var url = '/proxy/gui/switch/' + [cmd, s.dpid].join('/');
 		callURL(url);
 	},
-	c: function (cmd, c) {
+	ctrlCmd: function (cmd, c) {
 		var url = '/proxy/gui/controller/' + [cmd, c].join('/');
 		callURL(url);
+	},
+	addFlowCmd: function (src, dst) {
+		alert('add flow')
+	},
+	delFlowCmd: function (flow) {
+		alert('delete flow')
 	}
 };
 
-
-// if (parseURLParameters().mock) {
-// 	urls = mockURLs;
-// }
-
+// http://gui3.onlab.us:8081/gui/addflow/<src_dpid>/<src_port>/<dst_dpid>/<dst_port>/<srcMAC>/<dstMAC>
+// http://gui3.onlab.us:8081/gui/delflow/<flow_id>
 
 function linkUp(link) {
-	controllerFunctions.l('up', link);
+	controllerFunctions.linkCmd('up', link);
 }
 
 function linkDown(link) {
-	controllerFunctions.l('down', link);
+	controllerFunctions.linkCmd('down', link);
 }
 
 function switchUp(s) {
-	controllerFunctions.s('up', s);
+	controllerFunctions.switchCmd('up', s);
 }
 
 function switchDown(s) {
-	controllerFunctions.s('down', s);
+	controllerFunctions.switchCmd('down', s);
 }
 
 function controllerUp(c) {
-	controllerFunctions.c('up', c);
+	controllerFunctions.ctrlCmd('up', c);
 }
 
 function controllerDown(c) {
-	controllerFunctions.c('down', c);
+	controllerFunctions.ctrlCmd('down', c);
 }
 
-function createFlow(src, dst) {
-
+function addFlow(src, dst) {
+	controllerFunctions.addFlowCmd(src, dst);
 }
 
-function deleteFlow(src, dst) {
-
+function deleteFlow(flow) {
+	controllerFunctions.delFlowCmd(flow);
 }
\ No newline at end of file