GUI -- Starting to migrate the 'force layout' functionality from the old GUI.

Change-Id: I38c5e5cd45a3bdc9cf6f0ec68736f3b5fbcb289f
diff --git a/web/gui/src/main/webapp/app/fw/svg/svgUtil.js b/web/gui/src/main/webapp/app/fw/svg/svgUtil.js
new file mode 100644
index 0000000..ef6049c
--- /dev/null
+++ b/web/gui/src/main/webapp/app/fw/svg/svgUtil.js
@@ -0,0 +1,55 @@
+/*
+ * 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 -- SVG -- Util Service
+ */
+
+/*
+ The SVG Util Service provides a miscellany of utility functions.
+ */
+
+(function () {
+    'use strict';
+
+    // injected references
+    var $log, fs;
+
+    angular.module('onosSvg')
+        .factory('SvgUtilService', ['$log', 'FnService',
+        function (_$log_, _fs_) {
+            $log = _$log_;
+            fs = _fs_;
+
+            function createDragBehavior() {
+                $log.warn('SvgUtilService: createDragBehavior -- To Be Implemented');
+            }
+
+            function loadGlow() {
+                $log.warn('SvgUtilService: loadGlow -- To Be Implemented');
+            }
+
+            function cat7() {
+                $log.warn('SvgUtilService: cat7 -- To Be Implemented');
+            }
+
+            return {
+                createDragBehavior: createDragBehavior,
+                loadGlow: loadGlow,
+                cat7: cat7
+            };
+        }]);
+}());
diff --git a/web/gui/src/main/webapp/app/index.html b/web/gui/src/main/webapp/app/index.html
index bad7a6c..853db93 100644
--- a/web/gui/src/main/webapp/app/index.html
+++ b/web/gui/src/main/webapp/app/index.html
@@ -47,6 +47,7 @@
     <script src="fw/svg/geodata.js"></script>
     <script src="fw/svg/map.js"></script>
     <script src="fw/svg/zoom.js"></script>
+    <script src="fw/svg/svgUtil.js"></script>
 
     <script src="fw/remote/remote.js"></script>
     <script src="fw/remote/urlfn.js"></script>
@@ -74,6 +75,7 @@
     <script src="view/sample/sample.js"></script>
     <script src="view/topo/topo.js"></script>
     <script src="view/topo/topoEvent.js"></script>
+    <script src="view/topo/topoForce.js"></script>
     <script src="view/device/device.js"></script>
     <!-- TODO: inject javascript refs server-side -->
 
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 66386f9..8bff419 100644
--- a/web/gui/src/main/webapp/app/view/topo/topo.js
+++ b/web/gui/src/main/webapp/app/view/topo/topo.js
@@ -28,10 +28,10 @@
     ];
 
     // references to injected services etc.
-    var $log, ks, zs, gs, ms, ps, tes;
+    var $log, ks, zs, gs, ms, ps, tes, tfs;
 
     // DOM elements
-    var ovtopo, svg, defs, zoomLayer, map;
+    var ovtopo, svg, defs, zoomLayer, mapG, forceG;
 
     // Internal state
     var zoomer, evDispatcher;
@@ -86,7 +86,7 @@
         $log.log('ZOOM: translate = ' + tr + ', scale = ' + sc);
 
         // keep the map lines constant width while zooming
-        map.style('stroke-width', (2.0 / sc) + 'px');
+        mapG.style('stroke-width', (2.0 / sc) + 'px');
     }
 
     function setUpZoom() {
@@ -112,7 +112,7 @@
         var points = [
             [0, 0], [0, 1000], [1000, 0], [1000, 1000]
         ];
-        map.selectAll('circle')
+        mapG.selectAll('circle')
             .data(points)
             .enter()
             .append('circle')
@@ -123,12 +123,19 @@
     }
 
     function setUpMap() {
-        map = zoomLayer.append('g').attr('id', 'topo-map');
+        mapG = zoomLayer.append('g').attr('id', 'topo-map');
         //ms.loadMapInto(map, '*continental_us', {mapFillScale:0.5});
-        ms.loadMapInto(map, '*continental_us');
+        ms.loadMapInto(mapG, '*continental_us');
         //showCallibrationPoints();
     }
 
+    // --- Force Layout --------------------------------------------------
+
+    function setUpForce() {
+        forceG = zoomLayer.append('g').attr('id', 'topo-force');
+        tfs.initForce(forceG);
+    }
+
 
     // --- Controller Definition -----------------------------------------
 
@@ -137,10 +144,10 @@
         .controller('OvTopoCtrl', [
             '$scope', '$log', '$location', '$timeout',
             'KeyService', 'ZoomService', 'GlyphService', 'MapService',
-            'PanelService', 'TopoEventService',
+            'PanelService', 'TopoEventService', 'TopoForceService',
 
         function ($scope, _$log_, $loc, $timeout,
-                  _ks_, _zs_, _gs_, _ms_, _ps_, _tes_) {
+                  _ks_, _zs_, _gs_, _ms_, _ps_, _tes_, _tfs_) {
             var self = this;
             $log = _$log_;
             ks = _ks_;
@@ -149,6 +156,7 @@
             ms = _ms_;
             ps = _ps_;
             tes = _tes_;
+            tfs = _tfs_;
 
             self.notifyResize = function () {
                 svgResized(svg.style('width'), svg.style('height'));
@@ -172,6 +180,7 @@
             setUpDefs();
             setUpZoom();
             setUpMap();
+            setUpForce();
 
             // open up a connection to the server...
             tes.openSock();
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 73bcc25..5533e16 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoEvent.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
@@ -124,6 +124,6 @@
                 bindDispatcher: bindDispatcher,
                 openSock: openSock,
                 closeSock: closeSock
-            }
+            };
         }]);
 }());
diff --git a/web/gui/src/main/webapp/app/view/topo/topoForce.js b/web/gui/src/main/webapp/app/view/topo/topoForce.js
new file mode 100644
index 0000000..6c3d501
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -0,0 +1,136 @@
+/*
+ * 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 -- Topology Event Module.
+ Defines event handling for events received from the server.
+ */
+
+(function () {
+    'use strict';
+
+    // injected refs
+    var $log, sus;
+
+    // internal state
+    var settings,
+        force,      // force layout object
+        drag,       // drag behavior handler
+        network = {
+            nodes: [],
+            links: [],
+            lookup: {},
+            revLinkToKey: {}
+        };
+
+
+    // SVG elements;
+    var linkG, linkLabelG, nodeG;
+
+    // D3 selections;
+    var link, linkLabel, node;
+
+    // default settings for force layout
+    var defaultSettings = {
+        gravity: 0.4,
+        friction: 0.7,
+        charge: {
+            // note: key is node.class
+            device: -8000,
+            host: -5000,
+            _def_: -12000
+        },
+        linkDistance: {
+            // note: key is link.type
+            direct: 100,
+            optical: 120,
+            hostLink: 3,
+            _def_: 50
+        },
+        linkStrength: {
+            // note: key is link.type
+            // range: {0.0 ... 1.0}
+            //direct: 1.0,
+            //optical: 1.0,
+            //hostLink: 1.0,
+            _def_: 1.0
+        }
+    };
+
+
+    // force layout tick function
+    function tick() {
+
+    }
+
+
+    function selectCb() { }
+    function atDragEnd() {}
+    function dragEnabled() {}
+    function clickEnabled() {}
+
+
+    // ==========================
+
+    angular.module('ovTopo')
+    .factory('TopoForceService',
+        ['$log', 'SvgUtilService',
+
+        function (_$log_, _sus_) {
+            $log = _$log_;
+            sus = _sus_;
+
+            // forceG is the SVG group to display the force layout in
+            // w, h are the initial dimensions of the SVG
+            // opts are, well, optional :)
+            function initForce (forceG, w, h, opts) {
+                // TODO: create the force layout and initialize
+                settings = angular.extend({}, defaultSettings, opts);
+
+                linkG = forceG.append('g').attr('id', 'topo-links');
+                linkLabelG = forceG.append('g').attr('id', 'topo-linkLabels');
+                nodeG = forceG.append('g').attr('id', 'topo-nodes');
+
+                link = linkG.selectAll('.link');
+                linkLabel = linkLabelG.selectAll('.linkLabel');
+                node = nodeG.selectAll('.node');
+
+                force = d3.layout.force()
+                    .size(w, h)
+                    .nodes(network.nodes)
+                    .links(network.links)
+                    .gravity(settings.gravity)
+                    .friction(settings.friction)
+                    .charge(settings.charge._def_)
+                    .linkDistance(settings.linkDistance._def_)
+                    .linkStrength(settings.linkStrength._def_)
+                    .on('tick', tick);
+
+                drag = sus.createDragBehavior(force,
+                    selectCb, atDragEnd, dragEnabled, clickEnabled);
+            }
+
+            function resize(w, h) {
+                force.size(w, h);
+                // Review -- do we need to nudge the layout ?
+            }
+
+            return {
+                initForce: initForce,
+                resize: resize
+            };
+        }]);
+}());
diff --git a/web/gui/src/main/webapp/tests/app/fw/svg/svgUtil-spec.js b/web/gui/src/main/webapp/tests/app/fw/svg/svgUtil-spec.js
new file mode 100644
index 0000000..7caa83e
--- /dev/null
+++ b/web/gui/src/main/webapp/tests/app/fw/svg/svgUtil-spec.js
@@ -0,0 +1,46 @@
+/*
+ * 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 -- SVG -- SVG Util Service - Unit Tests
+ */
+describe('factory: fw/svg/svgUtil.js', function() {
+    var $log, fs, sus, d3Elem;
+
+    beforeEach(module('onosUtil', 'onosSvg'));
+
+    beforeEach(inject(function (_$log_, FnService, SvgUtilService) {
+        $log = _$log_;
+        fs = FnService;
+        sus = SvgUtilService;
+        d3Elem = d3.select('body').append('svg').append('defs').attr('id', 'myDefs');
+    }));
+
+    afterEach(function () {
+        d3.select('svg').remove();
+    });
+
+    it('should define SvgUtilService', function () {
+        expect(sus).toBeDefined();
+    });
+
+    it('should define api functions', function () {
+        expect(fs.areFunctions(sus, [
+            'createDragBehavior', 'loadGlow', 'cat7'
+        ])).toBeTruthy();
+    });
+
+});
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoEvent-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoEvent-spec.js
new file mode 100644
index 0000000..1fd1572
--- /dev/null
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoEvent-spec.js
@@ -0,0 +1,42 @@
+/*
+ * 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 -- Topo View -- Topo Event Service - Unit Tests
+ */
+describe('factory: view/topo/topoEvent.js', function() {
+    var $log, fs, tes;
+
+    beforeEach(module('ovTopo', 'onosUtil'));
+
+    beforeEach(inject(function (_$log_, FnService, TopoEventService) {
+        $log = _$log_;
+        fs = FnService;
+        tes = TopoEventService;
+    }));
+
+    it('should define TopoEventService', function () {
+        expect(tes).toBeDefined();
+    });
+
+    it('should define api functions', function () {
+        expect(fs.areFunctions(tes, [
+            'bindDispatcher', 'openSock', 'closeSock'
+        ])).toBeTruthy();
+    });
+
+    // TODO: more tests...
+});
diff --git a/web/gui/src/main/webapp/tests/app/view/topo/topoForce-spec.js b/web/gui/src/main/webapp/tests/app/view/topo/topoForce-spec.js
new file mode 100644
index 0000000..e5f0073
--- /dev/null
+++ b/web/gui/src/main/webapp/tests/app/view/topo/topoForce-spec.js
@@ -0,0 +1,42 @@
+/*
+ * 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 -- Topo View -- Topo Force Service - Unit Tests
+ */
+describe('factory: view/topo/topoForce.js', function() {
+    var $log, fs, tfs;
+
+    beforeEach(module('ovTopo', 'onosUtil'));
+
+    beforeEach(inject(function (_$log_, FnService, TopoForceService) {
+        $log = _$log_;
+        fs = FnService;
+        tfs = TopoForceService;
+    }));
+
+    it('should define TopoForceService', function () {
+        expect(tfs).toBeDefined();
+    });
+
+    it('should define api functions', function () {
+        expect(fs.areFunctions(tfs, [
+            'initForce'
+        ])).toBeTruthy();
+    });
+
+    // TODO: more tests...
+});
diff --git a/web/gui/src/main/webapp/tests/karma.conf.js b/web/gui/src/main/webapp/tests/karma.conf.js
index 3f08b15..784868e 100644
--- a/web/gui/src/main/webapp/tests/karma.conf.js
+++ b/web/gui/src/main/webapp/tests/karma.conf.js
@@ -25,12 +25,15 @@
         // production code...
         // make sure modules are defined first...
         '../app/onos.js',
-        '../app/directives.js',
+
         '../app/fw/util/util.js',
         '../app/fw/svg/svg.js',
         '../app/fw/remote/remote.js',
         '../app/fw/widget/widget.js',
         '../app/fw/layer/layer.js',
+
+        '../app/view/topo/topo.js',
+
         // now load services etc. that augment the modules
         '../app/**/*.js',