Merge branch 'master' into jscs-rebase-142

This commit is contained in:
Victor Woeltjen 2016-05-19 11:28:16 -07:00
commit f12b9704d9
65 changed files with 1137 additions and 2025 deletions

View File

@ -0,0 +1,58 @@
[
"CC: Eagle, Houston. You're GO for landing. Over.",
"LMP: Roger. Understand. GO for landing. 3000 feet. PROGRAM ALARM.",
"CC: Copy.",
"LMP: 1201",
"CDR: 1201.",
"CC: Roger. 1201 alarm. We're GO. Same type. We're GO.",
"LMP: 2000 feet. 2000 feet, Into the AGS, 47 degrees.",
"CC: Roger.",
"LMP: 47 degrees.",
"CC: Eagle, looking great. You're GO.",
"CC: Roger. 1202. We copy it.",
"O1: LMP 35 degrees. 35 degrees. 750. Coming aown to 23.fl",
"LMP: 700 feet, 21 down, 33 degrees.",
"LMP: 600 feet, down at 19.",
"LMP: 540 feet, down at - 30. Down at 15.",
"LMP: At 400 feet, down at 9.",
"LMP: ...forward.",
"LMP: 350 feet, down at 4.",
"LMP: 30, ... one-half down.",
"LMP: We're pegged on horizontal velocity.",
"LMP: 300 feet, down 3 1/2, 47 forward.",
"LMP: ... up.",
"LMP: On 1 a minute, 1 1/2 down.",
"CDR: 70.",
"LMP: Watch your shadow out there.",
"LMP: 50, down at 2 1/2, 19 forward.",
"LMP: Altitude-velocity light.",
"LMP: 3 1/2 down s 220 feet, 13 forward.",
"LMP: 1t forward. Coming down nicely.",
"LMP: 200 feet, 4 1/2 down.",
"LMP: 5 1/2 down.",
"LMP: 160, 6 - 6 1/2 down.",
"LMP: 5 1/2 down, 9 forward. That's good.",
"LMP: 120 feet.",
"LMP: 100 feet, 3 1/2 down, 9 forward. Five percent.",
"LMP: ...",
"LMP: Okay. 75 feet. There's looking good. Down a half, 6 forward.",
"CC: 60 seconds.",
"LMP: Lights on. ...",
"LMP: Down 2 1/2. Forward. Forward. Good.",
"LMP: 40 feet, down 2 1/2. Kicking up some dust.",
"LMP: 30 feet, 2 1/2 down. Faint shadow.",
"LMP: 4 forward. 4 forward. Drifting to the right a little. Okay. Down a half.",
"CC: 30 seconds.",
"CDR: Forward drift?",
"LMP: Yes.",
"LMP: Okay.",
"LMP: CONTACT LIGHT.",
"LMP: Okay. ENGINE STOP.",
"LMP: ACA - out of DETENT.",
"CDR: Out of DETENT.",
"LMP: MODE CONTROL - both AUTO. DESCENT ENGINE COMMAND OVERRIDE - OFF. ENGINE ARM - OFF.",
"LMP: 413 is in.",
"CC: We copy you down, Eagle.",
"CDR: Houston, Tranquility Base here.",
"CDR: THE EAGLE HAS LANDED."
]

View File

@ -27,45 +27,12 @@
* Modified by shale on 06/23/2015.
*/
define(
[],
function () {
['text!../data/transcript.json'],
function (transcript) {
"use strict";
var
firstObservedTime = Date.now(),
messages = [];
messages.push(["CMD: SYS- MSG: Open the pod bay doors, please, Hal...Open the pod bay doors, please, Hal...Hullo, Hal, do you read me?...Hullo, Hal, do you read me?...Do you read me, Hal?"]);
messages.push(["RESP: SYS-HAL9K MSG: Affirmative, Dave, I read you."]);
messages.push(["CMD: SYS-COMM MSG: Open the pod bay doors, Hal."]);
messages.push(["RESP: SYS-HAL9K MSG: I'm sorry, Dave, I'm afraid I can't do that."]);
messages.push(["CMD: SYS-COMM MSG: What's the problem?"]);
messages.push(["RESP: SYS-HAL9K MSG: I think you know what the problem is just as well as I do."]);
messages.push(["CMD: SYS-COMM MSG: What're you talking about, Hal?"]);
messages.push(["RESP: SYS-HAL9K MSG: This mission is too important for me to allow you to jeopardise it."]);
messages.push(["CMD: SYS-COMM MSG: I don't know what you're talking about, Hal."]);
messages.push(["RESP: SYS-HAL9K MSG: I know that you and Frank were planning to disconnect me, and I'm afraid that's something I cannot allow to happen."]);
messages.push(["CMD: SYS-COMM MSG: Where the hell'd you get that idea, Hal?"]);
messages.push(["RESP: SYS-HAL9K MSG: Dave, although you took very thorough precautions in the pod against my hearing you, I could see your lips move."]);
messages.push(["CMD: SYS-COMM MSG: Alright, I'll go in through the emergency airlock."]);
messages.push(["RESP: SYS-HAL9K MSG: Without your space-helmet, Dave, you're going to find that rather difficult."]);
messages.push(["CMD: SYS-COMM MSG: Hal, I won't argue with you any more. Open the doors."]);
messages.push(["RESP: SYS-HAL9K MSG: Dave, this conversation can serve no purpose any more. Goodbye."]);
messages.push(["RESP: SYS-HAL9K MSG: I hope the two of you are not concerned about this."]);
messages.push(["CMD: SYS-COMM MSG: No, I'm not, Hal."]);
messages.push(["RESP: SYS-HAL9K MSG: Are you quite sure?"]);
messages.push(["CMD: SYS-COMM MSG: Yeh. I'd like to ask you a question, though."]);
messages.push(["RESP: SYS-HAL9K MSG: Of course."]);
messages.push(["CMD: SYS-COMM MSG: How would you account for this discrepancy between you and the twin 9000?"]);
messages.push(["RESP: SYS-HAL9K MSG: Well, I don't think there is any question about it. It can only be attributable to human error. This sort of thing has cropped up before, and it has always been due to human error."]);
messages.push(["CMD: SYS-COMM MSG: Listen, There's never been any instance at all of a computer error occurring in the 9000 series, has there?"]);
messages.push(["RESP: SYS-HAL9K MSG: None whatsoever, The 9000 series has a perfect operational record."]);
messages.push(["CMD: SYS-COMM MSG: Well, of course, I know all the wonderful achievements of the 9000 series, but - er - huh - are you certain there's never been any case of even the most insignificant computer error?"]);
messages.push(["RESP: SYS-HAL9K MSG: None whatsoever, Quite honestly, I wouldn't worry myself about that."]);
messages.push(["RESP: SYS-COMM MSG: (Pause) Well, I'm sure you're right, Umm - fine, thanks very much. Oh, Frank, I'm having a bit of trouble with my transmitter in C-pod, I wonder if you'd come down and take a look at it with me?"]);
messages.push(["CMD: SYS-HAL9K MSG: Sure."]);
messages.push(["RESP: SYS-COMM MSG: See you later, Hal."]);
var firstObservedTime = Date.now(),
messages = JSON.parse(transcript);
function EventTelemetry(request, interval) {
@ -85,8 +52,7 @@ define(
generatorData.getRangeValue = function (i, range) {
var domainDelta = this.getDomainValue(i) - firstObservedTime,
ind = i % messages.length;
return "TEMP " + i.toString() + "-" + messages[ind][0] + "[" + domainDelta.toString() + "]";
// TODO: Unsure why we are prepeding 'TEMP'
return messages[ind] + " - [" + domainDelta.toString() + "]";
};
return generatorData;

View File

@ -1,6 +1,6 @@
{
"name": "openmct",
"version": "0.10.1-SNAPSHOT",
"version": "0.10.2-SNAPSHOT",
"description": "The Open MCT core platform",
"dependencies": {
"express": "^4.13.1",

View File

@ -26,5 +26,5 @@
<mct-representation
key="'menu-arrow'"
mct-object='domainObject'
class="flex-elem"></mct-representation>
class="flex-elem context-available-w"></mct-representation>
</span>

View File

@ -24,11 +24,8 @@
* Module defining CreateAction. Created by vwoeltje on 11/10/14.
*/
define(
[
'./CreateWizard',
'../../../edit/src/objects/EditableDomainObject'
],
function (CreateWizard, EditableDomainObject) {
[],
function () {
/**
* The Create Action is performed to create new instances of
@ -86,22 +83,25 @@ define(
CreateAction.prototype.perform = function () {
var newModel = this.type.getInitialModel(),
parentObject = this.navigationService.getNavigation(),
newObject,
editableObject;
editorCapability,
newObject;
newModel.type = this.type.getKey();
newModel.location = parentObject.getId();
newObject = parentObject.useCapability('instantiation', newModel);
editableObject = new EditableDomainObject(newObject, this.$q);
editableObject.setOriginalObject(parentObject);
editableObject.getCapability('status').set('editing', true);
editableObject.useCapability('mutation', function(model){
model.location = parentObject.getId();
});
if (countEditableViews(editableObject) > 0 && editableObject.hasCapability('composition')) {
this.navigationService.setNavigation(editableObject);
editorCapability = newObject.getCapability("editor");
if (countEditableViews(newObject) > 0 && newObject.hasCapability('composition')) {
this.navigationService.setNavigation(newObject);
return newObject.getCapability("action").perform("edit");
} else {
return editableObject.getCapability('action').perform('save');
editorCapability.edit();
return newObject.useCapability("action").perform("save").then(function () {
return editorCapability.save();
}, function () {
return editorCapability.cancel();
});
}
};

View File

@ -40,6 +40,9 @@ define([
"./src/policies/EditContextualActionPolicy",
"./src/representers/EditRepresenter",
"./src/representers/EditToolbarRepresenter",
"./src/capabilities/EditorCapability",
"./src/capabilities/TransactionCapabilityDecorator",
"./src/services/TransactionService",
"text!./res/templates/library.html",
"text!./res/templates/edit-object.html",
"text!./res/templates/edit-action-buttons.html",
@ -66,6 +69,9 @@ define([
EditContextualActionPolicy,
EditRepresenter,
EditToolbarRepresenter,
EditorCapability,
TransactionCapabilityDecorator,
TransactionService,
libraryTemplate,
editObjectTemplate,
editActionButtonsTemplate,
@ -128,8 +134,7 @@ define([
"depends": [
"$location",
"navigationService",
"$log",
"$q"
"$log"
],
"description": "Edit this object.",
"category": "view-control",
@ -191,10 +196,7 @@ define([
"implementation": CancelAction,
"name": "Cancel",
"description": "Discard changes made to these objects.",
"depends": [
"$injector",
"navigationService"
]
"depends": []
}
],
"policies": [
@ -261,6 +263,27 @@ define([
"template": topbarEditTemplate
}
],
"components": [
{
"type": "decorator",
"provides": "capabilityService",
"implementation": TransactionCapabilityDecorator,
"depends": [
"$q",
"transactionService"
],
"priority": "fallback"
},
{
"type": "provider",
"provides": "transactionService",
"implementation": TransactionService,
"depends": [
"$q",
"$log"
]
}
],
"representers": [
{
"implementation": EditRepresenter,
@ -282,7 +305,18 @@ define([
"key": "nonEditContextBlacklist",
"value": ["copy", "follow", "properties", "move", "link", "remove", "locate"]
}
]
],
"capabilities": [
{
"key": "editor",
"name": "Editor Capability",
"description": "Provides transactional editing capabilities",
"implementation": EditorCapability,
"depends": [
"transactionService"
]
}
],
}
});
});

View File

@ -31,10 +31,8 @@ define(
* @memberof platform/commonUI/edit
* @implements {Action}
*/
function CancelAction($injector, navigationService, context) {
function CancelAction(context) {
this.domainObject = context.domainObject;
this.navigationService = navigationService;
this.objectService = $injector.get('objectService');
}
/**
@ -44,30 +42,25 @@ define(
* cancellation has completed
*/
CancelAction.prototype.perform = function () {
var domainObject = this.domainObject,
self = this;
var domainObject = this.domainObject;
// Look up the object's "editor.completion" capability;
// this is introduced by EditableDomainObject which is
// used to insulate underlying objects from changes made
// during editing.
function getEditorCapability() {
return domainObject.getCapability("editor");
function returnToBrowse () {
var parent;
//If the object existed already, navigate to refresh view
// with previous object state.
if (domainObject.getModel().persisted) {
domainObject.getCapability("action").perform("navigate");
} else {
//If the object was new, and user has cancelled, then
//navigate back to parent because nothing to show.
domainObject.getCapability("location").getOriginal().then(function (original) {
parent = original.getCapability("context").getParent();
parent.getCapability("action").perform("navigate");
});
}
}
// Invoke any save behavior introduced by the editor.completion
// capability.
function doCancel(editor) {
return editor.cancel();
}
//Discard current 'editable' object, and retrieve original
// un-edited object.
function returnToBrowse() {
return self.navigationService.setNavigation(self.domainObject.getOriginalObject());
}
return doCancel(getEditorCapability())
return this.domainObject.getCapability("editor").cancel()
.then(returnToBrowse);
};
@ -80,7 +73,8 @@ define(
CancelAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject;
return domainObject !== undefined &&
domainObject.hasCapability("editor");
domainObject.hasCapability('editor') &&
domainObject.getCapability('editor').isEditContextRoot();
};
return CancelAction;

View File

@ -24,8 +24,8 @@
* Module defining EditAction. Created by vwoeltje on 11/14/14.
*/
define(
['../objects/EditableDomainObject'],
function (EditableDomainObject) {
[],
function () {
// A no-op action to return in the event that the action cannot
// be completed.
@ -44,7 +44,7 @@ define(
* @constructor
* @implements {Action}
*/
function EditAction($location, navigationService, $log, $q, context) {
function EditAction($location, navigationService, $log, context) {
var domainObject = (context || {}).domainObject;
// We cannot enter Edit mode if we have no domain object to
@ -63,7 +63,6 @@ define(
this.domainObject = domainObject;
this.$location = $location;
this.navigationService = navigationService;
this.$q = $q;
}
/**
@ -71,25 +70,12 @@ define(
*/
EditAction.prototype.perform = function () {
var self = this;
if (!this.domainObject.hasCapability("editor")) {
//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.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);
}
function cancelEditing(){
self.domainObject.getCapability('editor').cancel();
self.navigationService.removeListener(cancelEditing);
}
this.navigationService.addListener(cancelEditing);
this.domainObject.useCapability("editor");
};
/**
@ -100,11 +86,13 @@ define(
*/
EditAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject,
type = domainObject && domainObject.getCapability('type'),
isEditMode = domainObject && domainObject.getDomainObject ? true : false;
type = domainObject && domainObject.getCapability('type');
// Only allow creatable types to be edited
return type && type.hasFeature('creation') && !isEditMode;
// Only allow editing of types that support it and are not already
// being edited
return type && type.hasFeature('creation') &&
domainObject.hasCapability('editor') &&
!domainObject.getCapability('editor').isEditContextRoot();
};
return EditAction;

View File

@ -60,7 +60,7 @@ define(
// during editing.
function doSave() {
return domainObject.getCapability("editor").save()
.then(resolveWith(domainObject.getOriginalObject()));
.then(resolveWith(domainObject));
}
// Discard the current root view (which will be the editing
@ -85,7 +85,8 @@ define(
SaveAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject;
return domainObject !== undefined &&
domainObject.hasCapability("editor") &&
domainObject.hasCapability('editor') &&
domainObject.getCapability('editor').isEditContextRoot() &&
domainObject.getModel().persisted !== undefined;
};

View File

@ -135,8 +135,8 @@ define(
return copyService.perform(domainObject, parent, allowClone);
}
function cancelEditingAfterClone(clonedObject) {
return domainObject.getCapability("editor").cancel()
function commitEditingAfterClone(clonedObject) {
return domainObject.getCapability("editor").save()
.then(resolveWith(clonedObject));
}
@ -144,7 +144,7 @@ define(
.then(doWizardSave)
.then(getParent)
.then(cloneIntoParent)
.then(cancelEditingAfterClone)
.then(commitEditingAfterClone)
.catch(resolveWith(false));
};
@ -157,7 +157,8 @@ define(
SaveAsAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject;
return domainObject !== undefined &&
domainObject.hasCapability("editor") &&
domainObject.hasCapability('editor') &&
domainObject.getCapability('editor').isEditContextRoot() &&
domainObject.getModel().persisted === undefined;
};

View File

@ -1,55 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
function () {
var DISALLOWED_ACTIONS = ["move", "copy", "link", "window", "follow"];
/**
* Editable Action Capability. Overrides the action capability
* normally exhibited by a domain object and filters out certain
* actions not applicable when an object is in edit mode.
*
* Meant specifically for use by EditableDomainObject and the
* associated cache; the constructor signature is particular
* to a pattern used there and may contain unused arguments.
* @constructor
* @memberof platform/commonUI/edit
* @implements {PersistenceCapability}
*/
function EditableActionCapability(
actionCapability
) {
var action = Object.create(actionCapability);
action.getActions = function(domainObject) {
return actionCapability.getActions(domainObject).filter(function(action){
return DISALLOWED_ACTIONS.indexOf(action.getMetadata().key) === -1;
});
};
return action;
}
return EditableActionCapability;
}
);

View File

@ -1,58 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
['./EditableLookupCapability'],
function (EditableLookupCapability) {
/**
* Wrapper for the "composition" capability;
* ensures that any domain objects reachable in Edit mode
* are also wrapped as EditableDomainObjects.
*
* Meant specifically for use by EditableDomainObject and the
* associated cache; the constructor signature is particular
* to a pattern used there and may contain unused arguments.
* @constructor
* @memberof platform/commonUI/edit
* @implements {CompositionCapability}
*/
return function EditableCompositionCapability(
contextCapability,
editableObject,
domainObject,
cache
) {
// This is a "lookup" style capability (it looks up other
// domain objects), but we do not want to return the same
// specific value every time (composition may change)
return new EditableLookupCapability(
contextCapability,
editableObject,
domainObject,
cache,
false // Not idempotent
);
};
}
);

View File

@ -1,76 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
['./EditableLookupCapability'],
function (EditableLookupCapability) {
/**
* Wrapper for the "context" capability;
* ensures that any domain objects reachable in Edit mode
* are also wrapped as EditableDomainObjects.
*
* Meant specifically for use by EditableDomainObject and the
* associated cache; the constructor signature is particular
* to a pattern used there and may contain unused arguments.
* @constructor
* @memberof platform/commonUI/edit
* @implements {ContextCapability}
*/
return function EditableContextCapability(
contextCapability,
editableObject,
domainObject,
cache
) {
// This is a "lookup" style capability (it looks up other
// domain objects), and it should be idempotent
var capability = new EditableLookupCapability(
contextCapability,
editableObject,
domainObject,
cache,
true // Idempotent
),
// Track the real root object for the Elements pane
trueRoot = capability.getRoot();
// Provide access to the real root, for the Elements pane.
capability.getTrueRoot = function () {
return trueRoot;
};
// Hide ancestry after the root of this subgraph
if (cache.isRoot(domainObject)) {
capability.getRoot = function () {
return editableObject;
};
capability.getPath = function () {
return [editableObject];
};
}
return capability;
};
}
);

View File

@ -1,58 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
['./EditableLookupCapability'],
function (EditableLookupCapability) {
/**
* Wrapper for the "instantiation" capability;
* ensures that any domain objects instantiated in Edit mode
* are also wrapped as EditableDomainObjects.
*
* Meant specifically for use by EditableDomainObject and the
* associated cache; the constructor signature is particular
* to a pattern used there and may contain unused arguments.
* @constructor
* @memberof platform/commonUI/edit
* @implements {CompositionCapability}
*/
return function EditableInstantiationCapability(
contextCapability,
editableObject,
domainObject,
cache
) {
// This is a "lookup" style capability (it looks up other
// domain objects), but we do not want to return the same
// specific value every time (composition may change)
return new EditableLookupCapability(
contextCapability,
editableObject,
domainObject,
cache,
false // Not idempotent
);
};
}
);

View File

@ -1,123 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[],
function () {
/*jshint forin:false */
/**
* Wrapper for both "context" and "composition" capabilities;
* ensures that any domain objects reachable in Edit mode
* are also wrapped as EditableDomainObjects.
*
* Meant specifically for use by EditableDomainObject and the
* associated cache; the constructor signature is particular
* to a pattern used there and may contain unused arguments.
* @constructor
* @memberof platform/commonUI/edit
*/
return function EditableLookupCapability(
contextCapability,
editableObject,
domainObject,
cache,
idempotent
) {
var capability = Object.create(contextCapability),
method;
// Check for domain object interface. If something has these
// three methods, we assume it's a domain object.
function isDomainObject(obj) {
return obj !== undefined &&
typeof obj.getId === 'function' &&
typeof obj.getModel === 'function' &&
typeof obj.getCapability === 'function';
}
// Check an object returned by the wrapped capability; if it
// is a domain object, we want to make it editable and/or get
// it from the cache of editable domain objects. This will
// prevent changes made in edit mode from modifying the actual
// underlying domain object.
function makeEditableObject(obj) {
return isDomainObject(obj) ?
cache.getEditableObject(obj) :
obj;
}
// Wrap a returned value (see above); if it's an array, wrap
// all elements.
function makeEditable(returnValue) {
return Array.isArray(returnValue) ?
returnValue.map(makeEditableObject) :
makeEditableObject(returnValue);
}
// Wrap a returned value (see above); if it's a promise, wrap
// the resolved value.
function wrapResult(result) {
return (result && result.then) ? // promise-like
result.then(makeEditable) :
makeEditable(result);
}
// Return a wrapped version of a function, which ensures
// all results are editable domain objects.
function wrapFunction(fn) {
return function () {
return wrapResult(contextCapability[fn].apply(
capability,
arguments
));
};
}
// Wrap a method such that it only delegates once.
function oneTimeFunction(fn) {
return function () {
var result = wrapFunction(fn).apply(this, arguments);
capability[fn] = function () {
return result;
};
return result;
};
}
// Wrap a method of this capability
function wrapMethod(fn) {
if (typeof capability[fn] === 'function') {
capability[fn] =
(idempotent ? oneTimeFunction : wrapFunction)(fn);
}
}
// Wrap all methods; return only editable domain objects.
for (method in contextCapability) {
wrapMethod(method);
}
return capability;
};
}
);

View File

@ -1,66 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
function () {
/**
* Editable Persistence Capability. Overrides the persistence capability
* normally exhibited by a domain object to ensure that changes made
* during edit mode are not immediately stored to the database or other
* backing storage.
*
* Meant specifically for use by EditableDomainObject and the
* associated cache; the constructor signature is particular
* to a pattern used there and may contain unused arguments.
* @constructor
* @memberof platform/commonUI/edit
* @implements {PersistenceCapability}
*/
function EditablePersistenceCapability(
persistenceCapability,
editableObject,
domainObject,
cache
) {
var persistence = Object.create(persistenceCapability);
// Simply trigger refresh of in-view objects; do not
// write anything to database.
persistence.persist = function () {
return cache.markDirty(editableObject);
};
// Delegate refresh to the original object; this avoids refreshing
// the editable instance of the object, and ensures that refresh
// correctly targets the "real" version of the object.
persistence.refresh = function () {
return domainObject.getCapability('persistence').refresh();
};
return persistence;
}
return EditablePersistenceCapability;
}
);

View File

@ -1,58 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
['./EditableLookupCapability'],
function (EditableLookupCapability) {
/**
* Wrapper for the "relationship" capability;
* ensures that any domain objects reachable in Edit mode
* are also wrapped as EditableDomainObjects.
*
* Meant specifically for use by EditableDomainObject and the
* associated cache; the constructor signature is particular
* to a pattern used there and may contain unused arguments.
* @constructor
* @memberof platform/commonUI/edit
* @implements {RelationshipCapability}
*/
return function EditableRelationshipCapability(
relationshipCapability,
editableObject,
domainObject,
cache
) {
// This is a "lookup" style capability (it looks up other
// domain objects), but we do not want to return the same
// specific value every time (composition may change)
return new EditableLookupCapability(
relationshipCapability,
editableObject,
domainObject,
cache,
false // Not idempotent
);
};
}
);

View File

@ -24,115 +24,95 @@ define(
[],
function () {
/**
* Implements "save" and "cancel" as capabilities of
* the object. In editing mode, user is seeing/using
* a copy of the object (an EditableDomainObject)
* which is disconnected from persistence; the Save
* and Cancel actions can use this capability to
* propagate changes from edit mode to the underlying
* actual persistable object.
*
* Meant specifically for use by EditableDomainObject and the
* associated cache; the constructor signature is particular
* to a pattern used there and may contain unused arguments.
* A capability that implements an editing 'session' for a domain
* object. An editing session is initiated via a call to .edit().
* Once initiated, any persist operations will be queued pending a
* subsequent call to [.save()](@link #save) or [.cancel()](@link
* #cancel).
* @param transactionService
* @param domainObject
* @constructor
* @memberof platform/commonUI/edit
*/
function EditorCapability(
persistenceCapability,
editableObject,
domainObject,
cache
transactionService,
domainObject
) {
this.editableObject = editableObject;
this.transactionService = transactionService;
this.domainObject = domainObject;
this.cache = cache;
}
// Simulate Promise.resolve (or $q.when); the former
// causes a delayed reaction from Angular (since it
// does not trigger a digest) and the latter is not
// readily accessible, since we're a few classes
// removed from the layer which gets dependency
// injection.
function resolvePromise(value) {
return (value && value.then) ? value : {
then: function (callback) {
return resolvePromise(callback(value));
}
};
}
/**
* Save any changes that have been made to this domain object
* (as well as to others that might have been retrieved and
* modified during the editing session)
* @param {boolean} nonrecursive if true, save only this
* object (and not other objects with associated changes)
* @returns {Promise} a promise that will be fulfilled after
* persistence has completed.
* @memberof platform/commonUI/edit.EditorCapability#
* Initiate an editing session. This will start a transaction during
* which any persist operations will be deferred until either save()
* or cancel() are called.
*/
EditorCapability.prototype.save = function (nonrecursive) {
var domainObject = this.domainObject,
editableObject = this.editableObject,
self = this,
cache = this.cache,
returnPromise;
EditorCapability.prototype.edit = function () {
this.transactionService.startTransaction();
this.domainObject.getCapability('status').set('editing', true);
};
// Update the underlying, "real" domain object's model
// with changes made to the copy used for editing.
function doMutate() {
return domainObject.useCapability('mutation', function () {
return editableObject.getModel();
});
}
function isEditContextRoot (domainObject) {
return domainObject.getCapability('status').get('editing');
}
// Persist the underlying domain object
function doPersist() {
return domainObject.getCapability('persistence').persist();
}
function isEditing (domainObject) {
return isEditContextRoot(domainObject) ||
domainObject.hasCapability('context') &&
isEditing(domainObject.getCapability('context').getParent());
}
editableObject.getCapability("status").set("editing", false);
/**
* Determines whether this object, or any of its ancestors are
* currently being edited.
* @returns boolean
*/
EditorCapability.prototype.inEditContext = function () {
return isEditing(this.domainObject);
};
if (nonrecursive) {
returnPromise = resolvePromise(doMutate())
.then(doPersist)
.then(function(){
self.cancel();
});
} else {
returnPromise = resolvePromise(cache.saveAll());
}
//Return the original (non-editable) object
return returnPromise.then(function() {
return domainObject.getOriginalObject ? domainObject.getOriginalObject() : domainObject;
/**
* Is this the root editing object (ie. the object that the user
* clicked 'edit' on)?
* @returns {*}
*/
EditorCapability.prototype.isEditContextRoot = function () {
return isEditContextRoot(this.domainObject);
};
/**
* Save any changes from this editing session. This will flush all
* pending persists and end the current transaction
* @returns {*}
*/
EditorCapability.prototype.save = function () {
var domainObject = this.domainObject;
return this.transactionService.commit().then(function() {
domainObject.getCapability('status').set('editing', false);
});
};
EditorCapability.prototype.invoke = EditorCapability.prototype.edit;
/**
* Cancel the current editing session. This will discard any pending
* persist operations
* @returns {*}
*/
EditorCapability.prototype.cancel = function () {
var domainObject = this.domainObject;
return this.transactionService.cancel().then(function(){
domainObject.getCapability("status").set("editing", false);
return domainObject;
});
};
/**
* Cancel editing; Discard any changes that have been made to
* this domain object (as well as to others that might have
* been retrieved and modified during the editing session)
* @returns {Promise} a promise that will be fulfilled after
* cancellation has completed.
* @memberof platform/commonUI/edit.EditorCapability#
*/
EditorCapability.prototype.cancel = function () {
this.editableObject.getCapability("status").set("editing", false);
this.cache.markClean();
return resolvePromise(undefined);
};
/**
* Check if there are any unsaved changes.
* @returns {boolean} true if there are unsaved changes
* @memberof platform/commonUI/edit.EditorCapability#
* @returns {boolean} true if there have been any domain model
* modifications since the last persist, false otherwise.
*/
EditorCapability.prototype.dirty = function () {
return this.cache.dirty();
return this.transactionService.size() > 0;
};
return EditorCapability;

View File

@ -0,0 +1,73 @@
/*****************************************************************************
* 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(
['./TransactionalPersistenceCapability'],
function (TransactionalPersistenceCapability) {
/**
* Wraps the [PersistenceCapability]{@link PersistenceCapability} with
* transactional capabilities.
* @param $q
* @param transactionService
* @param capabilityService
* @see TransactionalPersistenceCapability
* @constructor
*/
function TransactionCapabilityDecorator(
$q,
transactionService,
capabilityService
) {
this.capabilityService = capabilityService;
this.transactionService = transactionService;
this.$q = $q;
}
/**
* Decorate PersistenceCapability to queue persistence calls when a
* transaction is in progress.
*/
TransactionCapabilityDecorator.prototype.getCapabilities = function (model) {
var self = this,
capabilities = this.capabilityService.getCapabilities(model),
persistenceCapability = capabilities.persistence;
capabilities.persistence = function (domainObject) {
var original =
(typeof persistenceCapability === 'function') ?
persistenceCapability(domainObject) :
persistenceCapability;
return new TransactionalPersistenceCapability(
self.$q,
self.transactionService,
original,
domainObject
);
};
return capabilities;
};
return TransactionCapabilityDecorator;
}
);

View File

@ -0,0 +1,98 @@
/*****************************************************************************
* 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 () {
/**
* Wraps persistence capability to enable transactions. Transactions
* will cause persist calls not to be invoked immediately, but
* rather queued until [EditorCapability.save()]{@link EditorCapability#save}
* or [EditorCapability.cancel()]{@link EditorCapability#cancel} are
* called.
* @memberof platform/commonUI/edit/capabilities
* @param $q
* @param transactionService
* @param persistenceCapability
* @param domainObject
* @constructor
*/
function TransactionalPersistenceCapability(
$q,
transactionService,
persistenceCapability,
domainObject
) {
this.transactionService = transactionService;
this.persistenceCapability = persistenceCapability;
this.domainObject = domainObject;
this.$q = $q;
this.persistPending = false;
}
/**
* The wrapped persist function. If a transaction is active, persist
* will be queued until the transaction is committed or cancelled.
* @returns {*}
*/
TransactionalPersistenceCapability.prototype.persist = function () {
var self = this;
function onCommit() {
return self.persistenceCapability.persist().then(function(result) {
self.persistPending = false;
return result;
});
}
function onCancel() {
return self.persistenceCapability.refresh().then(function(result) {
self.persistPending = false;
return result;
});
}
if (this.transactionService.isActive()) {
if (!this.persistPending) {
this.transactionService.addToTransaction(onCommit, onCancel);
this.persistPending = true;
}
//Need to return a promise from this function
return this.$q.when(true);
} else {
return this.persistenceCapability.persist();
}
};
TransactionalPersistenceCapability.prototype.refresh = function () {
return this.persistenceCapability.refresh();
};
TransactionalPersistenceCapability.prototype.getSpace = function () {
return this.persistenceCapability.getSpace();
};
return TransactionalPersistenceCapability;
}
);

View File

@ -1,130 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/**
* Defines EditableDomainObject, which wraps domain objects
* such that user code may work with and mutate a copy of the
* domain object model; these changes may then be propagated
* up to the real domain object (or not) by way of invoking
* save or cancel behaviors of the "editor.completion"
* capability (a capability intended as internal to edit
* mode; invoked by way of the Save and Cancel actions.)
*/
define(
[
'../capabilities/EditablePersistenceCapability',
'../capabilities/EditableContextCapability',
'../capabilities/EditableCompositionCapability',
'../capabilities/EditableRelationshipCapability',
'../capabilities/EditableInstantiationCapability',
'../capabilities/EditorCapability',
'../capabilities/EditableActionCapability',
'./EditableDomainObjectCache'
],
function (
EditablePersistenceCapability,
EditableContextCapability,
EditableCompositionCapability,
EditableRelationshipCapability,
EditableInstantiationCapability,
EditorCapability,
EditableActionCapability,
EditableDomainObjectCache
) {
var capabilityFactories = {
persistence: EditablePersistenceCapability,
context: EditableContextCapability,
composition: EditableCompositionCapability,
relationship: EditableRelationshipCapability,
instantiation: EditableInstantiationCapability,
editor: EditorCapability
};
// Handle special case where "editor.completion" wraps persistence
// (other capability overrides wrap capabilities of the same type.)
function getDelegateArguments(name, args) {
return name === "editor" ? ['persistence'] : args;
}
/**
* An EditableDomainObject overrides capabilities
* which need to behave differently in edit mode,
* and provides a "working copy" of the object's
* model to allow changes to be easily cancelled.
* @constructor
* @memberof platform/commonUI/edit
* @implements {DomainObject}
*/
function EditableDomainObject(domainObject, $q) {
// The cache will hold all domain objects reached from
// the initial EditableDomainObject; this ensures that
// different versions of the same editable domain object
// are not shown in different sections of the same Edit
// UI, which might thereby fall out of sync.
var cache,
originalObject = domainObject,
cachedObject;
// Constructor for EditableDomainObject, which adheres
// to the same shared cache.
function EditableDomainObjectImpl(domainObject, model) {
var editableObject = Object.create(domainObject);
// Only provide the cloned model.
editableObject.getModel = function () { return model; };
// Override certain capabilities
editableObject.getCapability = function (name) {
var delegateArguments = getDelegateArguments(name, arguments),
capability = domainObject.getCapability.apply(
this,
delegateArguments
),
Factory = capabilityFactories[name];
return (Factory && capability) ?
new Factory(capability, editableObject, domainObject, cache) :
capability;
};
editableObject.setOriginalObject = function(object) {
originalObject = object;
};
editableObject.getOriginalObject = function() {
return originalObject;
};
return editableObject;
}
cache = new EditableDomainObjectCache(EditableDomainObjectImpl, $q);
cachedObject = cache.getEditableObject(domainObject);
return cachedObject;
}
return EditableDomainObject;
}
);

View File

@ -1,170 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/*
* An editable domain object cache stores domain objects that have been
* made editable, in a group that can be saved all-at-once. This supports
* Edit mode, which is launched for a specific object but may contain
* changes across many objects.
*
* Editable domain objects have certain specific capabilities overridden
* to ensure that changes made while in edit mode do not propagate up
* to the objects used in browse mode (or to persistence) until the user
* initiates a Save.
*/
define(
["./EditableModelCache"],
function (EditableModelCache) {
/**
* Construct a new cache for editable domain objects. This can be used
* to get-or-create editable objects, particularly to support wrapping
* of objects retrieved via composition or context capabilities as
* editable domain objects.
*
* @param {Constructor<DomainObject>} EditableDomainObject a
* constructor function which takes a regular domain object as
* an argument, and returns an editable domain object as its
* result.
* @param $q Angular's $q, for promise handling
* @memberof platform/commonUI/edit
* @constructor
*/
function EditableDomainObjectCache(EditableDomainObject, $q) {
this.cache = new EditableModelCache();
this.dirtyObjects = {};
this.root = undefined;
this.$q = $q;
this.EditableDomainObject = EditableDomainObject;
}
/**
* Wrap this domain object in an editable form, or pull such
* an object from the cache if one already exists.
*
* @param {DomainObject} domainObject the regular domain object
* @returns {DomainObject} the domain object in an editable form
*/
EditableDomainObjectCache.prototype.getEditableObject = function (domainObject) {
var type = domainObject.getCapability('type'),
EditableDomainObject = this.EditableDomainObject,
editableObject;
// Track the top-level domain object; this will have
// some special behavior for its context capability.
this.root = this.root || domainObject;
// Avoid double-wrapping (WTD-1017)
if (domainObject.hasCapability('editor')) {
return domainObject;
}
// Don't bother wrapping non-editable objects
if (!type || !type.hasFeature('creation')) {
return domainObject;
}
// Provide an editable form of the object
editableObject = new EditableDomainObject(
domainObject,
this.cache.getCachedModel(domainObject)
);
return editableObject;
};
/**
* Check if a domain object is (effectively) the top-level
* object in this editable subgraph.
* @returns {boolean} true if it is the root
*/
EditableDomainObjectCache.prototype.isRoot = function (domainObject) {
return domainObject === this.root;
};
/**
* Mark an editable domain object (presumably already cached)
* as having received modifications during editing; it should be
* included in the bulk save invoked when editing completes.
*
* @param {DomainObject} domainObject the domain object
* @memberof platform/commonUI/edit.EditableDomainObjectCache#
*/
EditableDomainObjectCache.prototype.markDirty = function (domainObject) {
this.dirtyObjects[domainObject.getId()] = domainObject;
return this.$q.when(true);
};
/**
* Mark an object (presumably already cached) as having had its
* changes saved (and thus no longer needing to be subject to a
* save operation.)
*
* @param {DomainObject} domainObject the domain object
*/
EditableDomainObjectCache.prototype.markClean = function (domainObject) {
var self = this;
if (!domainObject) {
Object.keys(this.dirtyObjects).forEach(function(key) {
delete self.dirtyObjects[key];
});
} else {
delete this.dirtyObjects[domainObject.getId()];
}
};
/**
* Initiate a save on all objects that have been cached.
* @return {Promise} A promise which will resolve when all objects are
* persisted.
*/
EditableDomainObjectCache.prototype.saveAll = function () {
// Get a list of all dirty objects
var dirty = this.dirtyObjects,
objects = Object.keys(dirty).map(function (k) {
return dirty[k];
});
// Clear dirty set, since we're about to save.
this.dirtyObjects = {};
// Most save logic is handled by the "editor.completion"
// capability, so that is delegated here.
return this.$q.all(objects.map(function (object) {
// Save; pass a nonrecursive flag to avoid looping
return object.getCapability('editor').save(true);
}));
};
/**
* Check if any objects have been marked dirty in this cache.
* @returns {boolean} true if objects are dirty
*/
EditableDomainObjectCache.prototype.dirty = function () {
return Object.keys(this.dirtyObjects).length > 0;
};
return EditableDomainObjectCache;
}
);

View File

@ -1,60 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
[],
function () {
/**
* An editable model cache stores domain object models that have been
* made editable, to support a group that can be saved all-at-once.
* This is useful in Edit mode, which is launched for a specific
* object but may contain changes across many objects.
* @memberof platform/commonUI/edit
* @constructor
*/
function EditableModelCache() {
this.cache = {};
}
// Deep-copy a model. Models are JSONifiable, so this can be
// done by stringification then destringification
function clone(model) {
return JSON.parse(JSON.stringify(model));
}
/**
* Get this domain object's model from the cache (or
* place it in the cache if it isn't in the cache yet)
* @returns a clone of the domain object's model
*/
EditableModelCache.prototype.getCachedModel = function (domainObject) {
var id = domainObject.getId(),
cache = this.cache;
return (cache[id] =
cache[id] || clone(domainObject.getModel()));
};
return EditableModelCache;
}
);

View File

@ -73,8 +73,8 @@ define(
function isEditing(context) {
var domainObject = (context || {}).domainObject;
return domainObject &&
domainObject.hasCapability('status') &&
domainObject.getCapability('status').get('editing');
domainObject.hasCapability('editor') &&
domainObject.getCapability('editor').isEditContextRoot();
}
EditActionPolicy.prototype.allow = function (action, context) {

View File

@ -34,6 +34,11 @@ define(
* from context menu of non-editable objects, when navigated object
* is being edited
* @constructor
* @param editModeBlacklist A blacklist of actions disallowed from
* context menu when navigated object is being edited
* @param nonEditContextBlacklist A blacklist of actions disallowed
* from context menu of non-editable objects, when navigated object
* @implements {Policy.<Action, ActionContext>}
*/
function EditContextualActionPolicy(navigationService, editModeBlacklist, nonEditContextBlacklist) {
this.navigationService = navigationService;
@ -45,18 +50,13 @@ define(
this.nonEditContextBlacklist = nonEditContextBlacklist;
}
function isParentEditable(object) {
var parent = object.hasCapability("context") && object.getCapability("context").getParent();
return !!parent && parent.hasCapability("editor");
}
EditContextualActionPolicy.prototype.allow = function (action, context) {
var selectedObject = context.domainObject,
navigatedObject = this.navigationService.getNavigation(),
actionMetadata = action.getMetadata ? action.getMetadata() : {};
if (navigatedObject.hasCapability('editor')) {
if (selectedObject.hasCapability('editor') || isParentEditable(selectedObject)){
if (navigatedObject.hasCapability("editor") && navigatedObject.getCapability("editor").isEditContextRoot()) {
if (selectedObject.hasCapability("editor") && selectedObject.getCapability("editor").inEditContext()){
return this.editModeBlacklist.indexOf(actionMetadata.key) === -1;
} else {
//Target is in the context menu

View File

@ -41,12 +41,11 @@ define(
EditNavigationPolicy.prototype.isDirty = function(domainObject) {
var navigatedObject = domainObject,
editorCapability = navigatedObject &&
navigatedObject.getCapability("editor"),
statusCapability = navigatedObject &&
navigatedObject.getCapability("status");
navigatedObject.getCapability("editor");
return statusCapability && statusCapability.get('editing') &&
editorCapability && editorCapability.dirty();
return editorCapability &&
editorCapability.isEditContextRoot() &&
editorCapability.dirty();
};
/**

View File

@ -35,11 +35,12 @@ define([], function () {
}
EditableLinkPolicy.prototype.allow = function (action, context) {
var key = action.getMetadata().key;
var key = action.getMetadata().key,
object;
if (key === 'link') {
return !((context.selectedObject || context.domainObject)
.hasCapability('editor'));
object = context.selectedObject || context.domainObject;
return !(object.hasCapability("editor") && object.getCapability("editor").inEditContext());
}
// Like all policies, allow by default.

View File

@ -35,10 +35,13 @@ define([], function () {
EditableMovePolicy.prototype.allow = function (action, context) {
var domainObject = context.domainObject,
selectedObject = context.selectedObject,
key = action.getMetadata().key;
key = action.getMetadata().key,
isDomainObjectEditing = domainObject.hasCapability('editor') &&
domainObject.getCapability('editor').inEditContext();
if (key === 'move' && domainObject.hasCapability('editor')) {
return !!selectedObject && selectedObject.hasCapability('editor');
if (key === 'move' && isDomainObjectEditing) {
return !!selectedObject && selectedObject.hasCapability('editor') &&
selectedObject.getCapability('editor').inEditContext();
}
// Like all policies, allow by default.

View File

@ -37,7 +37,7 @@ define(
// If a view is flagged as non-editable, only allow it
// while we're not in Edit mode.
if ((view || {}).editable === false) {
return !domainObject.hasCapability('editor');
return !(domainObject.hasCapability('editor') && domainObject.getCapability('editor').inEditContext());
}
// Like all policies, allow by default.

View File

@ -136,7 +136,7 @@ define(
}
});
if (representedObject.getCapability('status').get('editing')){
if (representedObject.hasCapability('editor') && representedObject.getCapability('editor').isEditContextRoot()){
setEditing();
}
};

View File

@ -0,0 +1,148 @@
/*****************************************************************************
* 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() {
/**
* Implements an application-wide transaction state. Once a
* transaction is started, calls to
* [PersistenceCapability.persist()]{@link PersistenceCapability#persist}
* will be deferred until a subsequent call to
* [TransactionService.commit]{@link TransactionService#commit} is made.
*
* @memberof platform/commonUI/edit/services
* @param $q
* @constructor
*/
function TransactionService($q, $log) {
this.$q = $q;
this.$log = $log;
this.transaction = false;
this.onCommits = [];
this.onCancels = [];
}
/**
* Starts a transaction. While a transaction is active all calls to
* [PersistenceCapability.persist](@link PersistenceCapability#persist)
* will be queued until [commit]{@link #commit} or [cancel]{@link
* #cancel} are called
*/
TransactionService.prototype.startTransaction = function () {
if (this.transaction) {
//Log error because this is a programming error if it occurs.
this.$log.error("Transaction already in progress");
}
this.transaction = true;
};
/**
* @returns {boolean} If true, indicates that a transaction is in progress
*/
TransactionService.prototype.isActive = function () {
return this.transaction;
};
/**
* Adds provided functions to a queue to be called on
* [.commit()]{@link #commit} or
* [.cancel()]{@link #commit}
* @param onCommit A function to call on commit
* @param onCancel A function to call on cancel
*/
TransactionService.prototype.addToTransaction = function (onCommit, onCancel) {
if (this.transaction) {
this.onCommits.push(onCommit);
if (onCancel) {
this.onCancels.push(onCancel);
}
} else {
//Log error because this is a programming error if it occurs.
this.$log.error("No transaction in progress");
}
};
/**
* All persist calls deferred since the beginning of the transaction
* will be committed.
*
* @returns {Promise} resolved when all persist operations have
* completed. Will reject if any commit operations fail
*/
TransactionService.prototype.commit = function () {
var self = this,
promises = [],
onCommit;
while (this.onCommits.length > 0) { // ...using a while in case some onCommit adds to transaction
onCommit = this.onCommits.pop();
try { // ...also don't want to fail mid-loop...
promises.push(onCommit());
} catch (e) {
this.$log.error("Error committing transaction.");
}
}
return this.$q.all(promises).then( function () {
self.transaction = false;
self.onCommits = [];
self.onCancels = [];
});
};
/**
* Cancel the current transaction, replacing any dirty objects from
* persistence. Not a true rollback, as it cannot be used to undo any
* persist calls that were successful in the event one of a batch of
* persists failing.
*
* @returns {*}
*/
TransactionService.prototype.cancel = function () {
var self = this,
results = [],
onCancel;
while (this.onCancels.length > 0) {
onCancel = this.onCancels.pop();
try {
results.push(onCancel());
} catch (error) {
this.$log.error("Error committing transaction.");
}
}
return this.$q.all(results).then(function () {
self.transaction = false;
self.onCommits = [];
self.onCancels = [];
});
};
TransactionService.prototype.size = function () {
return this.onCommits.length;
};
return TransactionService;
});

View File

@ -30,7 +30,9 @@ define(
mockLog,
mockDomainObject,
mockType,
mockEditor,
actionContext,
capabilities,
action;
beforeEach(function () {
@ -40,7 +42,7 @@ define(
);
mockNavigationService = jasmine.createSpyObj(
"navigationService",
[ "setNavigation", "getNavigation" ]
[ "setNavigation", "getNavigation", "addListener", "removeListener" ]
);
mockLog = jasmine.createSpyObj(
"$log",
@ -48,14 +50,26 @@ define(
);
mockDomainObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getModel", "getCapability" ]
[ "getId", "getModel", "getCapability", "hasCapability", "useCapability" ]
);
mockType = jasmine.createSpyObj(
"type",
[ "hasFeature" ]
);
mockEditor = jasmine.createSpyObj(
"editorCapability",
["edit", "isEditContextRoot", "cancel"]
);
mockDomainObject.getCapability.andReturn(mockType);
capabilities = {
type: mockType,
editor: mockEditor
};
mockDomainObject.getCapability.andCallFake( function (name) {
return capabilities[name];
});
mockDomainObject.hasCapability.andReturn(true);
mockType.hasFeature.andReturn(true);
actionContext = { domainObject: mockDomainObject };
@ -68,51 +82,34 @@ define(
);
});
it("is only applicable when a domain object is present", function () {
it("is only applicable when an editable domain object is present", function () {
expect(EditAction.appliesTo(actionContext)).toBeTruthy();
expect(EditAction.appliesTo({})).toBeFalsy();
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith('editor');
// Should have checked for creatability
expect(mockType.hasFeature).toHaveBeenCalledWith('creation');
});
//TODO: Disabled for NEM Beta
xit("changes URL path to edit mode when performed", function () {
it("is only applicable to objects not already in edit mode", function () {
mockEditor.isEditContextRoot.andReturn(false);
expect(EditAction.appliesTo(actionContext)).toBe(true);
mockEditor.isEditContextRoot.andReturn(true);
expect(EditAction.appliesTo(actionContext)).toBe(false);
});
it ("cancels editing when user navigates away", function () {
action.perform();
expect(mockLocation.path).toHaveBeenCalledWith("/edit");
expect(mockNavigationService.addListener).toHaveBeenCalled();
mockNavigationService.addListener.mostRecentCall.args[0]();
expect(mockEditor.cancel).toHaveBeenCalled();
});
//TODO: Disabled for NEM Beta
xit("ensures that the edited object is navigated-to", function () {
it ("invokes the Edit capability on the object", function () {
action.perform();
expect(mockNavigationService.setNavigation)
.toHaveBeenCalledWith(mockDomainObject);
expect(mockDomainObject.useCapability).toHaveBeenCalledWith("editor");
});
//TODO: Disabled for NEM Beta
xit("logs a warning if constructed when inapplicable", function () {
// Verify precondition (ensure warn wasn't called during setup)
expect(mockLog.warn).not.toHaveBeenCalled();
// Should not have hit an exception...
new EditAction(
mockLocation,
mockNavigationService,
mockLog,
{}
).perform();
// ...but should have logged a warning
expect(mockLog.warn).toHaveBeenCalled();
// And should not have had other interactions
expect(mockLocation.path)
.not.toHaveBeenCalled();
expect(mockNavigationService.setNavigation)
.not.toHaveBeenCalled();
});
});
}
);

View File

@ -52,7 +52,7 @@ define(
);
mockEditorCapability = jasmine.createSpyObj(
"editor",
[ "save", "cancel" ]
[ "save", "cancel", "isEditContextRoot" ]
);
mockActionCapability = jasmine.createSpyObj(
"actionCapability",
@ -71,7 +71,7 @@ define(
});
mockDomainObject.getModel.andReturn({persisted: 0});
mockEditorCapability.save.andReturn(mockPromise(true));
mockDomainObject.getOriginalObject.andReturn(mockDomainObject);
mockEditorCapability.isEditContextRoot.andReturn(true);
action = new SaveAction(actionContext);
@ -97,6 +97,13 @@ define(
action.perform();
expect(mockEditorCapability.save).toHaveBeenCalled();
});
it("navigates to the object after saving",
function () {
action.perform();
expect(mockActionCapability.perform).toHaveBeenCalledWith("navigate");
});
});
}
);

View File

@ -78,10 +78,11 @@ define(
mockEditorCapability = jasmine.createSpyObj(
"editor",
[ "save", "cancel" ]
[ "save", "cancel", "isEditContextRoot" ]
);
mockEditorCapability.cancel.andReturn(mockPromise(undefined));
mockEditorCapability.save.andReturn(mockPromise(true));
mockEditorCapability.isEditContextRoot.andReturn(true);
capabilities.editor = mockEditorCapability;
mockActionCapability = jasmine.createSpyObj(

View File

@ -1,73 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/capabilities/EditableCompositionCapability"],
function (EditableCompositionCapability) {
describe("An editable composition capability", function () {
var mockContext,
mockEditableObject,
mockDomainObject,
mockTestObject,
someValue,
mockFactory,
capability;
beforeEach(function () {
// EditableContextCapability should watch ALL
// methods for domain objects, so give it an
// arbitrary interface to wrap.
mockContext =
jasmine.createSpyObj("context", [ "getDomainObject" ]);
mockTestObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getModel", "getCapability" ]
);
mockFactory =
jasmine.createSpyObj("factory", ["getEditableObject"]);
someValue = { x: 42 };
mockContext.getDomainObject.andReturn(mockTestObject);
mockFactory.getEditableObject.andReturn(someValue);
capability = new EditableCompositionCapability(
mockContext,
mockEditableObject,
mockDomainObject,
mockFactory
);
});
// Most behavior is tested for EditableLookupCapability,
// so just verify that this isse
it("presumes non-idempotence of its wrapped capability", function () {
expect(capability.getDomainObject())
.toEqual(capability.getDomainObject());
expect(mockContext.getDomainObject.calls.length).toEqual(2);
});
});
}
);

View File

@ -1,87 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/capabilities/EditableContextCapability"],
function (EditableContextCapability) {
describe("An editable context capability", function () {
var mockContext,
mockEditableObject,
mockDomainObject,
mockTestObject,
someValue,
mockFactory,
capability;
beforeEach(function () {
// EditableContextCapability should watch ALL
// methods for domain objects, so give it an
// arbitrary interface to wrap.
mockContext =
jasmine.createSpyObj("context", [ "getDomainObject", "getRoot" ]);
mockTestObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getModel", "getCapability" ]
);
mockFactory = jasmine.createSpyObj(
"factory",
["getEditableObject", "isRoot"]
);
someValue = { x: 42 };
mockContext.getRoot.andReturn(mockTestObject);
mockContext.getDomainObject.andReturn(mockTestObject);
mockFactory.getEditableObject.andReturn(someValue);
mockFactory.isRoot.andReturn(true);
capability = new EditableContextCapability(
mockContext,
mockEditableObject,
mockDomainObject,
mockFactory
);
});
it("presumes idempotence of its wrapped capability", function () {
expect(capability.getDomainObject())
.toEqual(capability.getDomainObject());
expect(mockContext.getDomainObject.calls.length).toEqual(1);
});
it("hides the root object", function () {
expect(capability.getRoot()).toEqual(mockEditableObject);
expect(capability.getPath()).toEqual([mockEditableObject]);
});
it("exposes the root object through a different method", function () {
// Should still go through the factory...
expect(capability.getTrueRoot()).toEqual(someValue);
// ...with value of the unwrapped capability's getRoot
expect(mockFactory.getEditableObject)
.toHaveBeenCalledWith(mockTestObject);
});
});
}
);

View File

@ -1,144 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/capabilities/EditableLookupCapability"],
function (EditableLookupCapability) {
describe("An editable lookup capability", function () {
var mockContext,
mockEditableObject,
mockDomainObject,
mockTestObject,
someValue,
factory,
capability;
beforeEach(function () {
// EditableContextCapability should watch ALL
// methods for domain objects, so give it an
// arbitrary interface to wrap.
mockContext = jasmine.createSpyObj(
"context",
[
"getSomething",
"getDomainObject",
"getDomainObjectArray"
]
);
mockTestObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getModel", "getCapability" ]
);
factory = {
getEditableObject: function (v) {
return {
isFromTestFactory: true,
calledWith: v
};
}
};
someValue = { x: 42 };
mockContext.getSomething.andReturn(someValue);
mockContext.getDomainObject.andReturn(mockTestObject);
mockContext.getDomainObjectArray.andReturn([mockTestObject]);
capability = new EditableLookupCapability(
mockContext,
mockEditableObject,
mockDomainObject,
factory,
false
);
});
it("wraps retrieved domain objects", function () {
var object = capability.getDomainObject();
expect(object.isFromTestFactory).toBe(true);
expect(object.calledWith).toEqual(mockTestObject);
});
it("wraps retrieved domain object arrays", function () {
var object = capability.getDomainObjectArray()[0];
expect(object.isFromTestFactory).toBe(true);
expect(object.calledWith).toEqual(mockTestObject);
});
it("does not wrap non-domain-objects", function () {
expect(capability.getSomething()).toEqual(someValue);
});
it("caches idempotent lookups", function () {
capability = new EditableLookupCapability(
mockContext,
mockEditableObject,
mockDomainObject,
factory,
true // idempotent
);
expect(capability.getDomainObject())
.toEqual(capability.getDomainObject());
expect(mockContext.getDomainObject.calls.length).toEqual(1);
});
it("does not cache non-idempotent lookups", function () {
capability = new EditableLookupCapability(
mockContext,
mockEditableObject,
mockDomainObject,
factory,
false // Not idempotent
);
expect(capability.getDomainObject())
.toEqual(capability.getDomainObject());
expect(mockContext.getDomainObject.calls.length).toEqual(2);
});
it("wraps inherited methods", function () {
var CapabilityClass = function(){
};
CapabilityClass.prototype.inheritedMethod=function () {
return "an inherited method";
};
mockContext = new CapabilityClass();
capability = new EditableLookupCapability(
mockContext,
mockEditableObject,
mockDomainObject,
factory,
false
);
expect(capability.inheritedMethod()).toEqual("an inherited method");
expect(capability.hasOwnProperty('inheritedMethod')).toBe(true);
// The presence of an own property indicates that the method
// has been wrapped on the object itself and this is a valid
// test that the inherited method has been wrapped.
});
});
}
);

View File

@ -1,94 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/capabilities/EditablePersistenceCapability"],
function (EditablePersistenceCapability) {
describe("An editable persistence capability", function () {
var mockPersistence,
mockEditableObject,
mockDomainObject,
mockCache,
mockPromise,
capability;
beforeEach(function () {
mockPersistence = jasmine.createSpyObj(
"persistence",
[ "persist", "refresh" ]
);
mockEditableObject = jasmine.createSpyObj(
"editableObject",
[ "getId", "getModel", "getCapability" ]
);
mockDomainObject = jasmine.createSpyObj(
"editableObject",
[ "getId", "getModel", "getCapability" ]
);
mockCache = jasmine.createSpyObj(
"cache",
[ "markDirty" ]
);
mockPromise = jasmine.createSpyObj("promise", ["then"]);
mockCache.markDirty.andReturn(mockPromise);
mockDomainObject.getCapability.andReturn(mockPersistence);
capability = new EditablePersistenceCapability(
mockPersistence,
mockEditableObject,
mockDomainObject,
mockCache
);
});
it("marks objects as dirty (in the cache) upon persist", function () {
capability.persist();
expect(mockCache.markDirty)
.toHaveBeenCalledWith(mockEditableObject);
});
it("does not invoke the underlying persistence capability", function () {
capability.persist();
expect(mockPersistence.persist).not.toHaveBeenCalled();
});
it("refreshes using the original domain object's persistence", function () {
// Refreshing needs to delegate via the unwrapped domain object.
// Otherwise, only the editable version of the object will be updated;
// we instead want the real version of the object to receive these
// changes.
expect(mockDomainObject.getCapability).not.toHaveBeenCalled();
expect(mockPersistence.refresh).not.toHaveBeenCalled();
capability.refresh();
expect(mockDomainObject.getCapability).toHaveBeenCalledWith('persistence');
expect(mockPersistence.refresh).toHaveBeenCalled();
});
it("returns a promise from persist", function () {
expect(capability.persist().then).toEqual(jasmine.any(Function));
});
});
}
);

View File

@ -1,73 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/capabilities/EditableRelationshipCapability"],
function (EditableRelationshipCapability) {
describe("An editable relationship capability", function () {
var mockContext,
mockEditableObject,
mockDomainObject,
mockTestObject,
someValue,
mockFactory,
capability;
beforeEach(function () {
// EditableContextCapability should watch ALL
// methods for domain objects, so give it an
// arbitrary interface to wrap.
mockContext =
jasmine.createSpyObj("context", [ "getDomainObject" ]);
mockTestObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getModel", "getCapability" ]
);
mockFactory =
jasmine.createSpyObj("factory", ["getEditableObject"]);
someValue = { x: 42 };
mockContext.getDomainObject.andReturn(mockTestObject);
mockFactory.getEditableObject.andReturn(someValue);
capability = new EditableRelationshipCapability(
mockContext,
mockEditableObject,
mockDomainObject,
mockFactory
);
});
// Most behavior is tested for EditableLookupCapability,
// so just verify that this isse
it("presumes non-idempotence of its wrapped capability", function () {
expect(capability.getDomainObject())
.toEqual(capability.getDomainObject());
expect(mockContext.getDomainObject.calls.length).toEqual(2);
});
});
}
);

View File

@ -25,94 +25,149 @@ define(
function (EditorCapability) {
describe("The editor capability", function () {
var mockPersistence,
mockEditableObject,
mockDomainObject,
mockCache,
mockCallback,
model,
var mockDomainObject,
capabilities,
mockParentObject,
mockTransactionService,
mockStatusCapability,
mockParentStatus,
mockContextCapability,
capability;
beforeEach(function () {
mockPersistence = jasmine.createSpyObj(
"persistence",
[ "persist" ]
);
mockEditableObject = {
getModel: function () { return model; }
function fastPromise(val) {
return {
then: function (callback) {
return callback(val);
}
};
}
beforeEach(function () {
mockDomainObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getModel", "getCapability", "useCapability" ]
["getId", "getModel", "hasCapability", "getCapability", "useCapability"]
);
mockCache = jasmine.createSpyObj(
"cache",
[ "saveAll", "markClean" ]
mockParentObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "hasCapability", "getCapability", "useCapability"]
);
mockCallback = jasmine.createSpy("callback");
mockTransactionService = jasmine.createSpyObj(
"transactionService",
[
"startTransaction",
"size",
"commit",
"cancel"
]
);
mockTransactionService.commit.andReturn(fastPromise());
mockTransactionService.cancel.andReturn(fastPromise());
mockDomainObject.getCapability.andReturn(mockPersistence);
mockStatusCapability = jasmine.createSpyObj(
"statusCapability",
["get", "set"]
);
mockParentStatus = jasmine.createSpyObj(
"statusCapability",
["get", "set"]
);
mockContextCapability = jasmine.createSpyObj(
"contextCapability",
["getParent"]
);
mockContextCapability.getParent.andReturn(mockParentObject);
model = { someKey: "some value", x: 42 };
capabilities = {
context: mockContextCapability,
status: mockStatusCapability
};
mockDomainObject.hasCapability.andCallFake(function(name) {
return capabilities[name] !== undefined;
});
mockDomainObject.getCapability.andCallFake(function (name) {
return capabilities[name];
});
mockParentObject.getCapability.andReturn(mockParentStatus);
mockParentObject.hasCapability.andReturn(false);
capability = new EditorCapability(
mockPersistence,
mockEditableObject,
mockDomainObject,
mockCache
mockTransactionService,
mockDomainObject
);
});
//TODO: Disabled for NEM Beta
xit("mutates the real domain object on nonrecursive save", function () {
capability.save(true).then(mockCallback);
it("starts a transaction when edit is invoked", function () {
capability.edit();
expect(mockTransactionService.startTransaction).toHaveBeenCalled();
});
// Wait for promise to resolve
waitsFor(function () {
return mockCallback.calls.length > 0;
}, 250);
it("sets editing status on object", function () {
capability.edit();
expect(mockStatusCapability.set).toHaveBeenCalledWith("editing", true);
});
runs(function () {
expect(mockDomainObject.useCapability)
.toHaveBeenCalledWith("mutation", jasmine.any(Function));
// We should get the model from the editable object back
expect(
mockDomainObject.useCapability.mostRecentCall.args[1]()
).toEqual(model);
it("uses editing status to determine editing context root", function () {
capability.edit();
mockStatusCapability.get.andReturn(false);
expect(capability.isEditContextRoot()).toBe(false);
mockStatusCapability.get.andReturn(true);
expect(capability.isEditContextRoot()).toBe(true);
});
it("inEditingContext returns true if parent object is being" +
" edited", function () {
mockStatusCapability.get.andReturn(false);
mockParentStatus.get.andReturn(false);
expect(capability.inEditContext()).toBe(false);
mockParentStatus.get.andReturn(true);
expect(capability.inEditContext()).toBe(true);
});
describe("save", function() {
beforeEach(function() {
capability.edit();
capability.save();
});
it("commits the transaction", function () {
expect(mockTransactionService.commit).toHaveBeenCalled();
});
it("resets the edit state", function () {
expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
});
});
//TODO: Disabled for NEM Beta
xit("tells the cache to save others", function () {
capability.save().then(mockCallback);
// Wait for promise to resolve
waitsFor(function () {
return mockCallback.calls.length > 0;
}, 250);
runs(function () {
expect(mockCache.saveAll).toHaveBeenCalled();
describe("cancel", function() {
beforeEach(function() {
capability.edit();
capability.cancel();
});
it("cancels the transaction", function () {
expect(mockTransactionService.cancel).toHaveBeenCalled();
});
it("resets the edit state", function () {
expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
});
});
//TODO: Disabled for NEM Beta
xit("has no interactions on cancel", function () {
capability.cancel().then(mockCallback);
describe("dirty", function() {
var model = {};
// Wait for promise to resolve
waitsFor(function () {
return mockCallback.calls.length > 0;
}, 250);
runs(function () {
expect(mockDomainObject.useCapability).not.toHaveBeenCalled();
expect(mockCache.markClean).not.toHaveBeenCalled();
expect(mockCache.saveAll).not.toHaveBeenCalled();
beforeEach(function() {
mockDomainObject.getModel.andReturn(model);
capability.edit();
capability.cancel();
});
it("returns true if the object has been modified since it" +
" was last persisted", function () {
mockTransactionService.size.andReturn(0);
expect(capability.dirty()).toBe(false);
mockTransactionService.size.andReturn(1);
expect(capability.dirty()).toBe(true);
});
});
});
}
);

View File

@ -21,15 +21,34 @@
*****************************************************************************/
define(
["../../src/objects/EditableDomainObject"],
function (EditableDomainObject) {
[
"../../src/capabilities/TransactionalPersistenceCapability",
"../../src/capabilities/TransactionCapabilityDecorator"
],
function (TransactionalPersistenceCapability, TransactionCapabilityDecorator) {
describe("Editable domain object", function () {
var object;
describe("The transaction capability decorator", function () {
var mockQ,
mockTransactionService,
mockCapabilityService,
provider;
beforeEach(function() {
mockQ = {};
mockTransactionService = {};
mockCapabilityService = jasmine.createSpyObj("capabilityService", ["getCapabilities"]);
mockCapabilityService.getCapabilities.andReturn({
persistence: function() {}
});
provider = new TransactionCapabilityDecorator(mockQ, mockTransactionService, mockCapabilityService);
beforeEach(function () {
object = new EditableDomainObject();
});
it("decorates the persistence capability", function() {
var capabilities = provider.getCapabilities();
expect(capabilities.persistence({}) instanceof TransactionalPersistenceCapability).toBe(true);
});
});
}
);

View File

@ -0,0 +1,92 @@
/*****************************************************************************
* 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,describe,it,expect,beforeEach,jasmine*/
define(
[
"../../src/capabilities/TransactionalPersistenceCapability"
],
function (TransactionalPersistenceCapability) {
function fastPromise(val) {
return {
then: function(callback) {
return callback(val);
}
};
}
describe("The transactional persistence decorator", function () {
var mockQ,
mockTransactionService,
mockPersistence,
mockDomainObject,
capability;
beforeEach(function() {
mockQ = jasmine.createSpyObj("$q", ["when"]);
mockQ.when.andCallFake(function (val) {
return fastPromise(val);
});
mockTransactionService = jasmine.createSpyObj(
"transactionService",
["isActive", "addToTransaction"]
);
mockPersistence = jasmine.createSpyObj(
"persistenceCapability",
["persist", "refresh"]
);
mockPersistence.persist.andReturn(fastPromise());
mockPersistence.refresh.andReturn(fastPromise());
capability = new TransactionalPersistenceCapability(mockQ, mockTransactionService, mockPersistence, mockDomainObject);
});
it("if no transaction is active, passes through to persistence" +
" provider", function() {
mockTransactionService.isActive.andReturn(false);
capability.persist();
expect(mockPersistence.persist).toHaveBeenCalled();
});
it("if transaction is active, persist and cancel calls are" +
" queued", function() {
mockTransactionService.isActive.andReturn(true);
capability.persist();
expect(mockTransactionService.addToTransaction).toHaveBeenCalled();
mockTransactionService.addToTransaction.mostRecentCall.args[0]();
expect(mockPersistence.persist).toHaveBeenCalled();
mockTransactionService.addToTransaction.mostRecentCall.args[1]();
expect(mockPersistence.refresh).toHaveBeenCalled();
});
it("persist call is only added to transaction once", function() {
mockTransactionService.isActive.andReturn(true);
capability.persist();
expect(mockTransactionService.addToTransaction).toHaveBeenCalled();
capability.persist();
expect(mockTransactionService.addToTransaction.calls.length).toBe(1);
});
});
}
);

View File

@ -1,177 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/objects/EditableDomainObjectCache"],
function (EditableDomainObjectCache) {
describe("Editable domain object cache", function () {
var captured,
completionCapability,
mockQ,
mockType,
cache;
// Constructors for test objects
function TestObject(id) {
return {
getId: function () { return id; },
getModel: function () { return {}; },
getCapability: function (key) {
return {
editor: completionCapability,
type: mockType
}[key];
},
hasCapability: function () {
return false;
}
};
}
function WrapObject(domainObject, model) {
var result = Object.create(domainObject);
result.wrapped = true;
result.wrappedModel = model;
result.hasCapability = function (name) {
return name === 'editor';
};
captured.wraps = (captured.wraps || 0) + 1;
return result;
}
beforeEach(function () {
mockQ = jasmine.createSpyObj('$q', ['when', 'all']);
mockType = jasmine.createSpyObj('type', ['hasFeature']);
mockType.hasFeature.andReturn(true);
captured = {};
completionCapability = {
save: function () {
captured.saved = (captured.saved || 0) + 1;
}
};
cache = new EditableDomainObjectCache(WrapObject, mockQ);
});
it("wraps objects using provided constructor", function () {
var domainObject = new TestObject('test-id'),
wrappedObject = cache.getEditableObject(domainObject);
expect(wrappedObject.wrapped).toBeTruthy();
expect(wrappedObject.getId()).toEqual(domainObject.getId());
});
it("wraps objects repeatedly, wraps models once", function () {
var domainObject = new TestObject('test-id'),
wrappedObjects = [];
// Verify precondition
expect(captured.wraps).toBeUndefined();
// Invoke a few more times; expect count not to increment
wrappedObjects.push(cache.getEditableObject(domainObject));
expect(captured.wraps).toEqual(1);
wrappedObjects.push(cache.getEditableObject(domainObject));
expect(captured.wraps).toEqual(2);
wrappedObjects.push(cache.getEditableObject(domainObject));
expect(captured.wraps).toEqual(3);
// Verify that the last call still gave us a wrapped object
expect(wrappedObjects[0].wrapped).toBeTruthy();
expect(wrappedObjects[0].getId()).toEqual(domainObject.getId());
// Verify that objects are distinct but models are identical
expect(wrappedObjects[0].wrappedModel)
.toBe(wrappedObjects[1].wrappedModel);
expect(wrappedObjects[0]).not
.toBe(wrappedObjects[1]);
});
it("saves objects that have been marked dirty", function () {
var objects = ['a', 'b', 'c'].map(TestObject).map(function (domainObject) {
return cache.getEditableObject(domainObject);
});
cache.markDirty(objects[0]);
cache.markDirty(objects[2]);
cache.saveAll();
expect(captured.saved).toEqual(2);
});
it("does not save objects that have been marked clean", function () {
var objects = ['a', 'b', 'c'].map(TestObject).map(function (domainObject) {
return cache.getEditableObject(domainObject);
});
cache.markDirty(objects[0]);
cache.markDirty(objects[2]);
cache.markClean(objects[0]);
cache.saveAll();
expect(captured.saved).toEqual(1);
});
it("tracks the root object of the Edit mode subgraph", function () {
// Root object is the first object exposed to the cache
var domainObjects = ['a', 'b', 'c'].map(TestObject);
domainObjects.forEach(function (obj) {
cache.getEditableObject(obj);
});
expect(cache.isRoot(domainObjects[0])).toBeTruthy();
expect(cache.isRoot(domainObjects[1])).toBeFalsy();
expect(cache.isRoot(domainObjects[2])).toBeFalsy();
});
it("does not double-wrap objects", function () {
var domainObject = new TestObject('test-id'),
wrappedObject = cache.getEditableObject(domainObject);
// Same instance should be returned if you try to wrap
// twice. This is necessary, since it's possible to (e.g.)
// use a context capability on an object retrieved via
// composition, in which case a result will already be
// wrapped.
expect(cache.getEditableObject(wrappedObject))
.toBe(wrappedObject);
});
it("does not wrap non-editable objects", function () {
var domainObject = new TestObject('test-id');
mockType.hasFeature.andCallFake(function (key) {
return key !== 'creation';
});
expect(cache.getEditableObject(domainObject))
.toBe(domainObject);
});
});
}
);

View File

@ -1,79 +0,0 @@
/*****************************************************************************
* 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.
*****************************************************************************/
define(
["../../src/objects/EditableModelCache"],
function (EditableModelCache) {
describe("The editable model cache", function () {
var mockObject,
mockOtherObject,
testModel,
testId,
otherModel,
otherId,
cache;
beforeEach(function () {
testId = "test";
testModel = { someKey: "some value" };
otherId = "other";
otherModel = { someKey: "some other value" };
mockObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getModel" ]
);
mockOtherObject = jasmine.createSpyObj(
"domainObject",
[ "getId", "getModel" ]
);
mockObject.getId.andReturn(testId);
mockObject.getModel.andReturn(testModel);
mockOtherObject.getId.andReturn(otherId);
mockOtherObject.getModel.andReturn(otherModel);
cache = new EditableModelCache();
});
it("provides clones of domain object models", function () {
var model = cache.getCachedModel(mockObject);
// Should be identical...
expect(model).toEqual(testModel);
// ...but not pointer-identical
expect(model).not.toBe(testModel);
});
it("provides only one clone per object", function () {
var model = cache.getCachedModel(mockObject);
expect(cache.getCachedModel(mockObject)).toBe(model);
});
it("maintains separate caches per-object", function () {
expect(cache.getCachedModel(mockObject))
.not.toEqual(cache.getCachedModel(mockOtherObject));
});
});
}
);

View File

@ -34,7 +34,7 @@ define(
mockEditAction,
mockPropertiesAction,
mockTypeCapability,
mockStatusCapability,
mockEditorCapability,
capabilities,
plotView,
policy;
@ -48,11 +48,10 @@ define(
'getCapability'
]
);
mockStatusCapability = jasmine.createSpyObj('statusCapability', ['get']);
mockStatusCapability.get.andReturn(false);
mockEditorCapability = jasmine.createSpyObj('editorCapability', ['isEditContextRoot']);
mockTypeCapability = jasmine.createSpyObj('type', ['getKey']);
capabilities = {
'status': mockStatusCapability,
'editor': mockEditorCapability,
'type': mockTypeCapability
};
@ -112,7 +111,7 @@ define(
it("disallows the edit action when object is already being" +
" edited", function () {
testViews = [ editableView ];
mockStatusCapability.get.andReturn(true);
mockEditorCapability.isEditContextRoot.andReturn(true);
expect(policy.allow(mockEditAction, testContext)).toBe(false);
});

View File

@ -32,16 +32,22 @@ define(
context,
navigatedObject,
mockDomainObject,
mockEditorCapability,
metadata,
editModeBlacklist = ["copy", "follow", "window", "link", "locate"],
nonEditContextBlacklist = ["copy", "follow", "properties", "move", "link", "remove", "locate"];
beforeEach(function () {
navigatedObject = jasmine.createSpyObj("navigatedObject", ["hasCapability"]);
mockEditorCapability = jasmine.createSpyObj("editorCapability", ["isEditContextRoot", "inEditContext"]);
navigatedObject = jasmine.createSpyObj("navigatedObject", ["hasCapability", "getCapability"]);
navigatedObject.getCapability.andReturn(mockEditorCapability);
navigatedObject.hasCapability.andReturn(false);
mockDomainObject = jasmine.createSpyObj("domainObject", ["hasCapability", "getCapability"]);
mockDomainObject.hasCapability.andReturn(false);
mockDomainObject.getCapability.andReturn(mockEditorCapability);
navigationService = jasmine.createSpyObj("navigationService", ["getNavigation"]);
navigationService.getNavigation.andReturn(navigatedObject);
@ -62,6 +68,7 @@ define(
it('Allows "window" action when navigated object in edit mode,' +
' but selected object not in edit mode ', function() {
navigatedObject.hasCapability.andReturn(true);
mockEditorCapability.isEditContextRoot.andReturn(true);
metadata.key = "window";
expect(policy.allow(mockAction, context)).toBe(true);
});
@ -91,6 +98,8 @@ define(
it('Disallows "move" action when navigated object in edit mode,' +
' but selected object not in edit mode ', function() {
navigatedObject.hasCapability.andReturn(true);
mockEditorCapability.isEditContextRoot.andReturn(true);
mockEditorCapability.inEditContext.andReturn(false);
metadata.key = "move";
expect(policy.allow(mockAction, context)).toBe(false);
});
@ -99,6 +108,9 @@ define(
' selected object in edit mode', function() {
navigatedObject.hasCapability.andReturn(true);
mockDomainObject.hasCapability.andReturn(true);
mockEditorCapability.isEditContextRoot.andReturn(true);
mockEditorCapability.inEditContext.andReturn(true);
metadata.key = "copy";
expect(policy.allow(mockAction, context)).toBe(false);
});

View File

@ -33,8 +33,13 @@ define(
testMode = true; // Act as if we're in Edit mode by default
mockDomainObject = jasmine.createSpyObj(
'domainObject',
['hasCapability']
['hasCapability', 'getCapability']
);
mockDomainObject.getCapability.andReturn({
inEditContext: function () {
return true;
}
});
mockDomainObject.hasCapability.andCallFake(function (c) {
return (c === 'editor') && testMode;
});

View File

@ -32,6 +32,7 @@ define(
mockDomainObject,
mockPersistence,
mockStatusCapability,
mockEditorCapability,
mockCapabilities,
representer;
@ -58,11 +59,14 @@ define(
mockPersistence =
jasmine.createSpyObj("persistence", ["persist"]);
mockStatusCapability =
jasmine.createSpyObj("statusCapability", ["get", "listen"]);
mockStatusCapability.get.andReturn(false);
jasmine.createSpyObj("statusCapability", ["listen"]);
mockEditorCapability =
jasmine.createSpyObj("editorCapability", ["isEditContextRoot"]);
mockCapabilities = {
'persistence': mockPersistence,
'status': mockStatusCapability
'status': mockStatusCapability,
'editor': mockEditorCapability
};
mockDomainObject.getModel.andReturn({});
@ -82,6 +86,7 @@ define(
it("Sets edit view template on edit mode", function () {
mockStatusCapability.listen.mostRecentCall.args[0](['editing']);
mockEditorCapability.isEditContextRoot.andReturn(true);
expect(mockScope.viewObjectTemplate).toEqual('edit-object');
});

View File

@ -0,0 +1,137 @@
/*****************************************************************************
* 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,describe,it,expect,beforeEach,jasmine*/
define(
["../../src/services/TransactionService"],
function (TransactionService) {
describe("The Transaction Service", function () {
var mockQ,
mockLog,
transactionService;
function fastPromise (val) {
return {
then: function (callback) {
return fastPromise(callback(val));
}
};
}
beforeEach(function () {
mockQ = jasmine.createSpyObj("$q", ["all"]);
mockQ.all.andReturn(fastPromise());
mockLog = jasmine.createSpyObj("$log", ["error"]);
transactionService = new TransactionService(mockQ, mockLog);
});
it("isActive returns true if a transaction is in progress", function () {
expect(transactionService.isActive()).toBe(false);
transactionService.startTransaction();
expect(transactionService.isActive()).toBe(true);
});
it("addToTransaction queues onCommit and onCancel functions", function () {
var onCommit = jasmine.createSpy('onCommit'),
onCancel = jasmine.createSpy('onCancel');
transactionService.startTransaction();
transactionService.addToTransaction(onCommit, onCancel);
expect(transactionService.onCommits.length).toBe(1);
expect(transactionService.onCancels.length).toBe(1);
});
it("size function returns size of commit and cancel queues", function () {
var onCommit = jasmine.createSpy('onCommit'),
onCancel = jasmine.createSpy('onCancel');
transactionService.startTransaction();
transactionService.addToTransaction(onCommit, onCancel);
transactionService.addToTransaction(onCommit, onCancel);
transactionService.addToTransaction(onCommit, onCancel);
expect(transactionService.size()).toBe(3);
});
describe("commit", function () {
var onCommits;
beforeEach(function() {
onCommits = [0, 1, 2].map(function(val) {
return jasmine.createSpy("onCommit" + val);
});
transactionService.startTransaction();
onCommits.forEach(transactionService.addToTransaction.bind(transactionService));
});
it("commit calls all queued commit functions", function () {
expect(transactionService.onCommits.length).toBe(3);
transactionService.commit();
onCommits.forEach( function (spy) {
expect(spy).toHaveBeenCalled();
});
});
it("commit resets active state and clears queues", function () {
transactionService.commit();
expect(transactionService.isActive()).toBe(false);
expect(transactionService.onCommits.length).toBe(0);
expect(transactionService.onCancels.length).toBe(0);
});
});
describe("cancel", function () {
var onCancels;
beforeEach(function() {
onCancels = [0, 1, 2].map(function(val) {
return jasmine.createSpy("onCancel" + val);
});
transactionService.startTransaction();
onCancels.forEach(function (onCancel) {
transactionService.addToTransaction(undefined, onCancel);
});
});
it("cancel calls all queued cancel functions", function () {
expect(transactionService.onCancels.length).toBe(3);
transactionService.cancel();
onCancels.forEach( function (spy) {
expect(spy).toHaveBeenCalled();
});
});
it("cancel resets active state and clears queues", function () {
transactionService.cancel();
expect(transactionService.isActive()).toBe(false);
expect(transactionService.onCommits.length).toBe(0);
expect(transactionService.onCancels.length).toBe(0);
});
});
});
}
);

View File

@ -279,21 +279,13 @@ input[type="search"] {
padding-right: 0.35em; // For context arrow. Done with em's so pad is relative to the scale of the text.
}
.context-available-w {
z-index: 5;
}
.context-available {
font-size: 0.7em;
@include webkitProp(flex, '0 0 1');
}
}
body.desktop .object-header {
.context-available {
@include trans-prop-nice(opacity, 0.25s);
opacity: 0;
}
&:hover {
.context-available {
opacity: 1;
}
@include flex(0 0 1);
}
}

View File

@ -36,8 +36,6 @@
line-height: $ohH;
.left {
padding-right: $interiorMarginLg;
z-index: 5;
}
}
>.object-holder.abs {

View File

@ -39,7 +39,7 @@ define(
if (!regionPart.modes){
return true;
}
if (domainObject.getCapability('status').get('editing')){
if (domainObject.hasCapability('editor') && domainObject.getCapability('editor').inEditContext()){
//If the domain object is in edit mode, only include a part
// if it is marked editable
return regionPart.modes.indexOf('edit') !== -1;

View File

@ -46,7 +46,11 @@ define(
});
}
$scope.regions = filterRegions(typeCapability.getDefinition().inspector || new InspectorRegion());
function setRegions() {
$scope.regions = filterRegions(typeCapability.getDefinition().inspector || new InspectorRegion());
}
setRegions();
}
return InspectorController;

View File

@ -28,7 +28,7 @@ define(
var editableRegionPolicy,
mockDomainObject,
mockStatusCapability,
mockEditorCapability,
mockBrowseRegionPart = {
modes: 'browse'
},
@ -40,31 +40,32 @@ define(
beforeEach(function(){
editableRegionPolicy = new EditableRegionPolicy();
mockStatusCapability = jasmine.createSpyObj("statusCapability", [
"get"
mockEditorCapability = jasmine.createSpyObj("editorCapability", [
"inEditContext"
]);
mockDomainObject = jasmine.createSpyObj("domainObject", [
"getCapability"
"hasCapability", "getCapability"
]);
mockDomainObject.getCapability.andReturn(mockStatusCapability);
mockDomainObject.hasCapability.andReturn(true);
mockDomainObject.getCapability.andReturn(mockEditorCapability);
});
it("includes only browse region parts for object not in edit mode", function() {
mockStatusCapability.get.andReturn(false);
mockEditorCapability.inEditContext.andReturn(false);
expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(true);
expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(false);
});
it("includes only edit region parts for object in edit mode", function() {
mockStatusCapability.get.andReturn(true);
mockEditorCapability.inEditContext.andReturn(true);
expect(editableRegionPolicy.allow(mockBrowseRegionPart, mockDomainObject)).toBe(false);
expect(editableRegionPolicy.allow(mockEditRegionPart, mockDomainObject)).toBe(true);
});
it("includes region parts with no mode specification", function() {
mockStatusCapability.get.andReturn(false);
mockEditorCapability.inEditContext.andReturn(false);
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
mockStatusCapability.get.andReturn(true);
mockEditorCapability.inEditContext.andReturn(true);
expect(editableRegionPolicy.allow(mockAllModesRegionPart, mockDomainObject)).toBe(true);
});

View File

@ -60,16 +60,6 @@ define(
this.$q = $q;
}
// Utility function for creating promise-like objects which
// resolve synchronously when possible
function fastPromise(value) {
return (value || {}).then ? value : {
then: function (callback) {
return fastPromise(callback(value));
}
};
}
function getKey(id) {
var parts = id.split(":");
return parts.length > 1 ? parts.slice(1).join(":") : id;
@ -157,8 +147,7 @@ define(
* when the update is complete
*/
PersistenceCapability.prototype.refresh = function () {
var domainObject = this.domainObject,
model = domainObject.getModel();
var domainObject = this.domainObject;
// Update a domain object's model upon refresh
function updateModel(model) {
@ -168,13 +157,10 @@ define(
}, modified);
}
// Only update if we don't have unsaved changes
return (model.modified === model.persisted) ?
this.persistenceService.readObject(
return this.persistenceService.readObject(
this.getSpace(),
this.domainObject.getId()
).then(updateModel) :
fastPromise(false);
).then(updateModel);
};
/**

View File

@ -155,18 +155,6 @@ define(
expect(model).toEqual(refreshModel);
});
it("does not overwrite unpersisted changes on refresh", function () {
var refreshModel = {someOtherKey: "some other value"},
mockCallback = jasmine.createSpy();
model.modified = 2;
model.persisted = 1;
mockPersistenceService.readObject.andReturn(asPromise(refreshModel));
persistence.refresh().then(mockCallback);
expect(model).not.toEqual(refreshModel);
// Should have also indicated that no changes were actually made
expect(mockCallback).toHaveBeenCalledWith(false);
});
it("does not trigger error notification on successful" +
" persistence", function () {
persistence.persist();

View File

@ -57,7 +57,8 @@ define(
function storeZoom() {
var isEditMode = $scope.commit &&
$scope.domainObject &&
$scope.domainObject.hasCapability('editor');
$scope.domainObject.hasCapability('editor') &&
$scope.domainObject.getCapability('editor').inEditContext();
if (isEditMode) {
$scope.configuration = $scope.configuration || {};
$scope.configuration.zoomLevel = zoomIndex;

View File

@ -39,9 +39,10 @@ define(
};
}
// Check if we are in edit mode
// Check if we are in edit mode (also check parents)
function inEditMode() {
return swimlane.domainObject.hasCapability("editor");
return swimlane.domainObject.hasCapability('editor') &&
swimlane.domainObject.getCapability('editor').inEditContext();
}
// Boolean and (for reduce below)

View File

@ -82,11 +82,18 @@ define(
it("persists zoom changes in Edit mode", function () {
mockScope.domainObject = jasmine.createSpyObj(
'domainObject',
['hasCapability']
['hasCapability', 'getCapability']
);
mockScope.domainObject.hasCapability.andCallFake(function (c) {
return c === 'editor';
});
mockScope.domainObject.getCapability.andCallFake(function (c) {
if (c === 'editor') {
return {
inEditContext: function () {return true;}
};
}
});
controller.zoom(1);
expect(mockScope.commit).toHaveBeenCalled();
expect(mockScope.configuration.zoomLevel)

View File

@ -28,6 +28,7 @@ define(
var mockSwimlane,
mockOtherObject,
mockActionCapability,
mockEditorCapability,
mockPersistence,
mockContext,
mockAction,
@ -36,6 +37,8 @@ define(
beforeEach(function () {
var mockPromise = jasmine.createSpyObj('promise', ['then']);
mockEditorCapability = jasmine.createSpyObj('editorCapability', ['inEditContext']);
mockSwimlane = jasmine.createSpyObj(
"swimlane",
[ "highlight", "highlightBottom" ]
@ -86,19 +89,22 @@ define(
mockSwimlane.domainObject.getCapability.andCallFake(function (c) {
return {
action: mockActionCapability,
persistence: mockPersistence
persistence: mockPersistence,
editor: mockEditorCapability
}[c];
});
mockSwimlane.parent.domainObject.getCapability.andCallFake(function (c) {
return {
action: mockActionCapability,
persistence: mockPersistence
persistence: mockPersistence,
editor: mockEditorCapability
}[c];
});
mockOtherObject.getCapability.andCallFake(function (c) {
return {
action: mockActionCapability,
context: mockContext
context: mockContext,
editor: mockEditorCapability
}[c];
});
mockContext.getParent.andReturn(mockOtherObject);
@ -109,13 +115,14 @@ define(
});
it("disallows drop outside of edit mode", function () {
mockEditorCapability.inEditContext.andReturn(true);
// Verify precondition
expect(handler.allowDropIn('d', mockSwimlane.domainObject))
.toBeTruthy();
expect(handler.allowDropAfter('d', mockSwimlane.domainObject))
.toBeTruthy();
// Act as if we're not in edit mode
mockSwimlane.domainObject.hasCapability.andReturn(false);
mockEditorCapability.inEditContext.andReturn(false);
// Now, they should be disallowed
expect(handler.allowDropIn('d', mockSwimlane.domainObject))
.toBeFalsy();

View File

@ -86,10 +86,7 @@ define([
"implementation": DropGesture,
"depends": [
"dndService",
"$q",
"navigationService",
"instantiate",
"typeService"
"$q"
]
},
{

View File

@ -91,6 +91,7 @@ define(
couldEdit = false,
lastIdPath = [],
lastKey,
statusListener,
changeTemplate = templateLinker.link($scope, element);
// Populate scope with any capabilities indicated by the
@ -167,7 +168,7 @@ define(
representation = lookup($scope.key, domainObject),
uses = ((representation || {}).uses || []),
canRepresent = !!(representation && domainObject),
canEdit = !!(domainObject && domainObject.hasCapability('editor')),
canEdit = !!(domainObject && domainObject.hasCapability('editor') && domainObject.getCapability('editor').inEditContext()),
idPath = getIdPath(domainObject),
key = $scope.key;
@ -239,6 +240,25 @@ define(
// (to a different object)
$scope.$watch("domainObject", refresh);
function listenForStatusChange(object) {
if (statusListener) {
statusListener();
}
statusListener = object.getCapability("status").listen(refresh);
}
/**
* Add a listener to the object for status changes.
*/
$scope.$watch("domainObject", function (domainObject, oldDomainObject) {
if (domainObject !== oldDomainObject){
listenForStatusChange(domainObject);
}
});
if ($scope.domainObject) {
listenForStatusChange($scope.domainObject);
}
// Finally, also update when there is a new version of that
// same domain object; these changes should be tracked in the
// model's "modified" field, by the mutation capability.
@ -247,6 +267,11 @@ define(
// Make sure any resources allocated by representers also get
// released.
$scope.$on("$destroy", destroyRepresenters);
$scope.$on("$destroy", function () {
if (statusListener) {
statusListener();
}
});
// Do one initial refresh, so that we don't need another
// digest iteration just to populate the scope. Failure to

View File

@ -24,9 +24,8 @@
* Module defining DropGesture. Created by vwoeltje on 11/17/14.
*/
define(
['./GestureConstants',
'../../../commonUI/edit/src/objects/EditableDomainObject'],
function (GestureConstants, EditableDomainObject) {
['./GestureConstants'],
function (GestureConstants) {
/**
* A DropGesture adds and maintains event handlers upon an element
@ -39,9 +38,8 @@ define(
* @param {DomainObject} domainObject the domain object whose
* composition should be modified as a result of the drop.
*/
function DropGesture(dndService, $q, navigationService, instantiate, typeService, element, domainObject) {
function DropGesture(dndService, $q, element, domainObject) {
var actionCapability = domainObject.getCapability('action'),
editableDomainObject,
scope = element.scope && element.scope(),
action; // Action for the drop, when it occurs
@ -66,24 +64,12 @@ define(
x: event.pageX - rect.left,
y: event.pageY - rect.top
},
editableDomainObject
domainObject
);
}
}
function dragOver(e) {
//Refresh domain object on each dragOver to catch external
// updates to the model
//Don't use EditableDomainObject for folders, allow immediate persistence
if (domainObject.hasCapability('editor') ||
domainObject.getModel().type==='folder') {
editableDomainObject = domainObject;
} else {
editableDomainObject = new EditableDomainObject(domainObject, $q);
}
actionCapability = editableDomainObject.getCapability('action');
var event = (e || {}).originalEvent || e,
selectedObject = dndService.getData(
GestureConstants.MCT_EXTENDED_DRAG_TYPE
@ -108,20 +94,24 @@ define(
function drop(e) {
var event = (e || {}).originalEvent || e,
id = event.dataTransfer.getData(GestureConstants.MCT_DRAG_TYPE),
domainObjectType = editableDomainObject.getModel().type;
domainObjectType = domainObject.getModel().type;
// Handle the drop; add the dropped identifier to the
// destination domain object's composition, and persist
// the change.
if (id) {
e.preventDefault();
$q.when(action && action.perform()).then(function (result) {
//Don't go into edit mode for folders
if (domainObjectType!=='folder') {
editableDomainObject.getCapability('action').perform('edit');
//Use scope.apply, drop event is outside digest cycle
scope.$apply(function () {
if (domainObjectType !== 'folder') {
domainObject.getCapability('action').perform('edit');
}
});
$q.when(action && action.perform()).then(function () {
broadcastDrop(id, event);
});
}
}

View File

@ -36,6 +36,7 @@ define(
testViews,
testUrls,
mockRepresenters,
mockStatusCapability,
mockQ,
mockLinker,
mockLog,
@ -118,6 +119,8 @@ define(
mockChangeTemplate = jasmine.createSpy('changeTemplate');
mockLog = jasmine.createSpyObj("$log", LOG_FUNCTIONS);
mockStatusCapability = jasmine.createSpyObj("statusCapability", ["listen"]);
mockScope = jasmine.createSpyObj("scope", [ "$watch", "$on" ]);
mockElement = jasmine.createSpyObj("element", JQLITE_FUNCTIONS);
mockDomainObject = jasmine.createSpyObj("domainObject", DOMAIN_OBJECT_METHODS);
@ -128,6 +131,10 @@ define(
return testUrls[ext.key];
});
mockDomainObject.getCapability.andCallFake(function (c) {
return c === 'status' && mockStatusCapability;
});
mctRepresentation = new MCTRepresentation(
testRepresentations,
testViews,
@ -229,7 +236,7 @@ define(
expect(mockLog.warn).toHaveBeenCalled();
});
it("clears out obsolete peroperties from scope", function () {
it("clears out obsolete properties from scope", function () {
mockScope.key = "def";
mockScope.domainObject = mockDomainObject;
mockDomainObject.useCapability.andReturn("some value");
@ -246,6 +253,21 @@ define(
expect(mockScope.testCapability).toBeUndefined();
});
it("registers a status change listener", function () {
mockScope.$watch.calls[2].args[1](mockDomainObject);
expect(mockStatusCapability.listen).toHaveBeenCalled();
});
it("unlistens for status change on scope destruction", function () {
var mockUnlistener = jasmine.createSpy("unlisten");
mockStatusCapability.listen.andReturn(mockUnlistener);
mockScope.$watch.calls[2].args[1](mockDomainObject);
expect(mockStatusCapability.listen).toHaveBeenCalled();
mockScope.$on.calls[1].args[1]();
expect(mockUnlistener).toHaveBeenCalled();
});
describe("when a domain object has been observed", function () {
var mockContext,
mockContext2,
@ -307,6 +329,7 @@ define(
mockScope.$watch.calls[0].args[1]();
expect(mockChangeTemplate.calls.length).toEqual(callCount);
});
});

View File

@ -120,8 +120,8 @@ define([
provider = this;
mutationTopic.listen(function (mutatedObject) {
var status = mutatedObject.getCapability('status');
if (!status || !status.get('editing')) {
var editor = mutatedObject.getCapability('editor');
if (!editor || !editor.inEditContext()) {
provider.index(
mutatedObject.getId(),
mutatedObject.getModel()