[Code Style] Use prototypes in Browse bundle

WTD-1482.
This commit is contained in:
Victor Woeltjen
2015-08-10 11:52:23 -07:00
parent 78146d97f8
commit a77920bd18
10 changed files with 418 additions and 365 deletions

View File

@ -37,15 +37,24 @@ define(
* @constructor
*/
function MenuArrowController($scope) {
function showMenu(event) {
var actionContext = {key: 'menu', domainObject: $scope.domainObject, event: event};
$scope.domainObject.getCapability('action').perform(actionContext);
this.$scope = $scope;
}
return {
showMenu: showMenu
/**
* Show a context menu for the domain object in this scope.
*
* @param event the browser event which caused this (used to
* position the menu)
*/
MenuArrowController.prototype.showMenu = function (event) {
var actionContext = {
key: 'menu',
domainObject: this.$scope.domainObject,
event: event
};
this.$scope.domainObject.getCapability('action').perform(actionContext);
};
}
return MenuArrowController;
}

View File

@ -35,7 +35,9 @@ define(
* is performed when a user uses the Create menu.
*
* @memberof platform/commonUI/browse
* @implements {Action}
* @constructor
*
* @param {Type} type the type of domain object to create
* @param {DomainObject} parent the domain object that should
* act as a container for the newly-created object
@ -50,6 +52,27 @@ define(
* of the newly-created domain object
*/
function CreateAction(type, parent, context, dialogService, creationService, policyService) {
this.metadata = {
key: 'create',
glyph: type.getGlyph(),
name: type.getName(),
type: type.getKey(),
description: type.getDescription(),
context: context
};
this.type = type;
this.parent = parent;
this.policyService = policyService;
this.dialogService = dialogService;
this.creationService = creationService;
}
/**
* Create a new object of the given type.
* This will prompt for user input first.
*/
CreateAction.prototype.perform = function () {
/*
Overview of steps in object creation:
@ -66,17 +89,18 @@ define(
a. ...use persistence capability.
*/
function perform() {
// The wizard will handle creating the form model based
// on the type...
var wizard = new CreateWizard(type, parent, policyService);
var wizard =
new CreateWizard(this.type, this.parent, this.policyService),
self = this;
// Create and persist the new object, based on user
// input.
function persistResult(formValue) {
var parent = wizard.getLocation(formValue),
newModel = wizard.createModel(formValue);
return creationService.createObject(newModel, parent);
return self.creationService.createObject(newModel, parent);
}
function doNothing() {
@ -84,45 +108,27 @@ define(
return false;
}
return dialogService.getUserInput(
return this.dialogService.getUserInput(
wizard.getFormStructure(),
wizard.getInitialFormValue()
).then(persistResult, doNothing);
}
};
return {
/**
* Create a new object of the given type.
* This will prompt for user input first.
* @method
* @memberof CreateAction
* @memberof platform/commonUI/browse.CreateAction#
*/
perform: perform,
/**
* Get metadata about this action. This includes fields:
* * `name`: Human-readable name
* * `key`: Machine-readable identifier ("create")
* * `glyph`: Glyph to use as an icon for this action
* * `description`: Human-readable description
* * `context`: The context in which this action will be performed.
*
* @return {object} metadata about the create action
* @memberof platform/commonUI/browse.CreateAction#
* Metadata associated with a Create action.
* @typedef {ActionMetadata} CreateActionMetadata
* @property {string} type the key for the type of domain object
* to be created
*/
getMetadata: function () {
return {
key: 'create',
glyph: type.getGlyph(),
name: type.getName(),
type: type.getKey(),
description: type.getDescription(),
context: context
/**
* Get metadata about this action.
* @returns {CreateActionMetadata} metadata about this action
*/
CreateAction.prototype.getMetadata = function () {
return this.metadata;
};
}
};
}
return CreateAction;
}

View File

@ -35,6 +35,8 @@ define(
*
* @memberof platform/commonUI/browse
* @constructor
* @implements {ActionService}
*
* @param {TypeService} typeService the type service, used to discover
* available types
* @param {DialogService} dialogService the dialog service, used by
@ -45,19 +47,17 @@ define(
* object creation.
*/
function CreateActionProvider(typeService, dialogService, creationService, policyService) {
return {
/**
* Get all Create actions which are applicable in the provided
* context.
* @memberof CreateActionProvider
* @method
* @returns {CreateAction[]}
* @memberof platform/commonUI/browse.CreateActionProvider#
*/
getActions: function (actionContext) {
this.typeService = typeService;
this.dialogService = dialogService;
this.creationService = creationService;
this.policyService = policyService;
}
CreateActionProvider.prototype.getActions = function (actionContext) {
var context = actionContext || {},
key = context.key,
destination = context.domainObject;
destination = context.domainObject,
self = this;
// We only provide Create actions, and we need a
// domain object to serve as the container for the
@ -68,21 +68,19 @@ define(
}
// Introduce one create action per type
return typeService.listTypes().filter(function (type) {
return this.typeService.listTypes().filter(function (type) {
return type.hasFeature("creation");
}).map(function (type) {
return new CreateAction(
type,
destination,
context,
dialogService,
creationService,
policyService
self.dialogService,
self.creationService,
self.policyService
);
});
}
};
}
return CreateActionProvider;
}

View File

@ -35,8 +35,25 @@ define(
* @constructor
*/
function CreateWizard(type, parent, policyService) {
var model = type.getInitialModel(),
properties = type.getProperties();
this.type = type;
this.model = type.getInitialModel();
this.properties = type.getProperties();
this.parent = parent;
this.policyService = policyService;
}
/**
* Get the form model for this wizard; this is a description
* that will be rendered to an HTML form. See the
* platform/forms bundle
*
* @return {FormModel} formModel the form model to
* show in the create dialog
*/
CreateWizard.prototype.getFormStructure = function () {
var sections = [],
type = this.type,
policyService = this.policyService;
function validateLocation(locatingObject) {
var locatingType = locatingObject &&
@ -48,22 +65,9 @@ define(
);
}
return {
/**
* Get the form model for this wizard; this is a description
* that will be rendered to an HTML form. See the
* platform/forms bundle
*
* @return {FormModel} formModel the form model to
* show in the create dialog
* @memberof platform/commonUI/browse.CreateWizard#
*/
getFormStructure: function () {
var sections = [];
sections.push({
name: "Properties",
rows: properties.map(function (property, index) {
rows: this.properties.map(function (property, index) {
// Property definition is same as form row definition
var row = Object.create(property.getDefinition());
@ -86,62 +90,59 @@ define(
return {
sections: sections,
name: "Create a New " + type.getName()
name: "Create a New " + this.type.getName()
};
},
};
/**
* Get the initial value for the form being described.
* This will include the values for all properties described
* in the structure.
*
* @returns {object} the initial value of the form
* @memberof platform/commonUI/browse.CreateWizard#
*/
getInitialFormValue: function () {
CreateWizard.prototype.getInitialFormValue = function () {
// Start with initial values for properties
var formValue = properties.map(function (property) {
var model = this.model,
formValue = this.properties.map(function (property) {
return property.getValue(model);
});
// Include the createParent
formValue.createParent = parent;
formValue.createParent = this.parent;
return formValue;
},
};
/**
* Based on a populated form, get the domain object which
* should be used as a parent for the newly-created object.
* @return {DomainObject}
* @memberof platform/commonUI/browse.CreateWizard#
*/
getLocation: function (formValue) {
return formValue.createParent || parent;
},
CreateWizard.prototype.getLocation = function (formValue) {
return formValue.createParent || this.parent;
};
/**
* Create the domain object model for a newly-created object,
* based on user input read from a formModel.
* @return {object} the domain object' model
* @memberof platform/commonUI/browse.CreateWizard#
* @return {object} the domain object model
*/
createModel: function (formValue) {
CreateWizard.prototype.createModel = function (formValue) {
// Clone
var newModel = JSON.parse(JSON.stringify(model));
var newModel = JSON.parse(JSON.stringify(this.model));
// Always use the type from the type definition
newModel.type = type.getKey();
newModel.type = this.type.getKey();
// Update all properties
properties.forEach(function (property, index) {
this.properties.forEach(function (property, index) {
property.setValue(newModel, formValue[index]);
});
return newModel;
}
};
}
return CreateWizard;
}
);

View File

@ -43,12 +43,35 @@ define(
* @constructor
*/
function CreationService(persistenceService, $q, $log) {
this.persistenceService = persistenceService;
this.$q = $q;
this.$log = $log;
}
/**
* Create a new domain object with the provided model, as
* a member of the provided parent domain object's composition.
* This parent will additionally determine which persistence
* space an object is created within (as it is possible to
* have multiple persistence spaces attached.)
*
* @param {object} model the model for the newly-created
* domain object
* @param {DomainObject} parent the domain object which
* should contain the newly-created domain object
* in its composition
* @return {Promise} a promise that will resolve when the domain
* object has been created
*/
CreationService.prototype.createObject = function (model, parent) {
var persistence = parent.getCapability("persistence"),
self = this;
// Persist the new domain object's model; it will be fully
// constituted as a domain object when loaded back, as all
// domain object models are.
function doPersist(space, id, model) {
return persistenceService.createObject(
return self.persistenceService.createObject(
space,
id,
model
@ -67,14 +90,14 @@ define(
}
} else {
// This is abnormal; composition should be an array
$log.warn(NO_COMPOSITION_WARNING + parent.getId());
self.$log.warn(NO_COMPOSITION_WARNING + parent.getId());
return false; // Cancel mutation
}
});
return $q.when(mutatationResult).then(function (result) {
return self.$q.when(mutatationResult).then(function (result) {
if (!result) {
$log.error("Could not mutate " + parent.getId());
self.$log.error("Could not mutate " + parent.getId());
return undefined;
}
@ -94,49 +117,25 @@ define(
});
}
// Create a new domain object with the provided model as a
// member of the specified parent's composition
function createObject(model, parent) {
var persistence = parent.getCapability("persistence");
// We need the parent's persistence capability to determine
// what space to create the new object's model in.
if (!persistence) {
$log.warn(NON_PERSISTENT_WARNING);
return $q.reject(new Error(NON_PERSISTENT_WARNING));
self.$log.warn(NON_PERSISTENT_WARNING);
return self.$q.reject(new Error(NON_PERSISTENT_WARNING));
}
// We create a new domain object in three sequential steps:
// 1. Get a new UUID for the object
// 2. Create a model with that ID in the persistence space
// 3. Add that ID to
return $q.when(
uuid()
).then(function (id) {
return self.$q.when(uuid()).then(function (id) {
return doPersist(persistence.getSpace(), id, model);
}).then(function (id) {
return addToComposition(id, parent, persistence);
});
}
return {
/**
* Create a new domain object with the provided model, as
* a member of the provided parent domain object's composition.
* This parent will additionally determine which persistence
* space an object is created within (as it is possible to
* have multiple persistence spaces attached.)
*
* @param {object} model the model for the newly-created
* domain object
* @param {DomainObject} parent the domain object which
* should contain the newly-created domain object
* in its composition
* @memberof platform/commonUI/browse.CreationService#
*/
createObject: createObject
};
}
return CreationService;
}

View File

@ -33,24 +33,24 @@ define(
* The navigate action navigates to a specific domain object.
* @memberof platform/commonUI/browse
* @constructor
* @implements {Action}
*/
function NavigateAction(navigationService, $q, context) {
var domainObject = context.domainObject;
function perform() {
// Set navigation, and wrap like a promise
return $q.when(navigationService.setNavigation(domainObject));
this.domainObject = context.domainObject;
this.$q = $q;
this.navigationService = navigationService;
}
return {
/**
* Navigate to the object described in the context.
* @returns {Promise} a promise that is resolved once the
* navigation has been updated
* @memberof platform/commonUI/browse.NavigateAction#
*/
perform: perform
};
NavigateAction.prototype.perform = function () {
// Set navigation, and wrap like a promise
return this.$q.when(
this.navigationService.setNavigation(this.domainObject)
);
}
/**

View File

@ -36,67 +36,52 @@ define(
* @constructor
*/
function NavigationService() {
var navigated,
callbacks = [];
// Getter for current navigation
function getNavigation() {
return navigated;
this.navigated = undefined;
this.callbacks = [];
}
// Setter for navigation; invokes callbacks
function setNavigation(value) {
if (navigated !== value) {
navigated = value;
callbacks.forEach(function (callback) {
/**
* Get the current navigation state.
* @returns {DomainObject} the object that is navigated-to
*/
NavigationService.prototype.getNavigation = function () {
return this.navigated;
};
/**
* Set the current navigation state. This will invoke listeners.
* @param {DomainObject} domainObject the domain object to navigate to
*/
NavigationService.prototype.setNavigation = function (value) {
if (this.navigated !== value) {
this.navigated = value;
this.callbacks.forEach(function (callback) {
callback(value);
});
}
}
};
// Adds a callback
function addListener(callback) {
callbacks.push(callback);
}
// Filters out a callback
function removeListener(callback) {
callbacks = callbacks.filter(function (cb) {
return cb !== callback;
});
}
return {
/**
* Get the current navigation state.
* @memberof platform/commonUI/browse.NavigationService#
*/
getNavigation: getNavigation,
/**
* Set the current navigation state. Thiswill invoke listeners.
* @param {DomainObject} value the domain object to navigate
* to
* @memberof platform/commonUI/browse.NavigationService#
*/
setNavigation: setNavigation,
/**
* Listen for changes in navigation. The passed callback will
* be invoked with the new domain object of navigation when
* this changes.
* @param {function} callback the callback to invoke when
* navigation state changes
* @memberof platform/commonUI/browse.NavigationService#
*/
addListener: addListener,
NavigationService.prototype.addListener = function (callback) {
this.callbacks.push(callback);
}
/**
* Stop listening for changes in navigation state.
* @param {function} callback the callback which should
* no longer be invoked when navigation state
* changes
* @memberof platform/commonUI/browse.NavigationService#
*/
removeListener: removeListener
};
NavigationService.prototype.removeListener = function (callback) {
this.callbacks = this.callbacks.filter(function (cb) {
return cb !== callback;
});
}
return NavigationService;

View File

@ -37,22 +37,17 @@ define(
* and regular in-window display.
* @memberof platform/commonUI/browse
* @constructor
* @implements {Action}
*/
function FullscreenAction(context) {
return {
/**
* Toggle full screen state
* @memberof platform/commonUI/browse.FullscreenAction#
*/
perform: function () {
this.context = context;
}
FullscreenAction.prototype.perform = function () {
screenfull.toggle();
},
/**
* Get metadata about this action, including the
* applicable glyph to display.
* @memberof platform/commonUI/browse.FullscreenAction#
*/
getMetadata: function () {
};
FullscreenAction.prototype.getMetadata = function () {
// We override getMetadata, because the glyph and
// description need to be determined at run-time
// based on whether or not we are currently
@ -62,11 +57,9 @@ define(
metadata.description = screenfull.isFullscreen ?
EXIT_FULLSCREEN : ENTER_FULLSCREEN;
metadata.group = "windowing";
metadata.context = context;
metadata.context = this.context;
return metadata;
}
};
}
return FullscreenAction;
}

View File

@ -35,33 +35,24 @@ define(
* into a new browser tab.
* @memberof platform/commonUI/browse
* @constructor
* @implements {Action}
*/
function NewTabAction(urlService, $window, context) {
// Returns the selected domain object
// when using the context menu or the top right button
// based on the context and the existance of the object
// It is set to object an returned
function getSelectedObject() {
var object;
if (context.selectedObject) {
object = context.selectedObject;
} else {
object = context.domainObject;
}
return object;
context = context || {};
this.urlService = urlService;
this.$window = $window;
// Choose the object to be opened into a new tab
this.domainObject = context.selectedObject || context.domainObject;
}
return {
// Performs the open in new tab function
// By calling the url service, the mode needed
// (browse) and the domainObject is passed in and
// the path is returned and opened in a new tab
perform: function () {
$window.open(urlService.urlForNewTab("browse", getSelectedObject()),
"_blank");
}
NewTabAction.prototype.perform = function () {
this.$window.open(
this.urlService.urlForNewTab("browse", this.domainObject),
"_blank"
);
};
}
return NewTabAction;
}

View File

@ -25,6 +25,76 @@ define(
function () {
"use strict";
/**
* Actions are reusable processes/behaviors performed by users within
* the system, typically upon domain objects. Actions are commonly
* exposed to users as menu items or buttons.
*
* Actions are usually registered via the `actions` extension
* category, or (in advanced cases) via an `actionService`
* implementation.
*
* @interface Action
*/
/**
* Perform the behavior associated with this action. The return type
* may vary depending on which action has been performed; in general,
* no return value should be expected.
*
* @method Action#perform
*/
/**
* Get metadata associated with this action.
*
* @method Action#getMetadata
* @returns {ActionMetadata}
*/
/**
* Metadata associated with an Action. Actions of specific types may
* extend this with additional properties.
*
* @typedef {Object} ActionMetadata
* @property {string} key machine-readable identifier for this action
* @property {string} name human-readable name for this action
* @property {string} description human-readable description
* @property {string} glyph character to display as icon
* @property {ActionContext} context the context in which the action
* will be performed.
*/
/**
* Provides actions that can be performed within specific contexts.
*
* @interface ActionService
*/
/**
* Get actions which can be performed within a certain context.
*
* @method ActionService#getActions
* @param {ActionContext} context the context in which the action will
* be performed
* @return {Action[]} relevant actions
*/
/**
* A description of the context in which an action may occur.
*
* @typedef ActionContext
* @property {DomainObject} [domainObject] the domain object being
* acted upon.
* @property {DomainObject} [selectedObject] the selection at the
* time of action (e.g. the dragged object in a
* drag-and-drop operation.)
* @property {string} [key] the machine-readable identifier of
* the relevant action
* @property {string} [category] a string identifying the category
* of action being performed
*/
/**
* The ActionAggregator makes several actionService
* instances act as those they were one. When requesting
@ -33,7 +103,8 @@ define(
*
* @memberof platform/core
* @constructor
* @param {ActionProvider[]} actionProviders an array
* @implements {ActionService}
* @param {ActionService[]} actionProviders an array
* of action services
*/
function ActionAggregator(actionProviders) {