GUI -- Further work on web socket service. Still WIP.

Change-Id: Ib787a72abed3d8bd1cce7efe110c584a04fe0cd4
diff --git a/web/gui/src/main/webapp/app/fw/remote/websocket.js b/web/gui/src/main/webapp/app/fw/remote/websocket.js
index 273f711..fb69cf9 100644
--- a/web/gui/src/main/webapp/app/fw/remote/websocket.js
+++ b/web/gui/src/main/webapp/app/fw/remote/websocket.js
@@ -20,16 +20,47 @@
 (function () {
     'use strict';
 
-    angular.module('onosRemote')
-    .factory('WebSocketService', ['$location', 'UrlFnService',
-        function ($loc, ufs) {
+    var fs;
 
-            // creates a web socket for the given path, returning a "handle"
-            function createWebSocket(path) {
-                return {
-                    path: ufs.wsUrl(path)
-                    // TODO: complete implementation...
-                };
+    angular.module('onosRemote')
+    .factory('WebSocketService', ['$location', 'UrlFnService', 'FnService',
+        function ($loc, ufs, _fs_) {
+            fs = _fs_;
+
+            // creates a web socket for the given path, returning a "handle".
+            // cb is the callbacks block.
+            function createWebSocket(path, cb) {
+                var fullUrl = ufs.wsUrl(path),
+                    ws = new WebSocket(fullUrl),
+                    api = {
+                        meta: { path: fullUrl, ws: ws },
+                        send: send,
+                        close: close
+                    };
+
+                ws.onopen = (cb && cb.onOpen) || null;
+                ws.onmessage = (cb && cb.onMessage) || null;
+                ws.onclose = (cb && cb.onClose) || null;
+
+                function send(msg) {
+                    if (msg) {
+                        if (ws) {
+                            ws.send(msg);
+                        } else {
+                            $log.warn('ws.send() no web socket open!',
+                                fullUrl, msg);
+                        }
+                    }
+                }
+
+                function close() {
+                    if (ws) {
+                        ws.close();
+                        ws = null;
+                    }
+                }
+
+                return api;
             }
 
             return {
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.css b/web/gui/src/main/webapp/app/view/topo/topo.css
index 1685e13..626ddf7 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.css
+++ b/web/gui/src/main/webapp/app/view/topo/topo.css
@@ -33,8 +33,8 @@
 }
 
 .light #ov-topo svg #topo-map {
-    stroke: #eee;
-    /*stroke: #88b;*/
+    /*stroke: #eee;*/
+    stroke: #88b;
 }
 .dark #ov-topo svg #topo-map {
     stroke: #444;
diff --git a/web/gui/src/main/webapp/app/view/topo/topo.js b/web/gui/src/main/webapp/app/view/topo/topo.js
index 802ecc7..805c10e 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -131,10 +131,34 @@
 
     // --- Web Socket Connection -----------------------------------------
 
+    function onWsOpen() {
+        $log.log('web socket opened...');
+
+    }
+
+    function onWsMessage(msg) {
+        $log.log('web socket message...', msg);
+
+    }
+
+    function onWsClose(msg) {
+        $log.log('web socket closed...', msg);
+
+    }
+
     function setUpWebSocket() {
-        var wsHandle = wss.createWebSocket('topology');
+        var wsHandle = wss.createWebSocket('topology', {
+            onOpen: onWsOpen,
+            onMessage: onWsMessage,
+            onClose: onWsClose
+        });
+
+        // TODO: handle "guiSuccessor" functionality (replace host)
+        // TODO: implement retry on close functionality
+
+
         $log.log('created web socket', wsHandle);
-        // TODO: complete implementation
+        // TODO: complete implementation...
 
     }
 
@@ -143,11 +167,11 @@
     angular.module('ovTopo', moduleDependencies)
 
         .controller('OvTopoCtrl', [
-            '$log',
+            '$scope', '$log',
             'KeyService', 'ZoomService', 'GlyphService', 'MapService',
             'WebSocketService',
 
-        function (_$log_, _ks_, _zs_, _gs_, _ms_, _wss_) {
+        function ($scope, _$log_, _ks_, _zs_, _gs_, _ms_, _wss_) {
             var self = this;
             $log = _$log_;
             ks = _ks_;
@@ -160,6 +184,13 @@
                 svgResized(svg.style('width'), svg.style('height'));
             };
 
+            $scope.$on('$destroy', function () {
+                $log.log('OvTopoCtrl is saying Buh-Bye!');
+                // TODO: cleanup when the scope is destroyed...
+                //  for example, closing the web socket.
+
+            });
+
             // svg layer and initialization of components
             ovtopo = d3.select('#ov-topo');
             svg = ovtopo.select('svg');
diff --git a/web/gui/src/main/webapp/tests/app/fw/remote/websocket-spec.js b/web/gui/src/main/webapp/tests/app/fw/remote/websocket-spec.js
new file mode 100644
index 0000000..8786109
--- /dev/null
+++ b/web/gui/src/main/webapp/tests/app/fw/remote/websocket-spec.js
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ ONOS GUI -- Remote -- Web Socket Service - Unit Tests
+ */
+describe('factory: fw/remote/websocket.js', function () {
+    var $log, fs, wss;
+
+    beforeEach(module('onosRemote'));
+
+    beforeEach(module(function($provide) {
+        $provide.factory('$location', function (){
+            return {
+                protocol: function () { return 'http'; },
+                host: function () { return 'foo'; },
+                port: function () { return '80'; }
+            };
+        })
+    }));
+
+    beforeEach(inject(function (_$log_, FnService, WebSocketService) {
+        $log = _$log_;
+        fs = FnService;
+        wss = WebSocketService;
+    }));
+
+
+    it('should define WebSocketService', function () {
+        expect(wss).toBeDefined();
+    });
+
+    it('should define api functions', function () {
+        expect(fs.areFunctions(wss, [
+            'createWebSocket'
+        ])).toBeTruthy();
+    });
+
+    it('should use the appropriate URL', function () {
+        var ws = wss.createWebSocket('foo/path');
+        expect(ws.meta.path).toEqual('ws://foo:80/onos/ui/ws/foo/path');
+    });
+
+    it('should install the appropriate callbacks', function () {
+        function oo() {}
+        function om() {}
+        function oc() {}
+
+        var ws = wss.createWebSocket('foo', {
+            onOpen: oo,
+            onMessage: om,
+            onClose: oc
+        });
+
+        expect(ws.meta.ws.onopen).toBe(oo);
+        expect(ws.meta.ws.onmessage).toBe(om);
+        expect(ws.meta.ws.onclose).toBe(oc);
+    });
+
+    it('should install partial callbacks', function () {
+        function oo() {}
+        function om() {}
+
+        var ws = wss.createWebSocket('foo', {
+            onOpen: oo,
+            onMessage: om
+        });
+
+        expect(ws.meta.ws.onopen).toBe(oo);
+        expect(ws.meta.ws.onmessage).toBe(om);
+        expect(ws.meta.ws.onclose).toBeNull();
+    });
+
+    // TODO: more testing to be done.
+
+});