diff --git a/platform/commonUI/browse/src/MenuArrowController.js b/platform/commonUI/browse/src/MenuArrowController.js index f3894df03c..5c4916e099 100644 --- a/platform/commonUI/browse/src/MenuArrowController.js +++ b/platform/commonUI/browse/src/MenuArrowController.js @@ -37,16 +37,25 @@ define( * @constructor */ function MenuArrowController($scope) { - function showMenu(event) { - var actionContext = {key: 'menu', domainObject: $scope.domainObject, event: event}; - $scope.domainObject.getCapability('action').perform(actionContext); - } - - return { - showMenu: showMenu - }; + this.$scope = $scope; } + /** + * 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; } ); diff --git a/platform/commonUI/browse/src/creation/CreateAction.js b/platform/commonUI/browse/src/creation/CreateAction.js index e30ff06d05..984b26cfe5 100644 --- a/platform/commonUI/browse/src/creation/CreateAction.js +++ b/platform/commonUI/browse/src/creation/CreateAction.js @@ -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,79 +52,83 @@ 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: 1. Show dialog - a. Prepare dialog contents - b. Invoke dialogService + a. Prepare dialog contents + b. Invoke dialogService 2. Create new object in persistence service - a. Generate UUID - b. Store model + a. Generate UUID + b. Store model 3. Mutate destination container - a. Get mutation capability - b. Add new id to composition + a. Get mutation capability + b. Add new id to composition 4. Persist destination container - a. ...use persistence capability. + 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); + // The wizard will handle creating the form model based + // on the type... + 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); - } - - function doNothing() { - // Create cancelled, do nothing - return false; - } - - return dialogService.getUserInput( - wizard.getFormStructure(), - wizard.getInitialFormValue() - ).then(persistResult, doNothing); + // Create and persist the new object, based on user + // input. + function persistResult(formValue) { + var parent = wizard.getLocation(formValue), + newModel = wizard.createModel(formValue); + return self.creationService.createObject(newModel, parent); } - 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, + function doNothing() { + // Create cancelled, do nothing + return false; + } - /** - * 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# - */ - getMetadata: function () { - return { - key: 'create', - glyph: type.getGlyph(), - name: type.getName(), - type: type.getKey(), - description: type.getDescription(), - context: context - }; - } - }; - } + return this.dialogService.getUserInput( + wizard.getFormStructure(), + wizard.getInitialFormValue() + ).then(persistResult, doNothing); + }; + + + /** + * Metadata associated with a Create action. + * @typedef {ActionMetadata} CreateActionMetadata + * @property {string} type the key for the type of domain object + * to be created + */ + + /** + * Get metadata about this action. + * @returns {CreateActionMetadata} metadata about this action + */ + CreateAction.prototype.getMetadata = function () { + return this.metadata; + }; return CreateAction; } diff --git a/platform/commonUI/browse/src/creation/CreateActionProvider.js b/platform/commonUI/browse/src/creation/CreateActionProvider.js index 1078089d3f..4ca2bce59f 100644 --- a/platform/commonUI/browse/src/creation/CreateActionProvider.js +++ b/platform/commonUI/browse/src/creation/CreateActionProvider.js @@ -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,45 +47,41 @@ 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) { - var context = actionContext || {}, - key = context.key, - destination = context.domainObject; - - // We only provide Create actions, and we need a - // domain object to serve as the container for the - // newly-created object (although the user may later - // make a different selection) - if (key !== 'create' || !destination) { - return []; - } - - // Introduce one create action per type - return typeService.listTypes().filter(function (type) { - return type.hasFeature("creation"); - }).map(function (type) { - return new CreateAction( - type, - destination, - context, - dialogService, - creationService, - policyService - ); - }); - } - }; + 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, + self = this; + + // We only provide Create actions, and we need a + // domain object to serve as the container for the + // newly-created object (although the user may later + // make a different selection) + if (key !== 'create' || !destination) { + return []; + } + + // Introduce one create action per type + return this.typeService.listTypes().filter(function (type) { + return type.hasFeature("creation"); + }).map(function (type) { + return new CreateAction( + type, + destination, + context, + self.dialogService, + self.creationService, + self.policyService + ); + }); + }; + return CreateActionProvider; } ); diff --git a/platform/commonUI/browse/src/creation/CreateWizard.js b/platform/commonUI/browse/src/creation/CreateWizard.js index bd013f5acc..4073ed7e90 100644 --- a/platform/commonUI/browse/src/creation/CreateWizard.js +++ b/platform/commonUI/browse/src/creation/CreateWizard.js @@ -35,112 +35,113 @@ 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 && - locatingObject.getCapability('type'); + locatingObject.getCapability('type'); return locatingType && policyService.allow( - "composition", - locatingType, - type - ); + "composition", + locatingType, + type + ); } + sections.push({ + name: "Properties", + rows: this.properties.map(function (property, index) { + // Property definition is same as form row definition + var row = Object.create(property.getDefinition()); + + // Use index as the key into the formValue; + // this correlates to the indexing provided by + // getInitialFormValue + row.key = index; + + return row; + }) + }); + + // Ensure there is always a "save in" section + sections.push({ name: 'Location', rows: [{ + name: "Save In", + control: "locator", + validate: validateLocation, + key: "createParent" + }]}); + 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) { - // Property definition is same as form row definition - var row = Object.create(property.getDefinition()); - - // Use index as the key into the formValue; - // this correlates to the indexing provided by - // getInitialFormValue - row.key = index; - - return row; - }) - }); - - // Ensure there is always a "save in" section - sections.push({ name: 'Location', rows: [{ - name: "Save In", - control: "locator", - validate: validateLocation, - key: "createParent" - }]}); - - return { - sections: sections, - name: "Create a New " + 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 () { - // Start with initial values for properties - var formValue = properties.map(function (property) { - return property.getValue(model); - }); - - // Include the createParent - formValue.createParent = 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; - }, - /** - * 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# - */ - createModel: function (formValue) { - // Clone - var newModel = JSON.parse(JSON.stringify(model)); - - // Always use the type from the type definition - newModel.type = type.getKey(); - - // Update all properties - properties.forEach(function (property, index) { - property.setValue(newModel, formValue[index]); - }); - - return newModel; - } + sections: sections, + 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 + */ + CreateWizard.prototype.getInitialFormValue = function () { + // Start with initial values for properties + var model = this.model, + formValue = this.properties.map(function (property) { + return property.getValue(model); + }); - } + // Include the createParent + 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} + */ + 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 + */ + CreateWizard.prototype.createModel = function (formValue) { + // Clone + var newModel = JSON.parse(JSON.stringify(this.model)); + + // Always use the type from the type definition + newModel.type = this.type.getKey(); + + // Update all properties + this.properties.forEach(function (property, index) { + property.setValue(newModel, formValue[index]); + }); + + return newModel; + }; return CreateWizard; } diff --git a/platform/commonUI/browse/src/creation/CreationService.js b/platform/commonUI/browse/src/creation/CreationService.js index a4dffdd8e4..44f47fe20f 100644 --- a/platform/commonUI/browse/src/creation/CreationService.js +++ b/platform/commonUI/browse/src/creation/CreationService.js @@ -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) { + self.$log.warn(NON_PERSISTENT_WARNING); + return self.$q.reject(new Error(NON_PERSISTENT_WARNING)); + } - // 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)); - } - - // 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) { + // 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 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; } diff --git a/platform/commonUI/browse/src/navigation/NavigateAction.js b/platform/commonUI/browse/src/navigation/NavigateAction.js index 04ada63690..47a8fa3e16 100644 --- a/platform/commonUI/browse/src/navigation/NavigateAction.js +++ b/platform/commonUI/browse/src/navigation/NavigateAction.js @@ -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; + this.domainObject = context.domainObject; + this.$q = $q; + this.navigationService = navigationService; + } - function perform() { - // Set navigation, and wrap like a promise - return $q.when(navigationService.setNavigation(domainObject)); - } - - 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 - }; + /** + * Navigate to the object described in the context. + * @returns {Promise} a promise that is resolved once the + * navigation has been updated + */ + NavigateAction.prototype.perform = function () { + // Set navigation, and wrap like a promise + return this.$q.when( + this.navigationService.setNavigation(this.domainObject) + ); } /** diff --git a/platform/commonUI/browse/src/navigation/NavigationService.js b/platform/commonUI/browse/src/navigation/NavigationService.js index cd2ab1b206..0779ce3026 100644 --- a/platform/commonUI/browse/src/navigation/NavigationService.js +++ b/platform/commonUI/browse/src/navigation/NavigationService.js @@ -36,67 +36,52 @@ define( * @constructor */ function NavigationService() { - var navigated, - callbacks = []; + this.navigated = undefined; + this.callbacks = []; + } - // Getter for current navigation - function getNavigation() { - return navigated; - } + /** + * Get the current navigation state. + * @returns {DomainObject} the object that is navigated-to + */ + NavigationService.prototype.getNavigation = function () { + return this.navigated; + }; - // Setter for navigation; invokes callbacks - function setNavigation(value) { - if (navigated !== value) { - navigated = value; - 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; + /** + * 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); }); } + }; - 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, - /** - * 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 - }; + /** + * 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 + */ + 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 + */ + NavigationService.prototype.removeListener = function (callback) { + this.callbacks = this.callbacks.filter(function (cb) { + return cb !== callback; + }); } return NavigationService; diff --git a/platform/commonUI/browse/src/windowing/FullscreenAction.js b/platform/commonUI/browse/src/windowing/FullscreenAction.js index cea492f892..82d92fbd40 100644 --- a/platform/commonUI/browse/src/windowing/FullscreenAction.js +++ b/platform/commonUI/browse/src/windowing/FullscreenAction.js @@ -37,37 +37,30 @@ 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 () { - screenfull.toggle(); - }, - /** - * Get metadata about this action, including the - * applicable glyph to display. - * @memberof platform/commonUI/browse.FullscreenAction# - */ - 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 - // full screen. - var metadata = Object.create(FullscreenAction); - metadata.glyph = screenfull.isFullscreen ? "_" : "z"; - metadata.description = screenfull.isFullscreen ? - EXIT_FULLSCREEN : ENTER_FULLSCREEN; - metadata.group = "windowing"; - metadata.context = context; - return metadata; - } - }; + this.context = context; } + FullscreenAction.prototype.perform = function () { + screenfull.toggle(); + }; + + 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 + // full screen. + var metadata = Object.create(FullscreenAction); + metadata.glyph = screenfull.isFullscreen ? "_" : "z"; + metadata.description = screenfull.isFullscreen ? + EXIT_FULLSCREEN : ENTER_FULLSCREEN; + metadata.group = "windowing"; + metadata.context = this.context; + return metadata; + }; + return FullscreenAction; } ); diff --git a/platform/commonUI/browse/src/windowing/NewTabAction.js b/platform/commonUI/browse/src/windowing/NewTabAction.js index 8d38f994e2..72ef1ebcb2 100644 --- a/platform/commonUI/browse/src/windowing/NewTabAction.js +++ b/platform/commonUI/browse/src/windowing/NewTabAction.js @@ -35,34 +35,25 @@ 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; - } - - 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"); - } - }; + context = context || {}; + + this.urlService = urlService; + this.$window = $window; + + // Choose the object to be opened into a new tab + this.domainObject = context.selectedObject || context.domainObject; } + NewTabAction.prototype.perform = function () { + this.$window.open( + this.urlService.urlForNewTab("browse", this.domainObject), + "_blank" + ); + }; + return NewTabAction; } ); diff --git a/platform/core/src/actions/ActionAggregator.js b/platform/core/src/actions/ActionAggregator.js index c12f3cf07d..d0094a8a90 100644 --- a/platform/core/src/actions/ActionAggregator.js +++ b/platform/core/src/actions/ActionAggregator.js @@ -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) {