+
+
+
diff --git a/platform/commonUI/browse/src/BrowseController.js b/platform/commonUI/browse/src/BrowseController.js
index d1d5aa92cc..db7d9182cb 100644
--- a/platform/commonUI/browse/src/BrowseController.js
+++ b/platform/commonUI/browse/src/BrowseController.js
@@ -27,14 +27,12 @@
*/
define(
[
- '../../../representation/src/gestures/GestureConstants',
- '../../edit/src/objects/EditableDomainObject'
+ '../../../representation/src/gestures/GestureConstants'
],
- function (GestureConstants, EditableDomainObject) {
+ function (GestureConstants) {
"use strict";
- var ROOT_ID = "ROOT",
- CONFIRM_MSG = "Unsaved changes will be lost if you leave this page.";
+ var ROOT_ID = "ROOT";
/**
* The BrowseController is used to populate the initial scope in Browse
@@ -47,26 +45,20 @@ define(
* @constructor
*/
function BrowseController(
- $scope,
- $route,
- $location,
- $q,
- objectService,
- navigationService,
- urlService,
+ $scope,
+ $route,
+ $location,
+ $window,
+ objectService,
+ navigationService,
+ urlService,
+ policyService,
defaultPath
) {
var path = [ROOT_ID].concat(
($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,31 +75,35 @@ 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));
}
// Callback for updating the in-scope reference to the object
// that is currently navigated-to.
function setNavigation(domainObject) {
+ var navigationAllowed = true;
+
if (domainObject === $scope.navigatedObject){
//do nothing;
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();
- }
+ policyService.allow("navigation", $scope.navigatedObject, domainObject, function(message){
+ navigationAllowed = $window.confirm(message + "\r\n\r\n" +
+ " Are you sure you want to continue?");
+ });
+
+ if (navigationAllowed) {
$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 +180,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/BrowseObjectController.js b/platform/commonUI/browse/src/BrowseObjectController.js
index b0a93a991a..71345d6f1b 100644
--- a/platform/commonUI/browse/src/BrowseObjectController.js
+++ b/platform/commonUI/browse/src/BrowseObjectController.js
@@ -22,11 +22,8 @@
/*global define,Promise*/
define(
- [
- '../../../representation/src/gestures/GestureConstants',
- '../../edit/src/objects/EditableDomainObject'
- ],
- function (GestureConstants, EditableDomainObject) {
+ [],
+ function () {
"use strict";
/**
@@ -35,7 +32,7 @@ define(
* @memberof platform/commonUI/browse
* @constructor
*/
- function BrowseObjectController($scope, $location, $route, $q, navigationService) {
+ function BrowseObjectController($scope, $location, $route) {
var navigatedObject;
function setViewForDomainObject(domainObject) {
@@ -57,10 +54,9 @@ define(
function updateQueryParam(viewKey) {
var unlisten,
- priorRoute = $route.current,
- isEditMode = $scope.domainObject && $scope.domainObject.hasCapability('editor');
+ priorRoute = $route.current;
- if (viewKey && !isEditMode) {
+ if (viewKey) {
$location.search('view', viewKey);
unlisten = $scope.$on('$locationChangeSuccess', function () {
// Checks path to make sure /browse/ is at front
@@ -76,10 +72,6 @@ define(
$scope.$watch('domainObject', setViewForDomainObject);
$scope.$watch('representation.selected.key', updateQueryParam);
- $scope.cancelEditing = function() {
- navigationService.setNavigation($scope.domainObject.getDomainObject());
- };
-
$scope.doAction = function (action){
return $scope[action] && $scope[action]();
};
diff --git a/platform/commonUI/browse/src/navigation/NavigationService.js b/platform/commonUI/browse/src/navigation/NavigationService.js
index 87e5582ef7..3b4d266bd1 100644
--- a/platform/commonUI/browse/src/navigation/NavigationService.js
+++ b/platform/commonUI/browse/src/navigation/NavigationService.js
@@ -59,6 +59,7 @@ define(
callback(value);
});
}
+ return true;
};
/**
diff --git a/platform/commonUI/browse/test/BrowseControllerSpec.js b/platform/commonUI/browse/test/BrowseControllerSpec.js
index e4fad10544..cc14b4d0e5 100644
--- a/platform/commonUI/browse/test/BrowseControllerSpec.js
+++ b/platform/commonUI/browse/test/BrowseControllerSpec.js
@@ -29,8 +29,7 @@ define(
function (BrowseController) {
"use strict";
- //TODO: Disabled for NEM Beta
- xdescribe("The browse controller", function () {
+ describe("The browse controller", function () {
var mockScope,
mockRoute,
mockLocation,
@@ -40,6 +39,8 @@ define(
mockUrlService,
mockDomainObject,
mockNextObject,
+ mockWindow,
+ mockPolicyService,
testDefaultRoot,
controller;
@@ -56,14 +57,25 @@ define(
mockScope,
mockRoute,
mockLocation,
+ mockWindow,
mockObjectService,
mockNavigationService,
mockUrlService,
+ mockPolicyService,
testDefaultRoot
);
}
beforeEach(function () {
+ mockWindow = jasmine.createSpyObj('$window', [
+ "confirm"
+ ]);
+ mockWindow.confirm.andReturn(true);
+
+ mockPolicyService = jasmine.createSpyObj('policyService', [
+ 'allow'
+ ]);
+
testDefaultRoot = "some-root-level-domain-object";
mockScope = jasmine.createSpyObj(
@@ -214,7 +226,10 @@ define(
// prior to setting $route.current
mockLocation.path.andReturn("/browse/");
+ mockNavigationService.setNavigation.andReturn(true);
+
// Exercise the Angular workaround
+ mockNavigationService.addListener.mostRecentCall.args[0]();
mockScope.$on.mostRecentCall.args[1]();
expect(mockUnlisten).toHaveBeenCalled();
@@ -225,6 +240,36 @@ define(
);
});
+ it("after successful navigation event sets the selected tree " +
+ "object", function () {
+ mockScope.navigatedObject = mockDomainObject;
+ mockNavigationService.setNavigation.andReturn(true);
+
+ //Simulate a change in selected tree object
+ mockScope.treeModel = {selectedObject: mockDomainObject};
+ mockScope.$watch.mostRecentCall.args[1](mockNextObject);
+
+ expect(mockScope.treeModel.selectedObject).toBe(mockNextObject);
+ expect(mockScope.treeModel.selectedObject).not.toBe(mockDomainObject);
+ });
+
+ it("after failed navigation event resets the selected tree" +
+ " object", function () {
+ mockScope.navigatedObject = mockDomainObject;
+ mockWindow.confirm.andReturn(false);
+ mockPolicyService.allow.andCallFake(function(category, object, context, callback){
+ callback("unsaved changes");
+ return false;
+ });
+
+ //Simulate a change in selected tree object
+ mockScope.treeModel = {selectedObject: mockDomainObject};
+ mockScope.$watch.mostRecentCall.args[1](mockNextObject);
+
+ expect(mockScope.treeModel.selectedObject).not.toBe(mockNextObject);
+ expect(mockScope.treeModel.selectedObject).toBe(mockDomainObject);
+ });
+
});
}
);
diff --git a/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js b/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js
index 6d7f3fd20d..410a5f1562 100644
--- a/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js
+++ b/platform/commonUI/browse/test/navigation/NavigationServiceSpec.js
@@ -86,4 +86,4 @@ define(
});
}
-);
\ No newline at end of file
+);
diff --git a/platform/commonUI/edit/bundle.js b/platform/commonUI/edit/bundle.js
index c4b0da1798..80a98a6927 100644
--- a/platform/commonUI/edit/bundle.js
+++ b/platform/commonUI/edit/bundle.js
@@ -22,10 +22,10 @@
/*global define*/
define([
- "./src/controllers/EditController",
"./src/controllers/EditActionController",
"./src/controllers/EditPanesController",
"./src/controllers/ElementsController",
+ "./src/controllers/EditObjectController",
"./src/directives/MCTBeforeUnload",
"./src/actions/LinkAction",
"./src/actions/EditAction",
@@ -34,9 +34,9 @@ define([
"./src/actions/SaveAction",
"./src/actions/CancelAction",
"./src/policies/EditActionPolicy",
+ "./src/policies/EditNavigationPolicy",
"./src/representers/EditRepresenter",
"./src/representers/EditToolbarRepresenter",
- "text!./res/templates/edit.html",
"text!./res/templates/library.html",
"text!./res/templates/edit-object.html",
"text!./res/templates/edit-action-buttons.html",
@@ -44,10 +44,10 @@ define([
"text!./res/templates/topbar-edit.html",
'legacyRegistry'
], function (
- EditController,
EditActionController,
EditPanesController,
ElementsController,
+ EditObjectController,
MCTBeforeUnload,
LinkAction,
EditAction,
@@ -56,9 +56,9 @@ define([
SaveAction,
CancelAction,
EditActionPolicy,
+ EditNavigationPolicy,
EditRepresenter,
EditToolbarRepresenter,
- editTemplate,
libraryTemplate,
editObjectTemplate,
editActionButtonsTemplate,
@@ -70,22 +70,7 @@ define([
legacyRegistry.register("platform/commonUI/edit", {
"extensions": {
- "routes": [
- {
- "when": "/edit",
- "template": editTemplate
- }
- ],
"controllers": [
- {
- "key": "EditController",
- "implementation": EditController,
- "depends": [
- "$scope",
- "$q",
- "navigationService"
- ]
- },
{
"key": "EditActionController",
"implementation": EditActionController,
@@ -106,6 +91,15 @@ define([
"depends": [
"$scope"
]
+ },
+ {
+ "key": "EditObjectController",
+ "implementation": EditObjectController,
+ "depends": [
+ "$scope",
+ "$location",
+ "policyService"
+ ]
}
],
"directives": [
@@ -192,7 +186,13 @@ define([
{
"category": "action",
"implementation": EditActionPolicy
+ },
+ {
+ "category": "navigation",
+ "message": "There are unsaved changes.",
+ "implementation": EditNavigationPolicy
}
+
],
"templates": [
{
@@ -206,6 +206,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..9da28714a7 100644
--- a/platform/commonUI/edit/res/templates/edit-object.html
+++ b/platform/commonUI/edit/res/templates/edit-object.html
@@ -19,50 +19,51 @@
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/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/capabilities/EditorCapability.js b/platform/commonUI/edit/src/capabilities/EditorCapability.js
index f674880203..b48c1988e6 100644
--- a/platform/commonUI/edit/src/capabilities/EditorCapability.js
+++ b/platform/commonUI/edit/src/capabilities/EditorCapability.js
@@ -124,7 +124,6 @@ define(
*/
EditorCapability.prototype.cancel = function () {
this.editableObject.getCapability("status").set("editing", false);
- //TODO: Reset the cache as well here.
this.cache.markClean();
return resolvePromise(undefined);
};
diff --git a/platform/commonUI/edit/src/controllers/EditController.js b/platform/commonUI/edit/src/controllers/EditObjectController.js
similarity index 53%
rename from platform/commonUI/edit/src/controllers/EditController.js
rename to platform/commonUI/edit/src/controllers/EditObjectController.js
index eaffe02186..d6121106ec 100644
--- a/platform/commonUI/edit/src/controllers/EditController.js
+++ b/platform/commonUI/edit/src/controllers/EditObjectController.js
@@ -26,41 +26,45 @@
* @namespace platform/commonUI/edit
*/
define(
- ["../objects/EditableDomainObject"],
- function (EditableDomainObject) {
+ [],
+ function () {
"use strict";
/**
* Controller which is responsible for populating the scope for
- * Edit mode; introduces an editable version of the currently
- * navigated domain object into the scope.
+ * Edit mode
* @memberof platform/commonUI/edit
* @constructor
*/
- function EditController($scope, $q, navigationService) {
- var self = this;
+ function EditObjectController($scope, $location, policyService) {
+ this.scope = $scope;
+ this.policyService = policyService;
- function setNavigation(domainObject) {
- // Wrap the domain object such that all mutation is
- // confined to edit mode (until Save)
- self.navigatedDomainObject =
- domainObject && new EditableDomainObject(domainObject, $q);
+ var navigatedObject;
+ function setViewForDomainObject(domainObject) {
+
+ var locationViewKey = $location.search().view;
+
+ function selectViewIfMatching(view) {
+ if (view.key === locationViewKey) {
+ $scope.representation = $scope.representation || {};
+ $scope.representation.selected = view;
+ }
+ }
+
+ if (locationViewKey) {
+ ((domainObject && domainObject.useCapability('view')) || [])
+ .forEach(selectViewIfMatching);
+ }
+ navigatedObject = domainObject;
}
- setNavigation(navigationService.getNavigation());
- navigationService.addListener(setNavigation);
- $scope.$on("$destroy", function () {
- navigationService.removeListener(setNavigation);
- });
- }
+ $scope.$watch('domainObject', setViewForDomainObject);
- /**
- * Get the domain object which is navigated-to.
- * @returns {DomainObject} the domain object that is navigated-to
- */
- EditController.prototype.navigatedObject = function () {
- return this.navigatedDomainObject;
- };
+ $scope.doAction = function (action){
+ return $scope[action] && $scope[action]();
+ };
+ }
/**
* Get the warning to show if the user attempts to navigate
@@ -68,17 +72,18 @@ define(
* @returns {string} the warning to show, or undefined if
* there are no unsaved changes
*/
- EditController.prototype.getUnloadWarning = function () {
- var navigatedObject = this.navigatedDomainObject,
- editorCapability = navigatedObject &&
- navigatedObject.getCapability("editor"),
- hasChanges = editorCapability && editorCapability.dirty();
+ EditObjectController.prototype.getUnloadWarning = function () {
+ var navigatedObject = this.scope.domainObject,
+ policyMessage;
+
+ this.policyService.allow("navigation", navigatedObject, undefined, function(message) {
+ policyMessage = message;
+ });
+
+ return policyMessage;
- return hasChanges ?
- "Unsaved changes will be lost if you leave this page." :
- undefined;
};
- return EditController;
+ return EditObjectController;
}
);
diff --git a/platform/commonUI/edit/src/policies/EditNavigationPolicy.js b/platform/commonUI/edit/src/policies/EditNavigationPolicy.js
new file mode 100644
index 0000000000..882e64935e
--- /dev/null
+++ b/platform/commonUI/edit/src/policies/EditNavigationPolicy.js
@@ -0,0 +1,67 @@
+/*****************************************************************************
+ * 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*/
+
+define(
+ [],
+ function () {
+ "use strict";
+
+ /**
+ * Policy controlling whether navigation events should proceed
+ * when object is being edited.
+ * @memberof platform/commonUI/edit
+ * @constructor
+ * @implements {Policy.
}
+ */
+ function EditNavigationPolicy(policyService) {
+ this.policyService = policyService;
+ }
+
+ /**
+ * @private
+ */
+ EditNavigationPolicy.prototype.isDirty = function(domainObject) {
+ var navigatedObject = domainObject,
+ editorCapability = navigatedObject &&
+ navigatedObject.getCapability("editor"),
+ statusCapability = navigatedObject &&
+ navigatedObject.getCapability("status");
+
+ return statusCapability && statusCapability.get('editing')
+ && editorCapability && editorCapability.dirty();
+ };
+
+ /**
+ * Allow navigation if an object is not dirty, or if the user elects
+ * to proceed anyway.
+ * @param currentNavigation
+ * @returns {boolean|*} true if the object model is clean; or if
+ * it's dirty and the user wishes to proceed anyway.
+ */
+ EditNavigationPolicy.prototype.allow = function (currentNavigation) {
+ return !this.isDirty(currentNavigation);
+ };
+
+ return EditNavigationPolicy;
+ }
+);
diff --git a/platform/commonUI/edit/src/representers/EditRepresenter.js b/platform/commonUI/edit/src/representers/EditRepresenter.js
index 0844f65e67..533c8031e0 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,18 @@ define(
// Place the "commit" method in the scope
scope.commit = commit;
scope.setEditable = setEditable;
+
+ // Clean up when the scope is destroyed
+ scope.$on("$destroy", function () {
+ self.destroy();
+ });
+
}
// 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 +122,32 @@ define(
// Ensure existing watches are released
this.destroy();
+
+ function setEditing(){
+ scope.viewObjectTemplate = 'edit-object';
+ }
+
+ /**
+ * 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){
+ setEditing();
+ } else {
+ delete scope.viewObjectTemplate;
+ }
+ });
+
+ if (representedObject.getCapability('status').get('editing')){
+ setEditing();
+ }
};
// Respond to the destruction of the current representation.
EditRepresenter.prototype.destroy = function destroy() {
- // Nothing to clean up
+ return this.listenHandle && this.listenHandle();
};
return EditRepresenter;
diff --git a/platform/commonUI/edit/test/controllers/EditControllerSpec.js b/platform/commonUI/edit/test/controllers/EditControllerSpec.js
index 4e66cebd08..0ed0ddecc7 100644
--- a/platform/commonUI/edit/test/controllers/EditControllerSpec.js
+++ b/platform/commonUI/edit/test/controllers/EditControllerSpec.js
@@ -22,102 +22,110 @@
/*global define,describe,it,expect,beforeEach,jasmine*/
define(
- ["../../src/controllers/EditController"],
- function (EditController) {
+ ["../../src/controllers/EditObjectController"],
+ function (EditObjectController) {
"use strict";
describe("The Edit mode controller", function () {
var mockScope,
- mockQ,
- mockNavigationService,
mockObject,
mockType,
+ mockLocation,
+ mockStatusCapability,
+ mockCapabilities,
+ mockPolicyService,
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 () {
+ mockPolicyService = jasmine.createSpyObj(
+ "policyService",
+ [
+ "allow"
+ ]
+ );
mockScope = jasmine.createSpyObj(
"$scope",
- [ "$on" ]
- );
- mockQ = jasmine.createSpyObj('$q', ['when', 'all']);
- mockNavigationService = jasmine.createSpyObj(
- "navigationService",
- [ "getNavigation", "addListener", "removeListener" ]
+ [ "$on", "$watch" ]
);
mockObject = jasmine.createSpyObj(
"domainObject",
- [ "getId", "getModel", "getCapability", "hasCapability" ]
+ [ "getId", "getModel", "getCapability", "hasCapability", "useCapability" ]
);
mockType = jasmine.createSpyObj(
"type",
[ "hasFeature" ]
);
+ mockStatusCapability = jasmine.createSpyObj('statusCapability',
+ ["get"]
+ );
+
+ mockCapabilities = {
+ "type" : mockType,
+ "status": mockStatusCapability
+ };
+
+ mockLocation = jasmine.createSpyObj('$location',
+ ["search"]
+ );
+ mockLocation.search.andReturn({"view": "fixed"});
- mockNavigationService.getNavigation.andReturn(mockObject);
mockObject.getId.andReturn("test");
mockObject.getModel.andReturn({ name: "Test object" });
mockObject.getCapability.andCallFake(function (key) {
- return key === 'type' && mockType;
+ return mockCapabilities[key];
});
mockType.hasFeature.andReturn(true);
- controller = new EditController(
+ mockScope.domainObject = mockObject;
+
+ controller = new EditObjectController(
mockScope,
- mockQ,
- mockNavigationService
+ mockLocation,
+ mockPolicyService
);
});
- it("exposes the currently-navigated object", function () {
- expect(controller.navigatedObject()).toBeDefined();
- expect(controller.navigatedObject().getId()).toEqual("test");
- });
-
- it("adds an editor capability to the navigated object", function () {
- // Should provide an editor capability...
- expect(controller.navigatedObject().getCapability("editor"))
- .toBeDefined();
- // Shouldn't have been the mock capability we provided
- expect(controller.navigatedObject().getCapability("editor"))
- .not.toEqual(mockType);
- });
-
- it("detaches its navigation listener when destroyed", function () {
- var navCallback = mockNavigationService
- .addListener.mostRecentCall.args[0];
-
- expect(mockScope.$on).toHaveBeenCalledWith(
- "$destroy",
- jasmine.any(Function)
- );
-
- // Verify precondition
- expect(mockNavigationService.removeListener)
- .not.toHaveBeenCalled();
-
- // Trigger destroy
- mockScope.$on.mostRecentCall.args[1]();
-
- // Listener should have been removed
- expect(mockNavigationService.removeListener)
- .toHaveBeenCalledWith(navCallback);
- });
-
it("exposes a warning message for unload", function () {
- var obj = controller.navigatedObject(),
- mockEditor = jasmine.createSpyObj('editor', ['dirty']);
+ var obj = mockObject,
+ errorMessage = "Unsaved changes";
// Normally, should be undefined
expect(controller.getUnloadWarning()).toBeUndefined();
- // Override the object's editor capability, make it look
- // like there are unsaved changes.
- obj.getCapability = jasmine.createSpy();
- obj.getCapability.andReturn(mockEditor);
- mockEditor.dirty.andReturn(true);
+ // Override the policy service to prevent navigation
+ mockPolicyService.allow.andCallFake(function(category, object, context, callback){
+ callback(errorMessage);
+ });
// Should have some warning message here now
- expect(controller.getUnloadWarning()).toEqual(jasmine.any(String));
+ expect(controller.getUnloadWarning()).toEqual(errorMessage);
+ });
+
+
+ it("sets the active view from query parameters", function () {
+ var testViews = [
+ { key: 'abc' },
+ { key: 'def', someKey: 'some value' },
+ { key: 'xyz' }
+ ];
+
+ mockObject.useCapability.andCallFake(function (c) {
+ return (c === 'view') && testViews;
+ });
+ mockLocation.search.andReturn({ view: 'def' });
+
+ fireWatch('domainObject', mockObject);
+ expect(mockScope.representation.selected)
+ .toEqual(testViews[1]);
});
});
diff --git a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js b/platform/commonUI/edit/test/representers/EditRepresenterSpec.js
index 79b336202a..3dcaa34627 100644
--- a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js
+++ b/platform/commonUI/edit/test/representers/EditRepresenterSpec.js
@@ -33,8 +33,8 @@ define(
testRepresentation,
mockDomainObject,
mockPersistence,
- mockCapabilities,
mockStatusCapability,
+ mockCapabilities,
representer;
function mockPromise(value) {
@@ -48,7 +48,7 @@ define(
beforeEach(function () {
mockQ = { when: mockPromise };
mockLog = jasmine.createSpyObj("$log", ["info", "debug"]);
- mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
+ mockScope = jasmine.createSpyObj("$scope", ["$watch", "$on"]);
testRepresentation = { key: "test" };
mockDomainObject = jasmine.createSpyObj("domainObject", [
"getId",
@@ -60,7 +60,7 @@ define(
mockPersistence =
jasmine.createSpyObj("persistence", ["persist"]);
mockStatusCapability =
- jasmine.createSpyObj("status", ["get"]);
+ jasmine.createSpyObj("statusCapability", ["get", "listen"]);
mockStatusCapability.get.andReturn(false);
mockCapabilities = {
'persistence': mockPersistence,
@@ -82,6 +82,17 @@ define(
expect(mockScope.commit).toEqual(jasmine.any(Function));
});
+ it("Sets edit view template on edit mode", function () {
+ mockStatusCapability.listen.mostRecentCall.args[0](['editing']);
+ expect(mockScope.viewObjectTemplate).toEqual('edit-object');
+ });
+
+ it("Cleans up listeners on scope destroy", function () {
+ representer.listenHandle = jasmine.createSpy('listen');
+ mockScope.$on.mostRecentCall.args[1]();
+ expect(representer.listenHandle).toHaveBeenCalled();
+ });
+
it("mutates and persists upon observed changes", function () {
mockScope.model = { someKey: "some value" };
mockScope.configuration = { someConfiguration: "something" };
@@ -112,4 +123,4 @@ define(
});
}
-);
\ No newline at end of file
+);
diff --git a/platform/commonUI/general/res/templates/object-inspector.html b/platform/commonUI/general/res/templates/object-inspector.html
index 56eaeb86cc..708814b3cc 100644
--- a/platform/commonUI/general/res/templates/object-inspector.html
+++ b/platform/commonUI/general/res/templates/object-inspector.html
@@ -20,7 +20,7 @@
at runtime from the About dialog for additional information.
-->
-
+
@@ -31,8 +31,8 @@
ng-model="ngModel"
class="flex-elem grows vscroll l-flex-col">
-
-
+
+
-
+
diff --git a/platform/representation/src/gestures/DropGesture.js b/platform/representation/src/gestures/DropGesture.js
index 1b7881a770..1e884c14f7 100644
--- a/platform/representation/src/gestures/DropGesture.js
+++ b/platform/representation/src/gestures/DropGesture.js
@@ -80,13 +80,6 @@ define(
}).length > 0;
}
- function shouldCreateVirtualPanel(domainObject){
- return domainObject.useCapability('view').filter(function (view){
- return (view.key === 'plot' || view.key === 'scrolling')
- && domainObject.getModel().type !== 'telemetry.panel';
- }).length > 0;
- }
-
function dragOver(e) {
//Refresh domain object on each dragOver to catch external
// updates to the model
@@ -111,9 +104,7 @@ define(
key: 'compose',
selectedObject: selectedObject
})[0];
- //TODO: Fix this. Define an action for creating new
- // virtual panel
- if (action || shouldCreateVirtualPanel(domainObject, selectedObject)) {
+ if (action) {
event.dataTransfer.dropEffect = 'move';
// Indicate that we will accept the drag
@@ -123,64 +114,23 @@ define(
}
}
- function createVirtualPanel(base, selectedObject){
-
- var typeKey = 'telemetry.panel',
- type = typeService.getType(typeKey),
- model = type.getInitialModel(),
- newPanel,
- composeAction;
-
- model.type = typeKey;
- newPanel = new EditableDomainObject(instantiate(model), $q);
- if (!canCompose(newPanel, selectedObject)) {
- return undefined;
- }
-
- [base.getId(), selectedObject.getId()].forEach(function(id){
- newPanel.getCapability('composition').add(id);
- });
-
- newPanel.getCapability('location')
- .setPrimaryLocation(base.getCapability('location')
- .getContextualLocation());
-
- newPanel.setOriginalObject(base);
- return newPanel;
-
- }
-
function drop(e) {
var event = (e || {}).originalEvent || e,
id = event.dataTransfer.getData(GestureConstants.MCT_DRAG_TYPE),
- domainObjectType = editableDomainObject.getModel().type,
- selectedObject = dndService.getData(
- GestureConstants.MCT_EXTENDED_DRAG_TYPE
- );
-
+ domainObjectType = editableDomainObject.getModel().type;
+
// Handle the drop; add the dropped identifier to the
// destination domain object's composition, and persist
// the change.
if (id) {
- if (shouldCreateVirtualPanel(domainObject, selectedObject)){
- editableDomainObject = createVirtualPanel(domainObject, selectedObject);
- if (editableDomainObject) {
- navigationService.setNavigation(editableDomainObject);
- broadcastDrop(id, event);
- editableDomainObject.getCapability('status').set('editing', true);
+ $q.when(action && action.perform()).then(function (result) {
+ //Don't go into edit mode for folders
+ if (domainObjectType!=='folder') {
+ editableDomainObject.getCapability('action').perform('edit');
}
- } 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);
- }
- broadcastDrop(id, event);
- });
- }
+ broadcastDrop(id, event);
+ });
}
- // TODO: Alert user if drag and drop is not allowed
}
// We can only handle drops if we have access to actions...