From 549dfab5aa28a4296a6bbe74e279a507151bc2d9 Mon Sep 17 00:00:00 2001 From: Henry Date: Tue, 26 Jan 2016 21:47:19 -0800 Subject: [PATCH] [Edit Mode] #627 remove edit concerns from browse controller --- platform/commonUI/browse/bundle.js | 4 + .../browse/res/templates/browse-object.html | 5 -- .../commonUI/browse/res/templates/browse.html | 4 +- .../browse/res/templates/view-object.html | 33 +++++++ .../commonUI/browse/src/BrowseController.js | 36 +++----- .../src/navigation/NavigationService.js | 33 +++++-- platform/commonUI/edit/bundle.js | 12 +++ .../edit/res/templates/edit-object.html | 88 ++++++++++--------- .../commonUI/edit/res/templates/edit.html | 2 +- .../edit/res/templates/inspector-edit.html | 80 +++++++++++++++++ .../commonUI/edit/src/actions/EditAction.js | 23 +++-- .../src/controllers/EditObjectController.js | 65 ++++++++++++++ .../edit/src/directives/MCTBeforeUnload.js | 24 +++-- .../edit/src/representers/EditRepresenter.js | 19 ++++ .../res/templates/inspector-browse.html | 64 ++++++++++++++ .../src/gestures/DropGesture.js | 10 ++- 16 files changed, 402 insertions(+), 100 deletions(-) create mode 100644 platform/commonUI/browse/res/templates/view-object.html create mode 100644 platform/commonUI/edit/res/templates/inspector-edit.html create mode 100644 platform/commonUI/edit/src/controllers/EditObjectController.js create mode 100644 platform/commonUI/general/res/templates/inspector-browse.html diff --git a/platform/commonUI/browse/bundle.js b/platform/commonUI/browse/bundle.js index bf76295b18..4715440b76 100644 --- a/platform/commonUI/browse/bundle.js +++ b/platform/commonUI/browse/bundle.js @@ -170,6 +170,10 @@ define([ } ], "representations": [ + { + "key": "view-region", + "templateUrl": "templates/view-object.html" + }, { "key": "browse-object", "template": browseObjectTemplate, diff --git a/platform/commonUI/browse/res/templates/browse-object.html b/platform/commonUI/browse/res/templates/browse-object.html index 562f8bdf6a..35d0d58f3f 100644 --- a/platform/commonUI/browse/res/templates/browse-object.html +++ b/platform/commonUI/browse/res/templates/browse-object.html @@ -47,11 +47,6 @@
- - diff --git a/platform/commonUI/browse/res/templates/browse.html b/platform/commonUI/browse/res/templates/browse.html index 3d3f22bd7d..5b74709b2e 100644 --- a/platform/commonUI/browse/res/templates/browse.html +++ b/platform/commonUI/browse/res/templates/browse.html @@ -63,7 +63,7 @@
@@ -71,7 +71,7 @@
- diff --git a/platform/commonUI/browse/res/templates/view-object.html b/platform/commonUI/browse/res/templates/view-object.html new file mode 100644 index 0000000000..b670e1645e --- /dev/null +++ b/platform/commonUI/browse/res/templates/view-object.html @@ -0,0 +1,33 @@ + + + + diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js index d1d5aa92cc..9f1834f80d 100644 --- a/platform/commonUI/browse/src/BrowseController.js +++ b/platform/commonUI/browse/src/BrowseController.js @@ -60,13 +60,6 @@ define( ($route.current.params.ids || defaultPath).split("/") ); - function isDirty(){ - var editorCapability = $scope.navigatedObject && - $scope.navigatedObject.getCapability("editor"), - hasChanges = editorCapability && editorCapability.dirty(); - return hasChanges; - } - function updateRoute(domainObject) { var priorRoute = $route.current, // Act as if params HADN'T changed to avoid page reload @@ -83,9 +76,7 @@ define( // urlService.urlForLocation used to adjust current // path to new, addressed, path based on // domainObject - $location.path(urlService.urlForLocation("browse", - domainObject.hasCapability('editor') ? - domainObject.getOriginalObject() : domainObject)); + $location.path(urlService.urlForLocation("browse", domainObject)); } @@ -97,17 +88,15 @@ define( return; } - if (isDirty() && !confirm(CONFIRM_MSG)) { - $scope.treeModel.selectedObject = $scope.navigatedObject; - navigationService.setNavigation($scope.navigatedObject); - } else { - if ($scope.navigatedObject && $scope.navigatedObject.hasCapability("editor")){ - $scope.navigatedObject.getCapability("editor").cancel(); - } + if (navigationService.setNavigation(domainObject)) { $scope.navigatedObject = domainObject; $scope.treeModel.selectedObject = domainObject; - navigationService.setNavigation(domainObject); updateRoute(domainObject); + } else { + //If navigation was unsuccessful (ie. blocked), reset + // the selected object in the tree to the currently + // navigated object + $scope.treeModel.selectedObject = $scope.navigatedObject ; } } @@ -184,18 +173,13 @@ define( selectedObject: navigationService.getNavigation() }; - $scope.beforeUnloadWarning = function() { - return isDirty() ? - "Unsaved changes will be lost if you leave this page." : - undefined; - }; - // Listen for changes in navigation state. navigationService.addListener(setNavigation); - // Also listen for changes which come from the tree + // Also listen for changes which come from the tree. Changes in + // the tree will trigger a change in browse navigation state. $scope.$watch("treeModel.selectedObject", setNavigation); - + // Clean up when the scope is destroyed $scope.$on("$destroy", function () { navigationService.removeListener(setNavigation); diff --git a/platform/commonUI/browse/src/navigation/NavigationService.js b/platform/commonUI/browse/src/navigation/NavigationService.js index 87e5582ef7..e2b7168b3c 100644 --- a/platform/commonUI/browse/src/navigation/NavigationService.js +++ b/platform/commonUI/browse/src/navigation/NavigationService.js @@ -37,7 +37,7 @@ define( */ function NavigationService() { this.navigated = undefined; - this.callbacks = []; + this.callbacks = {}; } /** @@ -53,12 +53,20 @@ define( * @param {DomainObject} domainObject the domain object to navigate to */ NavigationService.prototype.setNavigation = function (value) { + var canNavigate = true; if (this.navigated !== value) { - this.navigated = value; - this.callbacks.forEach(function (callback) { - callback(value); - }); + canNavigate = (this.callbacks['before'] || []) + .reduce(function (previous, callback) { + return callback(value) && previous; + }, true); + if (canNavigate) { + this.navigated = value; + this.callbacks['after'].forEach(function (callback) { + callback(value); + }); + } } + return canNavigate; }; /** @@ -67,9 +75,13 @@ define( * this changes. * @param {function} callback the callback to invoke when * navigation state changes + * @param {string} [event=after] the navigation event to listen to. + * One of 'before' or 'after'. */ - NavigationService.prototype.addListener = function (callback) { - this.callbacks.push(callback); + NavigationService.prototype.addListener = function (callback, event) { + event = event || 'after'; + this.callbacks[event] = this.callbacks[event] || []; + this.callbacks[event].push(callback); }; /** @@ -77,9 +89,12 @@ define( * @param {function} callback the callback which should * no longer be invoked when navigation state * changes + * @param {string} [event=after] the navigation event to the + * callback is registered to. One of 'before' or 'after'. */ - NavigationService.prototype.removeListener = function (callback) { - this.callbacks = this.callbacks.filter(function (cb) { + NavigationService.prototype.removeListener = function (callback, event) { + event = event || 'after'; + this.callbacks[event] = this.callbacks[event].filter(function (cb) { return cb !== callback; }); }; diff --git a/platform/commonUI/edit/bundle.js b/platform/commonUI/edit/bundle.js index c4b0da1798..9fc0c98939 100644 --- a/platform/commonUI/edit/bundle.js +++ b/platform/commonUI/edit/bundle.js @@ -26,6 +26,7 @@ define([ "./src/controllers/EditActionController", "./src/controllers/EditPanesController", "./src/controllers/ElementsController", + "./src/controllers/EditObjectController", "./src/directives/MCTBeforeUnload", "./src/actions/LinkAction", "./src/actions/EditAction", @@ -48,6 +49,7 @@ define([ EditActionController, EditPanesController, ElementsController, + EditObjectController, MCTBeforeUnload, LinkAction, EditAction, @@ -106,6 +108,13 @@ define([ "depends": [ "$scope" ] + }, + { + "key": "EditObjectController", + "implementation": EditObjectController, + "depends": [ + "$scope" + ] } ], "directives": [ @@ -206,6 +215,9 @@ define([ "template": editObjectTemplate, "uses": [ "view" + ], + "gestures": [ + "drop" ] }, { diff --git a/platform/commonUI/edit/res/templates/edit-object.html b/platform/commonUI/edit/res/templates/edit-object.html index 71dc233a82..7b2f32e1b3 100644 --- a/platform/commonUI/edit/res/templates/edit-object.html +++ b/platform/commonUI/edit/res/templates/edit-object.html @@ -19,50 +19,52 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> - - -
- -
- - - +
+
+
+ +
- - +
+
+ +
+ + + + + +
+ + +
+
diff --git a/platform/commonUI/edit/res/templates/edit.html b/platform/commonUI/edit/res/templates/edit.html index 07a677bce8..489eed822b 100644 --- a/platform/commonUI/edit/res/templates/edit.html +++ b/platform/commonUI/edit/res/templates/edit.html @@ -21,7 +21,7 @@ -->
diff --git a/platform/commonUI/edit/res/templates/inspector-edit.html b/platform/commonUI/edit/res/templates/inspector-edit.html new file mode 100644 index 0000000000..5f3e4d522e --- /dev/null +++ b/platform/commonUI/edit/res/templates/inspector-edit.html @@ -0,0 +1,80 @@ + + +
+ +
+
+
Inspection
+
    +
  • + Properties +
    +
    {{ data.name }}
    +
    {{ data.value }}
    +
    +
  • +
  • + Location + + + + +
  • +
  • + Original Location + + + + +
  • +
+
+
+ +
+
+ Elements + + +
+
+
+
+
diff --git a/platform/commonUI/edit/src/actions/EditAction.js b/platform/commonUI/edit/src/actions/EditAction.js index 6b8ba3e042..d771f75dd4 100644 --- a/platform/commonUI/edit/src/actions/EditAction.js +++ b/platform/commonUI/edit/src/actions/EditAction.js @@ -72,13 +72,26 @@ define( * Enter edit mode. */ EditAction.prototype.perform = function () { - var editableObject; + var self = this; if (!this.domainObject.hasCapability("editor")) { - editableObject = new EditableDomainObject(this.domainObject, this.$q); - editableObject.getCapability('status').set('editing', true); - this.navigationService.setNavigation(editableObject); + //TODO: This is only necessary because the drop gesture is + // wrapping the object itself, need to refactor this later. + // All responsibility for switching into edit mode should be + // in the edit action, and not duplicated in the gesture + this.domainObject = new EditableDomainObject(this.domainObject, this.$q); } - //this.$location.path("/edit"); + this.navigationService.setNavigation(this.domainObject); + this.domainObject.getCapability('status').set('editing', true); + + //Register a listener to automatically cancel this edit action + //if the user navigates away from this object. + function cancelEditing(navigatedTo){ + if (!navigatedTo || navigatedTo.getId() !== self.domainObject.getId()) { + self.domainObject.getCapability('editor').cancel(); + self.navigationService.removeListener(cancelEditing); + } + } + this.navigationService.addListener(cancelEditing); }; /** diff --git a/platform/commonUI/edit/src/controllers/EditObjectController.js b/platform/commonUI/edit/src/controllers/EditObjectController.js new file mode 100644 index 0000000000..eb5d494cac --- /dev/null +++ b/platform/commonUI/edit/src/controllers/EditObjectController.js @@ -0,0 +1,65 @@ +/***************************************************************************** + * 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*/ + +/** + * This bundle implements Edit mode. + * @namespace platform/commonUI/edit + */ +define( + [], + function () { + "use strict"; + + /** + * Controller which is responsible for populating the scope for + * Edit mode + * @memberof platform/commonUI/edit + * @constructor + */ + function EditObjectController($scope) { + this.scope = $scope; + } + + /** + * Get the warning to show if the user attempts to navigate + * away from Edit mode while unsaved changes are present. + * @returns {string} the warning to show, or undefined if + * there are no unsaved changes + */ + EditObjectController.prototype.getUnloadWarning = function () { + var navigatedObject = this.scope.domainObject, + editorCapability = navigatedObject && + navigatedObject.getCapability("editor"), + statusCapability = navigatedObject && + navigatedObject.getCapability("status"), + hasChanges = statusCapability && statusCapability.get('editing') + && editorCapability && editorCapability.dirty(); + + return hasChanges ? + "Unsaved changes will be lost if you leave this page." : + undefined; + }; + + return EditObjectController; + } +); diff --git a/platform/commonUI/edit/src/directives/MCTBeforeUnload.js b/platform/commonUI/edit/src/directives/MCTBeforeUnload.js index 3e7501c788..226e85f4a0 100644 --- a/platform/commonUI/edit/src/directives/MCTBeforeUnload.js +++ b/platform/commonUI/edit/src/directives/MCTBeforeUnload.js @@ -35,7 +35,7 @@ define( * @constructor * @param $window the window */ - function MCTBeforeUnload($window) { + function MCTBeforeUnload($window, navigationService) { var unloads = [], oldBeforeUnload = $window.onbeforeunload; @@ -57,6 +57,7 @@ define( // Stop using this unload expression function removeUnload() { + navigationService.removeListener(checkNavigationEvent, "before"); unloads = unloads.filter(function (callback) { return callback !== unload; }); @@ -65,17 +66,28 @@ define( } } - // Show a dialog before allowing a location change - function checkLocationChange(event) { + function shouldAllowNavigation(){ // Get an unload message (if any) var warning = unload(); // Prompt the user if there's an unload message - if (warning && !$window.confirm(warning)) { - // ...and prevent the route change if it was confirmed + return !warning || $window.confirm(warning); + } + + // Show a dialog before allowing a location change + function checkLocationChange(event) { + if (!shouldAllowNavigation()) { + // Prevent the route change if it was confirmed event.preventDefault(); } } + // Show a dialog before allowing a location change + function checkNavigationEvent(event) { + // Return a false value to the navigationService to + // indicate that the navigation event should be prevented + return shouldAllowNavigation(); + } + // If this is the first active instance of this directive, // register as the window's beforeunload handler if (unloads.length === 0) { @@ -90,6 +102,8 @@ define( // Also handle route changes scope.$on("$locationChangeStart", checkLocationChange); + + navigationService.addListener(checkNavigationEvent, "before"); } return { diff --git a/platform/commonUI/edit/src/representers/EditRepresenter.js b/platform/commonUI/edit/src/representers/EditRepresenter.js index 0844f65e67..e53318c1b0 100644 --- a/platform/commonUI/edit/src/representers/EditRepresenter.js +++ b/platform/commonUI/edit/src/representers/EditRepresenter.js @@ -49,6 +49,7 @@ define( var self = this; this.scope = scope; + this.listenHandle = undefined; // Mutate and persist a new version of a domain object's model. function doPersist(model) { @@ -100,10 +101,13 @@ define( // Place the "commit" method in the scope scope.commit = commit; scope.setEditable = setEditable; + } // Handle a specific representation of a specific domain object EditRepresenter.prototype.represent = function represent(representation, representedObject) { + var scope = this.scope, + self = this; // Track the key, to know which view configuration to save to. this.key = (representation || {}).key; // Track the represented object @@ -113,11 +117,26 @@ define( // Ensure existing watches are released this.destroy(); + + /** + * Listen for changes in object state. If the object becomes + * editable then change the view and inspector regions + * object representation accordingly + */ + this.listenHandle = this.domainObject.getCapability('status').listen(function(statuses){ + if (statuses.indexOf('editing')!=-1){ + scope.viewRegionTemplate = 'edit-object'; + scope.inspectorRegionTemplate = 'inspector-edit' + } else { + delete scope.viewRegionTemplate; + } + }); }; // Respond to the destruction of the current representation. EditRepresenter.prototype.destroy = function destroy() { // Nothing to clean up + this.listenHandle && this.listenHandle(); }; return EditRepresenter; diff --git a/platform/commonUI/general/res/templates/inspector-browse.html b/platform/commonUI/general/res/templates/inspector-browse.html new file mode 100644 index 0000000000..3a7a60f516 --- /dev/null +++ b/platform/commonUI/general/res/templates/inspector-browse.html @@ -0,0 +1,64 @@ + + +
+
Inspection
+
    +
  • + Properties +
    +
    {{ data.name }}
    +
    {{ data.value }}
    +
    +
  • +
  • + Location + + + + +
  • +
  • + Original Location + + + + +
  • +
+
+
+ diff --git a/platform/representation/src/gestures/DropGesture.js b/platform/representation/src/gestures/DropGesture.js index 1b7881a770..225211c3bd 100644 --- a/platform/representation/src/gestures/DropGesture.js +++ b/platform/representation/src/gestures/DropGesture.js @@ -165,16 +165,18 @@ define( if (shouldCreateVirtualPanel(domainObject, selectedObject)){ editableDomainObject = createVirtualPanel(domainObject, selectedObject); if (editableDomainObject) { - navigationService.setNavigation(editableDomainObject); + editableDomainObject.getCapability('action').perform('edit'); + //navigationService.setNavigation(editableDomainObject); broadcastDrop(id, event); - editableDomainObject.getCapability('status').set('editing', true); + //editableDomainObject.getCapability('status').set('editing', true); } } else { $q.when(action && action.perform()).then(function (result) { //Don't go into edit mode for folders if (domainObjectType!=='folder') { - navigationService.setNavigation(editableDomainObject); - editableDomainObject.getCapability('status').set('editing', true); + // navigationService.setNavigation(editableDomainObject); + //editableDomainObject.getCapability('status').set('editing', true); + editableDomainObject.getCapability('action').perform('edit'); } broadcastDrop(id, event); });