Topo2: Refactored Maps and Sprites to be managed by a BackgroundService

Change-Id: I75965fc76734436ce0c68e06dd75663baa924287
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 c26c6fd..f6d62d5 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
@@ -239,6 +239,20 @@
         addZoomPan(result, layout.id());
     }
 
+    private ObjectNode initialZoomForLayout(ObjectNode zoomPrefs, String id) {
+        ObjectNode zoomForLayout = defaultZoomForLayout();
+        zoomPrefs.set(id, zoomForLayout);
+        prefService.setPreference(userName, PKEY_TOPO_ZOOM, zoomPrefs);
+        return zoomPrefs;
+    }
+
+    private ObjectNode defaultZoomForLayout() {
+        return objectNode()
+            .put(ZOOM_SCALE, DEFAULT_SCALE)
+            .put(ZOOM_PAN_X, DEFAULT_PAN)
+            .put(ZOOM_PAN_Y, DEFAULT_PAN);
+    }
+
     private void addZoomPan(ObjectNode result, UiTopoLayoutId layoutId) {
         // need to look up topo_zoom settings from preferences service.
 
@@ -254,19 +268,16 @@
         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);
+            zoomPrefs = initialZoomForLayout(objectNode(), layoutId.id());
         }
+
         ObjectNode zoomData = (ObjectNode) zoomPrefs.get(layoutId.id());
 
+        if (zoomData == null) {
+            zoomPrefs = initialZoomForLayout(zoomPrefs, layoutId.id());
+            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());
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2.html b/web/gui/src/main/webapp/app/view/topo2/topo2.html
index b36a036..c83c4ce 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2.html
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.html
@@ -32,7 +32,7 @@
 
     <!-- Below here is good; Above here is temporary, for debugging -->
 
-    <svg viewBox="0 0 1000 1000"
+    <svg viewBox="0 0 1000 1000" id="topo2"
         resize offset-height="56" offset-width="12"
         notifier="notifyResize()">
     </svg>
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 e443d39..0022964 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2.js
@@ -188,36 +188,6 @@
             // make sure we can respond to topology events from the server
             t2es.bindHandlers();
 
-            // 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);
-
-            // 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?
-
-            // .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);
             t2bcs.init();
             t2kcs.init(t2fs);
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Background.js b/web/gui/src/main/webapp/app/view/topo2/topo2Background.js
new file mode 100644
index 0000000..611273b
--- /dev/null
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Background.js
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2017-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 Background Module.
+ Module that maintains the map and sprite layers
+ */
+
+(function () {
+
+    var $log;
+
+    var instance;
+
+    angular.module('ovTopo2')
+        .factory('Topo2BackgroundService', [
+            '$log', 'Topo2ViewController', 'Topo2SpriteLayerService', 'Topo2MapService',
+            'Topo2MapConfigService', 'Topo2RegionService',
+            function (_$log_, ViewController, t2sls, t2ms, t2mcs, t2rs) {
+
+                $log = _$log_;
+
+                var BackgroundView = ViewController.extend({
+
+                    id: 'topo2-background',
+                    displayName: 'Background',
+
+                    init: function () {
+                        instance = this;
+                        this.appendElement('#topo2-zoomlayer', 'g');
+                        t2sls.init();
+                        t2ms.init();
+                    },
+                    addLayout: function (data) {
+                        this.background = data;
+                        t2rs.bgRendered = false;
+
+                        if (data.bgType === 'geo') {
+
+                            // Hide Sprite Layer and show Map
+                            t2sls.hide();
+                            t2ms.show();
+
+                            t2ms.setUpMap(data.bgId, data.bgFilePath, data.bgZoomScale)
+                            .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);
+                                t2rs.backgroundRendered();
+                            });
+                        }
+
+                        if (data.bgType === 'grid') {
+
+                            // Hide Sprite Layer and show Map
+                            t2ms.hide();
+                            t2sls.show();
+
+                            t2sls.loadLayout(data.bgId).then(function () {
+                                t2rs.backgroundRendered();
+                            });
+                        }
+                    }
+                });
+
+
+                return instance || new BackgroundView();;
+            }
+        ]);
+})();
\ No newline at end of file
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Breadcrumb.js b/web/gui/src/main/webapp/app/view/topo2/topo2Breadcrumb.js
index 84421c9..032cfb6 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Breadcrumb.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Breadcrumb.js
@@ -62,8 +62,8 @@
             rid: data.id
         });
 
-        layout.createForceElements();
-        layout.transitionDownRegion();
+        layout().createForceElements();
+        layout().transitionDownRegion();
 
         render();
     }
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 98c8484..1c2ea67 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
@@ -26,7 +26,7 @@
     var $log,
         wss;
 
-    var t2is, t2rs, t2ls, t2vs, t2bcs, t2ss;
+    var t2is, t2rs, t2ls, t2vs, t2bcs, t2ss, t2bgs;
     var svg, forceG, uplink, dim, opts, zoomer;
 
     // D3 Selections
@@ -41,8 +41,9 @@
         dim = _dim_;
         opts = _opts_;
 
-        t2ls = t2ls(svg, forceG, uplink, dim, zoomer, opts);
         t2bcs.addLayout(t2ls);
+        t2bgs.init();
+        t2ls = t2ls(svg, forceG, uplink, dim, zoomer, opts);
         t2rs.layout = t2ls;
         t2ss.init(svg, zoomer);
     }
@@ -129,8 +130,9 @@
 
     function currentLayout(data) {
         $log.debug('>> topo2CurrentLayout event:', data);
+        t2rs.clear();
         t2bcs.addBreadcrumb(data.crumbs);
-        t2rs.addLayout(data);
+        t2bgs.addLayout(data);
     }
 
     function currentRegion(data) {
@@ -238,8 +240,9 @@
         '$log', 'WebSocketService', 'Topo2InstanceService',
         'Topo2RegionService', 'Topo2LayoutService', 'Topo2ViewService',
         'Topo2BreadcrumbService', 'Topo2ZoomService', 'Topo2SelectService',
+        'Topo2BackgroundService',
         function (_$log_, _wss_, _t2is_, _t2rs_, _t2ls_,
-            _t2vs_, _t2bcs_, zoomService, _t2ss_) {
+            _t2vs_, _t2bcs_, zoomService, _t2ss_, _t2bgs_) {
 
             $log = _$log_;
             wss = _wss_;
@@ -249,6 +252,7 @@
             t2vs = _t2vs_;
             t2bcs = _t2bcs_;
             t2ss = _t2ss_;
+            t2bgs = _t2bgs_;
 
             var onZoom = function () {
                 var nodes = [].concat(
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 0d49e39..911ec18 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2KeyCommands.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2KeyCommands.js
@@ -17,13 +17,13 @@
 (function () {
 
     // Injected Services
-    var ks, flash, wss, t2ps, t2ms, ps, t2is, t2sp, t2vs, t2rs, t2fs, t2sls;
+    var ks, flash, wss, t2ps, t2bgs, ps, t2is, t2sp, t2vs, t2rs, t2fs, t2sls;
 
     // Commmands
     var actionMap = {
         L: [cycleDeviceLabels, 'Cycle device labels'],
         G: [openMapSelection, 'Select background geo map'],
-        B: [toggleMap, 'Toggle background geo map'],
+        B: [toggleBackground, 'Toggle background'],
         I: [toggleInstancePanel, 'Toggle ONOS Instance Panel'],
         O: [toggleSummary, 'Toggle the Summary Panel'],
         R: [resetZoom, 'Reset pan / zoom'],
@@ -31,7 +31,6 @@
         E: [equalizeMasters, 'Equalize mastership roles'],
         X: [resetAllNodeLocations, 'Reset Node Location'],
         U: [unpinNode, 'Unpin node (mouse over)'],
-        S: [toggleSpriteLayer, 'Toggle sprite layer'],
 
         esc: handleEscape
     };
@@ -103,8 +102,8 @@
         t2ms.openMapSelection();
     }
 
-    function toggleMap(x) {
-        t2ms.toggle(x);
+    function toggleBackground(x) {
+        t2bgs.toggle(x);
     }
 
     function toggleInstancePanel(x) {
@@ -120,10 +119,6 @@
         flash.flash('Pan and zoom reset');
     }
 
-    function toggleSpriteLayer() {
-        t2sls.toggle();
-    }
-
     function togglePorts(x) {
         updatePrefsState('porthl', t2vs.togglePortHighlights(x));
         t2fs.updateLinks();
@@ -147,17 +142,17 @@
     angular.module('ovTopo2')
     .factory('Topo2KeyCommandService', [
         'KeyService', 'FlashService', 'WebSocketService', 'Topo2PrefsService',
-        'Topo2MapService', 'PrefsService', 'Topo2InstanceService',
+        'Topo2BackgroundService', 'PrefsService', 'Topo2InstanceService',
         'Topo2SummaryPanelService', 'Topo2ViewService', 'Topo2RegionService',
         'Topo2SpriteLayerService',
-        function (_ks_, _flash_, _wss_, _t2ps_, _t2ms_, _ps_, _t2is_, _t2sp_,
+        function (_ks_, _flash_, _wss_, _t2ps_, _t2bgs_, _ps_, _t2is_, _t2sp_,
                   _t2vs_, _t2rs_, _t2sls_) {
 
             ks = _ks_;
             flash = _flash_;
             wss = _wss_;
             t2ps = _t2ps_;
-            t2ms = _t2ms_;
+            t2bgs = _t2bgs_;
             t2is = _t2is_;
             ps = _ps_;
             t2sp = _t2sp_;
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 9350020..75105ba 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Layout.js
@@ -98,7 +98,6 @@
 
                 var Layout = ViewController.extend({
                     initialize: function (svg, forceG, uplink, dim, zoomer, opts) {
-
                         $log.debug('initialize Layout');
                         instance = this;
 
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 69716d8..0f559fa 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 $log, $loc, ps, ms, flash, sus, countryFilters;
+    var $log, $loc, ps, ms, flash, sus, t2zs, countryFilters;
 
     // Injected Classes
     var MapSelectionDialog;
@@ -31,43 +31,26 @@
     // internal state
     var instance, mapG, zoomLayer, zoomer;
 
-    function init(_zoomLayer_, _zoomer_) {
-        zoomLayer = _zoomLayer_;
-        zoomer = _zoomer_;
-        // This function no longer returns a promise.
-        //  TODO: call setUpMap() when we know which map we want (not from here)
-        // return setUpMap.bind(this)();
+    function init() {
+        this.appendElement('#topo2-background', 'g');
+        zoomLayer = d3.select('#topo2-zoomlayer');
+        zoomer = t2zs.getZoomer();
     }
 
     // TODO: to be re-worked: map-id, filePath, scale/pan to be passed as params
-    function setUpMap() {
-        var prefs = currentMap(),
-            mapId = prefs.mapid,
-            mapFilePath = prefs.mapfilepath,
-            mapScale = prefs.mapscale || 1,
-            loadMap = ms.loadMapInto,
+    function setUpMap(mapId, mapFilePath, mapScale) {
+
+        var loadMap = ms.loadMapInto,
             promise, cfilter;
 
-        mapG = d3.select('#topo2-map');
-
-        if (mapG.empty()) {
-            mapG = zoomLayer.append('g').attr('id', 'topo2-map');
-        } else {
-            mapG.each(function () {
-                d3.selectAll(this.childNodes).remove();
-            });
-        }
-
-        if (!ps.getPrefs('topo2_prefs')[this.prefs.visible]) {
-            this.hide();
-        }
+        this.node().selectAll("*").remove();
 
         if (mapFilePath === '*countries') {
             cfilter = countryFilters[mapId] || countryFilters.uk;
             loadMap = ms.loadMapRegionInto;
         }
 
-        promise = loadMap(mapG, mapFilePath, mapId, {
+        promise = loadMap(this.node(), mapFilePath, mapId, {
             countryFilters: cfilter,
             adjustScale: mapScale,
             shading: ''
@@ -118,11 +101,11 @@
     .factory('Topo2MapService', [
         '$log', '$location', 'Topo2ViewController', 'PrefsService',
         'MapService', 'FlashService', 'SvgUtilService', 'Topo2CountryFilters',
-        'Topo2MapDialog',
+        'Topo2MapDialog', 'Topo2ZoomService',
 
         function (_$log_, _$loc_, ViewController, _ps_,
                   _ms_, _flash_, _sus_, _t2cf_,
-                  _t2md_) {
+                  _t2md_, _t2zs_) {
 
             $log = _$log_;
             $loc = _$loc_;
@@ -132,6 +115,7 @@
             sus = _sus_;
             countryFilters = _t2cf_;
             MapSelectionDialog = _t2md_;
+            t2zs = _t2zs_;
 
             var MapLayer = ViewController.extend({
 
@@ -140,6 +124,7 @@
 
                 init: init,
                 setMap: setMap,
+                setUpMap: setUpMap,
                 openMapSelection: openMapSelection,
                 resetZoom: resetZoom
             });
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js b/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js
index aeac6d4..e29c45b 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2NodePosition.js
@@ -134,6 +134,7 @@
         // 1000 is a hardcoded HTML value of the SVG element (topo2.html)
         var x = scale * loc.gridX,
             y = (scale * loc.gridY) + yOffset;
+
         return [x, y];
     }
 
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
index 0202ca9..54c88c7 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Region.js
@@ -45,39 +45,24 @@
                 initialize: function () {
                     instance = this;
                     this.model = null;
+                    this.bgRendered = false;
                 },
-                addLayout: function (data) {
+                backgroundRendered: function () {
+                    this.bgRendered = true;
 
-                    var _this = this;
-
-                    if (data.bgType === 'geo') {
-                        t2ms.setMap({
-                            mapid: data.bgId,
-                            mapfilepath: data.bgFilePath
-                        }).then(function () {
-                            _.each(_this.regionNodes(), function (node) {
-                                node.resetPosition();
-                            });
-                        });
-
-                        if (t2ms.enabled()) {
-                            t2ms.show();
-                        }
-                    } else {
-                        t2ms.hide();
-                    }
-
-                    if (data.bgType === 'grid') {
-                        t2sls.loadLayout(data.bgId);
-
-                        if (t2sls.enabled()) {
-                            t2sls.show();
-                        }
-                    } else {
-                        t2sls.hide();
+                    if (this.regionData) {
+                        this.startRegion();
                     }
                 },
                 addRegion: function (data) {
+                    this.clear();
+                    this.regionData = data;
+
+                    if (this.bgRendered) {
+                        this.startRegion();
+                    }
+                },
+                startRegion: function () {
 
                     var RegionModel = Model.extend({
                         findNodeById: this.findNodeById,
@@ -85,15 +70,15 @@
                     });
 
                     this.model = new RegionModel({
-                        id: data.id,
-                        layerOrder: data.layerOrder
+                        id: this.regionData.id,
+                        layerOrder: this.regionData.layerOrder
                     });
 
                     this.model.set({
-                        subregions: t2sr.createSubRegionCollection(data.subregions, this),
-                        devices: t2ds.createDeviceCollection(data.devices, this),
-                        hosts: t2hs.createHostCollection(data.hosts, this),
-                        links: t2ls.createLinkCollection(data.links, this)
+                        subregions: t2sr.createSubRegionCollection(this.regionData.subregions, this),
+                        devices: t2ds.createDeviceCollection(this.regionData.devices, this),
+                        hosts: t2hs.createHostCollection(this.regionData.hosts, this),
+                        links: t2ls.createLinkCollection(this.regionData.links, this)
                     });
 
                     angular.forEach(this.model.get('links').models, function (link) {
@@ -105,6 +90,23 @@
                         t2bcs.hide();
                     }
 
+                    // this.layout.update();
+                },
+                clear: function () {
+
+                    this.regionData = null;
+
+                    if (!this.model)
+                        return;
+
+                    this.model.set({
+                        id: null,
+                        layerOrder: null,
+                        subregions: t2sr.createSubRegionCollection([], this),
+                        devices: t2ds.createDeviceCollection([], this),
+                        hosts: t2hs.createHostCollection([], this),
+                        links: t2ls.createLinkCollection([], this)
+                    });
                 },
                 isRootRegion: function () {
                     return this.model.get('id') === ROOT;
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 17c4262..6de99fe 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2SpriteLayer.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2SpriteLayer.js
@@ -40,10 +40,10 @@
                     id: 'topo2-sprites',
                     displayName: 'Sprite Layer',
 
-                    init: function(svg, zoomLayer) {
-                        this.svg = svg;
+                    init: function() {
+                        this.svg = d3.select('#topo2');
                         this.createSpriteDefs();
-                        this.container = zoomLayer.append('g').attr('id', this.id);
+                        this.container = this.appendElement('#topo2-background', 'g');
                     },
                     loadLayout: function (id) {
                         this.container.selectAll("*").remove();
@@ -57,6 +57,11 @@
                         if (fs.debugOn('sprite_grid')) {
                             this.renderGrid();
                         }
+
+                        // Returns a promise for consistency with Topo2MapService
+                        return new Promise(function(resolve) {
+                            resolve();
+                        });
                     },
                     createSpriteDefs: function () {
                        this.defs = this.svg.append('defs')
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 ad8dfa9..6c19fe9 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2ViewController.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2ViewController.js
@@ -39,6 +39,13 @@
                 visible: this.name + '_visible'
             }
         },
+        appendElement: function (parent, node) {
+            var el = d3.select('#' + this.id);
+            if (el.empty()) {
+                 return d3.select(parent).append(node).attr('id', this.id);
+            }
+            return el;
+        },
         node: function() {
             return d3.select('#' + this.id);
         },
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index f053f02..3930779 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -134,6 +134,7 @@
     <!-- Under development for Region support. -->
     <!--<script src="app/view/topo2/topo2.js"></script>
     <script src="app/view/topo2/topo2Breadcrumb.js"></script>
+    <script src="app/view/topo2/topo2Background.js"></script>
     <script src="app/view/topo2/topo2Collection.js"></script>
     <script src="app/view/topo2/topo2D3.js"></script>
     <script src="app/view/topo2/topo2Dialog.js"></script>