mirror of
https://github.com/nasa/openmct.git
synced 2025-03-28 06:38:40 +00:00
[Code Style] Use prototypes in Edit bundle
WTD-1482.
This commit is contained in:
parent
efc42aa8f2
commit
be5cad212a
platform
commonUI/edit
src
actions
CancelAction.jsEditAction.jsLinkAction.jsPropertiesAction.jsPropertiesDialog.jsRemoveAction.jsSaveAction.js
capabilities
EditableCompositionCapability.jsEditableContextCapability.jsEditablePersistenceCapability.jsEditableRelationshipCapability.jsEditorCapability.js
controllers
objects
policies
representers
test/objects
policy/src
@ -31,9 +31,24 @@ define(
|
|||||||
* capabilities to persist the changes that have been made.
|
* capabilities to persist the changes that have been made.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
|
* @implements {Action}
|
||||||
*/
|
*/
|
||||||
function CancelAction($location, urlService, context) {
|
function CancelAction($location, urlService, context) {
|
||||||
var domainObject = context.domainObject;
|
this.domainObject = context.domainObject;
|
||||||
|
this.$location = $location;
|
||||||
|
this.urlService = urlService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel editing.
|
||||||
|
*
|
||||||
|
* @returns {Promise} a promise that will be fulfilled when
|
||||||
|
* cancellation has completed
|
||||||
|
*/
|
||||||
|
CancelAction.prototype.perform = function () {
|
||||||
|
var domainObject = this.domainObject,
|
||||||
|
$location = this.$location,
|
||||||
|
urlService = this.urlService;
|
||||||
|
|
||||||
// Look up the object's "editor.completion" capability;
|
// Look up the object's "editor.completion" capability;
|
||||||
// this is introduced by EditableDomainObject which is
|
// this is introduced by EditableDomainObject which is
|
||||||
@ -58,26 +73,15 @@ define(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return doCancel(getEditorCapability())
|
||||||
/**
|
.then(returnToBrowse);
|
||||||
* Cancel editing.
|
};
|
||||||
*
|
|
||||||
* @returns {Promise} a promise that will be fulfilled when
|
|
||||||
* cancellation has completed
|
|
||||||
* @memberof platform/commonUI/edit.CancelAction#
|
|
||||||
*/
|
|
||||||
perform: function () {
|
|
||||||
return doCancel(getEditorCapability())
|
|
||||||
.then(returnToBrowse);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this action is applicable in a given context.
|
* Check if this action is applicable in a given context.
|
||||||
* This will ensure that a domain object is present in the context,
|
* This will ensure that a domain object is present in the context,
|
||||||
* and that this domain object is in Edit mode.
|
* and that this domain object is in Edit mode.
|
||||||
* @returns true if applicable
|
* @returns {boolean} true if applicable
|
||||||
*/
|
*/
|
||||||
CancelAction.appliesTo = function (context) {
|
CancelAction.appliesTo = function (context) {
|
||||||
var domainObject = (context || {}).domainObject;
|
var domainObject = (context || {}).domainObject;
|
||||||
|
@ -44,6 +44,7 @@ define(
|
|||||||
* route)
|
* route)
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @implements {Action}
|
||||||
*/
|
*/
|
||||||
function EditAction($location, navigationService, $log, context) {
|
function EditAction($location, navigationService, $log, context) {
|
||||||
var domainObject = (context || {}).domainObject;
|
var domainObject = (context || {}).domainObject;
|
||||||
@ -61,18 +62,19 @@ define(
|
|||||||
return NULL_ACTION;
|
return NULL_ACTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
this.domainObject = domainObject;
|
||||||
/**
|
this.$location = $location;
|
||||||
* Enter edit mode.
|
this.navigationService = navigationService;
|
||||||
* @memberof platform/commonUI/edit.EditAction#
|
|
||||||
*/
|
|
||||||
perform: function () {
|
|
||||||
navigationService.setNavigation(domainObject);
|
|
||||||
$location.path("/edit");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enter edit mode.
|
||||||
|
*/
|
||||||
|
EditAction.prototype.perform = function () {
|
||||||
|
this.navigationService.setNavigation(this.domainObject);
|
||||||
|
this.$location.path("/edit");
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for applicability; verify that a domain object is present
|
* Check for applicability; verify that a domain object is present
|
||||||
* for this action to be performed upon.
|
* for this action to be performed upon.
|
||||||
|
@ -31,42 +31,40 @@ define(
|
|||||||
* Add one domain object to another's composition.
|
* Add one domain object to another's composition.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
|
* @implements {Action}
|
||||||
*/
|
*/
|
||||||
function LinkAction(context) {
|
function LinkAction(context) {
|
||||||
var domainObject = (context || {}).domainObject,
|
this.domainObject = (context || {}).domainObject;
|
||||||
selectedObject = (context || {}).selectedObject,
|
this.selectedObject = (context || {}).selectedObject;
|
||||||
selectedId = selectedObject && selectedObject.getId();
|
this.selectedId = this.selectedObject && this.selectedObject.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkAction.prototype.perform = function () {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
// Add this domain object's identifier
|
// Add this domain object's identifier
|
||||||
function addId(model) {
|
function addId(model) {
|
||||||
if (Array.isArray(model.composition) &&
|
if (Array.isArray(model.composition) &&
|
||||||
model.composition.indexOf(selectedId) < 0) {
|
model.composition.indexOf(self.selectedId) < 0) {
|
||||||
model.composition.push(selectedId);
|
model.composition.push(self.selectedId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Persist changes to the domain object
|
// Persist changes to the domain object
|
||||||
function doPersist() {
|
function doPersist() {
|
||||||
var persistence = domainObject.getCapability('persistence');
|
var persistence =
|
||||||
|
self.domainObject.getCapability('persistence');
|
||||||
return persistence.persist();
|
return persistence.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Link these objects
|
// Link these objects
|
||||||
function doLink() {
|
function doLink() {
|
||||||
return domainObject.useCapability("mutation", addId)
|
return self.domainObject.useCapability("mutation", addId)
|
||||||
.then(doPersist);
|
.then(doPersist);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return this.selectedId && doLink();
|
||||||
/**
|
};
|
||||||
* Perform this action.
|
|
||||||
* @memberof platform/commonUI/edit.LinkAction#
|
|
||||||
*/
|
|
||||||
perform: function () {
|
|
||||||
return selectedId && doLink();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return LinkAction;
|
return LinkAction;
|
||||||
}
|
}
|
||||||
|
@ -32,60 +32,58 @@ define(
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an action which will allow an object's metadata to be
|
* Implements the "Edit Properties" action, which prompts the user
|
||||||
* edited.
|
* to modify a domain object's properties.
|
||||||
*
|
*
|
||||||
* @param {DialogService} dialogService a service which will show the dialog
|
* @param {DialogService} dialogService a service which will show the dialog
|
||||||
* @param {DomainObject} object the object to be edited
|
* @param {DomainObject} object the object to be edited
|
||||||
* @param {ActionContext} context the context in which this action is performed
|
* @param {ActionContext} context the context in which this action is performed
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
|
* @implements {Action}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function PropertiesAction(dialogService, context) {
|
function PropertiesAction(dialogService, context) {
|
||||||
var object = context.domainObject;
|
this.domainObject = (context || {}).domainObject;
|
||||||
|
this.dialogService = dialogService;
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertiesAction.prototype.perform = function () {
|
||||||
|
var type = this.domainObject.getCapability('type'),
|
||||||
|
domainObject = this.domainObject,
|
||||||
|
dialogService = this.dialogService;
|
||||||
|
|
||||||
// Persist modifications to this domain object
|
// Persist modifications to this domain object
|
||||||
function doPersist() {
|
function doPersist() {
|
||||||
var persistence = object.getCapability('persistence');
|
var persistence = domainObject.getCapability('persistence');
|
||||||
return persistence && persistence.persist();
|
return persistence && persistence.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the domain object model based on user input
|
// Update the domain object model based on user input
|
||||||
function updateModel(userInput, dialog) {
|
function updateModel(userInput, dialog) {
|
||||||
return object.useCapability('mutation', function (model) {
|
return domainObject.useCapability('mutation', function (model) {
|
||||||
dialog.updateModel(model, userInput);
|
dialog.updateModel(model, userInput);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showDialog(type) {
|
function showDialog(type) {
|
||||||
// Create a dialog object to generate the form structure, etc.
|
// Create a dialog object to generate the form structure, etc.
|
||||||
var dialog = new PropertiesDialog(type, object.getModel());
|
var dialog =
|
||||||
|
new PropertiesDialog(type, domainObject.getModel());
|
||||||
|
|
||||||
// Show the dialog
|
// Show the dialog
|
||||||
return dialogService.getUserInput(
|
return dialogService.getUserInput(
|
||||||
dialog.getFormStructure(),
|
dialog.getFormStructure(),
|
||||||
dialog.getInitialFormValue()
|
dialog.getInitialFormValue()
|
||||||
).then(function (userInput) {
|
).then(function (userInput) {
|
||||||
// Update the model, if user input was provided
|
// Update the model, if user input was provided
|
||||||
return userInput && updateModel(userInput, dialog);
|
return userInput && updateModel(userInput, dialog);
|
||||||
}).then(function (result) {
|
}).then(function (result) {
|
||||||
return result && doPersist();
|
return result && doPersist();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return type && showDialog(type);
|
||||||
/**
|
};
|
||||||
* Perform this action.
|
|
||||||
* @return {Promise} a promise which will be
|
|
||||||
* fulfilled when the action has completed.
|
|
||||||
* @memberof platform/commonUI/edit.PropertiesAction#
|
|
||||||
*/
|
|
||||||
perform: function () {
|
|
||||||
var type = object.getCapability('type');
|
|
||||||
return type && showDialog(type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter this action for applicability against a given context.
|
* Filter this action for applicability against a given context.
|
||||||
|
@ -35,57 +35,56 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function PropertiesDialog(type, model) {
|
function PropertiesDialog(type, model) {
|
||||||
var properties = type.getProperties();
|
this.type = type;
|
||||||
|
this.model = model;
|
||||||
return {
|
this.properties = type.getProperties();
|
||||||
/**
|
|
||||||
* Get sections provided by this dialog.
|
|
||||||
* @return {FormStructure} the structure of this form
|
|
||||||
* @memberof platform/commonUI/edit.PropertiesDialog#
|
|
||||||
*/
|
|
||||||
getFormStructure: function () {
|
|
||||||
return {
|
|
||||||
name: "Edit " + model.name,
|
|
||||||
sections: [{
|
|
||||||
name: "Properties",
|
|
||||||
rows: properties.map(function (property, index) {
|
|
||||||
// Property definition is same as form row definition
|
|
||||||
var row = Object.create(property.getDefinition());
|
|
||||||
row.key = index;
|
|
||||||
return row;
|
|
||||||
})
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Get the initial state of the form shown by this dialog
|
|
||||||
* (based on the object model)
|
|
||||||
* @returns {object} initial state of the form
|
|
||||||
* @memberof platform/commonUI/edit.PropertiesDialog#
|
|
||||||
*/
|
|
||||||
getInitialFormValue: function () {
|
|
||||||
// Start with initial values for properties
|
|
||||||
// Note that index needs to correlate to row.key
|
|
||||||
// from getFormStructure
|
|
||||||
return properties.map(function (property) {
|
|
||||||
return property.getValue(model);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Update a domain object model based on the value of a form.
|
|
||||||
* @memberof platform/commonUI/edit.PropertiesDialog#
|
|
||||||
*/
|
|
||||||
updateModel: function (model, formValue) {
|
|
||||||
// Update all properties
|
|
||||||
properties.forEach(function (property, index) {
|
|
||||||
property.setValue(model, formValue[index]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get sections provided by this dialog.
|
||||||
|
* @return {FormStructure} the structure of this form
|
||||||
|
*/
|
||||||
|
PropertiesDialog.prototype.getFormStructure = function () {
|
||||||
|
return {
|
||||||
|
name: "Edit " + this.model.name,
|
||||||
|
sections: [{
|
||||||
|
name: "Properties",
|
||||||
|
rows: this.properties.map(function (property, index) {
|
||||||
|
// Property definition is same as form row definition
|
||||||
|
var row = Object.create(property.getDefinition());
|
||||||
|
row.key = index;
|
||||||
|
return row;
|
||||||
|
})
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the initial state of the form shown by this dialog
|
||||||
|
* (based on the object model)
|
||||||
|
* @returns {object} initial state of the form
|
||||||
|
*/
|
||||||
|
PropertiesDialog.prototype.getInitialFormValue = function () {
|
||||||
|
var model = this.model;
|
||||||
|
|
||||||
|
// Start with initial values for properties
|
||||||
|
// Note that index needs to correlate to row.key
|
||||||
|
// from getFormStructure
|
||||||
|
return this.properties.map(function (property) {
|
||||||
|
return property.getValue(model);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a domain object model based on the value of a form.
|
||||||
|
*/
|
||||||
|
PropertiesDialog.prototype.updateModel = function (model, formValue) {
|
||||||
|
// Update all properties
|
||||||
|
this.properties.forEach(function (property, index) {
|
||||||
|
property.setValue(model, formValue[index]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return PropertiesDialog;
|
return PropertiesDialog;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -39,68 +39,64 @@ define(
|
|||||||
* @param {ActionContext} context the context in which this action is performed
|
* @param {ActionContext} context the context in which this action is performed
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @implements {Action}
|
||||||
*/
|
*/
|
||||||
function RemoveAction($q, context) {
|
function RemoveAction($q, context) {
|
||||||
var object = (context || {}).domainObject;
|
this.domainObject = (context || {}).domainObject;
|
||||||
|
this.$q = $q;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Perform this action.
|
||||||
|
* @return {Promise} a promise which will be
|
||||||
|
* fulfilled when the action has completed.
|
||||||
|
*/
|
||||||
|
RemoveAction.prototype.perform = function () {
|
||||||
|
var $q = this.$q,
|
||||||
|
domainObject = this.domainObject;
|
||||||
|
|
||||||
|
/*
|
||||||
* Check whether an object ID matches the ID of the object being
|
* Check whether an object ID matches the ID of the object being
|
||||||
* removed (used to filter a parent's composition to handle the
|
* removed (used to filter a parent's composition to handle the
|
||||||
* removal.)
|
* removal.)
|
||||||
* @memberof platform/commonUI/edit.RemoveAction#
|
|
||||||
*/
|
*/
|
||||||
function isNotObject(otherObjectId) {
|
function isNotObject(otherObjectId) {
|
||||||
return otherObjectId !== object.getId();
|
return otherObjectId !== domainObject.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Mutate a parent object such that it no longer contains the object
|
* Mutate a parent object such that it no longer contains the object
|
||||||
* which is being removed.
|
* which is being removed.
|
||||||
* @memberof platform/commonUI/edit.RemoveAction#
|
|
||||||
*/
|
*/
|
||||||
function doMutate(model) {
|
function doMutate(model) {
|
||||||
model.composition = model.composition.filter(isNotObject);
|
model.composition = model.composition.filter(isNotObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Invoke persistence on a domain object. This will be called upon
|
* Invoke persistence on a domain object. This will be called upon
|
||||||
* the removed object's parent (as its composition will have changed.)
|
* the removed object's parent (as its composition will have changed.)
|
||||||
* @memberof platform/commonUI/edit.RemoveAction#
|
|
||||||
*/
|
*/
|
||||||
function doPersist(domainObject) {
|
function doPersist(domainObject) {
|
||||||
var persistence = domainObject.getCapability('persistence');
|
var persistence = domainObject.getCapability('persistence');
|
||||||
return persistence && persistence.persist();
|
return persistence && persistence.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Remove the object from its parent, as identified by its context
|
* Remove the object from its parent, as identified by its context
|
||||||
* capability.
|
* capability.
|
||||||
* @param {ContextCapability} contextCapability the "context" capability
|
|
||||||
* of the domain object being removed.
|
|
||||||
* @memberof platform/commonUI/edit.RemoveAction#
|
|
||||||
*/
|
*/
|
||||||
function removeFromContext(contextCapability) {
|
function removeFromContext(contextCapability) {
|
||||||
var parent = contextCapability.getParent();
|
var parent = contextCapability.getParent();
|
||||||
$q.when(
|
return $q.when(
|
||||||
parent.useCapability('mutation', doMutate)
|
parent.useCapability('mutation', doMutate)
|
||||||
).then(function () {
|
).then(function () {
|
||||||
return doPersist(parent);
|
return doPersist(parent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return $q.when(this.domainObject.getCapability('context'))
|
||||||
/**
|
.then(removeFromContext);
|
||||||
* Perform this action.
|
};
|
||||||
* @return {module:core/promises.Promise} a promise which will be
|
|
||||||
* fulfilled when the action has completed.
|
|
||||||
* @memberof platform/commonUI/edit.RemoveAction#
|
|
||||||
*/
|
|
||||||
perform: function () {
|
|
||||||
return $q.when(object.getCapability('context'))
|
|
||||||
.then(removeFromContext);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Object needs to have a parent for Remove to be applicable
|
// Object needs to have a parent for Remove to be applicable
|
||||||
RemoveAction.appliesTo = function (context) {
|
RemoveAction.appliesTo = function (context) {
|
||||||
|
@ -31,10 +31,26 @@ define(
|
|||||||
* Edit Mode. Exits the editing user interface and invokes object
|
* Edit Mode. Exits the editing user interface and invokes object
|
||||||
* capabilities to persist the changes that have been made.
|
* capabilities to persist the changes that have been made.
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @implements {Action}
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
*/
|
*/
|
||||||
function SaveAction($location, urlService, context) {
|
function SaveAction($location, urlService, context) {
|
||||||
var domainObject = context.domainObject;
|
this.domainObject = (context || {}).domainObject;
|
||||||
|
this.$location = $location;
|
||||||
|
this.urlService = urlService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save changes and conclude editing.
|
||||||
|
*
|
||||||
|
* @returns {Promise} a promise that will be fulfilled when
|
||||||
|
* cancellation has completed
|
||||||
|
* @memberof platform/commonUI/edit.SaveAction#
|
||||||
|
*/
|
||||||
|
SaveAction.prototype.perform = function () {
|
||||||
|
var domainObject = this.domainObject,
|
||||||
|
$location = this.$location,
|
||||||
|
urlService = this.urlService;
|
||||||
|
|
||||||
// Invoke any save behavior introduced by the editor capability;
|
// Invoke any save behavior introduced by the editor capability;
|
||||||
// this is introduced by EditableDomainObject which is
|
// this is introduced by EditableDomainObject which is
|
||||||
@ -53,19 +69,8 @@ define(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return doSave().then(returnToBrowse);
|
||||||
/**
|
};
|
||||||
* Save changes and conclude editing.
|
|
||||||
*
|
|
||||||
* @returns {Promise} a promise that will be fulfilled when
|
|
||||||
* cancellation has completed
|
|
||||||
* @memberof platform/commonUI/edit.SaveAction#
|
|
||||||
*/
|
|
||||||
perform: function () {
|
|
||||||
return doSave().then(returnToBrowse);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this action is applicable in a given context.
|
* Check if this action is applicable in a given context.
|
||||||
|
@ -37,6 +37,7 @@ define(
|
|||||||
* to a pattern used there and may contain unused arguments.
|
* to a pattern used there and may contain unused arguments.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
|
* @implements {CompositionCapability}
|
||||||
*/
|
*/
|
||||||
return function EditableCompositionCapability(
|
return function EditableCompositionCapability(
|
||||||
contextCapability,
|
contextCapability,
|
||||||
|
@ -37,6 +37,7 @@ define(
|
|||||||
* to a pattern used there and may contain unused arguments.
|
* to a pattern used there and may contain unused arguments.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
|
* @implements {ContextCapability}
|
||||||
*/
|
*/
|
||||||
return function EditableContextCapability(
|
return function EditableContextCapability(
|
||||||
contextCapability,
|
contextCapability,
|
||||||
|
@ -37,6 +37,7 @@ define(
|
|||||||
* to a pattern used there and may contain unused arguments.
|
* to a pattern used there and may contain unused arguments.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
|
* @implements {PersistenceCapability}
|
||||||
*/
|
*/
|
||||||
function EditablePersistenceCapability(
|
function EditablePersistenceCapability(
|
||||||
persistenceCapability,
|
persistenceCapability,
|
||||||
|
@ -37,6 +37,7 @@ define(
|
|||||||
* to a pattern used there and may contain unused arguments.
|
* to a pattern used there and may contain unused arguments.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
|
* @implements {RelationshipCapability}
|
||||||
*/
|
*/
|
||||||
return function EditableRelationshipCapability(
|
return function EditableRelationshipCapability(
|
||||||
relationshipCapability,
|
relationshipCapability,
|
||||||
|
@ -42,26 +42,45 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
*/
|
*/
|
||||||
return function EditorCapability(
|
function EditorCapability(
|
||||||
persistenceCapability,
|
persistenceCapability,
|
||||||
editableObject,
|
editableObject,
|
||||||
domainObject,
|
domainObject,
|
||||||
cache
|
cache
|
||||||
) {
|
) {
|
||||||
|
this.editableObject = editableObject;
|
||||||
|
this.domainObject = domainObject;
|
||||||
|
this.cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
// Simulate Promise.resolve (or $q.when); the former
|
// Simulate Promise.resolve (or $q.when); the former
|
||||||
// causes a delayed reaction from Angular (since it
|
// causes a delayed reaction from Angular (since it
|
||||||
// does not trigger a digest) and the latter is not
|
// does not trigger a digest) and the latter is not
|
||||||
// readily accessible, since we're a few classes
|
// readily accessible, since we're a few classes
|
||||||
// removed from the layer which gets dependency
|
// removed from the layer which gets dependency
|
||||||
// injection.
|
// injection.
|
||||||
function resolvePromise(value) {
|
function resolvePromise(value) {
|
||||||
return (value && value.then) ? value : {
|
return (value && value.then) ? value : {
|
||||||
then: function (callback) {
|
then: function (callback) {
|
||||||
return resolvePromise(callback(value));
|
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#
|
||||||
|
*/
|
||||||
|
EditorCapability.prototype.save = function (nonrecursive) {
|
||||||
|
var domainObject = this.domainObject,
|
||||||
|
editableObject = this.editableObject,
|
||||||
|
cache = this.cache;
|
||||||
|
|
||||||
// Update the underlying, "real" domain object's model
|
// Update the underlying, "real" domain object's model
|
||||||
// with changes made to the copy used for editing.
|
// with changes made to the copy used for editing.
|
||||||
@ -76,42 +95,32 @@ define(
|
|||||||
return domainObject.getCapability('persistence').persist();
|
return domainObject.getCapability('persistence').persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return nonrecursive ?
|
||||||
/**
|
resolvePromise(doMutate()).then(doPersist) :
|
||||||
* Save any changes that have been made to this domain object
|
resolvePromise(cache.saveAll());
|
||||||
* (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#
|
|
||||||
*/
|
|
||||||
save: function (nonrecursive) {
|
|
||||||
return nonrecursive ?
|
|
||||||
resolvePromise(doMutate()).then(doPersist) :
|
|
||||||
resolvePromise(cache.saveAll());
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 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#
|
|
||||||
*/
|
|
||||||
cancel: function () {
|
|
||||||
return resolvePromise(undefined);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Check if there are any unsaved changes.
|
|
||||||
* @returns {boolean} true if there are unsaved changes
|
|
||||||
* @memberof platform/commonUI/edit.EditorCapability#
|
|
||||||
*/
|
|
||||||
dirty: function () {
|
|
||||||
return cache.dirty();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 () {
|
||||||
|
return resolvePromise(undefined);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there are any unsaved changes.
|
||||||
|
* @returns {boolean} true if there are unsaved changes
|
||||||
|
* @memberof platform/commonUI/edit.EditorCapability#
|
||||||
|
*/
|
||||||
|
EditorCapability.prototype.dirty = function () {
|
||||||
|
return cache.dirty();
|
||||||
|
};
|
||||||
|
|
||||||
|
return EditorCapability;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -38,12 +38,12 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EditController($scope, $q, navigationService) {
|
function EditController($scope, $q, navigationService) {
|
||||||
var navigatedObject;
|
var self = this;
|
||||||
|
|
||||||
function setNavigation(domainObject) {
|
function setNavigation(domainObject) {
|
||||||
// Wrap the domain object such that all mutation is
|
// Wrap the domain object such that all mutation is
|
||||||
// confined to edit mode (until Save)
|
// confined to edit mode (until Save)
|
||||||
navigatedObject =
|
self.navigatedDomainObject =
|
||||||
domainObject && new EditableDomainObject(domainObject, $q);
|
domainObject && new EditableDomainObject(domainObject, $q);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,35 +52,33 @@ define(
|
|||||||
$scope.$on("$destroy", function () {
|
$scope.$on("$destroy", function () {
|
||||||
navigationService.removeListener(setNavigation);
|
navigationService.removeListener(setNavigation);
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Get the domain object which is navigated-to.
|
|
||||||
* @returns {DomainObject} the domain object that is navigated-to
|
|
||||||
* @memberof platform/commonUI/edit.EditController#
|
|
||||||
*/
|
|
||||||
navigatedObject: function () {
|
|
||||||
return navigatedObject;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Get the warning to show if the user attempts to navigate
|
|
||||||
* away from Edit mode while unsaved changes are present.
|
|
||||||
* @returns {string} the warning to show, or undefined if
|
|
||||||
* there are no unsaved changes
|
|
||||||
* @memberof platform/commonUI/edit.EditController#
|
|
||||||
*/
|
|
||||||
getUnloadWarning: function () {
|
|
||||||
var editorCapability = navigatedObject &&
|
|
||||||
navigatedObject.getCapability("editor"),
|
|
||||||
hasChanges = editorCapability && editorCapability.dirty();
|
|
||||||
|
|
||||||
return hasChanges ?
|
|
||||||
"Unsaved changes will be lost if you leave this page." :
|
|
||||||
undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the domain object which is navigated-to.
|
||||||
|
* @returns {DomainObject} the domain object that is navigated-to
|
||||||
|
*/
|
||||||
|
EditController.prototype.navigatedObject = function () {
|
||||||
|
return this.navigatedDomainObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the warning to show if the user attempts to navigate
|
||||||
|
* away from Edit mode while unsaved changes are present.
|
||||||
|
* @returns {string} the warning to show, or undefined if
|
||||||
|
* there are no unsaved changes
|
||||||
|
*/
|
||||||
|
EditController.prototype.getUnloadWarning = function () {
|
||||||
|
var navigatedObject = this.navigatedDomainObject,
|
||||||
|
editorCapability = navigatedObject &&
|
||||||
|
navigatedObject.getCapability("editor"),
|
||||||
|
hasChanges = editorCapability && editorCapability.dirty();
|
||||||
|
|
||||||
|
return hasChanges ?
|
||||||
|
"Unsaved changes will be lost if you leave this page." :
|
||||||
|
undefined;
|
||||||
|
};
|
||||||
|
|
||||||
return EditController;
|
return EditController;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -32,12 +32,13 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EditPanesController($scope) {
|
function EditPanesController($scope) {
|
||||||
var root;
|
var self = this;
|
||||||
|
|
||||||
// Update root object based on represented object
|
// Update root object based on represented object
|
||||||
function updateRoot(domainObject) {
|
function updateRoot(domainObject) {
|
||||||
var context = domainObject &&
|
var root = self.rootDomainObject,
|
||||||
domainObject.getCapability('context'),
|
context = domainObject &&
|
||||||
|
domainObject.getCapability('context'),
|
||||||
newRoot = context && context.getTrueRoot(),
|
newRoot = context && context.getTrueRoot(),
|
||||||
oldId = root && root.getId(),
|
oldId = root && root.getId(),
|
||||||
newId = newRoot && newRoot.getId();
|
newId = newRoot && newRoot.getId();
|
||||||
@ -45,25 +46,21 @@ define(
|
|||||||
// Only update if this has actually changed,
|
// Only update if this has actually changed,
|
||||||
// to avoid excessive refreshing.
|
// to avoid excessive refreshing.
|
||||||
if (oldId !== newId) {
|
if (oldId !== newId) {
|
||||||
root = newRoot;
|
self.rootDomainObject = newRoot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update root when represented object changes
|
// Update root when represented object changes
|
||||||
$scope.$watch('domainObject', updateRoot);
|
$scope.$watch('domainObject', updateRoot);
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Get the root-level domain object, as reported by the
|
|
||||||
* represented domain object.
|
|
||||||
* @returns {DomainObject} the root object
|
|
||||||
* @memberof platform/commonUI/edit.EditPanesController#
|
|
||||||
*/
|
|
||||||
getRoot: function () {
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Get the root-level domain object, as reported by the
|
||||||
|
* represented domain object.
|
||||||
|
* @returns {DomainObject} the root object
|
||||||
|
*/
|
||||||
|
EditPanesController.prototype.getRoot = function () {
|
||||||
|
return this.rootDomainObject;
|
||||||
|
};
|
||||||
|
|
||||||
return EditPanesController;
|
return EditPanesController;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ define(
|
|||||||
* model to allow changes to be easily cancelled.
|
* model to allow changes to be easily cancelled.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
|
* @implements {DomainObject}
|
||||||
*/
|
*/
|
||||||
function EditableDomainObject(domainObject, $q) {
|
function EditableDomainObject(domainObject, $q) {
|
||||||
// The cache will hold all domain objects reached from
|
// The cache will hold all domain objects reached from
|
||||||
@ -94,10 +95,10 @@ define(
|
|||||||
this,
|
this,
|
||||||
delegateArguments
|
delegateArguments
|
||||||
),
|
),
|
||||||
factory = capabilityFactories[name];
|
Factory = capabilityFactories[name];
|
||||||
|
|
||||||
return (factory && capability) ?
|
return (Factory && capability) ?
|
||||||
factory(capability, editableObject, domainObject, cache) :
|
new Factory(capability, editableObject, domainObject, cache) :
|
||||||
capability;
|
capability;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ define(
|
|||||||
* of objects retrieved via composition or context capabilities as
|
* of objects retrieved via composition or context capabilities as
|
||||||
* editable domain objects.
|
* editable domain objects.
|
||||||
*
|
*
|
||||||
* @param {Constructor<EditableDomainObject>} EditableDomainObject a
|
* @param {Constructor<DomainObject>} EditableDomainObject a
|
||||||
* constructor function which takes a regular domain object as
|
* constructor function which takes a regular domain object as
|
||||||
* an argument, and returns an editable domain object as its
|
* an argument, and returns an editable domain object as its
|
||||||
* result.
|
* result.
|
||||||
@ -53,104 +53,108 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EditableDomainObjectCache(EditableDomainObject, $q) {
|
function EditableDomainObjectCache(EditableDomainObject, $q) {
|
||||||
var cache = new EditableModelCache(),
|
this.cache = new EditableModelCache();
|
||||||
dirty = {},
|
this.dirtyObjects = {};
|
||||||
root;
|
this.root = undefined;
|
||||||
|
this.$q = $q;
|
||||||
return {
|
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
|
|
||||||
* @memberof platform/commonUI/edit.EditableDomainObjectCache#
|
|
||||||
*/
|
|
||||||
getEditableObject: function (domainObject) {
|
|
||||||
var type = domainObject.getCapability('type');
|
|
||||||
|
|
||||||
// Track the top-level domain object; this will have
|
|
||||||
// some special behavior for its context capability.
|
|
||||||
root = 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
|
|
||||||
return new EditableDomainObject(
|
|
||||||
domainObject,
|
|
||||||
cache.getCachedModel(domainObject)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Check if a domain object is (effectively) the top-level
|
|
||||||
* object in this editable subgraph.
|
|
||||||
* @returns {boolean} true if it is the root
|
|
||||||
* @memberof platform/commonUI/edit.EditableDomainObjectCache#
|
|
||||||
*/
|
|
||||||
isRoot: function (domainObject) {
|
|
||||||
return domainObject === 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#
|
|
||||||
*/
|
|
||||||
markDirty: function (domainObject) {
|
|
||||||
dirty[domainObject.getId()] = domainObject;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* @memberof platform/commonUI/edit.EditableDomainObjectCache#
|
|
||||||
*/
|
|
||||||
markClean: function (domainObject) {
|
|
||||||
delete dirty[domainObject.getId()];
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Initiate a save on all objects that have been cached.
|
|
||||||
* @memberof platform/commonUI/edit.EditableDomainObjectCache#
|
|
||||||
*/
|
|
||||||
saveAll: function () {
|
|
||||||
// Get a list of all dirty objects
|
|
||||||
var objects = Object.keys(dirty).map(function (k) {
|
|
||||||
return dirty[k];
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear dirty set, since we're about to save.
|
|
||||||
dirty = {};
|
|
||||||
|
|
||||||
// Most save logic is handled by the "editor.completion"
|
|
||||||
// capability, so that is delegated here.
|
|
||||||
return $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
|
|
||||||
* @memberof platform/commonUI/edit.EditableDomainObjectCache#
|
|
||||||
*/
|
|
||||||
dirty: function () {
|
|
||||||
return Object.keys(dirty).length > 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
return new EditableDomainObject(
|
||||||
|
domainObject,
|
||||||
|
this.cache.getCachedModel(domainObject)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
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;
|
return EditableDomainObjectCache;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -35,31 +35,28 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EditableModelCache() {
|
function EditableModelCache() {
|
||||||
var cache = {};
|
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* @memberof platform/commonUI/edit.EditableModelCache#
|
|
||||||
*/
|
|
||||||
getCachedModel: function (domainObject) {
|
|
||||||
var id = domainObject.getId();
|
|
||||||
|
|
||||||
return (cache[id] =
|
|
||||||
cache[id] || clone(domainObject.getModel()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
return EditableModelCache;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -32,52 +32,44 @@ define(
|
|||||||
* (shown as buttons in the top-right of browse mode.)
|
* (shown as buttons in the top-right of browse mode.)
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @implements {Policy.<Action, ActionContext>}
|
||||||
*/
|
*/
|
||||||
function EditActionPolicy() {
|
function EditActionPolicy() {
|
||||||
// Get a count of views which are not flagged as non-editable.
|
}
|
||||||
function countEditableViews(context) {
|
|
||||||
var domainObject = (context || {}).domainObject,
|
|
||||||
views = domainObject && domainObject.useCapability('view'),
|
|
||||||
count = 0;
|
|
||||||
|
|
||||||
// A view is editable unless explicitly flagged as not
|
// Get a count of views which are not flagged as non-editable.
|
||||||
(views || []).forEach(function (view) {
|
function countEditableViews(context) {
|
||||||
count += (view.editable !== false) ? 1 : 0;
|
var domainObject = (context || {}).domainObject,
|
||||||
});
|
views = domainObject && domainObject.useCapability('view'),
|
||||||
|
count = 0;
|
||||||
|
|
||||||
return count;
|
// A view is editable unless explicitly flagged as not
|
||||||
|
(views || []).forEach(function (view) {
|
||||||
|
count += (view.editable !== false) ? 1 : 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditActionPolicy.prototype.allow = function (action, context) {
|
||||||
|
var key = action.getMetadata().key,
|
||||||
|
category = (context || {}).category;
|
||||||
|
|
||||||
|
// Only worry about actions in the view-control category
|
||||||
|
if (category === 'view-control') {
|
||||||
|
// Restrict 'edit' to cases where there are editable
|
||||||
|
// views (similarly, restrict 'properties' to when
|
||||||
|
// the converse is true)
|
||||||
|
if (key === 'edit') {
|
||||||
|
return countEditableViews(context) > 0;
|
||||||
|
} else if (key === 'properties') {
|
||||||
|
return countEditableViews(context) < 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
// Like all policies, allow by default.
|
||||||
/**
|
return true;
|
||||||
* Check whether or not a given action is allowed by this
|
};
|
||||||
* policy.
|
|
||||||
* @param {Action} action the action
|
|
||||||
* @param context the context
|
|
||||||
* @returns {boolean} true if not disallowed
|
|
||||||
* @memberof platform/commonUI/edit.EditActionPolicy#
|
|
||||||
*/
|
|
||||||
allow: function (action, context) {
|
|
||||||
var key = action.getMetadata().key,
|
|
||||||
category = (context || {}).category;
|
|
||||||
|
|
||||||
// Only worry about actions in the view-control category
|
|
||||||
if (category === 'view-control') {
|
|
||||||
// Restrict 'edit' to cases where there are editable
|
|
||||||
// views (similarly, restrict 'properties' to when
|
|
||||||
// the converse is true)
|
|
||||||
if (key === 'edit') {
|
|
||||||
return countEditableViews(context) > 0;
|
|
||||||
} else if (key === 'properties') {
|
|
||||||
return countEditableViews(context) < 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Like all policies, allow by default.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return EditActionPolicy;
|
return EditActionPolicy;
|
||||||
}
|
}
|
||||||
|
@ -30,30 +30,22 @@ define(
|
|||||||
* Policy controlling which views should be visible in Edit mode.
|
* Policy controlling which views should be visible in Edit mode.
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @implements {Policy.<View, DomainObject>}
|
||||||
*/
|
*/
|
||||||
function EditableViewPolicy() {
|
function EditableViewPolicy() {
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Check whether or not a given action is allowed by this
|
|
||||||
* policy.
|
|
||||||
* @param {Action} action the action
|
|
||||||
* @param domainObject the domain object which will be viewed
|
|
||||||
* @returns {boolean} true if not disallowed
|
|
||||||
* @memberof platform/commonUI/edit.EditableViewPolicy#
|
|
||||||
*/
|
|
||||||
allow: function (view, domainObject) {
|
|
||||||
// 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');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Like all policies, allow by default.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EditableViewPolicy.prototype.allow = function (view, domainObject) {
|
||||||
|
// 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');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Like all policies, allow by default.
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
return EditableViewPolicy;
|
return EditableViewPolicy;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -42,14 +42,16 @@ define(
|
|||||||
* representations resulting from changes there.
|
* representations resulting from changes there.
|
||||||
*
|
*
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
|
* @implements {Representer}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EditRepresenter($q, $log, scope) {
|
function EditRepresenter($q, $log, scope) {
|
||||||
var domainObject,
|
var self = this;
|
||||||
key;
|
|
||||||
|
|
||||||
// Mutate and persist a new version of a domain object's model.
|
// Mutate and persist a new version of a domain object's model.
|
||||||
function doPersist(model) {
|
function doPersist(model) {
|
||||||
|
var domainObject = self.domainObject;
|
||||||
|
|
||||||
// First, mutate; then, persist.
|
// First, mutate; then, persist.
|
||||||
return $q.when(domainObject.useCapability("mutation", function () {
|
return $q.when(domainObject.useCapability("mutation", function () {
|
||||||
return model;
|
return model;
|
||||||
@ -65,7 +67,8 @@ define(
|
|||||||
// Look up from scope; these will have been populated by
|
// Look up from scope; these will have been populated by
|
||||||
// mct-representation.
|
// mct-representation.
|
||||||
var model = scope.model,
|
var model = scope.model,
|
||||||
configuration = scope.configuration;
|
configuration = scope.configuration,
|
||||||
|
domainObject = self.domainObject;
|
||||||
|
|
||||||
// Log the commit message
|
// Log the commit message
|
||||||
$log.debug([
|
$log.debug([
|
||||||
@ -79,52 +82,33 @@ define(
|
|||||||
if (domainObject && domainObject.hasCapability("persistence")) {
|
if (domainObject && domainObject.hasCapability("persistence")) {
|
||||||
// Configurations for specific views are stored by
|
// Configurations for specific views are stored by
|
||||||
// key in the "configuration" field of the model.
|
// key in the "configuration" field of the model.
|
||||||
if (key && configuration) {
|
if (self.key && configuration) {
|
||||||
model.configuration = model.configuration || {};
|
model.configuration = model.configuration || {};
|
||||||
model.configuration[key] = configuration;
|
model.configuration[self.key] = configuration;
|
||||||
}
|
}
|
||||||
doPersist(model);
|
doPersist(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Respond to the destruction of the current representation.
|
|
||||||
function destroy() {
|
|
||||||
// Nothing to clean up
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle a specific representation of a specific domain object
|
|
||||||
function represent(representation, representedObject) {
|
|
||||||
// Track the key, to know which view configuration to save to.
|
|
||||||
key = (representation || {}).key;
|
|
||||||
// Track the represented object
|
|
||||||
domainObject = representedObject;
|
|
||||||
// Ensure existing watches are released
|
|
||||||
destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Place the "commit" method in the scope
|
// Place the "commit" method in the scope
|
||||||
scope.commit = commit;
|
scope.commit = commit;
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Set the current representation in use, and the domain
|
|
||||||
* object being represented.
|
|
||||||
*
|
|
||||||
* @param {RepresentationDefinition} representation the
|
|
||||||
* definition of the representation in use
|
|
||||||
* @param {DomainObject} domainObject the domain object
|
|
||||||
* being represented
|
|
||||||
* @memberof platform/commonUI/edit.EditRepresenter#
|
|
||||||
*/
|
|
||||||
represent: represent,
|
|
||||||
/**
|
|
||||||
* Release any resources associated with this representer.
|
|
||||||
* @memberof platform/commonUI/edit.EditRepresenter#
|
|
||||||
*/
|
|
||||||
destroy: destroy
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle a specific representation of a specific domain object
|
||||||
|
EditRepresenter.prototype.represent = function represent(representation, representedObject) {
|
||||||
|
// Track the key, to know which view configuration to save to.
|
||||||
|
this.key = (representation || {}).key;
|
||||||
|
// Track the represented object
|
||||||
|
this.domainObject = representedObject;
|
||||||
|
// Ensure existing watches are released
|
||||||
|
this.destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Respond to the destruction of the current representation.
|
||||||
|
EditRepresenter.prototype.destroy = function destroy() {
|
||||||
|
// Nothing to clean up
|
||||||
|
};
|
||||||
|
|
||||||
return EditRepresenter;
|
return EditRepresenter;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -42,122 +42,19 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EditToolbar(structure, commit) {
|
function EditToolbar(structure, commit) {
|
||||||
var toolbarStructure = Object.create(structure || {}),
|
var self = this;
|
||||||
toolbarState,
|
|
||||||
selection,
|
|
||||||
properties = [];
|
|
||||||
|
|
||||||
// Generate a new key for an item's property
|
// Generate a new key for an item's property
|
||||||
function addKey(property) {
|
function addKey(property) {
|
||||||
properties.push(property);
|
self.properties.push(property);
|
||||||
return properties.length - 1; // Return index of property
|
return self.properties.length - 1; // Return index of property
|
||||||
}
|
|
||||||
|
|
||||||
// Update value for this property in all elements of the
|
|
||||||
// selection which have this property.
|
|
||||||
function updateProperties(property, value) {
|
|
||||||
var changed = false;
|
|
||||||
|
|
||||||
// Update property in a selected element
|
|
||||||
function updateProperty(selected) {
|
|
||||||
// Ignore selected elements which don't have this property
|
|
||||||
if (selected[property] !== undefined) {
|
|
||||||
// Check if this is a setter, or just assignable
|
|
||||||
if (typeof selected[property] === 'function') {
|
|
||||||
changed =
|
|
||||||
changed || (selected[property]() !== value);
|
|
||||||
selected[property](value);
|
|
||||||
} else {
|
|
||||||
changed =
|
|
||||||
changed || (selected[property] !== value);
|
|
||||||
selected[property] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update property in all selected elements
|
|
||||||
selection.forEach(updateProperty);
|
|
||||||
|
|
||||||
// Return whether or not anything changed
|
|
||||||
return changed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look up the current value associated with a property
|
|
||||||
// in selection i
|
|
||||||
function lookupState(property, selected) {
|
|
||||||
var value = selected[property];
|
|
||||||
return (typeof value === 'function') ? value() : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get initial value for a given property
|
|
||||||
function initializeState(property) {
|
|
||||||
var result;
|
|
||||||
// Look through all selections for this property;
|
|
||||||
// values should all match by the time we perform
|
|
||||||
// this lookup anyway.
|
|
||||||
selection.forEach(function (selected) {
|
|
||||||
result = (selected[property] !== undefined) ?
|
|
||||||
lookupState(property, selected) :
|
|
||||||
result;
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if all elements of the selection which have this
|
|
||||||
// property have the same value for this property.
|
|
||||||
function isConsistent(property) {
|
|
||||||
var consistent = true,
|
|
||||||
observed = false,
|
|
||||||
state;
|
|
||||||
|
|
||||||
// Check if a given element of the selection is consistent
|
|
||||||
// with previously-observed elements for this property.
|
|
||||||
function checkConsistency(selected) {
|
|
||||||
var next;
|
|
||||||
// Ignore selections which don't have this property
|
|
||||||
if (selected[property] !== undefined) {
|
|
||||||
// Look up state of this element in the selection
|
|
||||||
next = lookupState(property, selected);
|
|
||||||
// Detect inconsistency
|
|
||||||
if (observed) {
|
|
||||||
consistent = consistent && (next === state);
|
|
||||||
}
|
|
||||||
// Track state for next iteration
|
|
||||||
state = next;
|
|
||||||
observed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through selections
|
|
||||||
selection.forEach(checkConsistency);
|
|
||||||
|
|
||||||
return consistent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to filter out items which are applicable (or not)
|
|
||||||
// to the current selection.
|
|
||||||
function isApplicable(item) {
|
|
||||||
var property = (item || {}).property,
|
|
||||||
method = (item || {}).method,
|
|
||||||
exclusive = !!(item || {}).exclusive;
|
|
||||||
|
|
||||||
// Check if a selected item defines this property
|
|
||||||
function hasProperty(selected) {
|
|
||||||
return (property && (selected[property] !== undefined)) ||
|
|
||||||
(method && (typeof selected[method] === 'function'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return selection.map(hasProperty).reduce(
|
|
||||||
exclusive ? and : or,
|
|
||||||
exclusive
|
|
||||||
) && isConsistent(property);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke all functions in selections with the given name
|
// Invoke all functions in selections with the given name
|
||||||
function invoke(method, value) {
|
function invoke(method, value) {
|
||||||
if (method) {
|
if (method) {
|
||||||
// Make the change in the selection
|
// Make the change in the selection
|
||||||
selection.forEach(function (selected) {
|
self.selection.forEach(function (selected) {
|
||||||
if (typeof selected[method] === 'function') {
|
if (typeof selected[method] === 'function') {
|
||||||
selected[method](value);
|
selected[method](value);
|
||||||
}
|
}
|
||||||
@ -190,75 +87,169 @@ define(
|
|||||||
return converted;
|
return converted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.toolbarState = [];
|
||||||
|
this.selection = undefined;
|
||||||
|
this.properties = [];
|
||||||
|
this.toolbarStructure = Object.create(structure || {});
|
||||||
|
this.toolbarStructure.sections =
|
||||||
|
((structure || {}).sections || []).map(convertSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if all elements of the selection which have this
|
||||||
|
// property have the same value for this property.
|
||||||
|
EditToolbar.prototype.isConsistent = function (property) {
|
||||||
|
var self = this,
|
||||||
|
consistent = true,
|
||||||
|
observed = false,
|
||||||
|
state;
|
||||||
|
|
||||||
|
// Check if a given element of the selection is consistent
|
||||||
|
// with previously-observed elements for this property.
|
||||||
|
function checkConsistency(selected) {
|
||||||
|
var next;
|
||||||
|
// Ignore selections which don't have this property
|
||||||
|
if (selected[property] !== undefined) {
|
||||||
|
// Look up state of this element in the selection
|
||||||
|
next = self.lookupState(property, selected);
|
||||||
|
// Detect inconsistency
|
||||||
|
if (observed) {
|
||||||
|
consistent = consistent && (next === state);
|
||||||
|
}
|
||||||
|
// Track state for next iteration
|
||||||
|
state = next;
|
||||||
|
observed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through selections
|
||||||
|
self.selection.forEach(checkConsistency);
|
||||||
|
|
||||||
|
return consistent;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used to filter out items which are applicable (or not)
|
||||||
|
// to the current selection.
|
||||||
|
EditToolbar.prototype.isApplicable = function (item) {
|
||||||
|
var property = (item || {}).property,
|
||||||
|
method = (item || {}).method,
|
||||||
|
exclusive = !!(item || {}).exclusive;
|
||||||
|
|
||||||
|
// Check if a selected item defines this property
|
||||||
|
function hasProperty(selected) {
|
||||||
|
return (property && (selected[property] !== undefined)) ||
|
||||||
|
(method && (typeof selected[method] === 'function'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.selection.map(hasProperty).reduce(
|
||||||
|
exclusive ? and : or,
|
||||||
|
exclusive
|
||||||
|
) && this.isConsistent(property);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Look up the current value associated with a property
|
||||||
|
EditToolbar.prototype.lookupState = function (property, selected) {
|
||||||
|
var value = selected[property];
|
||||||
|
return (typeof value === 'function') ? value() : value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current selection. Visibility of sections
|
||||||
|
* and items in the toolbar will be updated to match this.
|
||||||
|
* @param {Array} s the new selection
|
||||||
|
*/
|
||||||
|
EditToolbar.prototype.setSelection = function (s) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
// Show/hide controls in this section per applicability
|
// Show/hide controls in this section per applicability
|
||||||
function refreshSectionApplicability(section) {
|
function refreshSectionApplicability(section) {
|
||||||
var count = 0;
|
var count = 0;
|
||||||
// Show/hide each item
|
// Show/hide each item
|
||||||
(section.items || []).forEach(function (item) {
|
(section.items || []).forEach(function (item) {
|
||||||
item.hidden = !isApplicable(item);
|
item.hidden = !self.isApplicable(item);
|
||||||
count += item.hidden ? 0 : 1;
|
count += item.hidden ? 0 : 1;
|
||||||
});
|
});
|
||||||
// Hide this section if there are no applicable items
|
// Hide this section if there are no applicable items
|
||||||
section.hidden = !count;
|
section.hidden = !count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show/hide controls if they are applicable
|
// Get initial value for a given property
|
||||||
function refreshApplicability() {
|
function initializeState(property) {
|
||||||
toolbarStructure.sections.forEach(refreshSectionApplicability);
|
var result;
|
||||||
|
// Look through all selections for this property;
|
||||||
|
// values should all match by the time we perform
|
||||||
|
// this lookup anyway.
|
||||||
|
self.selection.forEach(function (selected) {
|
||||||
|
result = (selected[property] !== undefined) ?
|
||||||
|
self.lookupState(property, selected) :
|
||||||
|
result;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh toolbar state to match selection
|
this.selection = s;
|
||||||
function refreshState() {
|
this.toolbarStructure.sections.forEach(refreshSectionApplicability);
|
||||||
toolbarState = properties.map(initializeState);
|
this.toolbarState = this.properties.map(initializeState);
|
||||||
}
|
};
|
||||||
|
|
||||||
toolbarStructure.sections =
|
/**
|
||||||
((structure || {}).sections || []).map(convertSection);
|
* Get the structure of the toolbar, as appropriate to
|
||||||
|
* pass to `mct-toolbar`.
|
||||||
|
* @returns the toolbar structure
|
||||||
|
*/
|
||||||
|
EditToolbar.prototype.getStructure = function () {
|
||||||
|
return this.toolbarStructure;
|
||||||
|
};
|
||||||
|
|
||||||
toolbarState = [];
|
/**
|
||||||
|
* Get the current state of the toolbar, as appropriate
|
||||||
|
* to two-way bind to the state handled by `mct-toolbar`.
|
||||||
|
* @returns {Array} state of the toolbar
|
||||||
|
*/
|
||||||
|
EditToolbar.prototype.getState = function () {
|
||||||
|
return this.toolbarState;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
/**
|
||||||
/**
|
* Update state within the current selection.
|
||||||
* Set the current selection. Visisbility of sections
|
* @param {number} index the index of the corresponding
|
||||||
* and items in the toolbar will be updated to match this.
|
* element in the state array
|
||||||
* @param {Array} s the new selection
|
* @param value the new value to convey to the selection
|
||||||
* @memberof platform/commonUI/edit.EditToolbar#
|
*/
|
||||||
*/
|
EditToolbar.prototype.updateState = function (index, value) {
|
||||||
setSelection: function (s) {
|
var self = this;
|
||||||
selection = s;
|
|
||||||
refreshApplicability();
|
// Update value for this property in all elements of the
|
||||||
refreshState();
|
// selection which have this property.
|
||||||
},
|
function updateProperties(property, value) {
|
||||||
/**
|
var changed = false;
|
||||||
* Get the structure of the toolbar, as appropriate to
|
|
||||||
* pass to `mct-toolbar`.
|
// Update property in a selected element
|
||||||
* @returns the toolbar structure
|
function updateProperty(selected) {
|
||||||
* @memberof platform/commonUI/edit.EditToolbar#
|
// Ignore selected elements which don't have this property
|
||||||
*/
|
if (selected[property] !== undefined) {
|
||||||
getStructure: function () {
|
// Check if this is a setter, or just assignable
|
||||||
return toolbarStructure;
|
if (typeof selected[property] === 'function') {
|
||||||
},
|
changed =
|
||||||
/**
|
changed || (selected[property]() !== value);
|
||||||
* Get the current state of the toolbar, as appropriate
|
selected[property](value);
|
||||||
* to two-way bind to the state handled by `mct-toolbar`.
|
} else {
|
||||||
* @returns {Array} state of the toolbar
|
changed =
|
||||||
* @memberof platform/commonUI/edit.EditToolbar#
|
changed || (selected[property] !== value);
|
||||||
*/
|
selected[property] = value;
|
||||||
getState: function () {
|
}
|
||||||
return toolbarState;
|
}
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Update state within the current selection.
|
|
||||||
* @param {number} index the index of the corresponding
|
|
||||||
* element in the state array
|
|
||||||
* @param value the new value to convey to the selection
|
|
||||||
* @memberof platform/commonUI/edit.EditToolbar#
|
|
||||||
*/
|
|
||||||
updateState: function (index, value) {
|
|
||||||
return updateProperties(properties[index], value);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
// Update property in all selected elements
|
||||||
|
self.selection.forEach(updateProperty);
|
||||||
|
|
||||||
|
// Return whether or not anything changed
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateProperties(this.properties[index], value);
|
||||||
|
};
|
||||||
|
|
||||||
return EditToolbar;
|
return EditToolbar;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,10 @@ define(
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// No operation
|
// No operation
|
||||||
function noop() {}
|
var NOOP_REPRESENTER = {
|
||||||
|
represent: function () {},
|
||||||
|
destroy: function () {}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The EditToolbarRepresenter populates the toolbar in Edit mode
|
* The EditToolbarRepresenter populates the toolbar in Edit mode
|
||||||
@ -35,10 +38,10 @@ define(
|
|||||||
* @param {Scope} scope the Angular scope of the representation
|
* @param {Scope} scope the Angular scope of the representation
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @implements {Representer}
|
||||||
*/
|
*/
|
||||||
function EditToolbarRepresenter(scope, element, attrs) {
|
function EditToolbarRepresenter(scope, element, attrs) {
|
||||||
var toolbar,
|
var self = this;
|
||||||
toolbarObject = {};
|
|
||||||
|
|
||||||
// Mark changes as ready to persist
|
// Mark changes as ready to persist
|
||||||
function commit(message) {
|
function commit(message) {
|
||||||
@ -50,31 +53,33 @@ define(
|
|||||||
// Handle changes to the current selection
|
// Handle changes to the current selection
|
||||||
function updateSelection(selection) {
|
function updateSelection(selection) {
|
||||||
// Only update if there is a toolbar to update
|
// Only update if there is a toolbar to update
|
||||||
if (toolbar) {
|
if (self.toolbar) {
|
||||||
// Make sure selection is array-like
|
// Make sure selection is array-like
|
||||||
selection = Array.isArray(selection) ?
|
selection = Array.isArray(selection) ?
|
||||||
selection :
|
selection :
|
||||||
(selection ? [selection] : []);
|
(selection ? [selection] : []);
|
||||||
|
|
||||||
// Update the toolbar's selection
|
// Update the toolbar's selection
|
||||||
toolbar.setSelection(selection);
|
self.toolbar.setSelection(selection);
|
||||||
|
|
||||||
// ...and expose its structure/state
|
// ...and expose its structure/state
|
||||||
toolbarObject.structure = toolbar.getStructure();
|
self.toolbarObject.structure =
|
||||||
toolbarObject.state = toolbar.getState();
|
self.toolbar.getStructure();
|
||||||
|
self.toolbarObject.state =
|
||||||
|
self.toolbar.getState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get state (to watch it)
|
// Get state (to watch it)
|
||||||
function getState() {
|
function getState() {
|
||||||
return toolbarObject.state;
|
return self.toolbarObject.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update selection models to match changed toolbar state
|
// Update selection models to match changed toolbar state
|
||||||
function updateState(state) {
|
function updateState(state) {
|
||||||
// Update underlying state based on toolbar changes
|
// Update underlying state based on toolbar changes
|
||||||
var changed = (state || []).map(function (value, index) {
|
var changed = (state || []).map(function (value, index) {
|
||||||
return toolbar.updateState(index, value);
|
return self.toolbar.updateState(index, value);
|
||||||
}).reduce(function (a, b) {
|
}).reduce(function (a, b) {
|
||||||
return a || b;
|
return a || b;
|
||||||
}, false);
|
}, false);
|
||||||
@ -86,68 +91,62 @@ define(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize toolbar (expose object to parent scope)
|
this.commit = commit;
|
||||||
function initialize(definition) {
|
this.scope = scope;
|
||||||
// If we have been asked to expose toolbar state...
|
this.attrs = attrs;
|
||||||
if (attrs.toolbar) {
|
this.updateSelection = updateSelection;
|
||||||
// Initialize toolbar object
|
this.toolbar = undefined;
|
||||||
toolbar = new EditToolbar(definition, commit);
|
this.toolbarObject = {};
|
||||||
// Ensure toolbar state is exposed
|
|
||||||
scope.$parent[attrs.toolbar] = toolbarObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Represent a domain object using this definition
|
|
||||||
function represent(representation) {
|
|
||||||
// Get the newest toolbar definition from the view
|
|
||||||
var definition = (representation || {}).toolbar || {};
|
|
||||||
// Expose the toolbar object to the parent scope
|
|
||||||
initialize(definition);
|
|
||||||
// Create a selection scope
|
|
||||||
scope.selection = new EditToolbarSelection();
|
|
||||||
// Initialize toolbar to an empty selection
|
|
||||||
updateSelection([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy; remove toolbar object from parent scope
|
|
||||||
function destroy() {
|
|
||||||
// Clear exposed toolbar state (if any)
|
|
||||||
if (attrs.toolbar) {
|
|
||||||
delete scope.$parent[attrs.toolbar];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this representation exposes a toolbar, set up watches
|
// If this representation exposes a toolbar, set up watches
|
||||||
// to synchronize with it.
|
// to synchronize with it.
|
||||||
if (attrs.toolbar) {
|
if (attrs && attrs.toolbar) {
|
||||||
// Detect and handle changes to state from the toolbar
|
// Detect and handle changes to state from the toolbar
|
||||||
scope.$watchCollection(getState, updateState);
|
scope.$watchCollection(getState, updateState);
|
||||||
// Watch for changes in the current selection state
|
// Watch for changes in the current selection state
|
||||||
scope.$watchCollection("selection.all()", updateSelection);
|
scope.$watchCollection("selection.all()", updateSelection);
|
||||||
// Expose toolbar state under that name
|
// Expose toolbar state under that name
|
||||||
scope.$parent[attrs.toolbar] = toolbarObject;
|
scope.$parent[attrs.toolbar] = this.toolbarObject;
|
||||||
|
} else {
|
||||||
|
// No toolbar declared, so do nothing.
|
||||||
|
return NOOP_REPRESENTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Set the current representation in use, and the domain
|
|
||||||
* object being represented.
|
|
||||||
*
|
|
||||||
* @param {RepresentationDefinition} representation the
|
|
||||||
* definition of the representation in use
|
|
||||||
* @param {DomainObject} domainObject the domain object
|
|
||||||
* being represented
|
|
||||||
* @memberof platform/commonUI/edit.EditToolbarRepresenter#
|
|
||||||
*/
|
|
||||||
represent: (attrs || {}).toolbar ? represent : noop,
|
|
||||||
/**
|
|
||||||
* Release any resources associated with this representer.
|
|
||||||
* @memberof platform/commonUI/edit.EditToolbarRepresenter#
|
|
||||||
*/
|
|
||||||
destroy: (attrs || {}).toolbar ? destroy : noop
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Represent a domain object using this definition
|
||||||
|
EditToolbarRepresenter.prototype.represent = function (representation) {
|
||||||
|
// Get the newest toolbar definition from the view
|
||||||
|
var definition = (representation || {}).toolbar || {},
|
||||||
|
self = this;
|
||||||
|
|
||||||
|
// Initialize toolbar (expose object to parent scope)
|
||||||
|
function initialize(definition) {
|
||||||
|
// If we have been asked to expose toolbar state...
|
||||||
|
if (self.attrs.toolbar) {
|
||||||
|
// Initialize toolbar object
|
||||||
|
self.toolbar = new EditToolbar(definition, self.commit);
|
||||||
|
// Ensure toolbar state is exposed
|
||||||
|
self.scope.$parent[self.attrs.toolbar] = self.toolbarObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose the toolbar object to the parent scope
|
||||||
|
initialize(definition);
|
||||||
|
// Create a selection scope
|
||||||
|
this.scope.selection = new EditToolbarSelection();
|
||||||
|
// Initialize toolbar to an empty selection
|
||||||
|
this.updateSelection([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Destroy; remove toolbar object from parent scope
|
||||||
|
EditToolbarRepresenter.prototype.destroy = function () {
|
||||||
|
// Clear exposed toolbar state (if any)
|
||||||
|
if (this.attrs.toolbar) {
|
||||||
|
delete this.scope.$parent[this.attrs.toolbar];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return EditToolbarRepresenter;
|
return EditToolbarRepresenter;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -41,112 +41,91 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EditToolbarSelection() {
|
function EditToolbarSelection() {
|
||||||
var selection = [ {} ],
|
this.selection = [{}];
|
||||||
selecting = false,
|
this.selecting = false;
|
||||||
selected;
|
this.selectedObj = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove the currently-selected object
|
/**
|
||||||
function deselect() {
|
* Check if an object is currently selected.
|
||||||
// Nothing to do if we don't have a selected object
|
* @param {*} obj the object to check for selection
|
||||||
if (selecting) {
|
* @returns {boolean} true if selected, otherwise false
|
||||||
// Clear state tracking
|
*/
|
||||||
selecting = false;
|
EditToolbarSelection.prototype.selected = function (obj) {
|
||||||
selected = undefined;
|
return (obj === this.selectedObj) || (obj === this.selection[0]);
|
||||||
|
};
|
||||||
|
|
||||||
// Remove the selection
|
/**
|
||||||
selection.pop();
|
* Select an object.
|
||||||
|
* @param obj the object to select
|
||||||
return true;
|
* @returns {boolean} true if selection changed
|
||||||
}
|
*/
|
||||||
|
EditToolbarSelection.prototype.select = function (obj) {
|
||||||
|
// Proxy is always selected
|
||||||
|
if (obj === this.selection[0]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select an object
|
// Clear any existing selection
|
||||||
function select(obj) {
|
this.deselect();
|
||||||
// Proxy is always selected
|
|
||||||
if (obj === selection[0]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear any existing selection
|
// Note the current selection state
|
||||||
deselect();
|
this.selectedObj = obj;
|
||||||
|
this.selecting = true;
|
||||||
|
|
||||||
// Note the current selection state
|
// Add the selection
|
||||||
selected = obj;
|
this.selection.push(obj);
|
||||||
selecting = true;
|
};
|
||||||
|
|
||||||
// Add the selection
|
/**
|
||||||
selection.push(obj);
|
* Clear the current selection.
|
||||||
|
* @returns {boolean} true if selection changed
|
||||||
|
*/
|
||||||
|
EditToolbarSelection.prototype.deselect = function () {
|
||||||
|
// Nothing to do if we don't have a selected object
|
||||||
|
if (this.selecting) {
|
||||||
|
// Clear state tracking
|
||||||
|
this.selecting = false;
|
||||||
|
this.selectedObj = undefined;
|
||||||
|
|
||||||
|
// Remove the selection
|
||||||
|
this.selection.pop();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently-selected object.
|
||||||
|
* @returns the currently selected object
|
||||||
|
*/
|
||||||
|
EditToolbarSelection.prototype.get = function () {
|
||||||
|
return this.selectedObj;
|
||||||
|
};
|
||||||
|
|
||||||
// Check if an object is selected
|
/**
|
||||||
function isSelected(obj) {
|
* Get/set the view proxy (for toolbar actions taken upon
|
||||||
return (obj === selected) || (obj === selection[0]);
|
* the view itself.)
|
||||||
|
* @param [proxy] the view proxy (if setting)
|
||||||
|
* @returns the current view proxy
|
||||||
|
*/
|
||||||
|
EditToolbarSelection.prototype.proxy = function (p) {
|
||||||
|
if (arguments.length > 0) {
|
||||||
|
this.selection[0] = p;
|
||||||
}
|
}
|
||||||
|
return this.selection[0];
|
||||||
|
};
|
||||||
|
|
||||||
// Getter for current selection
|
/**
|
||||||
function get() {
|
* Get an array containing all selections, including the
|
||||||
return selected;
|
* selection proxy. It is generally not advisable to
|
||||||
}
|
* mutate this array directly.
|
||||||
|
* @returns {Array} all selections
|
||||||
// Getter/setter for view proxy
|
*/
|
||||||
function proxy(p) {
|
EditToolbarSelection.prototype.all = function () {
|
||||||
if (arguments.length > 0) {
|
return this.selection;
|
||||||
selection[0] = p;
|
};
|
||||||
}
|
|
||||||
return selection[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getter for the full array of selected objects (incl. view proxy)
|
|
||||||
function all() {
|
|
||||||
return selection;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Check if an object is currently selected.
|
|
||||||
* @returns true if selected, otherwise false
|
|
||||||
* @memberof platform/commonUI/edit.EditToolbarSelection#
|
|
||||||
*/
|
|
||||||
selected: isSelected,
|
|
||||||
/**
|
|
||||||
* Select an object.
|
|
||||||
* @param obj the object to select
|
|
||||||
* @returns {boolean} true if selection changed
|
|
||||||
* @memberof platform/commonUI/edit.EditToolbarSelection#
|
|
||||||
*/
|
|
||||||
select: select,
|
|
||||||
/**
|
|
||||||
* Clear the current selection.
|
|
||||||
* @returns {boolean} true if selection changed
|
|
||||||
* @memberof platform/commonUI/edit.EditToolbarSelection#
|
|
||||||
*/
|
|
||||||
deselect: deselect,
|
|
||||||
/**
|
|
||||||
* Get the currently-selected object.
|
|
||||||
* @returns the currently selected object
|
|
||||||
* @memberof platform/commonUI/edit.EditToolbarSelection#
|
|
||||||
*/
|
|
||||||
get: get,
|
|
||||||
/**
|
|
||||||
* Get/set the view proxy (for toolbar actions taken upon
|
|
||||||
* the view itself.)
|
|
||||||
* @param [proxy] the view proxy (if setting)
|
|
||||||
* @returns the current view proxy
|
|
||||||
* @memberof platform/commonUI/edit.EditToolbarSelection#
|
|
||||||
*/
|
|
||||||
proxy: proxy,
|
|
||||||
/**
|
|
||||||
* Get an array containing all selections, including the
|
|
||||||
* selection proxy. It is generally not advisable to
|
|
||||||
* mutate this array directly.
|
|
||||||
* @returns {Array} all selections
|
|
||||||
* @memberof platform/commonUI/edit.EditToolbarSelection#
|
|
||||||
*/
|
|
||||||
all: all
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return EditToolbarSelection;
|
return EditToolbarSelection;
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,9 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("saves objects that have been marked dirty", function () {
|
it("saves objects that have been marked dirty", function () {
|
||||||
var objects = ['a', 'b', 'c'].map(TestObject).map(cache.getEditableObject);
|
var objects = ['a', 'b', 'c'].map(TestObject).map(function (domainObject) {
|
||||||
|
return cache.getEditableObject(domainObject);
|
||||||
|
});
|
||||||
|
|
||||||
cache.markDirty(objects[0]);
|
cache.markDirty(objects[0]);
|
||||||
cache.markDirty(objects[2]);
|
cache.markDirty(objects[2]);
|
||||||
@ -123,7 +125,9 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("does not save objects that have been marked clean", function () {
|
it("does not save objects that have been marked clean", function () {
|
||||||
var objects = ['a', 'b', 'c'].map(TestObject).map(cache.getEditableObject);
|
var objects = ['a', 'b', 'c'].map(TestObject).map(function (domainObject) {
|
||||||
|
return cache.getEditableObject(domainObject);
|
||||||
|
});
|
||||||
|
|
||||||
cache.markDirty(objects[0]);
|
cache.markDirty(objects[0]);
|
||||||
cache.markDirty(objects[2]);
|
cache.markDirty(objects[2]);
|
||||||
|
@ -30,6 +30,29 @@ define(
|
|||||||
function () {
|
function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A policy is a participant in decision-making policies. Policies
|
||||||
|
* are divided into categories (identified symbolically by strings);
|
||||||
|
* within a given category, every given policy-driven decision will
|
||||||
|
* occur by consulting all available policies and requiring their
|
||||||
|
* collective consent (that is, every individual policy has the
|
||||||
|
* power to reject the decision entirely.)
|
||||||
|
*
|
||||||
|
* @interface Policy
|
||||||
|
* @template C, X
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this policy allows the described decision. The types
|
||||||
|
* of the arguments expected here vary depending on policy category.
|
||||||
|
*
|
||||||
|
* @method Policy#allow
|
||||||
|
* @template C, X
|
||||||
|
* @param {C} candidate the thing to allow or disallow
|
||||||
|
* @param {X} context the context in which the decision occurs
|
||||||
|
* @returns {boolean} false if disallowed; otherwise, true
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an implementation of `policyService` which consults
|
* Provides an implementation of `policyService` which consults
|
||||||
* various policy extensions to determine whether or not a specific
|
* various policy extensions to determine whether or not a specific
|
||||||
|
Loading…
x
Reference in New Issue
Block a user