Implement navigate-to-region (topo2navRegion) event; update scaffolding view (topoX) to exercise.

Change-Id: Ib342f791854664b31bd1724a446008c960231784
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
index c1f6e03..3513b79 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2ViewMessageHandler.java
@@ -94,6 +94,26 @@
     // ==================================================================
 
 
+    private ObjectNode mkLayoutMessage(UiTopoLayout currentLayout) {
+        List<UiTopoLayout> crumbs = topoSession.breadCrumbs();
+        return t2json.layout(currentLayout, crumbs);
+    }
+
+    private ObjectNode mkRegionMessage(UiTopoLayout currentLayout) {
+        UiRegion region = topoSession.getRegion(currentLayout);
+        Set<UiRegion> kids = topoSession.getSubRegions(currentLayout);
+        List<UiSynthLink> links = topoSession.getLinks(currentLayout);
+        return t2json.region(region, kids, links);
+    }
+
+    private ObjectNode mkPeersMessage(UiTopoLayout currentLayout) {
+        Set<UiNode> peers = topoSession.getPeerNodes(currentLayout);
+        ObjectNode peersPayload = objectNode();
+        peersPayload.set("peers", t2json.closedNodes(peers));
+        return peersPayload;
+    }
+
+
     private final class Topo2Start extends RequestHandler {
         private Topo2Start() {
             super(START);
@@ -112,34 +132,26 @@
             //  correctly
             topoSession.refreshModel();
 
-            // this is the list of ONOS cluster members
+            // start with the list of ONOS cluster members
             List<UiClusterMember> instances = topoSession.getAllInstances();
             sendMessage(ALL_INSTANCES, t2json.instances(instances));
 
+
+            // Send layout, region, peers data...
+
             // this is the layout that the user has chosen to display
             UiTopoLayout currentLayout = topoSession.currentLayout();
-            List<UiTopoLayout> crumbs = topoSession.breadCrumbs();
-            sendMessage(CURRENT_LAYOUT, t2json.layout(currentLayout, crumbs));
+            sendMessage(CURRENT_LAYOUT, mkLayoutMessage(currentLayout));
 
             // this is the region that is associated with the current layout
             //   this message includes details of the sub-regions, devices,
             //   hosts, and links within the region
             //   (as well as layer-order hints)
-            UiRegion region = topoSession.getRegion(currentLayout);
-            Set<UiRegion> kids = topoSession.getSubRegions(currentLayout);
-            List<UiSynthLink> links = topoSession.getLinks(currentLayout);
-            sendMessage(CURRENT_REGION, t2json.region(region, kids, links));
+            sendMessage(CURRENT_REGION, mkRegionMessage(currentLayout));
 
             // these are the regions/devices that are siblings to this region
-            Set<UiNode> peers = topoSession.getPeerNodes(currentLayout);
-            ObjectNode peersPayload = objectNode();
-            peersPayload.set("peers", t2json.closedNodes(peers));
-            sendMessage(PEER_REGIONS, peersPayload);
-
-            // finally, tell the UI that we are done : TODO review / delete??
-            sendMessage(TOPO_START_DONE, null);
+            sendMessage(PEER_REGIONS, mkPeersMessage(currentLayout));
         }
-
     }
 
     private final class Topo2NavRegion extends RequestHandler {
@@ -149,9 +161,19 @@
 
         @Override
         public void process(long sid, ObjectNode payload) {
-            String dir = string(payload, "dir");
             String rid = string(payload, "rid");
-            log.debug("NavRegion: dir={}, rid={}", dir, rid);
+            log.debug("topo2navRegion: rid={}", rid);
+
+            // NOTE: we are NOT re-issuing information about the cluster nodes
+
+            // switch to the selected region...
+            topoSession.navToRegion(rid);
+
+            // re-send layout, region, peers data...
+            UiTopoLayout currentLayout = topoSession.currentLayout();
+            sendMessage(CURRENT_LAYOUT, mkLayoutMessage(currentLayout));
+            sendMessage(CURRENT_REGION, mkRegionMessage(currentLayout));
+            sendMessage(PEER_REGIONS, mkPeersMessage(currentLayout));
         }
     }
 
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
index f6552dd..2b27347 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
@@ -250,4 +250,18 @@
     public void refreshModel() {
         sharedModel.refresh();
     }
+
+    /**
+     * Navigates to the specified region by setting the associated layout as
+     * current.
+     *
+     * @param regionId region identifier
+     */
+    public void navToRegion(String regionId) {
+        // 1. find the layout corresponding to the region ID
+        // 2. set this layout to be "current"
+        RegionId r = RegionId.regionId(regionId);
+        UiTopoLayout layout = layoutService.getLayout(r);
+        setCurrentLayout(layout);
+    }
 }
diff --git a/web/gui/src/main/webapp/app/view/topoX/topoX.css b/web/gui/src/main/webapp/app/view/topoX/topoX.css
index a0bedc6..3cb0514 100644
--- a/web/gui/src/main/webapp/app/view/topoX/topoX.css
+++ b/web/gui/src/main/webapp/app/view/topoX/topoX.css
@@ -27,18 +27,23 @@
 }
 
 #topoXtmp {
-    height: 600px;
+    height: 700px;
     width: 98%;
     overflow-y: scroll;
 }
 
 #topoXtmp div {
-    padding: 8px 24px;
-    margin: 8px;
+    padding: 2px 24px;
+    margin: 6px;
     background-color: #ddddff;
 }
 #topoXtmp div div {
-    padding: 4px 10px;
+    padding: 0 10px;
+}
+#topoXtmp div div span {
+    color: purple;
+    font-style: italic;
+    font-weight: bold;
 }
 
 #topoXtmp h4 {
@@ -54,4 +59,9 @@
     font-weight: bold;
     text-decoration: underline;
     cursor: pointer;
+    color: blue;
+}
+
+.subRegions div p {
+    width: 40px;
 }
diff --git a/web/gui/src/main/webapp/app/view/topoX/topoX.html b/web/gui/src/main/webapp/app/view/topoX/topoX.html
index 41a94a5..c5747dd 100644
--- a/web/gui/src/main/webapp/app/view/topoX/topoX.html
+++ b/web/gui/src/main/webapp/app/view/topoX/topoX.html
@@ -17,11 +17,33 @@
 <!-- Topology View partial HTML -->
 <div id="ov-topoX">
     <div id="topoXtmp">
-        <div class="parentRegion">
-            Parent Region: <span> - </span>
+        <div class="instances">
+            <h4>Instances</h4>
+            <div></div>
+        </div>
+        <div class="layoutData">
+
+            <h4>Layout Data</h4>
+
+            <div class="l_id">
+                Layout ID: <span> - </span>
+            </div>
+            <div class="l_parent">
+                Layout Parent: <span> - </span>
+            </div>
+            <div class="l_region">
+                Region ID: <span> - </span>
+            </div>
+            <div class="l_regionName">
+                Region Name: <span> - </span>
+            </div>
+            <div class="l_crumbs">
+                Breadcrumbs: <span> </span>
+            </div>
+
         </div>
         <div class="thisRegion">
-            This Region: <span> - </span>
+            <b>This Region ID:</b> <span></span>
         </div>
         <div class="subRegions">
             <h4>Subregions</h4>
diff --git a/web/gui/src/main/webapp/app/view/topoX/topoXForce.js b/web/gui/src/main/webapp/app/view/topoX/topoXForce.js
index 9797a45..b2e5e87 100644
--- a/web/gui/src/main/webapp/app/view/topoX/topoXForce.js
+++ b/web/gui/src/main/webapp/app/view/topoX/topoXForce.js
@@ -25,9 +25,13 @@
     // injected refs
     var $log, wss;
 
+    // selected DOM refs
+    var topdiv;
+
     // ========================== Helper Functions
 
     function init() {
+        topdiv = d3.select('#topoXtmp');
         $log.debug('Initialize topo force layout');
     }
 
@@ -35,44 +39,82 @@
         $log.debug('Destroy topo force layout');
     }
 
-    // ========================== Temporary Code (to be deleted later)
+    function rmP(div) {
+        div.selectAll('p').remove();
+    }
 
-    function request(dir, rid) {
+    function navRequest(rid) {
         wss.sendEvent('topo2navRegion', {
-            dir: dir,
             rid: rid
         });
     }
 
+    function doTmpInstances(data) {
+        var idiv = topdiv.select('.instances').select('div');
+        data.members.forEach(function (m) {
+            idiv.append('div').text(m.id);
+        });
+    }
+
     function doTmpCurrentLayout(data) {
-        var topdiv = d3.select('#topoXtmp');
-        var parentRegion = data.parent;
-        var span = topdiv.select('.parentRegion').select('span');
-        span.text(parentRegion || '[no parent]');
-        span.classed('nav-me', !!parentRegion);
+        var ldDiv = topdiv.select('.layoutData'),
+            bcs = ldDiv.select('.l_crumbs');
+
+        function setSpan(v, val) {
+            var cls = '.l_' + v,
+                span = ldDiv.select(cls).select('span'),
+                value = val || data[v];
+            span.html(value);
+        }
+
+        setSpan('id');
+        setSpan('parent');
+        setSpan('region');
+        setSpan('regionName');
+
+        addCrumbNav(bcs, data.crumbs, data.region);
+    }
+
+    function addCrumbNav(span, array, id) {
+        var rev = [];
+
+        span.selectAll('span').remove();
+
+        array.forEach(function (a) {
+            rev.unshift(a.id);
+        });
+
+        rev.forEach(function (rid, idx) {
+            if (idx) {
+                span.append('span').text(' +++ ');
+            }
+            if (rid != id) {
+                addNavigable(span, 'span', rid);
+            } else {
+                span.append('span').text(rid);
+            }
+        });
+    }
+
+    function addNavigable(span, what, rid) {
+        span.append(what).classed('nav-me', true)
+            .text(rid)
+            .on('click', function () { navRequest(rid); });
     }
 
     function doTmpCurrentRegion(data) {
-        var topdiv = d3.select('#topoXtmp');
         var span = topdiv.select('.thisRegion').select('span');
         var div;
-
         span.text(data.id);
 
         div = topdiv.select('.subRegions').select('div');
+        rmP(div);
         data.subregions.forEach(function (r) {
-
-            function nav() {
-                request('down', r.id);
-            }
-
-            div.append('p')
-                .classed('nav-me', true)
-                .text(r.id)
-                .on('click', nav);
+            addNavigable(div, 'p', r.id);
         });
 
         div = topdiv.select('.devices').select('div');
+        rmP(div);
         data.layerOrder.forEach(function (tag, idx) {
             var devs = data.devices[idx];
             devs.forEach(function (d) {
@@ -83,6 +125,7 @@
         });
 
         div = topdiv.select('.hosts').select('div');
+        rmP(div);
         data.layerOrder.forEach(function (tag, idx) {
             var hosts = data.hosts[idx];
             hosts.forEach(function (h) {
@@ -92,8 +135,8 @@
         });
 
         div = topdiv.select('.links').select('div');
-        var links = data.links;
-        links.forEach(function (lnk) {
+        rmP(div);
+        data.links.forEach(function (lnk) {
             div.append('p')
                 .text(lnk.id);
         });
@@ -106,26 +149,27 @@
     // ========================== Event Handlers
 
     function allInstances(data) {
-        $log.debug('>> topo2AllInstances event:', data)
-        doTmpCurrentLayout(data);
+        $log.debug('>> topo2AllInstances event:', data);
+        doTmpInstances(data);
     }
 
     function currentLayout(data) {
-        $log.debug('>> topo2CurrentLayout event:', data)
+        $log.debug('>> topo2CurrentLayout event:', data);
+        doTmpCurrentLayout(data);
     }
 
     function currentRegion(data) {
-        $log.debug('>> topo2CurrentRegion event:', data)
+        $log.debug('>> topo2CurrentRegion event:', data);
         doTmpCurrentRegion(data);
     }
 
     function peerRegions(data) {
-        $log.debug('>> topo2PeerRegions event:', data)
+        $log.debug('>> topo2PeerRegions event:', data);
         doTmpPeerRegions(data);
     }
 
     function startDone(data) {
-        $log.debug('>> topo2StartDone event:', data)
+        $log.debug('>> topo2StartDone event:', data);
     }
     
     // ========================== Main Service Definition