GUI -- WebSocket close now invokes the Veil Service.
- enableKeys(b) added to KeyService.
- minor cleanup of Veil Service.

Change-Id: I640720727a3f1249d81855a61f088a7f2e9525cc
diff --git a/web/gui/src/main/webapp/app/fw/layer/veil.js b/web/gui/src/main/webapp/app/fw/layer/veil.js
index 80c223d..0b4ae9f 100644
--- a/web/gui/src/main/webapp/app/fw/layer/veil.js
+++ b/web/gui/src/main/webapp/app/fw/layer/veil.js
@@ -24,56 +24,60 @@
     'use strict';
 
     // injected references
-    var $log, fs;
+    var $log, fs, ks;
 
     var veil, pdiv, svg;
 
+    // msg should be an array of strings
     function show(msg) {
+        var msgs = fs.isA(msg) || [msg];
         pdiv.selectAll('p').remove();
 
-        msg.forEach(function (line) {
+        msgs.forEach(function (line) {
             pdiv.append('p').text(line);
         });
 
         veil.style('display', 'block');
-
-        // TODO: disable key bindings
+        ks.enableKeys(false);
     }
 
     function hide() {
         veil.style('display', 'none');
-        // TODO: re-enable key bindings
+        ks.enableKeys(true);
     }
 
     angular.module('onosLayer')
-        .factory('VeilService', ['$log', 'FnService', 'GlyphService',
-            function (_$log_, _fs_, gs) {
-                $log = _$log_;
-                fs = _fs_;
+    .factory('VeilService',
+        ['$log', 'FnService', 'KeyService', 'GlyphService',
 
-                var wSize = fs.windowSize(),
-                    ww = wSize.width,
-                    wh = wSize.height,
-                    shrinkConst = wh-(wh * 0.7),
-                    birdDim = wh-shrinkConst,
-                    birdCenter = (ww / 2) - (birdDim / 2);
+        function (_$log_, _fs_, _ks_, gs) {
+            $log = _$log_;
+            fs = _fs_;
+            ks = _ks_;
 
-                veil = d3.select('#veil');
-                pdiv = veil.append('div').classed('msg', true);
+            var wSize = fs.windowSize(),
+                ww = wSize.width,
+                wh = wSize.height,
+                vbox = '0 0 ' + ww + ' ' +  wh,
+                shrink = wh * 0.3,
+                birdDim = wh - shrink,
+                birdCenter = (ww - birdDim) / 2;
 
-                svg = veil.append('svg').attr({
-                    width: (ww + 'px'),
-                    height: (wh + 'px'),
-                    viewBox: '0 0 ' + ww + ' ' +  wh
-                }).style('opacity', 0.2);
+            veil = d3.select('#veil');
+            pdiv = veil.append('div').classed('msg', true);
 
-                gs.addGlyph(svg, 'bird', (birdDim + 'px'),
-                    false, [birdCenter, shrinkConst/2]);
+            svg = veil.append('svg').attr({
+                width: ww,
+                height: wh,
+                viewBox: vbox
+            }).style('opacity', 0.2);
 
-                return {
-                    show: show,
-                    hide: hide
-                };
-        }]);
+            gs.addGlyph(svg, 'bird', birdDim, false, [birdCenter, shrink/2]);
+
+            return {
+                show: show,
+                hide: hide
+            };
+    }]);
 
 }());
diff --git a/web/gui/src/main/webapp/app/fw/util/keys.js b/web/gui/src/main/webapp/app/fw/util/keys.js
index 46a98cb..d451885 100644
--- a/web/gui/src/main/webapp/app/fw/util/keys.js
+++ b/web/gui/src/main/webapp/app/fw/util/keys.js
@@ -24,7 +24,8 @@
     var $log, fs, ts;
 
     // internal state
-    var keyHandler = {
+    var enabled = true,
+        keyHandler = {
             globalKeys: {},
             maskedKeys: {},
             viewKeys: {},
@@ -80,14 +81,16 @@
 
         d3.event.stopPropagation();
 
-        // global callback?
-        if (gcb && gcb(token, key, keyCode, event)) {
-            // if the event was 'handled', we are done
-            return;
-        }
-        // otherwise, let the view callback have a shot
-        if (vcb) {
-            vcb(token, key, keyCode, event);
+        if (enabled) {
+            // global callback?
+            if (gcb && gcb(token, key, keyCode, event)) {
+                // if the event was 'handled', we are done
+                return;
+            }
+            // otherwise, let the view callback have a shot
+            if (vcb) {
+                vcb(token, key, keyCode, event);
+            }
         }
     }
 
@@ -197,6 +200,9 @@
                     } else {
                         keyHandler.viewGestures = fs.isA(g) || [];
                     }
+                },
+                enableKeys: function (b) {
+                    enabled = b;
                 }
             };
     }]);
diff --git a/web/gui/src/main/webapp/app/view/topo/topoEvent.js b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
index 3a68f92..a412467 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoEvent.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
@@ -27,7 +27,7 @@
     'use strict';
 
     // injected refs
-    var $log, wss, wes, tps, tis, tfs, tss, tts;
+    var $log, wss, wes, vs, tps, tis, tfs, tss, tts;
 
     // internal state
     var wsock, evApis;
@@ -89,6 +89,7 @@
         $log.debug('web socket opened...');
         // start by requesting periodic summary data...
         dispatcher.sendEvent('requestSummary');
+        vs.hide();
     }
 
     function onWsMessage(ev) {
@@ -98,21 +99,27 @@
     function onWsClose(reason) {
         $log.log('web socket closed; reason=', reason);
         wsock = null;
+        vs.show([
+            'Oops!',
+            'Web-socket connection to server closed...',
+            'Try refreshing the page.'
+        ]);
     }
 
     // ==========================
 
     angular.module('ovTopo')
     .factory('TopoEventService',
-        ['$log', '$location', 'WebSocketService', 'WsEventService',
+        ['$log', '$location', 'WebSocketService', 'WsEventService', 'VeilService',
             'TopoPanelService', 'TopoInstService', 'TopoForceService',
             'TopoSelectService', 'TopoTrafficService',
 
-        function (_$log_, $loc, _wss_, _wes_,
+        function (_$log_, $loc, _wss_, _wes_, _vs_,
                   _tps_, _tis_, _tfs_, _tss_, _tts_) {
             $log = _$log_;
             wss = _wss_;
             wes = _wes_;
+            vs = _vs_;
             tps = _tps_;
             tis = _tis_;
             tfs = _tfs_;
diff --git a/web/gui/src/main/webapp/tests/app/fw/util/keys-spec.js b/web/gui/src/main/webapp/tests/app/fw/util/keys-spec.js
index 60ea8ad..1873e4f 100644
--- a/web/gui/src/main/webapp/tests/app/fw/util/keys-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/util/keys-spec.js
@@ -43,6 +43,16 @@
         d3.select('#ptest').remove();
     });
 
+    it('should define the key service', function () {
+        expect(ks).toBeDefined();
+    });
+
+    it('should define api functions', function () {
+        expect(fs.areFunctions(ks, [
+            'installOn', 'keyBindings', 'gestureNotes', 'enableKeys'
+        ])).toBeTruthy();
+    });
+
     // Code to emulate key presses....
     // NOTE: kinda messy, but it seems to get the job done.
     function jsKeyDown(element, code) {
@@ -207,6 +217,29 @@
         expect(count).toEqual(1);
     });
 
+    it('should block keys when disabled', function () {
+        var cbCount = 0;
+
+        function cb() { cbCount++; }
+
+        function pressA() { jsKeyDown(elem, 65); }  // 65 == 'A' keycode
+
+        ks.keyBindings({ A: cb });
+
+        expect(cbCount).toBe(0);
+
+        pressA();
+        expect(cbCount).toBe(1);
+
+        ks.enableKeys(false);
+        pressA();
+        expect(cbCount).toBe(1);
+
+        ks.enableKeys(true);
+        pressA();
+        expect(cbCount).toBe(2);
+    });
+
     // === Gesture notes related tests
     it('should start with no notes', function () {
         expect(ks.gestureNotes()).toEqual([]);