From f24db1561e5eec53862fc5b24b0e2fb8afd1aa1e Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 13:00:03 -0700
Subject: [PATCH 01/15] [Addressability] Get IDs from URL

Add a route parameter to Browse mode which includes domain
object identifiers. WTD-1149.
---
 platform/commonUI/browse/bundle.json             | 6 +++---
 platform/commonUI/browse/src/BrowseController.js | 4 ++--
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/platform/commonUI/browse/bundle.json b/platform/commonUI/browse/bundle.json
index dcf1ba9a38..014f09effa 100644
--- a/platform/commonUI/browse/bundle.json
+++ b/platform/commonUI/browse/bundle.json
@@ -2,7 +2,7 @@
     "extensions": {
         "routes": [
             {
-                "when": "/browse",
+                "when": "/browse/:id*",
                 "templateUrl": "templates/browse.html"
             },
             {
@@ -14,7 +14,7 @@
             {
                 "key": "BrowseController",
                 "implementation": "BrowseController.js",
-                "depends": [ "$scope", "objectService", "navigationService" ]
+                "depends": [ "$scope", "$routeParams", "objectService", "navigationService" ]
             },
             {
                 "key": "CreateMenuController",
@@ -150,4 +150,4 @@
             }
         ]
     }
-}
\ No newline at end of file
+}
diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js
index e2c2484e1b..291986f7ba 100644
--- a/platform/commonUI/browse/src/BrowseController.js
+++ b/platform/commonUI/browse/src/BrowseController.js
@@ -40,7 +40,7 @@ define(
          *
          * @constructor
          */
-        function BrowseController($scope, objectService, navigationService) {
+        function BrowseController($scope, $routeParams, objectService, navigationService) {
             // Callback for updating the in-scope reference to the object
             // that is currently navigated-to.
             function setNavigation(domainObject) {
@@ -91,4 +91,4 @@ define(
 
         return BrowseController;
     }
-);
\ No newline at end of file
+);

From 8f18d887052f029f81e07fddd304e323899db9e1 Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 13:14:10 -0700
Subject: [PATCH 02/15] [Addressability] Navigate down path from params

Traverse down the path from route parameters to initially navigate to a
domain object in Browse mode. WTD-1149.
---
 platform/commonUI/browse/bundle.json          |  2 +-
 .../commonUI/browse/src/BrowseController.js   | 61 +++++++++++++------
 2 files changed, 45 insertions(+), 18 deletions(-)

diff --git a/platform/commonUI/browse/bundle.json b/platform/commonUI/browse/bundle.json
index 014f09effa..a4b6678541 100644
--- a/platform/commonUI/browse/bundle.json
+++ b/platform/commonUI/browse/bundle.json
@@ -2,7 +2,7 @@
     "extensions": {
         "routes": [
             {
-                "when": "/browse/:id*",
+                "when": "/browse/:ids*",
                 "templateUrl": "templates/browse.html"
             },
             {
diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js
index 291986f7ba..83f5b4061c 100644
--- a/platform/commonUI/browse/src/BrowseController.js
+++ b/platform/commonUI/browse/src/BrowseController.js
@@ -29,7 +29,7 @@ define(
     function () {
         "use strict";
 
-        var ROOT_OBJECT = "ROOT";
+        var DEFAULT_PATH = "ROOT/mine";
 
         /**
          * The BrowseController is used to populate the initial scope in Browse
@@ -41,6 +41,8 @@ define(
          * @constructor
          */
         function BrowseController($scope, $routeParams, objectService, navigationService) {
+            var path = ($routeParams.ids || DEFAULT_PATH).split("/");
+
             // Callback for updating the in-scope reference to the object
             // that is currently navigated-to.
             function setNavigation(domainObject) {
@@ -49,26 +51,51 @@ define(
                 navigationService.setNavigation(domainObject);
             }
 
+            function navigateTo(domainObject) {
+                // Check if an object has been navigated-to already...
+                if (!navigationService.getNavigation()) {
+                    // If not, pick a default as the last
+                    // root-level component (usually "mine")
+                    navigationService.setNavigation(domainObject);
+                } else {
+                    // Otherwise, just expose it in the scope
+                    $scope.navigatedObject = navigationService.getNavigation();
+                }
+            }
+
+            function findObject(domainObjects, id) {
+                var i;
+                for (i = 0; i < domainObjects.length; i += 1) {
+                    if (domainObjects[i].getId() === id) {
+                        return domainObjects[i];
+                    }
+                }
+            }
+
+            // Navigate to the domain object identified by path[index],
+            // which we expect to find in the composition of the passed
+            // domain object.
+            function doNavigate(domainObject, index) {
+                var composition = domainObject.useCapability("composition");
+                if (composition) {
+                    composition.then(function (c) {
+                        var nextObject = findObject(c, path[index]);
+                        if (index + 1 >= path.length) {
+                            navigateTo(nextObject);
+                        } else {
+                            doNavigate(nextObject, index + 1);
+                        }
+                    });
+                }
+            }
+
             // Load the root object, put it in the scope.
             // Also, load its immediate children, and (possibly)
             // navigate to one of them, so that navigation state has
             // a useful initial value.
-            objectService.getObjects([ROOT_OBJECT]).then(function (objects) {
-                var composition = objects[ROOT_OBJECT].useCapability("composition");
-                $scope.domainObject = objects[ROOT_OBJECT];
-                if (composition) {
-                    composition.then(function (c) {
-                        // Check if an object has been navigated-to already...
-                        if (!navigationService.getNavigation()) {
-                            // If not, pick a default as the last
-                            // root-level component (usually "mine")
-                            navigationService.setNavigation(c[c.length - 1]);
-                        } else {
-                            // Otherwise, just expose it in the scope
-                            $scope.navigatedObject = navigationService.getNavigation();
-                        }
-                    });
-                }
+            objectService.getObjects([path[0]]).then(function (objects) {
+                $scope.domainObject = objects[path[0]];
+                doNavigate($scope.domainObject, 1);
             });
 
             // Provide a model for the tree to modify

From 9fae2db04a65b7279b1f7a9c02015dff8c4d825f Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 13:16:55 -0700
Subject: [PATCH 03/15] [Addressability] Infer ROOT

Treat the path element for the root domain object as implicit, such
that it does not appear in the full URL. WTD-1149.
---
 platform/commonUI/browse/src/BrowseController.js | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js
index 83f5b4061c..9bd118e458 100644
--- a/platform/commonUI/browse/src/BrowseController.js
+++ b/platform/commonUI/browse/src/BrowseController.js
@@ -29,7 +29,8 @@ define(
     function () {
         "use strict";
 
-        var DEFAULT_PATH = "ROOT/mine";
+        var ROOT_ID = "ROOT",
+            DEFAULT_PATH = "mine";
 
         /**
          * The BrowseController is used to populate the initial scope in Browse
@@ -41,7 +42,9 @@ define(
          * @constructor
          */
         function BrowseController($scope, $routeParams, objectService, navigationService) {
-            var path = ($routeParams.ids || DEFAULT_PATH).split("/");
+            var path = [ROOT_ID].concat(
+                ($routeParams.ids || DEFAULT_PATH).split("/")
+            );
 
             // Callback for updating the in-scope reference to the object
             // that is currently navigated-to.

From 084d6b68591a4f1a0797399af76dcdb1302b8a6a Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 13:28:19 -0700
Subject: [PATCH 04/15] [Addressability] Update path on navigation

Update path in browse mode when navigation state changes.
WTD-1149.
---
 platform/commonUI/browse/bundle.json             |  2 +-
 platform/commonUI/browse/src/BrowseController.js | 13 ++++++++++++-
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/platform/commonUI/browse/bundle.json b/platform/commonUI/browse/bundle.json
index a4b6678541..b367d02510 100644
--- a/platform/commonUI/browse/bundle.json
+++ b/platform/commonUI/browse/bundle.json
@@ -14,7 +14,7 @@
             {
                 "key": "BrowseController",
                 "implementation": "BrowseController.js",
-                "depends": [ "$scope", "$routeParams", "objectService", "navigationService" ]
+                "depends": [ "$scope", "$route", "$location", "objectService", "navigationService" ]
             },
             {
                 "key": "CreateMenuController",
diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js
index 9bd118e458..eebd11faf9 100644
--- a/platform/commonUI/browse/src/BrowseController.js
+++ b/platform/commonUI/browse/src/BrowseController.js
@@ -41,17 +41,28 @@ define(
          *
          * @constructor
          */
-        function BrowseController($scope, $routeParams, objectService, navigationService) {
+        function BrowseController($scope, $routeParams, $location, objectService, navigationService) {
             var path = [ROOT_ID].concat(
                 ($routeParams.ids || DEFAULT_PATH).split("/")
             );
 
+            function updateRoute(domainObject) {
+                var context = domainObject.getCapability('context'),
+                    objectPath = context.getPath(),
+                    ids = objectPath.map(function (domainObject) {
+                        return domainObject.getId();
+                    });
+
+                $location.path("/browse/" + ids.slice(1).join("/"));
+            }
+
             // Callback for updating the in-scope reference to the object
             // that is currently navigated-to.
             function setNavigation(domainObject) {
                 $scope.navigatedObject = domainObject;
                 $scope.treeModel.selectedObject = domainObject;
                 navigationService.setNavigation(domainObject);
+                updateRoute(domainObject);
             }
 
             function navigateTo(domainObject) {

From 3738ea16d76ffa1167bc63941b7841c1de1af6ec Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 13:44:35 -0700
Subject: [PATCH 05/15] [Addressability] Update route without reinstantiating

Work around normal behavior of Angular when path changes;
instead of reinstantiating controller for Browse mode
when Browse-initiated path changes occur, act as if the
route hadn't changed (so that the URL updates but the
currently-displayed state, e.g. tree expansion, is
preserved.) WTD-1149.
---
 platform/commonUI/browse/src/BrowseController.js | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js
index eebd11faf9..79a4c6d48d 100644
--- a/platform/commonUI/browse/src/BrowseController.js
+++ b/platform/commonUI/browse/src/BrowseController.js
@@ -41,9 +41,9 @@ define(
          *
          * @constructor
          */
-        function BrowseController($scope, $routeParams, $location, objectService, navigationService) {
+        function BrowseController($scope, $route, $location, objectService, navigationService) {
             var path = [ROOT_ID].concat(
-                ($routeParams.ids || DEFAULT_PATH).split("/")
+                ($route.current.ids || DEFAULT_PATH).split("/")
             );
 
             function updateRoute(domainObject) {
@@ -51,6 +51,12 @@ define(
                     objectPath = context.getPath(),
                     ids = objectPath.map(function (domainObject) {
                         return domainObject.getId();
+                    }),
+                    priorRoute = $route.current,
+                    // Act as if params HADN'T changed to avoid page reload
+                    unlisten = $scope.$on('$locationChangeSuccess', function () {
+                        $route.current = priorRoute;
+                        unlisten();
                     });
 
                 $location.path("/browse/" + ids.slice(1).join("/"));

From d7b79b6b69ffa6e926ba7e2ed6de83b7c2a8f2a7 Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 14:29:22 -0700
Subject: [PATCH 06/15] [Addressability] Preserve nav. state on leaving Edit

Preserve navigation state when leaving Edit mode, in the context
of addressability changes. WTD-1149.
---
 platform/commonUI/browse/src/BrowseController.js | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js
index 79a4c6d48d..f705319d5b 100644
--- a/platform/commonUI/browse/src/BrowseController.js
+++ b/platform/commonUI/browse/src/BrowseController.js
@@ -43,7 +43,7 @@ define(
          */
         function BrowseController($scope, $route, $location, objectService, navigationService) {
             var path = [ROOT_ID].concat(
-                ($route.current.ids || DEFAULT_PATH).split("/")
+                ($route.current.params.ids || DEFAULT_PATH).split("/")
             );
 
             function updateRoute(domainObject) {
@@ -73,13 +73,16 @@ define(
 
             function navigateTo(domainObject) {
                 // Check if an object has been navigated-to already...
-                if (!navigationService.getNavigation()) {
+                // If not, or if an ID path has been explicitly set in the URL,
+                // navigate to the URL-specified object.
+                if (!navigationService.getNavigation() || $route.current.params.ids) {
                     // If not, pick a default as the last
                     // root-level component (usually "mine")
                     navigationService.setNavigation(domainObject);
                 } else {
-                    // Otherwise, just expose it in the scope
+                    // Otherwise, just expose the currently navigated object.
                     $scope.navigatedObject = navigationService.getNavigation();
+                    updateRoute($scope.navigatedObject);
                 }
             }
 

From 26fd56a00354231d7d377fd6d785e00e417e981a Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 14:35:15 -0700
Subject: [PATCH 07/15] [Addressability] Handle bad paths

Handle paths that cannot be completely followed, WTD-1149.
---
 platform/commonUI/browse/src/BrowseController.js | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js
index f705319d5b..826493f6a6 100644
--- a/platform/commonUI/browse/src/BrowseController.js
+++ b/platform/commonUI/browse/src/BrowseController.js
@@ -103,12 +103,22 @@ define(
                 if (composition) {
                     composition.then(function (c) {
                         var nextObject = findObject(c, path[index]);
-                        if (index + 1 >= path.length) {
-                            navigateTo(nextObject);
+                        if (nextObject) {
+                            if (index + 1 >= path.length) {
+                                navigateTo(nextObject);
+                            } else {
+                                doNavigate(nextObject, index + 1);
+                            }
                         } else {
-                            doNavigate(nextObject, index + 1);
+                            // Couldn't find the next element of the path
+                            // so navigate to the last path object we did find
+                            navigateTo(domainObject);
                         }
                     });
+                } else {
+                    // Similar to above case; this object has no composition,
+                    // so navigate to it instead of subsequent path elements.
+                    navigateTo(domainObject);
                 }
             }
 

From fec6f06849bb947ae531c8202960a8bec31169a2 Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 14:58:03 -0700
Subject: [PATCH 08/15] [Addressability] Obey view query string param

Obey the query string parameter for a view, if present.
WTD-1149.
---
 platform/commonUI/browse/bundle.json          |  5 ++
 .../browse/res/templates/browse-object.html   |  4 +-
 .../browse/src/BrowseObjectController.js      | 55 +++++++++++++++++++
 .../src/controllers/ViewSwitcherController.js | 16 +++---
 4 files changed, 71 insertions(+), 9 deletions(-)
 create mode 100644 platform/commonUI/browse/src/BrowseObjectController.js

diff --git a/platform/commonUI/browse/bundle.json b/platform/commonUI/browse/bundle.json
index b367d02510..1f019991e9 100644
--- a/platform/commonUI/browse/bundle.json
+++ b/platform/commonUI/browse/bundle.json
@@ -16,6 +16,11 @@
                 "implementation": "BrowseController.js",
                 "depends": [ "$scope", "$route", "$location", "objectService", "navigationService" ]
             },
+            {
+                "key": "BrowseObjectController",
+                "implementation": "BrowseObjectController.js",
+                "depends": [ "$scope", "$location" ]
+            },
             {
                 "key": "CreateMenuController",
                 "implementation": "creation/CreateMenuController",
diff --git a/platform/commonUI/browse/res/templates/browse-object.html b/platform/commonUI/browse/res/templates/browse-object.html
index d88f32c18b..6730d5a186 100644
--- a/platform/commonUI/browse/res/templates/browse-object.html
+++ b/platform/commonUI/browse/res/templates/browse-object.html
@@ -19,7 +19,7 @@
  this source code distribution or the Licensing information page available
  at runtime from the About dialog for additional information.
 -->
-<span>
+<span ng-controller="BrowseObjectController">
     <div class="object-browse-bar bar abs">
         <div class="items-select left abs">
             <mct-representation key="'object-header'" mct-object="domainObject">
@@ -44,4 +44,4 @@
                             mct-object="representation.selected.key && domainObject">
         </mct-representation>
     </div>
-</span>
\ No newline at end of file
+</span>
diff --git a/platform/commonUI/browse/src/BrowseObjectController.js b/platform/commonUI/browse/src/BrowseObjectController.js
new file mode 100644
index 0000000000..2385a153d1
--- /dev/null
+++ b/platform/commonUI/browse/src/BrowseObjectController.js
@@ -0,0 +1,55 @@
+/*****************************************************************************
+ * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT Web is 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.
+ *
+ * Open MCT Web includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+/*global define,Promise*/
+
+define(
+    [],
+    function () {
+        "use strict";
+
+        /**
+         * Controller for the `browse-object` representation of a domain
+         * object (the right-hand side of Browse mode.)
+         * @constructor
+         */
+        function BrowseObjectController($scope, $location) {
+            function setViewForDomainObject(domainObject) {
+                var locationViewKey = $location.search().view;
+
+                function selectViewIfMatching(view) {
+                    if (view.key === locationViewKey) {
+                        $scope.representation.selected = view;
+                    }
+                }
+
+                if (locationViewKey) {
+                    ((domainObject && domainObject.useCapability('view')) || [])
+                        .forEach(selectViewIfMatching);
+                }
+            }
+
+            $scope.$watch('domainObject', setViewForDomainObject);
+        }
+
+        return BrowseObjectController;
+    }
+);
diff --git a/platform/commonUI/general/src/controllers/ViewSwitcherController.js b/platform/commonUI/general/src/controllers/ViewSwitcherController.js
index a821d1f326..69674013d5 100644
--- a/platform/commonUI/general/src/controllers/ViewSwitcherController.js
+++ b/platform/commonUI/general/src/controllers/ViewSwitcherController.js
@@ -53,12 +53,14 @@ define(
 
             // Get list of views, read from capability
             function updateOptions(views) {
-                $timeout(function () {
-                    $scope.ngModel.selected = findMatchingOption(
-                        views || [],
-                        ($scope.ngModel || {}).selected
-                    );
-                }, 0);
+                if (Array.isArray(views)) {
+                    $timeout(function () {
+                        $scope.ngModel.selected = findMatchingOption(
+                            views,
+                            ($scope.ngModel || {}).selected
+                        );
+                    }, 0);
+                }
             }
 
             // Update view options when the in-scope results of using the
@@ -68,4 +70,4 @@ define(
 
         return ViewSwitcherController;
     }
-);
\ No newline at end of file
+);

From d559dae1e2b7d91b3dc4030d060477d7b26f59bb Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 15:09:42 -0700
Subject: [PATCH 09/15] [Addressability] Expose current view in query param

Expose the currently selected view as a query string parameter,
WTD-1149.
---
 platform/commonUI/browse/src/BrowseObjectController.js     | 7 +++++++
 .../general/src/controllers/ViewSwitcherController.js      | 2 +-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/platform/commonUI/browse/src/BrowseObjectController.js b/platform/commonUI/browse/src/BrowseObjectController.js
index 2385a153d1..9a95c7ea97 100644
--- a/platform/commonUI/browse/src/BrowseObjectController.js
+++ b/platform/commonUI/browse/src/BrowseObjectController.js
@@ -47,7 +47,14 @@ define(
                 }
             }
 
+            function updateQueryParam(viewKey) {
+                if (viewKey) {
+                    $location.search('view', viewKey);
+                }
+            }
+
             $scope.$watch('domainObject', setViewForDomainObject);
+            $scope.$watch('representation.selected.key', updateQueryParam);
         }
 
         return BrowseObjectController;
diff --git a/platform/commonUI/general/src/controllers/ViewSwitcherController.js b/platform/commonUI/general/src/controllers/ViewSwitcherController.js
index 69674013d5..3bf14d8640 100644
--- a/platform/commonUI/general/src/controllers/ViewSwitcherController.js
+++ b/platform/commonUI/general/src/controllers/ViewSwitcherController.js
@@ -53,7 +53,7 @@ define(
 
             // Get list of views, read from capability
             function updateOptions(views) {
-                if (Array.isArray(views)) {
+                if (Array.isArray(views) && !($scope.ngModel || {}).selected) {
                     $timeout(function () {
                         $scope.ngModel.selected = findMatchingOption(
                             views,

From 9942e2403967d9e4a1da07897ae5e6a9ecddf15a Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 15:23:55 -0700
Subject: [PATCH 10/15] [Addressability] Work around reload again

Work around Angular's page refresh on URL change to avoid
unintended effects when chosen view changes (such as tree
collapse), WTD-1149.
---
 platform/commonUI/browse/bundle.json                   | 8 +++++---
 platform/commonUI/browse/src/BrowseController.js       | 5 +++--
 platform/commonUI/browse/src/BrowseObjectController.js | 8 +++++++-
 3 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/platform/commonUI/browse/bundle.json b/platform/commonUI/browse/bundle.json
index 1f019991e9..7b98571441 100644
--- a/platform/commonUI/browse/bundle.json
+++ b/platform/commonUI/browse/bundle.json
@@ -3,11 +3,13 @@
         "routes": [
             {
                 "when": "/browse/:ids*",
-                "templateUrl": "templates/browse.html"
+                "templateUrl": "templates/browse.html",
+                "reloadOnSearch": false
             },
             {
                 "when": "",
-                "templateUrl": "templates/browse.html"
+                "templateUrl": "templates/browse.html",
+                "reloadOnSearch": false
             }
         ],
         "controllers": [
@@ -19,7 +21,7 @@
             {
                 "key": "BrowseObjectController",
                 "implementation": "BrowseObjectController.js",
-                "depends": [ "$scope", "$location" ]
+                "depends": [ "$scope", "$location", "$route" ]
             },
             {
                 "key": "CreateMenuController",
diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js
index 826493f6a6..c1cd84c908 100644
--- a/platform/commonUI/browse/src/BrowseController.js
+++ b/platform/commonUI/browse/src/BrowseController.js
@@ -47,8 +47,9 @@ define(
             );
 
             function updateRoute(domainObject) {
-                var context = domainObject.getCapability('context'),
-                    objectPath = context.getPath(),
+                var context = domainObject &&
+                        domainObject.getCapability('context'),
+                    objectPath = context ? context.getPath() : [],
                     ids = objectPath.map(function (domainObject) {
                         return domainObject.getId();
                     }),
diff --git a/platform/commonUI/browse/src/BrowseObjectController.js b/platform/commonUI/browse/src/BrowseObjectController.js
index 9a95c7ea97..ed94fe0c01 100644
--- a/platform/commonUI/browse/src/BrowseObjectController.js
+++ b/platform/commonUI/browse/src/BrowseObjectController.js
@@ -31,7 +31,7 @@ define(
          * object (the right-hand side of Browse mode.)
          * @constructor
          */
-        function BrowseObjectController($scope, $location) {
+        function BrowseObjectController($scope, $location, $route) {
             function setViewForDomainObject(domainObject) {
                 var locationViewKey = $location.search().view;
 
@@ -48,8 +48,14 @@ define(
             }
 
             function updateQueryParam(viewKey) {
+                var unlisten, priorRoute = $route.current;
+
                 if (viewKey) {
                     $location.search('view', viewKey);
+                    unlisten = $scope.$on('$locationChangeSuccess', function () {
+                        $route.current = priorRoute;
+                        unlisten();
+                    });
                 }
             }
 

From 699cd3bd90865f90792eb4adb2531b3552d3da0b Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 15:35:40 -0700
Subject: [PATCH 11/15] [Addressability] Update spec for BrowseController

Update spec to reflect changes for BrowseController to support
addressability of domain objects, WTD-1149.
---
 .../browse/test/BrowseControllerSpec.js       | 26 +++++++++++++------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/platform/commonUI/browse/test/BrowseControllerSpec.js b/platform/commonUI/browse/test/BrowseControllerSpec.js
index e0e885aa85..f41f40a8ab 100644
--- a/platform/commonUI/browse/test/BrowseControllerSpec.js
+++ b/platform/commonUI/browse/test/BrowseControllerSpec.js
@@ -31,6 +31,8 @@ define(
 
         describe("The browse controller", function () {
             var mockScope,
+                mockRoute,
+                mockLocation,
                 mockObjectService,
                 mockNavigationService,
                 mockRootObject,
@@ -50,6 +52,11 @@ define(
                     "$scope",
                     [ "$on", "$watch" ]
                 );
+                mockRoute = { current: { params: {} } };
+                mockLocation = jasmine.createSpyObj(
+                    "$location",
+                    [ "path" ]
+                );
                 mockObjectService = jasmine.createSpyObj(
                     "objectService",
                     [ "getObjects" ]
@@ -75,21 +82,25 @@ define(
                 mockObjectService.getObjects.andReturn(mockPromise({
                     ROOT: mockRootObject
                 }));
-
+                mockRootObject.useCapability.andReturn(mockPromise([
+                    mockDomainObject
+                ]));
+                mockDomainObject.getId.andReturn("mine");
 
                 controller = new BrowseController(
                     mockScope,
+                    mockRoute,
+                    mockLocation,
                     mockObjectService,
                     mockNavigationService
                 );
             });
 
             it("uses composition to set the navigated object, if there is none", function () {
-                mockRootObject.useCapability.andReturn(mockPromise([
-                    mockDomainObject
-                ]));
                 controller = new BrowseController(
                     mockScope,
+                    mockRoute,
+                    mockLocation,
                     mockObjectService,
                     mockNavigationService
                 );
@@ -98,12 +109,11 @@ define(
             });
 
             it("does not try to override navigation", function () {
-                // This behavior is needed if object navigation has been
-                // determined by query string parameters
-                mockRootObject.useCapability.andReturn(mockPromise([null]));
                 mockNavigationService.getNavigation.andReturn(mockDomainObject);
                 controller = new BrowseController(
                     mockScope,
+                    mockRoute,
+                    mockLocation,
                     mockObjectService,
                     mockNavigationService
                 );
@@ -132,4 +142,4 @@ define(
 
         });
     }
-);
\ No newline at end of file
+);

From bcb4e9c49563e5940acc6fc0c1f1128cc2b088bf Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 15:40:19 -0700
Subject: [PATCH 12/15] [Addressability] Remove excessive check

Remove unneeded check from ViewSwitcherController; not necessary
to avoid overwriting query string selection of view. WTD-1149.
---
 .../commonUI/general/src/controllers/ViewSwitcherController.js  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/platform/commonUI/general/src/controllers/ViewSwitcherController.js b/platform/commonUI/general/src/controllers/ViewSwitcherController.js
index 3bf14d8640..69674013d5 100644
--- a/platform/commonUI/general/src/controllers/ViewSwitcherController.js
+++ b/platform/commonUI/general/src/controllers/ViewSwitcherController.js
@@ -53,7 +53,7 @@ define(
 
             // Get list of views, read from capability
             function updateOptions(views) {
-                if (Array.isArray(views) && !($scope.ngModel || {}).selected) {
+                if (Array.isArray(views)) {
                     $timeout(function () {
                         $scope.ngModel.selected = findMatchingOption(
                             views,

From c1c633db80172051bb364c493ee0a0ea5d04ae65 Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 15:57:08 -0700
Subject: [PATCH 13/15] [Addressability] Add test cases

Add test cases to BrowseController which reflect changes for
addressability, WTD-1149.
---
 .../commonUI/browse/src/BrowseController.js   |  1 +
 .../browse/test/BrowseControllerSpec.js       | 80 +++++++++++++++++++
 2 files changed, 81 insertions(+)

diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js
index c1cd84c908..a5713eaeca 100644
--- a/platform/commonUI/browse/src/BrowseController.js
+++ b/platform/commonUI/browse/src/BrowseController.js
@@ -80,6 +80,7 @@ define(
                     // If not, pick a default as the last
                     // root-level component (usually "mine")
                     navigationService.setNavigation(domainObject);
+                    $scope.navigatedObject = domainObject;
                 } else {
                     // Otherwise, just expose the currently navigated object.
                     $scope.navigatedObject = navigationService.getNavigation();
diff --git a/platform/commonUI/browse/test/BrowseControllerSpec.js b/platform/commonUI/browse/test/BrowseControllerSpec.js
index f41f40a8ab..335e2f94eb 100644
--- a/platform/commonUI/browse/test/BrowseControllerSpec.js
+++ b/platform/commonUI/browse/test/BrowseControllerSpec.js
@@ -37,6 +37,7 @@ define(
                 mockNavigationService,
                 mockRootObject,
                 mockDomainObject,
+                mockNextObject,
                 controller;
 
             function mockPromise(value) {
@@ -78,6 +79,10 @@ define(
                     "domainObject",
                     [ "getId", "getCapability", "getModel", "useCapability" ]
                 );
+                mockNextObject = jasmine.createSpyObj(
+                    "nextObject",
+                    [ "getId", "getCapability", "getModel", "useCapability" ]
+                );
 
                 mockObjectService.getObjects.andReturn(mockPromise({
                     ROOT: mockRootObject
@@ -85,6 +90,11 @@ define(
                 mockRootObject.useCapability.andReturn(mockPromise([
                     mockDomainObject
                 ]));
+                mockDomainObject.useCapability.andReturn(mockPromise([
+                    mockNextObject
+                ]));
+                mockNextObject.useCapability.andReturn(undefined);
+                mockNextObject.getId.andReturn("next");
                 mockDomainObject.getId.andReturn("mine");
 
                 controller = new BrowseController(
@@ -140,6 +150,76 @@ define(
                 );
             });
 
+            it("uses route parameters to choose initially-navigated object", function () {
+                mockRoute.current.params.ids = "mine/next";
+                controller = new BrowseController(
+                    mockScope,
+                    mockRoute,
+                    mockLocation,
+                    mockObjectService,
+                    mockNavigationService
+                );
+                expect(mockScope.navigatedObject).toBe(mockNextObject);
+                expect(mockNavigationService.setNavigation)
+                    .toHaveBeenCalledWith(mockNextObject);
+            });
+
+            it("handles invalid IDs by going as far as possible", function () {
+                // Idea here is that if we get a bad path of IDs,
+                // browse controller should traverse down it until
+                // it hits an invalid ID.
+                mockRoute.current.params.ids = "mine/junk";
+                controller = new BrowseController(
+                    mockScope,
+                    mockRoute,
+                    mockLocation,
+                    mockObjectService,
+                    mockNavigationService
+                );
+                expect(mockScope.navigatedObject).toBe(mockDomainObject);
+                expect(mockNavigationService.setNavigation)
+                    .toHaveBeenCalledWith(mockDomainObject);
+            });
+
+            it("handles compositionless objects by going as far as possible", function () {
+                // Idea here is that if we get a path which passes
+                // through an object without a composition, browse controller
+                // should stop at it since remaining IDs cannot be loaded.
+                mockRoute.current.params.ids = "mine/next/junk";
+                controller = new BrowseController(
+                    mockScope,
+                    mockRoute,
+                    mockLocation,
+                    mockObjectService,
+                    mockNavigationService
+                );
+                expect(mockScope.navigatedObject).toBe(mockNextObject);
+                expect(mockNavigationService.setNavigation)
+                    .toHaveBeenCalledWith(mockNextObject);
+            });
+
+            it("updates the displayed route to reflect current navigation", function () {
+                var mockContext = jasmine.createSpyObj('context', ['getPath']),
+                    mockUnlisten = jasmine.createSpy('unlisten');
+
+                mockContext.getPath.andReturn(
+                    [mockRootObject, mockDomainObject, mockNextObject]
+                );
+                mockNextObject.getCapability.andCallFake(function (c) {
+                    return c === 'context' && mockContext;
+                });
+                mockScope.$on.andReturn(mockUnlisten);
+                // Provide a navigation change
+                mockNavigationService.addListener.mostRecentCall.args[0](
+                    mockNextObject
+                );
+                expect(mockLocation.path).toHaveBeenCalledWith("/browse/mine/next");
+
+                // Exercise the Angular workaround
+                mockScope.$on.mostRecentCall.args[1]();
+                expect(mockUnlisten).toHaveBeenCalled();
+            });
+
         });
     }
 );

From 5849f8afe26147474d38a5380239b02ad7fe3c25 Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 16:09:22 -0700
Subject: [PATCH 14/15] [Addressability] Add spec for BrowseObjectController

Add spec for BrowseObjectController, added to track current
view selection from/to query string params, WTD-1149.
---
 .../browse/src/BrowseObjectController.js      |  1 +
 .../browse/test/BrowseObjectControllerSpec.js | 99 +++++++++++++++++++
 platform/commonUI/browse/test/suite.json      |  3 +-
 3 files changed, 102 insertions(+), 1 deletion(-)
 create mode 100644 platform/commonUI/browse/test/BrowseObjectControllerSpec.js

diff --git a/platform/commonUI/browse/src/BrowseObjectController.js b/platform/commonUI/browse/src/BrowseObjectController.js
index ed94fe0c01..c511898871 100644
--- a/platform/commonUI/browse/src/BrowseObjectController.js
+++ b/platform/commonUI/browse/src/BrowseObjectController.js
@@ -37,6 +37,7 @@ define(
 
                 function selectViewIfMatching(view) {
                     if (view.key === locationViewKey) {
+                        $scope.representation = $scope.representation || {};
                         $scope.representation.selected = view;
                     }
                 }
diff --git a/platform/commonUI/browse/test/BrowseObjectControllerSpec.js b/platform/commonUI/browse/test/BrowseObjectControllerSpec.js
new file mode 100644
index 0000000000..e498c1dc12
--- /dev/null
+++ b/platform/commonUI/browse/test/BrowseObjectControllerSpec.js
@@ -0,0 +1,99 @@
+/*****************************************************************************
+ * Open MCT Web, Copyright (c) 2014-2015, United States Government
+ * as represented by the Administrator of the National Aeronautics and Space
+ * Administration. All rights reserved.
+ *
+ * Open MCT Web is 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.
+ *
+ * Open MCT Web includes source code licensed under additional open source
+ * licenses. See the Open Source Licenses file (LICENSES.md) included with
+ * this source code distribution or the Licensing information page available
+ * at runtime from the About dialog for additional information.
+ *****************************************************************************/
+/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
+
+
+define(
+    ["../src/BrowseObjectController"],
+    function (BrowseObjectController) {
+        "use strict";
+
+        describe("The browse object controller", function () {
+            var mockScope,
+                mockLocation,
+                mockRoute,
+                mockUnlisten,
+                controller;
+
+            // Utility function; look for a $watch on scope and fire it
+            function fireWatch(expr, value) {
+                mockScope.$watch.calls.forEach(function (call) {
+                    if (call.args[0] === expr) {
+                        call.args[1](value);
+                    }
+                });
+            }
+
+            beforeEach(function () {
+                mockScope = jasmine.createSpyObj(
+                    "$scope",
+                    [ "$on", "$watch" ]
+                );
+                mockRoute = { current: { params: {} } };
+                mockLocation = jasmine.createSpyObj(
+                    "$location",
+                    [ "path", "search" ]
+                );
+                mockUnlisten = jasmine.createSpy("unlisten");
+
+                mockScope.$on.andReturn(mockUnlisten);
+
+                controller = new BrowseObjectController(
+                    mockScope,
+                    mockLocation,
+                    mockRoute
+                );
+            });
+
+            it("updates query parameters when selected view changes", function () {
+                fireWatch("representation.selected.key", "xyz");
+                expect(mockLocation.search).toHaveBeenCalledWith('view', "xyz");
+
+                // Exercise the Angular workaround
+                mockScope.$on.mostRecentCall.args[1]();
+                expect(mockUnlisten).toHaveBeenCalled();
+            });
+
+            it("sets the active view from query parameters", function () {
+                var mockDomainObject = jasmine.createSpyObj(
+                        "domainObject",
+                        ['getId', 'getModel', 'getCapability', 'useCapability']
+                    ),
+                    testViews = [
+                        { key: 'abc' },
+                        { key: 'def', someKey: 'some value' },
+                        { key: 'xyz' }
+                    ];
+
+                mockDomainObject.useCapability.andCallFake(function (c) {
+                    return (c === 'view') && testViews;
+                });
+                mockLocation.search.andReturn({ view: 'def' });
+
+                fireWatch('domainObject', mockDomainObject);
+                expect(mockScope.representation.selected)
+                    .toEqual(testViews[1]);
+            });
+
+        });
+    }
+);
diff --git a/platform/commonUI/browse/test/suite.json b/platform/commonUI/browse/test/suite.json
index 21d76dae05..e36f345caa 100644
--- a/platform/commonUI/browse/test/suite.json
+++ b/platform/commonUI/browse/test/suite.json
@@ -1,5 +1,6 @@
 [
     "BrowseController",
+    "BrowseObjectController",
     "creation/CreateAction",
     "creation/CreateActionProvider",
     "creation/CreateMenuController",
@@ -10,4 +11,4 @@
     "navigation/NavigationService",
     "windowing/FullscreenAction",
     "windowing/WindowTitler"
-]
\ No newline at end of file
+]

From ee69eb3a01a2bc63ab65cad29f090dd517a15458 Mon Sep 17 00:00:00 2001
From: Victor Woeltjen <victor.woeltjen@nasa.gov>
Date: Tue, 16 Jun 2015 16:10:31 -0700
Subject: [PATCH 15/15] [Addressability] Fix code style

Change code style to satisfy JSLint for changes from WTD-1149.
---
 platform/commonUI/browse/src/BrowseController.js | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js
index a5713eaeca..241c39bb47 100644
--- a/platform/commonUI/browse/src/BrowseController.js
+++ b/platform/commonUI/browse/src/BrowseController.js
@@ -55,10 +55,12 @@ define(
                     }),
                     priorRoute = $route.current,
                     // Act as if params HADN'T changed to avoid page reload
-                    unlisten = $scope.$on('$locationChangeSuccess', function () {
-                        $route.current = priorRoute;
-                        unlisten();
-                    });
+                    unlisten;
+
+                unlisten = $scope.$on('$locationChangeSuccess', function () {
+                    $route.current = priorRoute;
+                    unlisten();
+                });
 
                 $location.path("/browse/" + ids.slice(1).join("/"));
             }