TopoRegions: created skeleton Topo2 UI view for development of the "region-aware" topology.
 - Added initial event generation (layout/region/ etc.) -- WIP

Change-Id: I2f93eea7505ff0400085d7f67491f6b61231cb86
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 0f370cf..bd1f44d 100644
--- a/web/gui/src/main/webapp/app/fw/remote/websocket.js
+++ b/web/gui/src/main/webapp/app/fw/remote/websocket.js
@@ -256,6 +256,8 @@
         });
     }
 
+    // TODO: simplify listener handling (see theme.js for sample code)
+
     function addOpenListener(callback) {
         var id = nextListenerId++,
             cb = fs.isF(callback),
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css b/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
new file mode 100644
index 0000000..63b5dfb
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016-present 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 View (theme) -- CSS file
+ */
+
+/* --- Base SVG Layer --- */
+
+#ov-topo2 svg {
+    /*background-color: #f4f4f4;*/
+    background-color: goldenrod; /* just for testing */
+}
+
+/* --- "No Devices" Layer --- */
+
+#ov-topo2 svg .noDevsBird {
+    fill: #db7773;
+}
+
+#ov-topo2 svg #topo2-noDevsLayer text {
+    fill: #7e9aa8;
+}
+
+/* --- Topo Map --- */
+
+#ov-topo2 svg #topo2-map {
+    stroke-width: 2px;
+    stroke: #f4f4f4;
+    fill: #e5e5e6;
+}
+
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.css b/web/gui/src/main/webapp/app/view/topo2/topo2.css
new file mode 100644
index 0000000..7f878a5
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.css
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016-present 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 View (layout) -- CSS file
+ */
+
+/* --- Base SVG Layer --- */
+
+#ov-topo2 svg {
+    /* prevents the little cut/copy/paste square that would appear on iPad */
+    -webkit-user-select: none;
+}
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.html b/web/gui/src/main/webapp/app/view/topo2/topo2.html
new file mode 100644
index 0000000..9b987a6
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.html
@@ -0,0 +1,7 @@
+<!-- Topology View partial HTML -->
+<div id="ov-topo2">
+    <svg viewBox="0 0 1000 1000"
+         resize offset-height="56" offset-width="12"
+         notifier="notifyResize()">
+    </svg>
+</div>
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.js b/web/gui/src/main/webapp/app/view/topo2/topo2.js
new file mode 100644
index 0000000..ce90af6
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.js
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2016-present 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 View Module
+
+ NOTE: currently under development to support Regions.
+ */
+
+(function () {
+    'use strict';
+
+    // references to injected services
+    var $scope, $log, $loc,
+        fs, mast, ks, zs,
+        gs, ms, sus, flash,
+        wss, ps, th,
+        t2es, t2fs;
+
+    // DOM elements
+    var ovtopo2, svg, defs, zoomLayer, mapG, spriteG, forceG, noDevsLayer;
+
+    // Internal state
+    var zoomer, actionMap;
+
+
+    // === Helper Functions
+
+    // callback invoked when the SVG view has been resized..
+    function svgResized(s) {
+        $log.debug("topo2 view resized", s);
+    }
+
+    function setUpKeys(overlayKeys) {
+        $log.debug('topo2: set up keys....');
+    }
+
+    // === Controller Definition -----------------------------------------
+
+    angular.module('ovTopo2', ['onosUtil', 'onosSvg', 'onosRemote'])
+    .controller('OvTopo2Ctrl', 
+        ['$scope', '$log', '$location',
+        'FnService', 'MastService', 'KeyService', 'ZoomService',
+        'GlyphService', 'MapService', 'SvgUtilService', 'FlashService',
+        'WebSocketService', 'PrefsService', 'ThemeService',
+        'Topo2EventService', 'Topo2ForceService',
+
+        function (_$scope_, _$log_, _$loc_,
+                  _fs_, _mast_, _ks_, _zs_,
+                  _gs_, _ms_, _sus_, _flash_,
+                  _wss_, _ps_, _th_,
+                  _t2es_, _t2fs_) {
+
+            var params = _$loc_.search(),
+                projection,
+                dim,
+                wh,
+                uplink = {
+                    // provides function calls back into this space
+                    // showNoDevs: showNoDevs,
+                    // projection: function () { return projection; },
+                    // zoomLayer: function () { return zoomLayer; },
+                    // zoomer: function () { return zoomer; },
+                    // opacifyMap: opacifyMap,
+                    // topoStartDone: topoStartDone
+                };
+
+            $scope = _$scope_;
+            $log = _$log_;
+            $loc = _$loc_;
+
+            fs = _fs_;
+            mast = _mast_;
+            ks = _ks_;
+            zs = _zs_;
+
+            gs = _gs_;
+            ms = _ms_;
+            sus = _sus_;
+            flash = _flash_;
+
+            wss = _wss_;
+            ps = _ps_;
+            th = _th_;
+            
+            t2es = _t2es_;
+            t2fs = _t2fs_;
+
+            // capture selected intent parameters (if they are set in the
+            //  query string) so that the traffic overlay can highlight
+            //  the path for that intent
+            if (params.intentKey && params.intentAppId && params.intentAppName) {
+                $scope.intentData = {
+                    key: params.intentKey,
+                    appId: params.intentAppId,
+                    appName: params.intentAppName
+                };
+            }
+
+            $scope.notifyResize = function () {
+                svgResized(fs.windowSize(mast.mastHeight()));
+            };
+
+            // Cleanup on destroyed scope..
+            $scope.$on('$destroy', function () {
+                $log.log('OvTopo2Ctrl is saying Buh-Bye!');
+                t2es.stop();
+                ks.unbindKeys();
+                t2fs.destroy();
+            });
+
+            // svg layer and initialization of components
+            ovtopo2 = d3.select('#ov-topo2');
+            svg = ovtopo2.select('svg');
+            // set the svg size to match that of the window, less the masthead
+            wh = fs.windowSize(mast.mastHeight());
+            $log.debug('setting topo SVG size to', wh);
+            svg.attr(wh);
+            dim = [wh.width, wh.height];
+
+
+            // set up our keyboard shortcut bindings
+            setUpKeys();
+
+            // make sure we can respond to topology events from the server
+            t2es.bindHandlers();
+
+            // initialize the force layout, ready to render the topology
+            t2fs.init();
+
+
+            // =-=-=-=-=-=-=-=-
+            // TODO: in future, we will load background map data
+            //  asynchronously (hence the promise) and then chain off 
+            //  there to send the topo2start event to the server.
+            // For now, we'll send the event inline...
+            t2es.start();
+
+            
+            // === ORIGINAL CODE ===
+            
+            // setUpKeys();
+            // setUpToolbar();
+            // setUpDefs();
+            // setUpZoom();
+            // setUpNoDevs();
+            /*
+            setUpMap().then(
+                function (proj) {
+                    var z = ps.getPrefs('topo_zoom', { tx:0, ty:0, sc:1 });
+                    zoomer.panZoom([z.tx, z.ty], z.sc);
+                    $log.debug('** Zoom restored:', z);
+
+                    projection = proj;
+                    $log.debug('** We installed the projection:', proj);
+                    flash.enable(false);
+                    toggleMap(prefsState.bg);
+                    flash.enable(true);
+                    mapShader(true);
+
+                    // now we have the map projection, we are ready for
+                    //  the server to send us device/host data...
+                    tes.start();
+                    // need to do the following so we immediately get
+                    //  the summary panel data back from the server
+                    restoreSummaryFromPrefs();
+                }
+            );
+            */
+            // tes.bindHandlers();
+            // setUpSprites();
+
+            // forceG = zoomLayer.append('g').attr('id', 'topo-force');
+            // tfs.initForce(svg, forceG, uplink, dim);
+            // tis.initInst({ showMastership: tfs.showMastership });
+            // tps.initPanels();
+
+            // restoreConfigFromPrefs();
+            // ttbs.setDefaultOverlay(prefsState.ovidx);
+
+            // $log.debug('registered overlays...', tov.list());
+            
+            $log.log('OvTopo2Ctrl has been created');
+        }]);
+}());
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Event.js b/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
new file mode 100644
index 0000000..83dcae2
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2016-present 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 the conduit between the client and the server:
+ - provides a clean API for sending events to the server
+ - dispatches incoming events from the server to the appropriate sub-module
+
+ */
+
+(function () {
+    'use strict';
+
+    // injected refs
+    var $log, wss, t2fs;
+
+    // internal state
+    var handlerMap,
+        openListener;
+
+    // TODO: only add heartbeat timer etc. if we really need to be doing that..
+
+    // ========================== Helper Functions
+
+    function createHandlerMap() {
+        handlerMap = {
+            topo2AllInstances: t2fs,
+            topo2CurrentLayout: t2fs,
+            topo2CurrentRegion: t2fs,
+            topo2StartDone: t2fs
+
+            // Add further event names / module references as needed
+        };
+    }
+
+    function wsOpen(host, url) {
+        $log.debug('topo2Event: WSopen - cluster node:', host, 'URL:', url);
+        // tell the server we are ready to receive topo events
+        wss.sendEvent('topo2Start');
+    }
+
+    // bind our event handlers to the web socket service, so that our
+    //  callbacks get invoked for incoming events
+    function bindHandlers() {
+        wss.bindHandlers(handlerMap);
+        $log.debug('topo2 event handlers bound');
+    }
+
+    // tell the server we are ready to receive topology events
+    function start() {
+        // in case we fail over to a new server,
+        // listen for wsock-open events
+        openListener = wss.addOpenListener(wsOpen);
+        wss.sendEvent('topo2Start');
+        $log.debug('topo2 comms started');
+    }
+
+    // tell the server we no longer wish to receive topology events
+    function stop() {
+        wss.sendEvent('topo2Stop');
+        wss.unbindHandlers(handlerMap);
+        wss.removeOpenListener(openListener);
+        openListener = null;
+        $log.debug('topo2 comms stopped');
+    }
+
+    // ========================== Main Service Definition
+
+    angular.module('ovTopo2')
+    .factory('Topo2EventService',
+        ['$log', 'WebSocketService', 'Topo2ForceService',
+
+        function (_$log_, _wss_, _t2fs_) {
+            $log = _$log_;
+            wss = _wss_;
+            t2fs = _t2fs_;
+
+            // deferred creation of handler map, so module references are good
+            createHandlerMap();
+
+            return {
+                bindHandlers: bindHandlers,
+                start: start,
+                stop: stop
+            };
+        }]);
+}());
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
new file mode 100644
index 0000000..db6a1c2
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016-present 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 Force Module.
+ Visualization of the topology in an SVG layer, using a D3 Force Layout.
+ */
+
+(function () {
+    'use strict';
+
+    // injected refs
+    var $log, wss;
+
+    // ========================== Helper Functions
+
+    function init() {
+        $log.debug('Initialize topo force layout');
+    }
+
+    function destroy() {
+        $log.debug('Destroy topo force layout');
+    }
+
+    // ========================== Event Handlers
+
+    function allInstances(data) {
+        $log.debug('>> topo2AllInstances event:', data)
+    }
+
+    function currentLayout(data) {
+        $log.debug('>> topo2CurrentLayout event:', data)
+    }
+
+    function currentRegion(data) {
+        $log.debug('>> topo2CurrentRegion event:', data)
+    }
+
+    function startDone(data) {
+        $log.debug('>> topo2StartDone event:', data)
+    }
+    
+    // ========================== Main Service Definition
+
+    angular.module('ovTopo2')
+    .factory('Topo2ForceService',
+        ['$log', 'WebSocketService',
+
+        function (_$log_, _wss_) {
+            $log = _$log_;
+            wss = _wss_;
+            
+            return {
+                init: init,
+                destroy: destroy,
+                topo2AllInstances: allInstances,
+                topo2CurrentLayout: currentLayout,
+                topo2CurrentRegion: currentRegion,
+                topo2StartDone: startDone
+            };
+        }]);
+}());
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index e4fa6e2..22865a1 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -126,6 +126,13 @@
     <link rel="stylesheet" href="app/fw/widget/table.css">
     <link rel="stylesheet" href="app/fw/widget/table-theme.css">
 
+    <!-- Under development for Region support. -->
+    <script src="app/view/topo2/topo2.js"></script>
+    <script src="app/view/topo2/topo2Event.js"></script>
+    <script src="app/view/topo2/topo2Force.js"></script>
+    <link rel="stylesheet" href="app/view/topo2/topo2.css">
+    <link rel="stylesheet" href="app/view/topo2/topo2-theme.css">
+
     <!-- Builtin views javascript. -->
     <script src="app/view/topo/topo.js"></script>
     <script src="app/view/topo/topoD3.js"></script>