Topo2: Update links on websocket events

Change-Id: I6a8b05cc0eaf67e2b10dd39aeb4b876c61c40ec6
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js b/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js
index a0dd430..311c170 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Collection.js
@@ -42,21 +42,25 @@
 
     Collection.prototype = {
         model: Model,
-        add: function (data) {
+        addModel: function (data) {
+            var CollectionModel = this.model;
+            var model = new CollectionModel(data);
+            model.collection = this;
 
+            this.models.push(model);
+            this._byId[data.id] = model;
+
+            return model;
+        },
+        add: function (data) {
             var _this = this;
 
             if (angular.isArray(data)) {
-
                 data.forEach(function (d) {
-
-                    var CollectionModel = _this.model;
-                    var model = new CollectionModel(d);
-                    model.collection = _this;
-
-                    _this.models.push(model);
-                    _this._byId[d.id] = model;
+                    _this.addModel(d);
                 });
+            } else {
+                return this.addModel(data);
             }
         },
         get: function (id) {
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js b/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
index 9849393..2f6f0d2 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
@@ -256,7 +256,7 @@
                         var regionLinks = t2rs.regionLinks();
 
                         this.link = this.elements.linkG.selectAll('.link')
-                            .data(regionLinks, function (d) { return d.get('key'); });
+                            .data(regionLinks);
 
                         // operate on entering links:
                         var entering = this.link.enter()
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Link.js b/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
index bcd315d..73ac150 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
@@ -319,6 +319,18 @@
                 if (this.get('enhanced')) {
                     this.enhance();
                 }
+            },
+            remove: function () {
+
+                var width = linkScale(widthRatio) / t2zs.scale();
+
+                this.el.transition()
+                    .duration(300)
+                    .attr('stroke', '#ff0000')
+                    .style('stroke-width', width * 4)
+                    .transition()
+                    .delay(1000)
+                    .style('opacity', 0);
             }
         });
 
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
index 09b8cd8..7938fd6 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
@@ -26,7 +26,7 @@
     var Model;
 
     // Internal
-    var instance
+    var instance;
 
     // 'static' vars
     var ROOT = '(root)';
@@ -101,6 +101,9 @@
                 regionLinks: function () {
                     return (this.model) ? this.model.get('links').models : [];
                 },
+                getLink: function (linkId) {
+                    return this.model.get('links').get(linkId);
+                },
                 filterRegionNodes: function (predicate) {
                     var nodes = this.regionNodes();
                     return _.filter(nodes, predicate);
@@ -138,6 +141,27 @@
                     }
 
                     return false;
+                },
+
+                update: function (event) {
+                    if (this[event.type]) {
+                        this[event.type](event);
+                        this.layout.update();
+                    } else {
+                        $log.error("Unhanded topology update", event);
+                    }
+                },
+
+                // Topology update event handlers
+                LINK_ADDED_OR_UPDATED: function (event) {
+                    if (event.memo === 'added') {
+                        var link = this.model.get('links').add(event.data);
+                        link.createLink();
+                    }
+                },
+                LINK_REMOVED: function (event) {
+                    var link = this.getLink(event.subject);
+                    link.remove();
                 }
             });
 
@@ -146,6 +170,7 @@
             }
 
             return getInstance();
+
         }]);
 
 })();