ONOS-293 Added summary pane and related keyboard shortcuts; also tweaked key help sizes and dropped instances toggle from mast. Fixed ONOS-295 bug.

Change-Id: I694901957451cf88df06e6fca3a8d71de144f68e
diff --git a/web/gui/src/main/webapp/feedback.css b/web/gui/src/main/webapp/feedback.css
index 952688d..06716bb 100644
--- a/web/gui/src/main/webapp/feedback.css
+++ b/web/gui/src/main/webapp/feedback.css
@@ -23,7 +23,7 @@
 #feedback svg {
     position: absolute;
     bottom: 0;
-    opacity: 0.5;
+    opacity: 0.8;
 }
 
 #feedback svg g.feedbackItem {
@@ -31,14 +31,11 @@
 }
 
 #feedback svg g.feedbackItem rect {
-    fill: #888;
-    stroke: #666;
-    stroke-width: 3;
-    opacity: 0.5
+    fill: #ccc;
 }
 
 #feedback svg g.feedbackItem text {
-    fill: #000;
+    fill: #333;
     stroke: none;
     text-anchor: middle;
     alignment-baseline: middle;
diff --git a/web/gui/src/main/webapp/feedback.js b/web/gui/src/main/webapp/feedback.js
index 10f87c9..644202e 100644
--- a/web/gui/src/main/webapp/feedback.js
+++ b/web/gui/src/main/webapp/feedback.js
@@ -33,7 +33,7 @@
     var w = '100%',
         h = 200,
         fade = 200,
-        showFor = 500,
+        showFor = 1200,
         vb = '-200 -' + (h/2) + ' 400 ' + h,
         xpad = 20,
         ypad = 10;
diff --git a/web/gui/src/main/webapp/floatPanel.css b/web/gui/src/main/webapp/floatPanel.css
index bacf29e..2a9b8a2 100644
--- a/web/gui/src/main/webapp/floatPanel.css
+++ b/web/gui/src/main/webapp/floatPanel.css
@@ -24,8 +24,8 @@
     position: absolute;
     z-index: 100;
     display: block;
-    top: 10%;
-    width: 280px;
+    top: 64px;
+    width: 260px;
     right: -300px;
     opacity: 0;
     background-color: rgba(255,255,255,0.8);
diff --git a/web/gui/src/main/webapp/keymap.css b/web/gui/src/main/webapp/keymap.css
index 7e6ade0..e6b9715 100644
--- a/web/gui/src/main/webapp/keymap.css
+++ b/web/gui/src/main/webapp/keymap.css
@@ -31,10 +31,10 @@
 }
 
 #keymap svg text.title {
-    font-size: 12pt;
+    font-size: 10pt;
     font-style: italic;
     text-anchor: middle;
-    fill: #444;
+    fill: #999;
 }
 
 #keymap svg g.keyItem {
@@ -47,17 +47,17 @@
 }
 
 #keymap svg text {
-    font-size: 10pt;
+    font-size: 7pt;
     alignment-baseline: middle;
 }
 
 #keymap svg text.key {
-    font-size: 10pt;
-    fill: #8aa;
+    font-size: 7pt;
+    fill: #add;
 }
 
 #keymap svg text.desc {
-    font-size: 10pt;
-    fill: #888;
+    font-size: 7pt;
+    fill: #aaa;
 }
 
diff --git a/web/gui/src/main/webapp/keymap.js b/web/gui/src/main/webapp/keymap.js
index 85f2539..3d96a56 100644
--- a/web/gui/src/main/webapp/keymap.js
+++ b/web/gui/src/main/webapp/keymap.js
@@ -35,9 +35,9 @@
         fade = 500,
         vb = '-220 -220 440 440',
         paneW = 400,
-        paneH = 340,
+        paneH = 280,
         offy = 65,
-        dy = 20,
+        dy = 14,
         offKey = 40,
         offDesc = offKey + 50,
         lineW = paneW - (2*offKey);
diff --git a/web/gui/src/main/webapp/onos2.js b/web/gui/src/main/webapp/onos2.js
index d2a7baa..a9cb1b0 100644
--- a/web/gui/src/main/webapp/onos2.js
+++ b/web/gui/src/main/webapp/onos2.js
@@ -763,7 +763,8 @@
                 var pos = position || 'TR',
                     cfg = fpConfig[pos],
                     el,
-                    fp;
+                    fp,
+                    on = false;
 
                 if (fpanels[id]) {
                     buildError('Float panel with id "' + id + '" already exists.');
@@ -792,15 +793,20 @@
                     id: id,
                     el: el,
                     pos: pos,
+                    isVisible: function () {
+                        return on;
+                    },
 
                     show: function () {
                         console.log('show pane: ' + id);
+                        on = true;
                         el.transition().duration(750)
                             .style(cfg.side, pxShow())
                             .style('opacity', 1);
                     },
                     hide: function () {
                         console.log('hide pane: ' + id);
+                        on = false;
                         el.transition().duration(750)
                             .style(cfg.side, pxHide())
                             .style('opacity', 0);
diff --git a/web/gui/src/main/webapp/topo2.css b/web/gui/src/main/webapp/topo2.css
index 809458d..d982ee2 100644
--- a/web/gui/src/main/webapp/topo2.css
+++ b/web/gui/src/main/webapp/topo2.css
@@ -177,10 +177,66 @@
     font-size: 9pt;
 }
 
+/* Fly-in summary pane */
+
+#topo-summary {
+    /* gets base CSS from .fpanel in floatPanel.css */
+    top: 64px;
+}
+
+#topo-summary svg {
+    display: inline-block;
+    width: 42px;
+    height: 42px;
+}
+
+#topo-summary svg .glyphIcon {
+    fill: black;
+    stroke: none;
+    fill-rule: evenodd;
+}
+
+#topo-summary h2 {
+    position: absolute;
+    margin: 0px 4px;
+    top: 20px;
+    left: 50px;
+    color: black;
+}
+
+#topo-summary h3 {
+    margin: 0px 4px;
+    top: 20px;
+    left: 50px;
+    color: black;
+}
+
+#topo-summary p, table {
+    margin: 4px 4px;
+}
+
+#topo-summary td.label {
+    font-style: italic;
+    color: #777;
+    padding-right: 12px;
+}
+
+#topo-summary td.value {
+}
+
+#topo-summary hr {
+    height: 1px;
+    color: #ccc;
+    background-color: #ccc;
+    border: 0;
+}
+
 /* Fly-in details pane */
 
 #topo-detail {
-/* gets base CSS from .fpanel in floatPanel.css */
+    /* gets base CSS from .fpanel in floatPanel.css */
+    top: 320px;
+
 }
 
 #topo-detail svg {
diff --git a/web/gui/src/main/webapp/topo2.js b/web/gui/src/main/webapp/topo2.js
index 9db6461..44b90f0 100644
--- a/web/gui/src/main/webapp/topo2.js
+++ b/web/gui/src/main/webapp/topo2.js
@@ -141,6 +141,8 @@
         S: injectStartupEvents,
         space: injectTestEvent,
 
+        O: [toggleSummary, 'Toggle ONOS summary pane'],
+        I: [toggleInstances, 'Toggle ONOS instances pane'],
         B: [toggleBg, 'Toggle background image'],
         L: [cycleLabels, 'Cycle Device labels'],
         P: togglePorts,
@@ -182,6 +184,7 @@
         selections = {},
         selectOrder = [],
         hovered = null,
+        summaryPane,
         detailPane,
         antTimer = null,
         onosInstances = {},
@@ -329,7 +332,7 @@
         if (hoverMode === hoverModes.length) {
             hoverMode = 0;
         }
-        view.flash('Hover Mode: ' + hoverModes[hoverMode]);
+        view.flash('Mode: ' + hoverModes[hoverMode]);
     }
 
     function togglePorts(view) {
@@ -347,8 +350,12 @@
     function handleEscape(view) {
         if (oiShowMaster) {
             cancelAffinity();
-        } else {
+        } else if (detailPane.isVisible()) {
             deselectAll();
+        } else if (oiBox.isVisible()) {
+            oiBox.hide();
+        } else if (summaryPane.isVisible()) {
+            cancelSummary();
         }
     }
 
@@ -585,6 +592,7 @@
         removeHost: removeHost,
 
         showDetails: showDetails,
+        showSummary: showSummary,
         showTraffic: showTraffic
     };
 
@@ -737,6 +745,12 @@
         }
     }
 
+    function showSummary(data) {
+        evTrace(data);
+        populateSummary(data.payload);
+        summaryPane.show();
+    }
+
     function showDetails(data) {
         evTrace(data);
         populateDetails(data.payload);
@@ -824,6 +838,33 @@
         return true;
     }
 
+
+    function toggleInstances() {
+        if (!oiBox.isVisible()) {
+            oiBox.show();
+        } else {
+            oiBox.hide();
+        }
+    }
+
+    function toggleSummary() {
+        if (!summaryPane.isVisible()) {
+            requestSummary();
+        } else {
+            cancelSummary();
+        }
+    }
+
+    // request overall summary data
+    function requestSummary() {
+        sendMessage('requestSummary', {});
+    }
+
+    function cancelSummary() {
+        sendMessage('cancelSummary', {});
+        summaryPane.hide();
+    }
+
     // request details for the selected element
     // invoked from selection of a single node.
     function requestDetails() {
@@ -845,16 +886,20 @@
     }
 
     function showTrafficAction() {
-        // force intents hover mode
+        cancelTraffic();
         hoverMode = 1;
         showSelectTraffic();
         network.view.flash('Related Traffic');
     }
 
+    function cancelTraffic() {
+        sendMessage('cancelTraffic', {});
+    }
+
     function showSelectTraffic() {
         // if nothing is hovered over, and nothing selected, send cancel request
         if (!hovered && nSel() === 0) {
-            sendMessage('cancelTraffic', {});
+            cancelTraffic();
             return;
         }
 
@@ -870,12 +915,13 @@
     }
 
     function showAllTrafficAction() {
+        cancelTraffic();
         sendMessage('requestAllTraffic', {});
         network.view.flash('All Traffic');
     }
 
     function showDeviceLinkFlowsAction() {
-        // force intents hover mode
+        cancelTraffic();
         hoverMode = 2;
         showDeviceLinkFlows();
         network.view.flash('Device Flows');
@@ -884,7 +930,7 @@
     function showDeviceLinkFlows() {
         // if nothing is hovered over, and nothing selected, send cancel request
         if (!hovered && nSel() === 0) {
-            sendMessage('cancelTraffic', {});
+            cancelTraffic();
             return;
         }
         var hoverId = (flowsHover() && hovered && hovered.class === 'device') ?
@@ -907,7 +953,6 @@
             'xlink:href': iid,
             width: dim,
             height: dim
-
         });
     }
 
@@ -940,6 +985,15 @@
             });
             var dim = 30;
             appendGlyph(svg, 2, 2, 30, '#node');
+            svg.append('use')
+                .attr({
+                    class: 'birdBadge',
+                    transform: translate(8,10),
+                    'xlink:href': '#bird',
+                    width: 18,
+                    height: 18,
+                    fill: '#fff'
+                });
 
             $('<div>').attr('class', 'onosTitle').text(d.id).appendTo(el);
 
@@ -1720,6 +1774,8 @@
 
             webSock.ws.onopen = function() {
                 noWebSock(false);
+                requestSummary();
+                oiBox.show();
             };
 
             webSock.ws.onmessage = function(m) {
@@ -1881,12 +1937,17 @@
         updateDetailPane();
     }
 
+    // update the state of the sumary pane
+    function updateSummaryPane() {
+
+    }
+
     // update the state of the detail pane, based on current selections
     function updateDetailPane() {
         var nSel = selectOrder.length;
         if (!nSel) {
             detailPane.hide();
-            showTrafficAction();        // sends cancelTraffic event
+            cancelTraffic();
         } else if (nSel === 1) {
             singleSelect();
         } else {
@@ -1936,6 +1997,40 @@
         addMultiSelectActions();
     }
 
+    // TODO: refactor to consolidate with populateDetails
+    function populateSummary(data) {
+        summaryPane.empty();
+
+        var svg = summaryPane.append('svg'),
+            iid = iconGlyphUrl(data);
+
+        var title = summaryPane.append('h2'),
+            table = summaryPane.append('table'),
+            tbody = table.append('tbody');
+
+        appendGlyph(svg, 0, 0, 40, iid);
+
+        svg.append('use')
+            .attr({
+                class: 'birdBadge',
+                transform: translate(8,12),
+                'xlink:href': '#bird',
+                width: 24,
+                height: 24,
+                fill: '#fff'
+            });
+
+        title.text('ONOS Summary');
+
+        data.propOrder.forEach(function(p) {
+            if (p === '-') {
+                addSep(tbody);
+            } else {
+                addProp(tbody, p, data.props[p]);
+            }
+        });
+    }
+
     function populateDetails(data) {
         detailPane.empty();
 
@@ -2056,7 +2151,7 @@
     // TODO: toggle button (and other widgets in the masthead) should be provided
     //  by the framework; not generated by the view.
 
-    var showInstances;
+    //var showInstances;
 
     function addButtonBar(view) {
         var bb = d3.select('#mast')
@@ -2069,20 +2164,20 @@
                 .on('click', cb);
         }
 
-        showInstances = mkTogBtn('Show Instances', toggleInst);
+        //showInstances = mkTogBtn('Show Instances', toggleInst);
     }
 
-    function instShown() {
-        return showInstances.classed('active');
-    }
-    function toggleInst() {
-        showInstances.classed('active', !instShown());
-        if (instShown()) {
-            oiBox.show();
-        } else {
-            oiBox.hide();
-        }
-    }
+    //function instShown() {
+    //    return showInstances.classed('active');
+    //}
+    //function toggleInst() {
+    //    showInstances.classed('active', !instShown());
+    //    if (instShown()) {
+    //        oiBox.show();
+    //    } else {
+    //        oiBox.hide();
+    //    }
+    //}
 
     function panZoom() {
         return false;
@@ -2370,6 +2465,7 @@
         resize: resize
     });
 
+    summaryPane = onos.ui.addFloatingPanel('topo-summary');
     detailPane = onos.ui.addFloatingPanel('topo-detail');
     oiBox = onos.ui.addFloatingPanel('topo-oibox', 'TL');