Resolved merge conflicts

This commit is contained in:
Henry 2016-05-12 16:14:31 -07:00
parent 5bf750c90c
commit 44f4a82fa1
37 changed files with 741 additions and 332 deletions

View File

@ -87,10 +87,9 @@ define(
newObject;
newModel.type = this.type.getKey();
newModel.location = parentObject.getId();
newObject = parentObject.useCapability('instantiation', newModel);
newObject.useCapability('mutation', function(model){
model.location = parentObject.getId();
});
editorCapability = newObject.getCapability("editor");
if (countEditableViews(newObject) > 0 && newObject.hasCapability('composition')) {
@ -101,7 +100,7 @@ define(
return newObject.useCapability("action").perform("save").then(function () {
return editorCapability.save();
}, function () {
return editorCapability.cancel()
return editorCapability.cancel();
});
}
};

View File

@ -41,9 +41,8 @@ define([
"./src/representers/EditRepresenter",
"./src/representers/EditToolbarRepresenter",
"./src/capabilities/EditorCapability",
"./src/capabilities/TransactionDecorator",
"./src/capabilities/TransactionCapabilityDecorator",
"./src/services/TransactionService",
"./src/services/DirtyModelCache",
"text!./res/templates/library.html",
"text!./res/templates/edit-object.html",
"text!./res/templates/edit-action-buttons.html",
@ -71,9 +70,8 @@ define([
EditRepresenter,
EditToolbarRepresenter,
EditorCapability,
TransactionDecorator,
TransactionCapabilityDecorator,
TransactionService,
DirtyModelCache,
libraryTemplate,
editObjectTemplate,
editActionButtonsTemplate,
@ -136,8 +134,7 @@ define([
"depends": [
"$location",
"navigationService",
"$log",
"$q"
"$log"
],
"description": "Edit this object.",
"category": "view-control",
@ -270,11 +267,10 @@ define([
{
"type": "decorator",
"provides": "capabilityService",
"implementation": TransactionDecorator,
"implementation": TransactionCapabilityDecorator,
"depends": [
"$q",
"transactionService",
"dirtyModelCache"
"transactionService"
]
},
{
@ -283,15 +279,7 @@ define([
"implementation": TransactionService,
"depends": [
"$q",
"dirtyModelCache"
]
},
{
"type": "provider",
"provides": "dirtyModelCache",
"implementation": DirtyModelCache,
"depends": [
"topic"
"$log"
]
}
],
@ -324,8 +312,7 @@ define([
"description": "Provides transactional editing capabilities",
"implementation": EditorCapability,
"depends": [
"transactionService",
"dirtyModelCache"
"transactionService"
]
}
],

View File

@ -46,10 +46,19 @@ define(
function returnToBrowse () {
var parent;
domainObject.getCapability("location").getOriginal().then(function (original) {
parent = original.getCapability("context").getParent();
parent.getCapability("action").perform("navigate");
});
//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");
});
}
}
return this.domainObject.getCapability("editor").cancel()
.then(returnToBrowse);
@ -64,7 +73,8 @@ define(
CancelAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject;
return domainObject !== undefined &&
domainObject.getCapability("status").get("editing");
domainObject.hasCapability('editor') &&
domainObject.getCapability('editor').isEditContextRoot();
};
return CancelAction;

View File

@ -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;
}
/**
@ -89,8 +88,11 @@ define(
var domainObject = (context || {}).domainObject,
type = domainObject && domainObject.getCapability('type');
// Only allow creatable types to be edited
return type && type.hasFeature('creation') && !domainObject.getCapability('status').get('editing');
// 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

@ -85,8 +85,9 @@ define(
SaveAction.appliesTo = function (context) {
var domainObject = (context || {}).domainObject;
return domainObject !== undefined &&
domainObject.getModel().persisted !== undefined &&
domainObject.getCapability("status").get("editing");
domainObject.hasCapability('editor') &&
domainObject.getCapability('editor').isEditContextRoot() &&
domainObject.getModel().persisted !== undefined;
};
return SaveAction;

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.getCapability("status").get("editing") &&
domainObject.hasCapability('editor') &&
domainObject.getCapability('editor').isEditContextRoot() &&
domainObject.getModel().persisted === undefined;
};

View File

@ -24,24 +24,42 @@ define(
[],
function () {
/**
* 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
*/
function EditorCapability(
transactionService,
dirtyModelCache,
domainObject
) {
this.transactionService = transactionService;
this.dirtyModelCache = dirtyModelCache;
this.domainObject = domainObject;
}
/**
* 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.edit = function () {
this.transactionService.startTransaction();
this.domainObject.getCapability('status').set('editing', true);
};
function isEditContextRoot (domainObject) {
return domainObject.getCapability('status').get('editing');
}
function isEditing (domainObject) {
return domainObject.getCapability('status').get('editing') ||
domainObject.hasCapability('context') && isEditing(domainObject.getCapability('context').getParent());
return isEditContextRoot(domainObject) ||
domainObject.hasCapability('context') &&
isEditing(domainObject.getCapability('context').getParent());
}
/**
@ -53,6 +71,20 @@ define(
return isEditing(this.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() {
@ -62,6 +94,11 @@ define(
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(){
@ -70,15 +107,14 @@ define(
});
};
/**
* @returns {boolean} true if there have been any domain model
* modifications since the last persist, false otherwise.
*/
EditorCapability.prototype.dirty = function () {
return this.dirtyModelCache.isDirty(this.domainObject);
return (this.domainObject.getModel().modified || 0) > (this.domainObject.getModel().persisted || 0);
};
EditorCapability.prototype.appliesTo = function(context) {
var domainObject = context.domainObject;
return domainObject && domainObject.getType().hasFeature("creation");
}
return EditorCapability;
}
);

View File

@ -26,23 +26,30 @@ define(
function (TransactionalPersistenceCapability) {
'use strict';
function TransactionDecorator(
/**
* Wraps the [PersistenceCapability]{@link PersistenceCapability} with
* transactional capabilities.
* @param $q
* @param transactionService
* @param capabilityService
* @see TransactionalPersistenceCapability
* @constructor
*/
function TransactionCapabilityDecorator(
$q,
transactionService,
dirtyModelCache,
capabilityService
) {
this.capabilityService = capabilityService;
this.transactionService = transactionService;
this.dirtyModelCache = dirtyModelCache;
this.$q = $q;
}
/**
* Decorate PersistenceCapability to ignore persistence calls when a
* Decorate PersistenceCapability to queue persistence calls when a
* transaction is in progress.
*/
TransactionDecorator.prototype.getCapabilities = function (model) {
TransactionCapabilityDecorator.prototype.getCapabilities = function (model) {
var self = this,
capabilities = this.capabilityService.getCapabilities(model),
persistenceCapability = capabilities.persistence;
@ -55,7 +62,6 @@ define(
return new TransactionalPersistenceCapability(
self.$q,
self.transactionService,
self.dirtyModelCache,
original,
domainObject
);
@ -63,6 +69,6 @@ define(
return capabilities;
};
return TransactionDecorator;
return TransactionCapabilityDecorator;
}
);

View File

@ -26,44 +26,51 @@ define(
function () {
'use strict';
/**
* 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,
dirtyModelCache,
persistenceCapability,
domainObject
) {
this.transactionService = transactionService;
this.dirtyModelCache = dirtyModelCache;
this.persistenceCapability = Object.create(persistenceCapability);
this.persistenceCapability = persistenceCapability;
this.domainObject = domainObject;
this.$q = $q;
}
/**
* 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 domainObject = this.domainObject,
dirtyModelCache = this.dirtyModelCache;
if (this.transactionService.isActive() && !this.transactionService.isCommitting()) {
dirtyModelCache.markDirty(domainObject);
//Using $q here because need to return something
// from which 'catch' can be chained
if (this.transactionService.isActive()) {
this.transactionService.addToTransaction(
this.persistenceCapability.persist.bind(this.persistenceCapability),
this.persistenceCapability.refresh.bind(this.persistenceCapability)
);
//Need to return a promise from this function
return this.$q.when(true);
} else {
return this.persistenceCapability.persist().then(function (result) {
dirtyModelCache.markClean(domainObject);
return result;
});
return this.persistenceCapability.persist();
}
};
TransactionalPersistenceCapability.prototype.refresh = function () {
var domainObject = this.domainObject,
dirtyModelCache = this.dirtyModelCache;
return this.persistenceCapability.refresh().then(function (result) {
dirtyModelCache.markClean(domainObject);
return result;
});
return this.persistenceCapability.refresh();
};
TransactionalPersistenceCapability.prototype.getSpace = function () {

View File

@ -73,7 +73,8 @@ define(
function isEditing(context) {
var domainObject = (context || {}).domainObject;
return domainObject
&& 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 navigationService
* @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) {
@ -51,7 +56,7 @@ define(
navigatedObject = this.navigationService.getNavigation(),
actionMetadata = action.getMetadata ? action.getMetadata() : {};
if (navigatedObject.getCapability("status").get("editing")) {
if (navigatedObject.hasCapability("editor") && navigatedObject.getCapability("editor").isEditContextRoot()) {
if (selectedObject.hasCapability("editor") && selectedObject.getCapability("editor").inEditContext()){
//Target is within the editing context
return this.editBlacklist.indexOf(actionMetadata.key) === -1;

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,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') && domainObject.getCapability('editor').inEditContext()) {
return !!selectedObject && selectedObject.hasCapability('editor') && selectedObject.getCapability('editor').inEditContext();
if (key === 'move' && isDomainObjectEditing) {
return !!selectedObject && selectedObject.hasCapability('editor') &&
selectedObject.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

@ -1,47 +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.
*****************************************************************************/
/*global define*/
define(
[],
function() {
function DirtyModelCache(topic) {
this.cache = {};
}
DirtyModelCache.prototype.get = function () {
return this.cache;
};
DirtyModelCache.prototype.isDirty = function (domainObject) {
return !!this.cache[domainObject.getId()];
};
DirtyModelCache.prototype.markDirty = function (domainObject) {
this.cache[domainObject.getId()] = domainObject;
};
DirtyModelCache.prototype.markClean = function (domainObject) {
delete this.cache[domainObject.getId()];
};
return DirtyModelCache;
});

View File

@ -25,64 +25,90 @@ define(
function() {
/**
* Implements an application-wide transaction state. Once a
* transaction is started, calls to PersistenceCapability.persist()
* transaction is started, calls to
* [PersistenceCapability.persist()]{@link PersistenceCapability#persist}
* will be deferred until a subsequent call to
* TransactionService.commit() is made.
* [TransactionService.commit]{@link TransactionService#commit} is made.
*
* @memberof platform/commonUI/edit/services
* @param $q
* @constructor
*/
function TransactionService($q, dirtyModelCache) {
function TransactionService($q, $log) {
this.$q = $q;
this.$log = $log;
this.transaction = false;
this.committing = false;
this.cache = dirtyModelCache;
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)
console.error("Transaction already in progress")
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;
};
TransactionService.prototype.isCommitting = function () {
return this.committing;
/**
* 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. Any failures will be reported via a promise
* rejection.
* @returns {*}
* 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;
cache = this.cache.get();
var self = this,
promises = [],
onCommit;
this.committing = true;
function keyToObject(key) {
return cache[key];
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;
function objectToPromise(object) {
return object.getCapability('persistence').persist();
}
return this.$q.all(
Object.keys(cache)
.map(keyToObject)
.map(objectToPromise))
.then(function () {
self.transaction = false;
this.committing = false;
}).catch(function() {
return this.committing = false;
});
self.onCommits = [];
self.onCancels = [];
});
};
/**
@ -95,23 +121,23 @@ define(
*/
TransactionService.prototype.cancel = function () {
var self = this,
cache = this.cache.get();
results = [],
onCancel;
function keyToObject(key) {
return cache[key];
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;
function objectToPromise(object) {
return self.$q.when(object.getModel().persisted && object.getCapability('persistence').refresh());
}
return this.$q.all(Object.keys(cache)
.map(keyToObject)
.map(objectToPromise))
.then(function () {
self.transaction = false;
this.committing = false;
});
self.onCommits = [];
self.onCancels = [];
});
};
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

@ -25,94 +25,150 @@ 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",
"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);
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 () {
model.modified = 0;
model.persisted = 0;
expect(capability.dirty()).toBe(false);
runs(function () {
expect(mockDomainObject.useCapability).not.toHaveBeenCalled();
expect(mockCache.markClean).not.toHaveBeenCalled();
expect(mockCache.saveAll).not.toHaveBeenCalled();
model.modified = 1;
expect(capability.dirty()).toBe(true);
});
});
});
}
);

View File

@ -0,0 +1,59 @@
/*****************************************************************************
* 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,waitsFor,runs,jasmine,xit,xdescribe*/
define(
[
"../../src/capabilities/TransactionalPersistenceCapability",
"../../src/capabilities/TransactionCapabilityDecorator"
],
function (TransactionalPersistenceCapability, TransactionCapabilityDecorator) {
"use strict";
describe("The transaction capability decorator", function () {
var mockQ,
mockTransactionService,
mockCapabilityService,
provider;
beforeEach(function() {
//mockQ = jasmine.createSpyObj("$q", []);
mockQ = {};
//mockTransactionService =
// jasmine.createSpyObj("transactionService", []);
mockTransactionService = {};
mockCapabilityService = jasmine.createSpyObj("capabilityService", ["getCapabilities"]);
mockCapabilityService.getCapabilities.andReturn({
persistence: function() {}
});
provider = new TransactionCapabilityDecorator(mockQ, mockTransactionService, mockCapabilityService);
});
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,waitsFor,runs,jasmine,xit,xdescribe*/
define(
[
"../../src/capabilities/TransactionalPersistenceCapability"
],
function (TransactionalPersistenceCapability) {
"use strict";
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"]
);
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 call is queued", function() {
mockTransactionService.isActive.andReturn(true);
capability.persist();
expect(mockTransactionService.addToTransaction).toHaveBeenCalled();
//Test that it was the persist call that was queued
mockTransactionService.addToTransaction.mostRecentCall.args[0]();
expect(mockPersistence.persist).toHaveBeenCalled();
});
it("if transaction is active, refresh call is queued as cancel" +
" function", function() {
mockTransactionService.isActive.andReturn(true);
capability.persist();
//Test that it was the persist call that was queued
mockTransactionService.addToTransaction.mostRecentCall.args[1]();
expect(mockPersistence.refresh).toHaveBeenCalled();
});
});
}
);

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,127 @@
/*****************************************************************************
* 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) {
"use strict";
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);
});
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

@ -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

@ -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

@ -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

@ -40,7 +40,7 @@ define(
}
// Check if we are in edit mode (also check parents)
function inEditMode(swimlane) {
function inEditMode() {
return swimlane.domainObject.hasCapability('editor') &&
swimlane.domainObject.getCapability('editor').inEditContext();
}
@ -174,7 +174,7 @@ define(
* @returns {boolean} true if this should be allowed
*/
allowDropIn: function (id, domainObject) {
return inEditMode(swimlane) &&
return inEditMode() &&
!pathContains(swimlane, id) &&
!contains(swimlane, id) &&
canDrop(swimlane.domainObject, domainObject);
@ -189,7 +189,7 @@ define(
allowDropAfter: function (id, domainObject) {
var target = expandedForDropInto() ?
swimlane : swimlane.parent;
return inEditMode(swimlane) &&
return inEditMode() &&
target &&
!pathContains(target, id) &&
canDrop(target.domainObject, domainObject);

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

@ -53,9 +53,7 @@ define(
* @param {ViewDefinition[]} views an array of view extensions
*/
function MCTRepresentation(representations, views, representers, $q, templateLinker, $log) {
var representationMap = {},
listeners = 0;
domainObjectListener;
var representationMap = {};
// Assemble all representations and views
// The distinction between views and representations is
@ -250,10 +248,10 @@ define(
}
/**
* Add a listener for status changes to the object itself.
* Add a listener to the object for status changes.
*/
$scope.$watch("domainObject", function(domainObject, oldDomainObject) {
if (domainObject!==oldDomainObject){
$scope.$watch("domainObject", function (domainObject, oldDomainObject) {
if (domainObject !== oldDomainObject){
listenForStatusChange(domainObject);
}
});

View File

@ -103,13 +103,18 @@ define(
// the change.
if (id) {
e.preventDefault();
if (domainObjectType!=='folder') {
domainObject.getCapability('action').perform('edit');
}
//Use scope.apply, drop event is outside digest cycle
// and if not applied here causes visual artifacts.
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,

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()