diff --git a/platform/representation/src/MCTRepresentation.js b/platform/representation/src/MCTRepresentation.js index 8a02fa07e7..49b2ae0f57 100644 --- a/platform/representation/src/MCTRepresentation.js +++ b/platform/representation/src/MCTRepresentation.js @@ -238,6 +238,25 @@ define( }; } + /** + * A representer participates in the process of instantiating a + * representation of a domain object. + * + * @interface Representer + * @augments {Destroyable} + */ + /** + * Set the current representation in use, and the domain + * object being represented. + * + * @method Representer#represent + * @param {RepresentationDefinition} representation the + * definition of the representation in use + * @param {DomainObject} domainObject the domain object + * being represented + */ + + return MCTRepresentation; } ); diff --git a/platform/representation/src/actions/ContextMenuAction.js b/platform/representation/src/actions/ContextMenuAction.js index 9544f26ca9..14367c7f55 100644 --- a/platform/representation/src/actions/ContextMenuAction.js +++ b/platform/representation/src/actions/ContextMenuAction.js @@ -47,71 +47,78 @@ define( * @param $rootScope Angular's root scope * @param actionContexr the context in which the action * should be performed + * @implements {Action} */ function ContextMenuAction($compile, $document, $window, $rootScope, actionContext) { - - function perform() { - var winDim = [$window.innerWidth, $window.innerHeight], - eventCoors = [actionContext.event.pageX, actionContext.event.pageY], - menuDim = GestureConstants.MCT_MENU_DIMENSIONS, - body = $document.find('body'), - scope = $rootScope.$new(), - goLeft = eventCoors[0] + menuDim[0] > winDim[0], - goUp = eventCoors[1] + menuDim[1] > winDim[1], - menu; - - // Remove the context menu - function dismiss() { - menu.remove(); - body.off("click", dismiss); - dismissExistingMenu = undefined; - } - - // Dismiss any menu which was already showing - if (dismissExistingMenu) { - dismissExistingMenu(); - } - - // ...and record the presence of this menu. - dismissExistingMenu = dismiss; - - // Set up the scope, including menu positioning - scope.domainObject = actionContext.domainObject; - scope.menuStyle = {}; - scope.menuStyle[goLeft ? "right" : "left"] = - (goLeft ? (winDim[0] - eventCoors[0]) : eventCoors[0]) + 'px'; - scope.menuStyle[goUp ? "bottom" : "top"] = - (goUp ? (winDim[1] - eventCoors[1]) : eventCoors[1]) + 'px'; - scope.menuClass = { - "go-left": goLeft, - "go-up": goUp, - "context-menu-holder": true - }; - - // Create the context menu - menu = $compile(MENU_TEMPLATE)(scope); - - // Add the menu to the body - body.append(menu); - - // Stop propagation so that clicks on the menu do not close the menu - menu.on('mousedown', function (event) { - event.stopPropagation(); - }); - - // Dismiss the menu when body is clicked elsewhere - // ('mousedown' because 'click' breaks left-click context menus) - body.on('mousedown', dismiss); - - // Don't launch browser's context menu - actionContext.event.preventDefault(); - } - - return { - perform: perform - }; + this.$compile = $compile; + this.actionContext = actionContext; + this.getDocument = function () { return $document; }; + this.getWindow = function () { return $window; }; + this.getRootScope = function () { return $rootScope; }; } + ContextMenuAction.prototype.perform = function () { + var $compile = this.$compile, + $document = this.getDocument(), + $window = this.getWindow(), + $rootScope = this.getRootScope(), + actionContext = this.actionContext, + winDim = [$window.innerWidth, $window.innerHeight], + eventCoors = [actionContext.event.pageX, actionContext.event.pageY], + menuDim = GestureConstants.MCT_MENU_DIMENSIONS, + body = $document.find('body'), + scope = $rootScope.$new(), + goLeft = eventCoors[0] + menuDim[0] > winDim[0], + goUp = eventCoors[1] + menuDim[1] > winDim[1], + menu; + + // Remove the context menu + function dismiss() { + menu.remove(); + body.off("click", dismiss); + dismissExistingMenu = undefined; + } + + // Dismiss any menu which was already showing + if (dismissExistingMenu) { + dismissExistingMenu(); + } + + // ...and record the presence of this menu. + dismissExistingMenu = dismiss; + + // Set up the scope, including menu positioning + scope.domainObject = actionContext.domainObject; + scope.menuStyle = {}; + scope.menuStyle[goLeft ? "right" : "left"] = + (goLeft ? (winDim[0] - eventCoors[0]) : eventCoors[0]) + 'px'; + scope.menuStyle[goUp ? "bottom" : "top"] = + (goUp ? (winDim[1] - eventCoors[1]) : eventCoors[1]) + 'px'; + scope.menuClass = { + "go-left": goLeft, + "go-up": goUp, + "context-menu-holder": true + }; + + // Create the context menu + menu = $compile(MENU_TEMPLATE)(scope); + + // Add the menu to the body + body.append(menu); + + // Stop propagation so that clicks on the menu do not close the menu + menu.on('mousedown', function (event) { + event.stopPropagation(); + }); + + // Dismiss the menu when body is clicked elsewhere + // ('mousedown' because 'click' breaks left-click context menus) + body.on('mousedown', dismiss); + + // Don't launch browser's context menu + actionContext.event.preventDefault(); + }; + return ContextMenuAction; } ); diff --git a/platform/representation/src/gestures/ContextMenuGesture.js b/platform/representation/src/gestures/ContextMenuGesture.js index a0de0cce85..5d6d7c325f 100644 --- a/platform/representation/src/gestures/ContextMenuGesture.js +++ b/platform/representation/src/gestures/ContextMenuGesture.js @@ -36,33 +36,31 @@ define( * @memberof platform/representation * @constructor * @param element the jqLite-wrapped element which should exhibit - * the context mennu + * the context menu * @param {DomainObject} domainObject the object on which actions * in the context menu will be performed + * @implements {Gesture} */ function ContextMenuGesture(element, domainObject) { - var actionContext, - stop; - + function showMenu(event) { + domainObject.getCapability('action').perform({ + key: 'menu', + domainObject: domainObject, + event: event + }); + } + // When context menu event occurs, show object actions instead - element.on('contextmenu', function (event) { - actionContext = {key: 'menu', domainObject: domainObject, event: event}; - stop = domainObject.getCapability('action').perform(actionContext); - }); - - return { - /** - * Detach any event handlers associated with this gesture. - * @method - * @memberof ContextMenuGesture - * @memberof platform/representation.ContextMenuGesture# - */ - destroy: function () { - element.off('contextmenu', stop); - } - }; + element.on('contextmenu', showMenu); + + this.showMenuCallback = showMenu; + this.element = element; } + ContextMenuGesture.prototype.destroy = function () { + this.element.off('contextmenu', this.showMenu); + }; + return ContextMenuGesture; } ); diff --git a/platform/representation/src/gestures/DragGesture.js b/platform/representation/src/gestures/DragGesture.js index 6a4dfb0d63..f240aa156e 100644 --- a/platform/representation/src/gestures/DragGesture.js +++ b/platform/representation/src/gestures/DragGesture.js @@ -35,6 +35,7 @@ define( * * @memberof platform/representation * @constructor + * @implements {Gesture} * @param $log Angular's logging service * @param element the jqLite-wrapped element which should become * draggable @@ -104,22 +105,19 @@ define( element.on('dragstart', startDrag); element.on('dragend', endDrag); - return { - /** - * Detach any event handlers associated with this gesture. - * @memberof DragGesture - * @method - * @memberof platform/representation.DragGesture# - */ - destroy: function () { - // Detach listener - element.removeAttr('draggable'); - element.off('dragstart', startDrag); - } - }; + this.element = element; + this.startDragCallback = startDrag; + this.endDragCallback = endDrag; } + DragGesture.prototype.destroy = function () { + // Detach listener + this.element.removeAttr('draggable'); + this.element.off('dragstart', this.startDragCallback); + this.element.off('dragend', this.endDragCallback); + }; + return DragGesture; } ); diff --git a/platform/representation/src/gestures/DropGesture.js b/platform/representation/src/gestures/DropGesture.js index 7afffc3c03..bfcb85d3bb 100644 --- a/platform/representation/src/gestures/DropGesture.js +++ b/platform/representation/src/gestures/DropGesture.js @@ -32,7 +32,7 @@ define( /** * A DropGesture adds and maintains event handlers upon an element * such that it may act as a drop target for drag-drop composition. - + * * @memberof platform/representation * @constructor * @param $q Angular's $q, for promise handling @@ -40,7 +40,6 @@ define( * @param {DomainObject} domainObject the domain object whose * composition should be modified as a result of the drop. */ - function DropGesture(dndService, $q, element, domainObject) { var actionCapability = domainObject.getCapability('action'), action; // Action for the drop, when it occurs @@ -122,19 +121,16 @@ define( element.on('drop', drop); } - return { - /** - * Detach any event handlers associated with this gesture. - * @memberof platform/representation.DropGesture# - */ - destroy: function () { - element.off('dragover', dragOver); - element.off('drop', drop); - } - }; - + this.element = element; + this.dragOverCallback = dragOver; + this.dropCallback = drop; } + DropGesture.prototype.destroy = function () { + this.element.off('dragover', this.dragOverCallback); + this.element.off('drop', this.dropCallback); + }; + return DropGesture; } diff --git a/platform/representation/src/gestures/GestureProvider.js b/platform/representation/src/gestures/GestureProvider.js index 255c33a28f..30b463505d 100644 --- a/platform/representation/src/gestures/GestureProvider.js +++ b/platform/representation/src/gestures/GestureProvider.js @@ -29,6 +29,29 @@ define( function () { "use strict"; + /** + * Handles the attachment of gestures (responses to DOM events, + * generally) to DOM elements which represent domain objects. + * + * @interface GestureService + */ + /** + * Attach a set of gestures (indicated by key) to a + * DOM element which represents a specific domain object. + * @method GestureService#attachGestures + * @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 {Destroyable} an object with a `destroy` + * method which can (and should) be used when + * gestures should no longer be applied to an element. + */ + /** * The GestureProvider exposes defined gestures. Gestures are used * do describe and handle general-purpose interactions with the DOM @@ -41,6 +64,7 @@ define( * where they are used. * * @memberof platform/representation + * @implements {GestureService} * @constructor * @param {Gesture[]} gestures an array of all gestures which are * available as extensions @@ -48,19 +72,28 @@ define( 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(); - } - } + // Assemble all gestures into a map, for easy look up + gestures.forEach(function (gesture) { + gestureMap[gesture.key] = gesture; + }); - 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) { + this.gestureMap = 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(); + } + } + + GestureProvider.prototype.attachGestures = 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 gestureMap = this.gestureMap, + attachedGestures = gestureKeys.map(function (key) { return gestureMap[key]; }).filter(function (Gesture) { return Gesture !== undefined && (Gesture.appliesTo ? @@ -70,42 +103,32 @@ define( return new Gesture(element, domainObject); }); - return { - destroy: function () { - // Just call all the individual "destroy" methods - attachedGestures.forEach(releaseGesture); - } - }; - } - - // Assemble all gestures into a map, for easy look up - gestures.forEach(function (gesture) { - gestureMap[gesture.key] = gesture; - }); - - 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. - * @memberof platform/representation.GestureProvider# - */ - attachGestures: attachGestures + destroy: function () { + // Just call all the individual "destroy" methods + attachedGestures.forEach(releaseGesture); + } }; - } + }; + + /** + * A destroyable object may have resources allocated which require + * explicit release. + * + * @interface Destroyable + */ + /** + * Release any resources associated with this object. + * + * @method Destroyable#destroy + */ + + /** + * A gesture describes manners in which certain representations of + * domain objects may respond to DOM events upon those representations. + * @interface Gesture + * @augments Destroyable + */ return GestureProvider; } diff --git a/platform/representation/src/gestures/GestureRepresenter.js b/platform/representation/src/gestures/GestureRepresenter.js index db74d923eb..9353722ae8 100644 --- a/platform/representation/src/gestures/GestureRepresenter.js +++ b/platform/representation/src/gestures/GestureRepresenter.js @@ -38,50 +38,33 @@ define( * @param {Scope} scope the Angular scope for this representation * @param element the JQLite-wrapped mct-representation element * @constructor + * @implements {Representer} * @memberof platform/representation */ function GestureRepresenter(gestureService, scope, element) { - var gestureHandle; - - function destroy() { - // Release any resources associated with these gestures - if (gestureHandle) { - gestureHandle.destroy(); - } - } - - function represent(representation, domainObject) { - // Clear out any existing gestures - destroy(); - - // Attach gestures - by way of the service. - gestureHandle = gestureService.attachGestures( - element, - domainObject, - (representation || {}).gestures || [] - ); - } - - 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/representation.GestureRepresenter# - */ - represent: represent, - /** - * Release any resources associated with this representer. - * @memberof platform/representation.GestureRepresenter# - */ - destroy: destroy - }; + this.gestureService = gestureService; + this.element = element; } + GestureRepresenter.prototype.represent = function represent(representation, domainObject) { + // Clear out any existing gestures + this.destroy(); + + // Attach gestures - by way of the service. + this.gestureHandle = this.gestureService.attachGestures( + this.element, + domainObject, + (representation || {}).gestures || [] + ); + }; + + GestureRepresenter.prototype.destroy = function () { + // Release any resources associated with these gestures + if (this.gestureHandle) { + this.gestureHandle.destroy(); + } + }; + return GestureRepresenter; } );