GUI - Added ability to differentiate between host types.
- Added addHostIcon(...) function.
- Added bullhorn glyph to library.
- Added bgpSpeaker host to links scenario.

Change-Id: I09db2467c0c8ec23933790a794fe2c93dc443141
diff --git a/web/gui/src/main/webapp/glyphs.js b/web/gui/src/main/webapp/glyphs.js
index 838cf82..a794c62 100644
--- a/web/gui/src/main/webapp/glyphs.js
+++ b/web/gui/src/main/webapp/glyphs.js
@@ -24,6 +24,8 @@
 (function (onos) {
     'use strict';
 
+    // TODO: refactor this library...
+
     var birdData = "M427.7,300.4 c-6.9,0.6-13.1,5-19.2,7.1" +
         "c-18.1,6.2-33.9,9.1-56.5,4.7c24.6,17.2,36.6,13,63.7,0.1" +
         "c-0.5,0.6-0.7,1.3-1.3,1.9c1.4-0.4,2.4-1.7,3.4-2.2" +
@@ -39,7 +41,6 @@
         "c-2-11.2-8.4-21.5-19.7-24.8c-1-0.3-1.1-0.3-0.9,0" +
         "c9.6,17.1,7.2,38.3,3.1,54.2C429.9,285.5,426.7,293.2,427.7,300.4z";
 
-
     function defBird(defs) {
         defs.append('symbol')
             .attr({
@@ -49,9 +50,25 @@
             .append('path').attr('d', birdData);
     }
 
+    var bullhornData = "M0,13c0,3.733,2.561,6.148,6.019,6.809 " +
+        "C6.013,19.873,6,19.935,6,20v8 c0,1.105,0.895,2,2,2h3 " +
+        "c1.105,0,2-0.896,2-2v-8h3V6H8C3.582,6,0,8.582,0,13z " +
+        "M18,20h3V6h-3V20z M30,0l-7,4.667v16.667L30,26 c1.105,0,2-0.895,2-2" +
+        "V2 C32,0.896,31.105,0,30,0z";
+
+    function defBullhorn(defs) {
+        defs.append('symbol')
+            .attr({
+                id: 'bullhorn',
+                viewBox: '-4 -5 40 40'
+            })
+            .append('path').attr('d', bullhornData);
+    }
+
     // === register the functions as a library
     onos.ui.addLib('glyphs', {
-        defBird: defBird
+        defBird: defBird,
+        defBullhorn: defBullhorn
     });
 
 }(ONOS));
diff --git a/web/gui/src/main/webapp/json/ev/links/ev_22_onos.json b/web/gui/src/main/webapp/json/ev/links/ev_22_onos.json
new file mode 100644
index 0000000..b0b51b1
--- /dev/null
+++ b/web/gui/src/main/webapp/json/ev/links/ev_22_onos.json
@@ -0,0 +1,18 @@
+{
+  "event": "addHost",
+  "payload": {
+    "id": "0E:2A:69:30:BB:BB/-1",
+    "type": "bgpSpeaker",
+    "ingress": "0E:2A:69:30:BB:BB/-1/0-of:0000ffffffff0007/2",
+    "egress": "of:0000ffffffff0007/2-0E:2A:69:30:BB:BB/-1/0",
+    "cp": {
+      "device": "of:0000ffffffff0007",
+      "port": 2
+    },
+    "labels": [
+      "BGP",
+      "0E:2A:69:30:BB:BB"
+    ],
+    "props": {}
+  }
+}
diff --git a/web/gui/src/main/webapp/onos2.css b/web/gui/src/main/webapp/onos2.css
index 2073acf..e092da0 100644
--- a/web/gui/src/main/webapp/onos2.css
+++ b/web/gui/src/main/webapp/onos2.css
@@ -101,12 +101,6 @@
 
 
 
-svg .node.host circle {
-    fill: #c96;
-    stroke: #000;
-}
-
-
 /* for debugging */
 svg .node circle.debug {
     fill: white;
diff --git a/web/gui/src/main/webapp/topo2.css b/web/gui/src/main/webapp/topo2.css
index 35ddc5a..f1dbf5e 100644
--- a/web/gui/src/main/webapp/topo2.css
+++ b/web/gui/src/main/webapp/topo2.css
@@ -31,6 +31,11 @@
 }
 
 
+#topo svg .glyph {
+    fill: white;
+    stroke: none;
+}
+
 /* NODES */
 
 #topo svg .node {
@@ -93,14 +98,22 @@
 /* Host Nodes */
 
 #topo svg .node.host {
-    fill: #846;
+    stroke: #000;
 }
 
 #topo svg .node.host text {
     fill: #846;
+    stroke: none;
     font: 9pt sans-serif;
 }
 
+svg .node.host circle {
+    fill: #c96;
+}
+
+#topo svg .node.host.bgpSpeaker circle {
+    fill: #853;
+}
 
 /* LINKS */
 
@@ -262,7 +275,7 @@
 }
 #topo-oibox .onosInst.mastership.affinity {
     opacity: 1.0;
-    box-shadow: 0px 2px 8px #33e;
+    box-shadow: 0 2px 8px #33e;
 }
 
 
diff --git a/web/gui/src/main/webapp/topo2.js b/web/gui/src/main/webapp/topo2.js
index e89d4bc..8a7efd3 100644
--- a/web/gui/src/main/webapp/topo2.js
+++ b/web/gui/src/main/webapp/topo2.js
@@ -1060,7 +1060,7 @@
         if (!node.type) {
             node.type = 'endstation';
         }
-        node.svgClass = 'node host';
+        node.svgClass = 'node host ' + node.type;
         positionNode(node);
 
         // cache label array length
@@ -1237,6 +1237,18 @@
         }
     }
 
+    function addHostIcon(node, radius, iconId) {
+        var dim = radius * 1.5,
+            xlate = -dim / 2;
+
+        node.append('use')
+            .classed('glyph', true)
+            .attr('transform', translate(xlate,xlate))
+            .attr('xlink:href', '#' + iconId)
+            .attr('width', dim)
+            .attr('height', dim);
+    }
+
     function updateNodes() {
         node = nodeG.selectAll('.node')
             .data(network.nodes, function (d) { return d.id; });
@@ -1310,20 +1322,36 @@
             }
         });
 
+        // TODO: better place for this configuration state
+        var defaultHostRadius = 9,
+            hostRadius = {
+                bgpSpeaker: 20
+            },
+            hostIcon = {
+                bgpSpeaker: 'bullhorn'
+            };
+
+
         // augment host nodes...
         entering.filter('.host').each(function (d) {
             var node = d3.select(this),
-                box;
+                r = hostRadius[d.type] || defaultHostRadius,
+                textDy = r + 10,
+                icon = hostIcon[d.type];
 
             // provide ref to element from backing data....
             d.el = node;
 
             node.append('circle')
-                .attr('r', 8);     // TODO: define host circle radius
+                .attr('r', r);
+
+            if (icon) {
+                addHostIcon(node, r, icon);
+            }
 
             node.append('text')
                 .text(hostLabel)
-                .attr('dy', '1.3em')
+                .attr('dy', textDy)
                 .attr('text-anchor', 'middle');
 
             // debug function to show the modelled x,y coordinates of nodes...
@@ -1786,6 +1814,11 @@
         showTrafficOnHover.classed('active', !trafficHover());
     }
 
+    function loadGlyphs(svg) {
+        var defs = svg.append('defs');
+        gly.defBird(defs);
+        gly.defBullhorn(defs);
+    }
 
     // ==============================
     // View life-cycle callbacks
@@ -1802,8 +1835,7 @@
         svg = view.$div.append('svg').attr('viewBox', viewBox);
         setSize(svg, view);
 
-        var defs = svg.append('defs');
-        gly.defBird(defs);
+        loadGlyphs(svg);
 
         zoomPanContainer = svg.append('g').attr('id', 'zoomPanContainer');
         setupZoomPan();