[Representation] Add JSDoc

Add in-line documentation for the representation component,
WTD-521.
This commit is contained in:
Victor Woeltjen 2014-11-23 15:28:46 -08:00
parent 2e0178ffa9
commit f89402132b
7 changed files with 138 additions and 6 deletions

View File

@ -57,7 +57,8 @@ define(
// Use the included controller to populate scope
controller: controller,
// Use ng-include as a template; it gets the real template path
// Use ng-include as a template; "inclusion" will be the real
// template path
template: '<ng-include src="inclusion"></ng-include>',
// Two-way bind key, ngModel, and parameters

View File

@ -55,13 +55,21 @@ define(
function link($scope, element) {
var gestureHandle;
// General-purpose refresh mechanism; should set up the scope
// as appropriate for current representation key and
// domain object.
function refresh() {
var representation = representationMap[$scope.key],
domainObject = $scope.domainObject,
uses = ((representation || {}).uses || []),
gestureKeys = ((representation || {}).gestures || []);
// Create an empty object named "representation", for this
// representation to store local variables into.
$scope.representation = {};
// Look up the actual template path, pass it to ng-include
// via the "inclusion" field
$scope.inclusion = pathMap[$scope.key];
// Any existing gestures are no longer valid; release them.
@ -69,12 +77,19 @@ define(
gestureHandle.destroy();
}
// Log if a key was given, but no matching representation
// was found.
if (!representation && $scope.key) {
$log.warn("No representation found for " + $scope.key);
}
// Populate scope with fields associated with the current
// domain object (if one has been passed in)
if (domainObject) {
// Always provide the model, as "model"
$scope.model = domainObject.getModel();
// Also provide any of the capabilities requested
uses.forEach(function (used) {
$log.debug([
"Requesting capability ",
@ -89,6 +104,8 @@ define(
});
});
// Finally, wire up any gestures that should be
// associated with this representation.
gestureHandle = gestureService.attachGestures(
element,
domainObject,
@ -97,15 +114,33 @@ define(
}
}
// Update the representation when the key changes (e.g. if a
// different representation has been selected)
$scope.$watch("key", refresh);
// Also update when the represented domain object changes
// (to a different object)
$scope.$watch("domainObject", refresh);
// Finally, also update when there is a new version of that
// same domain object; these changes should be tracked in the
// model's "modified" field, by the mutation capability.
$scope.$watch("domainObject.getModel().modified", refresh);
}
return {
// Only applicable at the element level
restrict: "E",
// Handle Angular's linking step
link: link,
// Use ng-include as a template; "inclusion" will be the real
// template path
template: '<ng-include src="inclusion"></ng-include>',
// Two-way bind key and parameters, get the represented domain
// object as "mct-object"
scope: { key: "=", domainObject: "=mctObject", parameters: "=" }
};
}

View File

@ -16,10 +16,18 @@ define(
dismissExistingMenu;
/**
* Add listeners to a view such that it launches a context menu for the
* object it contains.
* Add listeners to a representation such that it launches a
* custom context menu for the domain object it contains.
*
* @constructor
* @param $compile Angular's $compile service
* @param $document the current document
* @param $window the active window
* @param $rootScope Angular's root scope
* @param element the jqLite-wrapped element which should exhibit
* the context mennu
* @param {DomainObject} domainObject the object on which actions
* in the context menu will be performed
*/
function ContextMenuGesture($compile, $document, $window, $rootScope, element, domainObject) {
function showMenu(event) {
@ -73,6 +81,12 @@ define(
element.on('contextmenu', showMenu);
return {
/**
* Detach any event handlers associated with this gesture,
* and dismiss any visible menu.
* @method
* @memberof ContextMenuGesture
*/
destroy: function () {
// Scope has been destroyed, so remove all listeners.
if (dismissExistingMenu) {

View File

@ -9,10 +9,16 @@ define(
"use strict";
/**
* Add event handlers to a representation such that it may be
* dragged as the source for drag-drop composition.
*
* @constructor
* @param $log Angular's logging service
* @param element the jqLite-wrapped element which should become
* draggable
* @param {DomainObject} domainObject the domain object which
* is represented; this will be passed on drop.
*/
function DragGesture($log, element, domainObject) {
function startDrag(e) {
var event = (e || {}).originalEvent || e;
@ -20,7 +26,10 @@ define(
$log.debug("Initiating drag");
try {
// Set the data associated with the drag-drop operation
event.dataTransfer.effectAllowed = 'move';
// Support drop as plain-text (JSON); not used internally
event.dataTransfer.setData(
'text/plain',
JSON.stringify({
@ -28,12 +37,18 @@ define(
model: domainObject.getModel()
})
);
// For internal use, pass the object's identifier as
// part of the drag
event.dataTransfer.setData(
GestureConstants.MCT_DRAG_TYPE,
domainObject.getId()
);
} catch (err) {
// Exceptions at this point indicate that the browser
// do not fully support drag-and-drop (e.g. if
// dataTransfer is undefined)
$log.warn([
"Could not initiate drag due to ",
err.message
@ -42,11 +57,17 @@ define(
}
// Mark the element as draggable, and handle the dragstart event
$log.debug("Attaching drag gesture");
element.attr('draggable', 'true');
element.on('dragstart', startDrag);
return {
/**
* Detach any event handlers associated with this gesture.
* @memberof DragGesture
* @method
*/
destroy: function () {
// Detach listener
element.removeAttr('draggable');

View File

@ -9,8 +9,14 @@ define(
"use strict";
/**
*
* A DropGesture adds and maintains event handlers upon an element
* such that it may act as a drop target for drag-drop composition.
* @constructor
* @param $q Angular's $q, for promise handling
* @param element the jqLite-wrapped representation element
* @param {DomainObject} domainObject the domain object whose
* composition should be modified as a result of the drop.
*/
function DropGesture($q, element, domainObject) {
@ -25,6 +31,8 @@ define(
// TODO: Vary this based on modifier keys
event.dataTransfer.dropEffect = 'move';
// Indicate that we will accept the drag
event.preventDefault(); // Required in Chrome?
return false;
}
@ -33,11 +41,15 @@ define(
var event = (e || {}).originalEvent || e,
id = event.dataTransfer.getData(GestureConstants.MCT_DRAG_TYPE);
// Handle the drop; add the dropped identifier to the
// destination domain object's composition, and persist
// the change.
if (id) {
$q.when(domainObject.useCapability(
'mutation',
function (model) {
var composition = model.composition;
// Don't store the same id more than once
if (composition && // not-contains
!(composition.map(function (i) {
return i === id;
@ -48,17 +60,23 @@ define(
}
}
)).then(function (result) {
// If mutation was successful, persist the change
return result && doPersist();
});
}
}
// Listen for dragover, to indicate we'll accept a drag
element.on('dragover', dragOver);
// Listen for the drop itself
element.on('drop', drop);
return {
/**
* Detach any event handlers associated with this gesture.
*/
destroy: function () {
element.off('dragover', dragOver);
element.off('drop', drop);

View File

@ -4,6 +4,15 @@
* Module defining GestureConstants. Created by vwoeltje on 11/17/14.
*/
define({
/**
* The string identifier for the data type used for drag-and-drop
* composition of domain objects. (e.g. in event.dataTransfer.setData
* calls.)
*/
MCT_DRAG_TYPE: 'mct-domain-object-id',
/**
* An estimate for the dimensions of a context menu, used for
* positioning.
*/
MCT_MENU_DIMENSIONS: [ 170, 200 ]
});

View File

@ -9,19 +9,35 @@ define(
"use strict";
/**
* The GestureProvider exposes defined gestures. Gestures are used
* do describe and handle general-purpose interactions with the DOM
* that should be interpreted as interactions with domain objects,
* such as right-clicking to expose context menus.
*
* Gestures are defined individually as extensions of the
* `gestures` category. The gesture provider merely serves as an
* intermediary between these and the `mct-representation` directive
* where they are used.
*
* @constructor
* @param {Gesture[]} gestures an array of all gestures which are
* available as extensions
*/
function GestureProvider(gestures) {
var gestureMap = {};
function releaseGesture(gesture) {
// Invoke the gesture's "destroy" method (if there is one)
// to release any held resources and detach event handlers.
if (gesture && gesture.destroy) {
gesture.destroy();
}
}
function attachGestures(element, domainObject, gestureKeys) {
// Look up the desired gestures, filter for applicability,
// and instantiate them. Maintain a reference to allow them
// to be destroyed as a group later.
var attachedGestures = gestureKeys.map(function (key) {
return gestureMap[key];
}).filter(function (Gesture) {
@ -34,6 +50,7 @@ define(
return {
destroy: function () {
// Just call all the individual "destroy" methods
attachedGestures.forEach(releaseGesture);
}
};
@ -46,6 +63,23 @@ define(
return {
/**
* Attach a set of gestures (indicated by key) to a
* DOM element which represents a specific domain object.
* @method
* @memberof GestureProvider
* @param element the jqLite-wrapped DOM element which the
* user will interact with
* @param {DomainObject} domainObject the domain object which
* is represented by that element
* @param {string[]} gestureKeys an array of keys identifying
* which gestures should apply; these will be matched
* against the keys defined in the gestures' extension
* definitions
* @return {{ destroy: function }} an object with a `destroy`
* method which can (and should) be used when a
* gesture should no longer be applied to an element.
*/
attachGestures: attachGestures
};
}