Clean up topo2 initialization.  (WIP)

Change-Id: I417800019a5ebdf90da0f29ef11e7c05a4999b77
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
index e1d403d..be0ab24 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
@@ -92,7 +92,7 @@
         this.directory = directory;
         this.userName = userName;
 
-        Topo2Jsonifier t2json = new Topo2Jsonifier(directory);
+        Topo2Jsonifier t2json = new Topo2Jsonifier(directory, userName);
         UiSharedTopologyModel sharedModel = directory.get(UiSharedTopologyModel.class);
         UiTopoLayoutService layoutService = directory.get(UiTopoLayoutService.class);
 
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
index 4b12e68..c26c6fd 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
@@ -42,6 +42,7 @@
 import org.onosproject.net.topology.TopologyService;
 import org.onosproject.ui.JsonUtils;
 import org.onosproject.ui.UiExtensionService;
+import org.onosproject.ui.UiPreferencesService;
 import org.onosproject.ui.UiTopoMap;
 import org.onosproject.ui.UiTopoMapFactory;
 import org.onosproject.ui.impl.topo.model.UiModelEvent;
@@ -54,6 +55,7 @@
 import org.onosproject.ui.model.topo.UiRegion;
 import org.onosproject.ui.model.topo.UiSynthLink;
 import org.onosproject.ui.model.topo.UiTopoLayout;
+import org.onosproject.ui.model.topo.UiTopoLayoutId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -91,10 +93,22 @@
     private static final String DATA = "data";
     private static final String MEMO = "memo";
 
+    private static final String GEO = "geo";
+    private static final String GRID = "grid";
+    private static final String PKEY_TOPO_ZOOM = "topo2_zoom";
+    private static final String ZOOM_SCALE = "zoomScale";
+    private static final String ZOOM_PAN_X = "zoomPanX";
+    private static final String ZOOM_PAN_Y = "zoomPanY";
+    private static final String DEFAULT_SCALE = "1.0";
+    private static final String DEFAULT_PAN = "0.0";
+
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private final ObjectMapper mapper = new ObjectMapper();
 
+    // preferences are stored per user name...
+    private final String userName;
+
     private ServiceDirectory directory;
     private ClusterService clusterService;
     private DeviceService deviceService;
@@ -108,6 +122,7 @@
     private TopologyService topologyService;
     private TunnelService tunnelService;
     private UiExtensionService uiextService;
+    private UiPreferencesService prefService;
 
 
     // NOTE: we'll stick this here for now, but maybe there is a better home?
@@ -121,9 +136,11 @@
      * on the fly.
      *
      * @param directory service directory
+     * @param userName  logged in user name
      */
-    public Topo2Jsonifier(ServiceDirectory directory) {
+    public Topo2Jsonifier(ServiceDirectory directory, String userName) {
         this.directory = checkNotNull(directory, "Directory cannot be null");
+        this.userName = checkNotNull(userName, "User name cannot be null");
 
         clusterService = directory.get(ClusterService.class);
         deviceService = directory.get(DeviceService.class);
@@ -137,10 +154,12 @@
         topologyService = directory.get(TopologyService.class);
         tunnelService = directory.get(TunnelService.class);
         uiextService = directory.get(UiExtensionService.class);
+        prefService = directory.get(UiPreferencesService.class);
     }
 
     // for unit testing
     Topo2Jsonifier() {
+        userName = "(unit-test)";
     }
 
     private ObjectNode objectNode() {
@@ -212,11 +231,45 @@
         String sprId = layout.sprites();
 
         if (mapId != null) {
-            result.put("bgType", "geo").put("bgId", mapId);
+            result.put("bgType", GEO).put("bgId", mapId);
             addMapParameters(result, mapId);
         } else if (sprId != null) {
-            result.put("bgType", "grid").put("bgId", sprId);
+            result.put("bgType", GRID).put("bgId", sprId);
         }
+        addZoomPan(result, layout.id());
+    }
+
+    private void addZoomPan(ObjectNode result, UiTopoLayoutId layoutId) {
+        // need to look up topo_zoom settings from preferences service.
+
+        // NOTE:
+        // UiPreferencesService API only allows us to retrieve ALL prefs for
+        // the given user. It would be better if we could call something like:
+        //
+        //   ObjectNode value = prefService.getPreference(userName, prefKey);
+        //
+        // to get back a single value.
+
+        Map<String, ObjectNode> userPrefs = prefService.getPreferences(userName);
+        ObjectNode zoomPrefs = userPrefs.get(PKEY_TOPO_ZOOM);
+
+        if (zoomPrefs == null) {
+            // no zoom prefs structure yet.. so initialize..
+            ObjectNode zoomForLayout = objectNode()
+                    .put(ZOOM_SCALE, DEFAULT_SCALE)
+                    .put(ZOOM_PAN_X, DEFAULT_PAN)
+                    .put(ZOOM_PAN_Y, DEFAULT_PAN);
+
+            zoomPrefs = objectNode();
+            zoomPrefs.set(layoutId.id(), zoomForLayout);
+
+            prefService.setPreference(userName, PKEY_TOPO_ZOOM, zoomPrefs);
+        }
+        ObjectNode zoomData = (ObjectNode) zoomPrefs.get(layoutId.id());
+
+        result.put("bgZoomScale", zoomData.get(ZOOM_SCALE).asText());
+        result.put("bgZoomPanX", zoomData.get(ZOOM_PAN_X).asText());
+        result.put("bgZoomPanY", zoomData.get(ZOOM_PAN_Y).asText());
     }
 
     private void addMapParameters(ObjectNode result, String mapId) {
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 ea774d9..d9e9c47 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
@@ -79,7 +79,7 @@
 
         // get the topo session from the UiWebSocket
         topoSession = ((UiWebSocket) connection).topoSession();
-        t2json = new Topo2Jsonifier(directory);
+        t2json = new Topo2Jsonifier(directory, connection.userName());
     }
 
     @Override
diff --git a/web/gui/src/main/webapp/app/fw/svg/svgUtil.js b/web/gui/src/main/webapp/app/fw/svg/svgUtil.js
index 60a8be1..8b1c499 100644
--- a/web/gui/src/main/webapp/app/fw/svg/svgUtil.js
+++ b/web/gui/src/main/webapp/app/fw/svg/svgUtil.js
@@ -154,6 +154,7 @@
             .attr('in', String);
     }
 
+    // deprecated -- we'll use something else to highlight instances for affinity
     function loadGlowDefs(defs) {
         loadGlow(defs, 0.0, 0.0, 0.7, 'blue-glow');
         loadGlow(defs, 1.0, 1.0, 0.3, 'yellow-glow');
diff --git a/web/gui/src/main/webapp/app/fw/util/prefs.js b/web/gui/src/main/webapp/app/fw/util/prefs.js
index 16f2607..1cf590b 100644
--- a/web/gui/src/main/webapp/app/fw/util/prefs.js
+++ b/web/gui/src/main/webapp/app/fw/util/prefs.js
@@ -77,7 +77,17 @@
         cache[name] = obj;
         wss.sendEvent('updatePrefReq', { key: name, value: obj });
     }
-    
+
+    // merge preferences:
+    // The assumption here is that obj is a sparse object, and that the
+    //  defined keys should overwrite the corresponding values, but any
+    //  existing keys that are NOT explicitly defined here should be left
+    //  alone (not deleted).
+    function mergePrefs(name, obj) {
+        var merged = cache[name] || {};
+        setPrefs(name, angular.extend(merged, obj));
+    }
+
     function updatePrefs(data) {
         cache = data;
         listeners.forEach(function (lsnr) { lsnr(); });
@@ -114,6 +124,7 @@
                 getPrefs: getPrefs,
                 asNumbers: asNumbers,
                 setPrefs: setPrefs,
+                mergePrefs: mergePrefs,
                 addListener: addListener,
                 removeListener: removeListener
             };
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
index 4a7a44a..a90bae5 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2-theme.css
@@ -30,29 +30,36 @@
     fill: #db7773;
 }
 
-#ov-topo2 svg #topo-noDevsLayer text {
+#ov-topo2 svg #topo2-noDevsLayer text {
     fill: #7e9aa8;
 }
 
 /* --- Topo Map --- */
 
-#ov-topo2 svg #topo-map {
+#ov-topo2 svg #topo2-map {
     stroke-width: 2px;
     stroke: #f4f4f4;
     fill: #e5e5e6;
 }
 
-/* --- general topo-panel styling --- */
+/* --- general topo2-panel styling --- */
 
-.topo-p svg {
-    background: #c0242b;
+.topo2-p h2 {
+    display: inline-block;
+    padding: 6px;
 }
 
-.topo-p svg .glyph {
+.topo2-p svg {
+    background: #c0242b;
+    width: 28px;
+    height: 28px;
+}
+
+.topo2-p svg .glyph {
     fill: #ffffff;
 }
 
-.topo-p hr {
+.topo2-p hr {
     background-color: #cccccc;
 }
 
@@ -359,35 +366,35 @@
 /* ------------------------------------------------- */
 /* Sprite Layer */
 
-#ov-topo2 svg #topo-sprites .gold1 use {
+#ov-topo2 svg #topo2-sprites .gold1 use {
     stroke: #fda;
     fill: none;
 }
-#ov-topo2 svg #topo-sprites .gold1 text {
+#ov-topo2 svg #topo2-sprites .gold1 text {
     fill: #eda;
 }
 
-#ov-topo2 svg #topo-sprites .blue1 use {
+#ov-topo2 svg #topo2-sprites .blue1 use {
     stroke: #bbd;
     fill: none;
 }
-#ov-topo2 svg #topo-sprites .blue1 text {
+#ov-topo2 svg #topo2-sprites .blue1 text {
     fill: #cce;
 }
 
-#ov-topo2 svg #topo-sprites .gray1 use {
+#ov-topo2 svg #topo2-sprites .gray1 use {
     stroke: #ccc;
     fill: none;
 }
-#ov-topo2 svg #topo-sprites .gray1 text {
+#ov-topo2 svg #topo2-sprites .gray1 text {
     fill: #ddd;
 }
 
 /* fills */
-#ov-topo2 svg #topo-sprites use.fill-gray2 {
+#ov-topo2 svg #topo2-sprites use.fill-gray2 {
     fill: #eee;
 }
 
-#ov-topo2 svg #topo-sprites use.fill-blue2 {
+#ov-topo2 svg #topo2-sprites use.fill-blue2 {
     fill: #bce;
 }
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.css b/web/gui/src/main/webapp/app/view/topo2/topo2.css
index 5e05ff3..12be44a 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2.css
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.css
@@ -133,3 +133,77 @@
     height: 28px;
 }
 
+
+/* --- general topo2-panel styling --- */
+
+.topo2-p div.header {
+    margin-bottom: 10px;
+}
+
+.topo2-p div.header div.icon {
+    vertical-align: middle;
+    display: inline-block;
+}
+.topo2-p div.body {
+    overflow-y: scroll;
+}
+
+.topo2-p div.body::-webkit-scrollbar {
+    display: none;
+}
+
+.topo2-p svg {
+    display: inline-block;
+    width: 26px;
+    height: 26px;
+}
+
+
+.topo2-p h2 {
+    padding: 0 0 0 10px;
+    margin: 0;
+    font-weight: lighter;
+    word-wrap: break-word;
+    display: inline-block;
+    vertical-align: middle;
+}
+
+.topo2-p h3 {
+    padding: 0 4px;
+    margin: 0;
+    word-wrap: break-word;
+    top: 20px;
+    left: 50px;
+}
+
+.topo2-p p,
+.topo2-p table {
+    padding: 0;
+    margin: 0;
+    width: 100%;
+}
+
+.topo2-p td {
+    word-wrap: break-word;
+}
+.topo2-p td.label {
+    font-weight: bold;
+    padding: 0 10px 0 0;
+}
+.topo2-p td.value {
+    padding: 0;
+}
+
+#topo2-p-summary  td.label {
+    width: 50%;
+}
+
+#topo2-p-detail  div.actionBtns {
+    padding-top: 6px;
+}
+
+.topo2-p hr {
+    height: 1px;
+    border: 0;
+    margin: 4px -3px;
+}
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.js b/web/gui/src/main/webapp/app/view/topo2/topo2.js
index be3f65b..e443d39 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.js
@@ -15,7 +15,7 @@
 */
 
 /*
- ONOS GUI -- Topology View Module
+ ONOS GUI -- Topology (2) View Module
 
  NOTE: currently under development to support Regions.
  */
@@ -31,13 +31,17 @@
     var ovtopo2, svg, defs, zoomLayer, forceG;
 
     // Internal state
-    var zoomer;
+    var zoomer,
+        currentLayoutId = '_default_';  // NOTE: see UiTopoLayoutId.DEFAULT_STR
+
 
     // --- Glyphs, Icons, and the like -----------------------------------
 
     function setUpDefs() {
         defs = svg.append('defs');
         gs.loadDefs(defs);
+
+        // TODO: consider using something other than the "glow" styles
         sus.loadGlowDefs(defs);
     }
 
@@ -60,13 +64,20 @@
 
     function zoomCallback() {
         var sc = zoomer.scale(),
-            tr = zoomer.translate();
+            tr = zoomer.translate(),
+            sparse = {};
 
-        ps.setPrefs('topo_zoom', { tx: tr[0], ty: tr[1], sc: sc });
+        sparse[currentLayoutId] =  {
+            zoomScale: sc,
+            zoomPanX: tr[0],
+            zoomPanY: tr[1]
+        };
+
+        ps.mergePrefs('topo2_zoom', sparse);
     }
 
     function setUpZoom() {
-        zoomLayer = svg.append('g').attr('id', 'topo-zoomlayer');
+        zoomLayer = svg.append('g').attr('id', 'topo2-zoomlayer');
 
         zoomer = t2zs.createZoomer({
             svg: svg,
@@ -80,22 +91,25 @@
 
     angular.module('ovTopo2', ['onosUtil', 'onosSvg', 'onosRemote'])
     .controller('OvTopo2Ctrl', [
-        '$scope', '$log', '$location', 'FnService', 'MastService', 'KeyService',
-        'GlyphService', 'MapService', 'SvgUtilService', 'FlashService',
-        'WebSocketService', 'PrefsService', 'ThemeService',
+        '$scope', '$log', '$location',
+        'FnService', 'MastService', 'KeyService', 'GlyphService', 'MapService',
+        'SvgUtilService', 'FlashService', 'WebSocketService',
+        'PrefsService', 'ThemeService',
         'Topo2EventService', 'Topo2ForceService', 'Topo2InstanceService',
         'Topo2BreadcrumbService', 'Topo2KeyCommandService', 'Topo2MapService',
-        'Topo2MapConfigService', 'Topo2ZoomService',
-        'Topo2SummaryPanelService', 'Topo2DeviceDetailsPanel', 'Topo2SpriteLayerService',
+        'Topo2MapConfigService', 'Topo2ZoomService', 'Topo2SpriteLayerService',
+        'Topo2SummaryPanelService', 'Topo2DeviceDetailsPanel',
 
-        function (_$scope_, _$log_, _$loc_,
-            _fs_, _mast_, _ks_,
-            _gs_, _ms_, _sus_, _flash_,
-            _wss_, _ps_, _th_,
-            _t2es_, _t2fs_, _t2is_, _t2bcs_, _t2kcs_, _t2ms_, _t2mcs_,
-            _t2zs_, summaryPanel, detailsPanel, t2sls
+        function (
+            _$scope_, _$log_, _$loc_,
+            _fs_, _mast_, _ks_, _gs_, _ms_,
+            _sus_, _flash_, _wss_,
+            _ps_, _th_,
+            _t2es_, _t2fs_, _t2is_,
+            _t2bcs_, _t2kcs_, _t2ms_,
+            _t2mcs_, _t2zs_, t2sls,
+            summaryPanel, detailsPanel
         ) {
-
             var params = _$loc_.search(),
                 dim,
                 wh,
@@ -106,7 +120,6 @@
                     zoomLayer: function () { return zoomLayer; },
                     zoomer: function () { return zoomer; }
                     // opacifyMap: opacifyMap,
-                    // topoStartDone: topoStartDone
                 };
 
             $scope = _$scope_;
@@ -115,10 +128,8 @@
             fs = _fs_;
             mast = _mast_;
             ks = _ks_;
-
             gs = _gs_;
             sus = _sus_;
-
             ps = _ps_;
 
             t2es = _t2es_;
@@ -140,7 +151,8 @@
                 $scope.intentData = {
                     key: params.intentKey,
                     appId: params.intentAppId,
-                    appName: params.intentAppName
+                    appName: params.intentAppName,
+                    intentType: params.intentType
                 };
             }
 
@@ -176,21 +188,34 @@
             // make sure we can respond to topology events from the server
             t2es.bindHandlers();
 
-            // Add the map SVG Group
-            t2ms.init(zoomLayer, zoomer).then(
-                function (proj) {
-                    var z = ps.getPrefs('topo_zoom', { tx: 0, ty: 0, sc: 1 });
-                    zoomer.panZoom([z.tx, z.ty], z.sc);
+            // Add the map SVG Group, but don't load a map yet...
+            //   we will wait until the server tells us whether we should
+            //   be loading a geomap or a sprite layer
+            t2ms.init(zoomLayer, zoomer);
 
-                    t2mcs.projection(proj);
-                    $log.debug('** Zoom restored:', z);
-                    $log.debug('** We installed the projection:', proj);
+            // TODO: figure out from where to call this code...
+            // we still need to do the equivalent of this when we load
+            //  a geo map, just not here.
+            //
+            // ... NOTE: we still have to position the nodes AFTER the map
+            //           has loaded and the projection has been established...
+            //           maybe another promise ending with a "positionNodes()"
+            //           call?
 
-                    // Now the map has load and we have a projection we can
-                    // get the info from the server
-                    t2es.start();
-                }
-            );
+            // .then(
+            //     function (proj) {
+            //         var z = ps.getPrefs('topo2_zoom', { tx: 0, ty: 0, sc: 1 });
+            //         zoomer.panZoom([z.tx, z.ty], z.sc);
+            //
+            //         t2mcs.projection(proj);
+            //         $log.debug('** Zoom restored:', z);
+            //         $log.debug('** We installed the projection:', proj);
+            //
+            //         // Now the map has load and we have a projection we can
+            //         // get the info from the server
+            //         t2es.start();
+            //     }
+            // );
 
             t2sls.init(svg, zoomLayer);
             t2fs.init(svg, forceG, uplink, dim, zoomer);
@@ -219,6 +244,10 @@
             summaryPanel.init();
             detailsPanel.init();
 
+            // Now that we are initialized, ask the server for what we
+            //  need to show.
+            t2es.start();
+
             $log.log('OvTopo2Ctrl has been created');
         }]);
 })();
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2DetailsPanel.js b/web/gui/src/main/webapp/app/view/topo2/topo2DetailsPanel.js
index 16ab45b8..29773f8 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2DetailsPanel.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2DetailsPanel.js
@@ -30,7 +30,7 @@
 
     // configuration
     var id = 'topo2-p-detail',
-        className = 'topo-p',
+        className = 'topo2-p',
         panelOpts = {
             width: 260          // summary and detail panel width
         };
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Dialog.js b/web/gui/src/main/webapp/app/view/topo2/topo2Dialog.js
index c7661fc..71a33d2 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Dialog.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Dialog.js
@@ -23,9 +23,9 @@
     'use strict';
 
     // constants
-    var idDialog = 'topo-p-dialog',
+    var idDialog = 'topo2-p-dialog',
         opts = {
-            cssCls: 'topo-p'
+            cssCls: 'topo2-p'
         };
 
     // ==========================
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Event.js b/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
index 76a573e..a03dab4 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
@@ -33,7 +33,6 @@
     var handlerMap,
         openListener;
 
-    // TODO: only add heartbeat timer etc. if we really need to be doing that..
     // ========================== Helper Functions
 
     function createHandlerMap() {
@@ -42,7 +41,6 @@
             topo2CurrentLayout: t2fs,
             topo2CurrentRegion: t2fs,
             topo2PeerRegions: t2fs,
-            topo2StartDone: t2fs,
 
             topo2UiModelEvent: t2fs
 
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
index 1795df4..98c8484 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
@@ -145,10 +145,6 @@
         doTmpPeerRegions(data);
     }
 
-    function startDone(data) {
-        $log.debug('>> topo2StartDone event:', data);
-    }
-
     function modelEvent(data) {
         $log.debug('>> topo2UiModelEvent event:', data);
 
@@ -276,7 +272,6 @@
                 topo2AllInstances: allInstances,
                 topo2CurrentLayout: currentLayout,
                 topo2CurrentRegion: currentRegion,
-                topo2StartDone: startDone,
 
                 topo2UiModelEvent: modelEvent,
 
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Instance.js b/web/gui/src/main/webapp/app/view/topo2/topo2Instance.js
index 307c2db..8a3cf20 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Instance.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Instance.js
@@ -212,7 +212,7 @@
     // ==========================
     function logicError(msg) {
         if (showLogicErrors) {
-            $log.warn('TopoInstService: ' + msg);
+            $log.warn('Topo2InstService: ' + msg);
         }
     }
 
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2KeyCommands.js b/web/gui/src/main/webapp/app/view/topo2/topo2KeyCommands.js
index 0f54273..0d49e39 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2KeyCommands.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2KeyCommands.js
@@ -79,7 +79,7 @@
 
     function updatePrefsState(what, b) {
         prefsState[what] = b ? 1 : 0;
-        ps.setPrefs('topo_prefs', prefsState);
+        ps.setPrefs('topo2_prefs', prefsState);
     }
 
     function deviceLabelFlashMessage(index) {
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js b/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
index 9a639bf..9350020 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
@@ -121,15 +121,15 @@
 
                         this.prevForce = this.forceG;
 
-                        this.forceG = d3.select('#topo-zoomlayer')
-                            .append('g').attr('class', 'topo-force');
+                        this.forceG = d3.select('#topo2-zoomlayer')
+                            .append('g').attr('class', 'topo2-force');
 
                         this.elements = {
-                            linkG: this.addElement(this.forceG, 'topo-links'),
-                            linkLabelG: this.addElement(this.forceG, 'topo-linkLabels'),
-                            numLinksLabels: this.addElement(this.forceG, 'topo-numLinkLabels'),
-                            nodeG: this.addElement(this.forceG, 'topo-nodes'),
-                            portLabels: this.addElement(this.forceG, 'topo-portLabels')
+                            linkG: this.addElement(this.forceG, 'topo2-links'),
+                            linkLabelG: this.addElement(this.forceG, 'topo2-linkLabels'),
+                            numLinksLabels: this.addElement(this.forceG, 'topo2-numLinkLabels'),
+                            nodeG: this.addElement(this.forceG, 'topo2-nodes'),
+                            portLabels: this.addElement(this.forceG, 'topo2-portLabels')
                         };
                     },
                     addElement: function (parent, className) {
@@ -180,9 +180,9 @@
                         this.update();
                     },
                     centerLayout: function () {
-                        d3.select('#topo-zoomlayer').attr('data-layout', t2rs.model.get('id'));
+                        d3.select('#topo2-zoomlayer').attr('data-layout', t2rs.model.get('id'));
 
-                        var zoomer = d3.select('#topo-zoomlayer').node().getBBox(),
+                        var zoomer = d3.select('#topo2-zoomlayer').node().getBBox(),
                             layoutBBox = this.forceG.node().getBBox(),
                             scale = (zoomer.height - 150) / layoutBBox.height,
                             x = (zoomer.width / 2) - ((layoutBBox.x + layoutBBox.width / 2) * scale),
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Link.js b/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
index 09377e9..be8d03c 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Link.js
@@ -160,7 +160,7 @@
                     this.set('enhanced', true);
                     point = this.locatePortLabel();
                     angular.extend(point, {
-                        id: 'topo-port-tgt',
+                        id: 'topo2-port-tgt',
                         num: this.get('portB')
                     });
                     data.push(point);
@@ -168,13 +168,13 @@
                     if (this.get('portA')) {
                         point = this.locatePortLabel(1);
                         angular.extend(point, {
-                            id: 'topo-port-src',
+                            id: 'topo2-port-src',
                             num: this.get('portA')
                         });
                         data.push(point);
                     }
 
-                    var entering = d3.select('#topo-portLabels')
+                    var entering = d3.select('#topo2-portLabels')
                         .selectAll('.portLabel')
                         .data(data)
                         .enter().append('g')
@@ -204,7 +204,7 @@
             },
             unenhance: function () {
                 this.set('enhanced', false);
-                d3.select('#topo-portLabels').selectAll('.portLabel').remove();
+                d3.select('#topo2-portLabels').selectAll('.portLabel').remove();
             },
             getSelected: function () {
                 return this.collection.filter(function (m) {
@@ -307,7 +307,7 @@
 
                 var labelScale = labelDim / (labelDim * t2zs.scale());
 
-                d3.select('#topo-portLabels')
+                d3.select('#topo2-portLabels')
                     .selectAll('.portLabel')
                     .selectAll('*')
                     .style('transform', 'scale(' + labelScale + ')');
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Map.js b/web/gui/src/main/webapp/app/view/topo2/topo2Map.js
index aa9766e..69716d8 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Map.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Map.js
@@ -23,7 +23,7 @@
     'use strict';
 
     // Injected Services
-    var $loc, ps, ms, flash, sus, countryFilters;
+    var $log, $loc, ps, ms, flash, sus, countryFilters;
 
     // Injected Classes
     var MapSelectionDialog;
@@ -34,9 +34,12 @@
     function init(_zoomLayer_, _zoomer_) {
         zoomLayer = _zoomLayer_;
         zoomer = _zoomer_;
-        return setUpMap.bind(this)();
+        // This function no longer returns a promise.
+        //  TODO: call setUpMap() when we know which map we want (not from here)
+        // return setUpMap.bind(this)();
     }
 
+    // TODO: to be re-worked: map-id, filePath, scale/pan to be passed as params
     function setUpMap() {
         var prefs = currentMap(),
             mapId = prefs.mapid,
@@ -45,17 +48,17 @@
             loadMap = ms.loadMapInto,
             promise, cfilter;
 
-        mapG = d3.select('#topo-map');
+        mapG = d3.select('#topo2-map');
 
         if (mapG.empty()) {
-            mapG = zoomLayer.append('g').attr('id', 'topo-map');
+            mapG = zoomLayer.append('g').attr('id', 'topo2-map');
         } else {
             mapG.each(function () {
                 d3.selectAll(this.childNodes).remove();
             });
         }
 
-        if (!ps.getPrefs('topo_prefs')[this.prefs.visible]) {
+        if (!ps.getPrefs('topo2_prefs')[this.prefs.visible]) {
             this.hide();
         }
 
@@ -73,9 +76,11 @@
         return promise;
     }
 
+    // TODO: deprecated - the layout will tell us which map
+    //   no longer stored in user preferences
     function currentMap() {
         return ps.getPrefs(
-            'topo_mapid',
+            'topo2_mapid',
             {
                 mapid: 'usa',
                 mapscale: 1,
@@ -86,20 +91,23 @@
         );
     }
 
+    // TODO: deprecated - maps are defined per layout on the server side.
     function setMap(map) {
-        ps.setPrefs('topo_mapid', map);
+        ps.setPrefs('topo2_mapid', map);
         return setUpMap.bind(this)();
     }
 
+    // TODO: deprecated - map selection does not make sense in Topo2
     function openMapSelection() {
+        $log.warn('openMapSelection DISABLED');
 
-        MapSelectionDialog.prototype.currentMap = currentMap;
-
-        new MapSelectionDialog({
-            okHandler: function (preferences) {
-                setMap(preferences);
-            }
-        }).open();
+        // MapSelectionDialog.prototype.currentMap = currentMap;
+        //
+        // new MapSelectionDialog({
+        //     okHandler: function (preferences) {
+        //         setMap(preferences);
+        //     }
+        // }).open();
     }
 
     function resetZoom() {
@@ -108,11 +116,15 @@
 
     angular.module('ovTopo2')
     .factory('Topo2MapService', [
-        '$location', 'Topo2ViewController', 'PrefsService', 'MapService', 'FlashService',
-        'SvgUtilService', 'Topo2CountryFilters', 'Topo2MapDialog',
+        '$log', '$location', 'Topo2ViewController', 'PrefsService',
+        'MapService', 'FlashService', 'SvgUtilService', 'Topo2CountryFilters',
+        'Topo2MapDialog',
 
-        function (_$loc_, ViewController, _ps_, _ms_, _flash_, _sus_, _t2cf_, _t2md_) {
+        function (_$log_, _$loc_, ViewController, _ps_,
+                  _ms_, _flash_, _sus_, _t2cf_,
+                  _t2md_) {
 
+            $log = _$log_;
             $loc = _$loc_;
             ps = _ps_;
             ms = _ms_;
@@ -123,7 +135,7 @@
 
             var MapLayer = ViewController.extend({
 
-                id: 'topo-map',
+                id: 'topo2-map',
                 displayName: 'Map',
 
                 init: init,
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Prefs.js b/web/gui/src/main/webapp/app/view/topo2/topo2Prefs.js
index 9eaf243..ef3698e 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Prefs.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Prefs.js
@@ -34,7 +34,7 @@
     };
 
     function topo2Prefs() {
-        return ps.getPrefs('topo_prefs', defaultPrefsState);
+        return ps.getPrefs('topo2_prefs', defaultPrefsState);
     }
 
     function get(key) {
@@ -45,7 +45,7 @@
     function set(key, value) {
         var preferences = topo2Prefs();
         preferences[key] = value;
-        ps.setPrefs('topo_prefs', preferences);
+        ps.setPrefs('topo2_prefs', preferences);
         return preferences[key];
     }
 
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2SpriteLayer.js b/web/gui/src/main/webapp/app/view/topo2/topo2SpriteLayer.js
index 8820a30..17c4262 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2SpriteLayer.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2SpriteLayer.js
@@ -37,7 +37,7 @@
 
                 var SpriteLayer = ViewController.extend({
 
-                    id: 'topo-sprites',
+                    id: 'topo2-sprites',
                     displayName: 'Sprite Layer',
 
                     init: function(svg, zoomLayer) {
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2SummaryPanel.js b/web/gui/src/main/webapp/app/view/topo2/topo2SummaryPanel.js
index 6017504..07a9562 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2SummaryPanel.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2SummaryPanel.js
@@ -30,7 +30,7 @@
 
     // configuration
     var id = 'topo2-p-summary',
-        className = 'topo-p',
+        className = 'topo2-p',
         panelOpts = {
             show: true,
             width: 260 // summary and detail panel width
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2ViewController.js b/web/gui/src/main/webapp/app/view/topo2/topo2ViewController.js
index ac475be..ad8dfa9 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2ViewController.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2ViewController.js
@@ -43,7 +43,7 @@
             return d3.select('#' + this.id);
         },
         enabled: function () {
-            return ps.getPrefs('topo_prefs')[this.prefs.visible];
+            return ps.getPrefs('topo2_prefs')[this.prefs.visible];
         },
         isVisible: function () {
             return this.node().style('visibility') === 'visible';
@@ -81,9 +81,9 @@
             this.updatePrefState(this.prefs.visible, on);
         },
         updatePrefState: function (key, value) {
-            var state = ps.getPrefs('topo_prefs');
+            var state = ps.getPrefs('topo2_prefs');
             state[key] = value ? 1 : 0;
-            ps.setPrefs('topo_prefs', state);
+            ps.setPrefs('topo2_prefs', state);
         }
     };
 
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Zoom.js b/web/gui/src/main/webapp/app/view/topo2/topo2Zoom.js
index 7dcf325..d52154c 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Zoom.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Zoom.js
@@ -15,48 +15,44 @@
  */
 
 /*
- ONOS GUI -- Topology Breadcrumb Module.
- Module that renders the breadcrumbs for regions
+ ONOS GUI -- Topology Zoom Module.
+ Module that handles Zoom events.
  */
 
 (function () {
-
     'use strict';
 
-    var zs, ps;
+    // injected references
+    var fs, zs, ps;
 
+    // internal state
     var zoomer,
         zoomEventListeners = [];
 
+    function createZoomer(options) {
+        // need to wrap the original zoom callback to extend its behavior
+        var origCallback = fs.isF(options.zoomCallback) || function () {};
+
+        options.zoomCallback = function () {
+            origCallback();
+
+            angular.forEach(zoomEventListeners, function (ev) {
+                ev(zoomer);
+            });
+        };
+
+        zoomer = zs.createZoomer(options);
+        return zoomer;
+    }
+
     function getZoomer() {
         return zoomer;
     }
 
-    function createZoomer(options) {
-        var settings = angular.extend({}, options, {
-            zoomCallback: zoomCallback
-        });
-
-        zoomer = zs.createZoomer(settings);
-        return zoomer;
-    }
-
-    function zoomCallback() {
-        var sc = zoomer.scale(),
-            tr = zoomer.translate();
-
-        ps.setPrefs('topo_zoom', { tx: tr[0], ty: tr[1], sc: sc });
-
-        angular.forEach(zoomEventListeners, function (ev) {
-            ev(zoomer);
-        });
-    }
-
     function findZoomEventListener(ev) {
-        for (var i = 0, l = zoomEventListeners.length; i < l; i++) {
+        for (var i = 0, len = zoomEventListeners.length; i < len; i++) {
             if (zoomEventListeners[i] === ev) return i;
         }
-
         return -1;
     }
 
@@ -65,7 +61,6 @@
     }
 
     function removeZoomEventListener(callback) {
-
         var evIndex = findZoomEventListener(callback);
 
         if (evIndex !== -1) {
@@ -83,9 +78,10 @@
 
     angular.module('ovTopo2')
     .factory('Topo2ZoomService', [
-        'ZoomService', 'PrefsService',
-        function (_zs_, _ps_) {
+        'FnService', 'ZoomService', 'PrefsService',
+        function (_fs_, _zs_, _ps_) {
 
+            fs = _fs_;
             zs = _zs_;
             ps = _ps_;
 
diff --git a/web/gui/src/main/webapp/tests/app/fw/util/prefs-spec.js b/web/gui/src/main/webapp/tests/app/fw/util/prefs-spec.js
index a047aba..dc4a7d5 100644
--- a/web/gui/src/main/webapp/tests/app/fw/util/prefs-spec.js
+++ b/web/gui/src/main/webapp/tests/app/fw/util/prefs-spec.js
@@ -44,7 +44,7 @@
 
     it('should define api functions', function () {
         expect(fs.areFunctions(ps, [
-            'getPrefs', 'asNumbers', 'setPrefs',
+            'getPrefs', 'asNumbers', 'setPrefs', 'mergePrefs',
             'addListener', 'removeListener'
         ])).toBe(true);
     });
@@ -58,4 +58,7 @@
     // === Tests for setPrefs()
     // TODO unit tests for setPrefs()
 
+    // === Tests for mergePrefs()
+    // TODO unit tests for mergePrefs()
+
 });