[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 * @constructor
*/ */
function MenuArrowController($scope) { function MenuArrowController($scope) {
function showMenu(event) { this.$scope = $scope;
var actionContext = {key: 'menu', domainObject: $scope.domainObject, event: event};
$scope.domainObject.getCapability('action').perform(actionContext);
} }
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; return MenuArrowController;
} }

View File

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

View File

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

View File

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

View File

@ -43,12 +43,35 @@ define(
* @constructor * @constructor
*/ */
function CreationService(persistenceService, $q, $log) { 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 // Persist the new domain object's model; it will be fully
// constituted as a domain object when loaded back, as all // constituted as a domain object when loaded back, as all
// domain object models are. // domain object models are.
function doPersist(space, id, model) { function doPersist(space, id, model) {
return persistenceService.createObject( return self.persistenceService.createObject(
space, space,
id, id,
model model
@ -67,14 +90,14 @@ define(
} }
} else { } else {
// This is abnormal; composition should be an array // 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 false; // Cancel mutation
} }
}); });
return $q.when(mutatationResult).then(function (result) { return self.$q.when(mutatationResult).then(function (result) {
if (!result) { if (!result) {
$log.error("Could not mutate " + parent.getId()); self.$log.error("Could not mutate " + parent.getId());
return undefined; 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 // We need the parent's persistence capability to determine
// what space to create the new object's model in. // what space to create the new object's model in.
if (!persistence) { if (!persistence) {
$log.warn(NON_PERSISTENT_WARNING); self.$log.warn(NON_PERSISTENT_WARNING);
return $q.reject(new Error(NON_PERSISTENT_WARNING)); return self.$q.reject(new Error(NON_PERSISTENT_WARNING));
} }
// We create a new domain object in three sequential steps: // We create a new domain object in three sequential steps:
// 1. Get a new UUID for the object // 1. Get a new UUID for the object
// 2. Create a model with that ID in the persistence space // 2. Create a model with that ID in the persistence space
// 3. Add that ID to // 3. Add that ID to
return $q.when( return self.$q.when(uuid()).then(function (id) {
uuid()
).then(function (id) {
return doPersist(persistence.getSpace(), id, model); return doPersist(persistence.getSpace(), id, model);
}).then(function (id) { }).then(function (id) {
return addToComposition(id, parent, persistence); 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; return CreationService;
} }

View File

@ -33,24 +33,24 @@ define(
* The navigate action navigates to a specific domain object. * The navigate action navigates to a specific domain object.
* @memberof platform/commonUI/browse * @memberof platform/commonUI/browse
* @constructor * @constructor
* @implements {Action}
*/ */
function NavigateAction(navigationService, $q, context) { function NavigateAction(navigationService, $q, context) {
var domainObject = context.domainObject; this.domainObject = context.domainObject;
this.$q = $q;
function perform() { this.navigationService = navigationService;
// Set navigation, and wrap like a promise
return $q.when(navigationService.setNavigation(domainObject));
} }
return {
/** /**
* Navigate to the object described in the context. * Navigate to the object described in the context.
* @returns {Promise} a promise that is resolved once the * @returns {Promise} a promise that is resolved once the
* navigation has been updated * 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 * @constructor
*/ */
function NavigationService() { function NavigationService() {
var navigated, this.navigated = undefined;
callbacks = []; this.callbacks = [];
// Getter for current navigation
function getNavigation() {
return navigated;
} }
// Setter for navigation; invokes callbacks /**
function setNavigation(value) { * Get the current navigation state.
if (navigated !== value) { * @returns {DomainObject} the object that is navigated-to
navigated = value; */
callbacks.forEach(function (callback) { 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); 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 * Listen for changes in navigation. The passed callback will
* be invoked with the new domain object of navigation when * be invoked with the new domain object of navigation when
* this changes. * this changes.
* @param {function} callback the callback to invoke when * @param {function} callback the callback to invoke when
* navigation state changes * 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. * Stop listening for changes in navigation state.
* @param {function} callback the callback which should * @param {function} callback the callback which should
* no longer be invoked when navigation state * no longer be invoked when navigation state
* changes * 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; return NavigationService;

View File

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

View File

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

View File

@ -25,6 +25,76 @@ define(
function () { function () {
"use strict"; "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 * The ActionAggregator makes several actionService
* instances act as those they were one. When requesting * instances act as those they were one. When requesting
@ -33,7 +103,8 @@ define(
* *
* @memberof platform/core * @memberof platform/core
* @constructor * @constructor
* @param {ActionProvider[]} actionProviders an array * @implements {ActionService}
* @param {ActionService[]} actionProviders an array
* of action services * of action services
*/ */
function ActionAggregator(actionProviders) { function ActionAggregator(actionProviders) {