From ed53808556d7fbd3e01a288838f3dbb6f2b6ce7d Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 12 Aug 2015 13:45:48 -0700 Subject: [PATCH] [Code Style] Use prototypes in Layout bundle WTD-1482 --- .../features/layout/src/FixedController.js | 199 ++++++------ .../features/layout/src/FixedDragHandle.js | 147 +++++---- platform/features/layout/src/FixedProxy.js | 58 ++-- .../layout/src/LayoutCompositionPolicy.js | 25 +- .../features/layout/src/LayoutController.js | 292 +++++++++--------- platform/features/layout/src/LayoutDrag.js | 110 +++---- .../features/layout/src/elements/BoxProxy.js | 1 - .../layout/src/elements/ElementFactory.js | 41 ++- .../layout/src/elements/ElementProxy.js | 186 +++++------ .../layout/src/elements/ImageProxy.js | 1 + .../layout/src/elements/LineHandle.js | 82 ++--- .../features/layout/src/elements/LineProxy.js | 1 + .../layout/src/elements/ResizeHandle.js | 66 ++-- .../layout/src/elements/TelemetryProxy.js | 1 + .../features/layout/src/elements/TextProxy.js | 1 + 15 files changed, 615 insertions(+), 596 deletions(-) diff --git a/platform/features/layout/src/FixedController.js b/platform/features/layout/src/FixedController.js index 4458e988ea..410c0d2f94 100644 --- a/platform/features/layout/src/FixedController.js +++ b/platform/features/layout/src/FixedController.js @@ -39,20 +39,16 @@ define( * @param {Scope} $scope the controller's Angular scope */ function FixedController($scope, $q, dialogService, telemetrySubscriber, telemetryFormatter) { - var gridSize = DEFAULT_GRID_SIZE, - dragging, + var self = this, subscription, - elementProxies = [], names = {}, // Cache names by ID values = {}, // Cache values by ID - elementProxiesById = {}, - handles = [], - moveHandle, - selection; + elementProxiesById = {}; // Convert from element x/y/width/height to an - // apropriate ng-style argument, to position elements. + // appropriate ng-style argument, to position elements. function convertPosition(elementProxy) { + var gridSize = self.gridSize; // Multiply position/dimensions by grid size return { left: (gridSize[0] * elementProxy.x()) + 'px', @@ -64,7 +60,7 @@ define( // Update the style for a selected element function updateSelectionStyle() { - var element = selection && selection.get(); + var element = self.selection && self.selection.get(); if (element) { element.style = convertPosition(element); } @@ -74,7 +70,7 @@ define( function generateDragHandle(elementHandle) { return new FixedDragHandle( elementHandle, - gridSize, + self.gridSize, updateSelectionStyle, $scope.commit ); @@ -85,17 +81,6 @@ define( return element.handles().map(generateDragHandle); } - // Select an element - function select(element) { - if (selection) { - // Update selection... - selection.select(element); - // ...as well as move, resize handles - moveHandle = generateDragHandle(element); - handles = generateDragHandles(element); - } - } - // Update the displayed value for this object function updateValue(telemetryObject) { var id = telemetryObject && telemetryObject.getId(), @@ -121,9 +106,9 @@ define( // Update element positions when grid size changes function updateElementPositions(layoutGrid) { // Update grid size from model - gridSize = layoutGrid || DEFAULT_GRID_SIZE; + self.gridSize = layoutGrid || DEFAULT_GRID_SIZE; - elementProxies.forEach(function (elementProxy) { + self.elementProxies.forEach(function (elementProxy) { elementProxy.style = convertPosition(elementProxy); }); } @@ -154,7 +139,7 @@ define( function refreshElements() { // Cache selection; we are instantiating new proxies // so we may want to restore this. - var selected = selection && selection.get(), + var selected = self.selection && self.selection.get(), elements = (($scope.configuration || {}).elements || []), index = -1; // Start with a 'not-found' value @@ -164,20 +149,20 @@ define( } // Create the new proxies... - elementProxies = elements.map(makeProxyElement); + self.elementProxies = elements.map(makeProxyElement); // Clear old selection, and restore if appropriate - if (selection) { - selection.deselect(); + if (self.selection) { + self.selection.deselect(); if (index > -1) { - select(elementProxies[index]); + self.select(self.elementProxies[index]); } } // Finally, rebuild lists of elements by id to // facilitate faster update when new telemetry comes in. elementProxiesById = {}; - elementProxies.forEach(function (elementProxy) { + self.elementProxies.forEach(function (elementProxy) { var id = elementProxy.id; if (elementProxy.element.type === 'fixed.telemetry') { // Provide it a cached name/value to avoid flashing @@ -232,7 +217,9 @@ define( // Refresh displayed elements refreshElements(); // Select the newly-added element - select(elementProxies[elementProxies.length - 1]); + self.select( + self.elementProxies[self.elementProxies.length - 1] + ); // Mark change as persistable if ($scope.commit) { $scope.commit("Dropped an element."); @@ -249,8 +236,8 @@ define( // Store the position of this element. addElement({ type: "fixed.telemetry", - x: Math.floor(position.x / gridSize[0]), - y: Math.floor(position.y / gridSize[1]), + x: Math.floor(position.x / self.gridSize[0]), + y: Math.floor(position.y / self.gridSize[1]), id: id, stroke: "transparent", color: "#cccccc", @@ -260,12 +247,17 @@ define( }); } + this.gridSize = DEFAULT_GRID_SIZE; + this.elementProxies = []; + this.generateDragHandle = generateDragHandle; + this.generateDragHandles = generateDragHandles; + // Track current selection state - selection = $scope.selection; + this.selection = $scope.selection; // Expose the view's selection proxy - if (selection) { - selection.proxy(new FixedProxy(addElement, $q, dialogService)); + if (this.selection) { + this.selection.proxy(new FixedProxy(addElement, $q, dialogService)); } // Refresh list of elements whenever model changes @@ -285,73 +277,80 @@ define( // Position panes where they are dropped $scope.$on("mctDrop", handleDrop); - - return { - /** - * Get the size of the grid, in pixels. The returned array - * is in the form `[x, y]`. - * @returns {number[]} the grid size - * @memberof platform/features/layout.FixedController# - */ - getGridSize: function () { - return gridSize; - }, - /** - * Get an array of elements in this panel; these are - * decorated proxies for both selection and display. - * @returns {Array} elements in this panel - * @memberof platform/features/layout.FixedController# - */ - getElements: function () { - return elementProxies; - }, - /** - * Check if the element is currently selected, or (if no - * argument is supplied) get the currently selected element. - * @returns {boolean} true if selected - * @memberof platform/features/layout.FixedController# - */ - selected: function (element) { - return selection && ((arguments.length > 0) ? - selection.selected(element) : selection.get()); - }, - /** - * Set the active user selection in this view. - * @param element the element to select - * @memberof platform/features/layout.FixedController# - */ - select: select, - /** - * Clear the current user selection. - * @memberof platform/features/layout.FixedController# - */ - clearSelection: function () { - if (selection) { - selection.deselect(); - handles = []; - moveHandle = undefined; - } - }, - /** - * Get drag handles. - * @returns {Array} drag handles for the current selection - * @memberof platform/features/layout.FixedController# - */ - handles: function () { - return handles; - }, - /** - * Get the handle to handle dragging to reposition an element. - * @returns {FixedDragHandle} the drag handle - * @memberof platform/features/layout.FixedController# - */ - moveHandle: function () { - return moveHandle; - } - }; - } + /** + * Get the size of the grid, in pixels. The returned array + * is in the form `[x, y]`. + * @returns {number[]} the grid size + * @memberof platform/features/layout.FixedController# + */ + FixedController.prototype.getGridSize = function () { + return this.gridSize; + }; + + /** + * Get an array of elements in this panel; these are + * decorated proxies for both selection and display. + * @returns {Array} elements in this panel + */ + FixedController.prototype.getElements = function () { + return this.elementProxies; + }; + + /** + * Check if the element is currently selected, or (if no + * argument is supplied) get the currently selected element. + * @returns {boolean} true if selected + */ + FixedController.prototype.selected = function (element) { + var selection = this.selection; + return selection && ((arguments.length > 0) ? + selection.selected(element) : selection.get()); + }; + + /** + * Set the active user selection in this view. + * @param element the element to select + */ + FixedController.prototype.select = function select(element) { + if (this.selection) { + // Update selection... + this.selection.select(element); + // ...as well as move, resize handles + this.mvHandle = this.generateDragHandle(element); + this.resizeHandles = this.generateDragHandles(element); + } + }; + + /** + * Clear the current user selection. + */ + FixedController.prototype.clearSelection = function () { + if (this.selection) { + this.selection.deselect(); + this.resizeHandles = []; + this.mvHandle = undefined; + } + }; + + /** + * Get drag handles. + * @returns {platform/features/layout.FixedDragHandle[]} + * drag handles for the current selection + */ + FixedController.prototype.handles = function () { + return this.resizeHandles; + }; + + /** + * Get the handle to handle dragging to reposition an element. + * @returns {platform/features/layout.FixedDragHandle} the drag handle + */ + FixedController.prototype.moveHandle = function () { + return this.mvHandle; + }; + return FixedController; } ); diff --git a/platform/features/layout/src/FixedDragHandle.js b/platform/features/layout/src/FixedDragHandle.js index a72bbbadb6..afc98eabaf 100644 --- a/platform/features/layout/src/FixedDragHandle.js +++ b/platform/features/layout/src/FixedDragHandle.js @@ -37,86 +37,77 @@ define( * @constructor */ function FixedDragHandle(elementHandle, gridSize, update, commit) { - var self = {}, - dragging; - - // Generate ng-style-appropriate style for positioning - function getStyle() { - // Adjust from grid to pixel coordinates - var x = elementHandle.x() * gridSize[0], - y = elementHandle.y() * gridSize[1]; - - // Convert to a CSS style centered on that point - return { - left: (x - DRAG_HANDLE_SIZE[0] / 2) + 'px', - top: (y - DRAG_HANDLE_SIZE[1] / 2) + 'px', - width: DRAG_HANDLE_SIZE[0] + 'px', - height: DRAG_HANDLE_SIZE[1] + 'px' - }; - } - - // Begin a drag gesture - function startDrag() { - // Cache initial x/y positions - dragging = { x: elementHandle.x(), y: elementHandle.y() }; - } - - // Reposition during drag - function continueDrag(delta) { - if (dragging) { - // Update x/y positions (snapping to grid) - elementHandle.x( - dragging.x + Math.round(delta[0] / gridSize[0]) - ); - elementHandle.y( - dragging.y + Math.round(delta[1] / gridSize[1]) - ); - // Invoke update callback - if (update) { - update(); - } - } - } - - // Conclude a drag gesture - function endDrag() { - // Clear cached state - dragging = undefined; - // Mark change as complete - if (commit) { - commit("Dragged handle."); - } - } - - return { - /** - * Get a CSS style to position this drag handle. - * @returns CSS style object (for `ng-style`) - * @memberof platform/features/layout.FixedDragHandle# - */ - style: getStyle, - /** - * Start a drag gesture. This should be called when a drag - * begins to track initial state. - * @memberof platform/features/layout.FixedDragHandle# - */ - startDrag: startDrag, - /** - * Continue a drag gesture; update x/y positions. - * @param {number[]} delta x/y pixel difference since drag - * started - * @memberof platform/features/layout.FixedDragHandle# - */ - continueDrag: continueDrag, - /** - * End a drag gesture. This should be callled when a drag - * concludes to trigger commit of changes. - * @memberof platform/features/layout.FixedDragHandle# - */ - endDrag: endDrag - }; + this.elementHandle = elementHandle; + this.gridSize = gridSize; + this.update = update; + this.commit = commit; } + /** + * Get a CSS style to position this drag handle. + * @returns CSS style object (for `ng-style`) + * @memberof platform/features/layout.FixedDragHandle# + */ + FixedDragHandle.prototype.style = function () { + // Adjust from grid to pixel coordinates + var x = this.elementHandle.x() * this.gridSize[0], + y = this.elementHandle.y() * this.gridSize[1]; + + // Convert to a CSS style centered on that point + return { + left: (x - DRAG_HANDLE_SIZE[0] / 2) + 'px', + top: (y - DRAG_HANDLE_SIZE[1] / 2) + 'px', + width: DRAG_HANDLE_SIZE[0] + 'px', + height: DRAG_HANDLE_SIZE[1] + 'px' + }; + }; + + /** + * Start a drag gesture. This should be called when a drag + * begins to track initial state. + */ + FixedDragHandle.prototype.startDrag = function startDrag() { + // Cache initial x/y positions + this.dragging = { + x: this.elementHandle.x(), + y: this.elementHandle.y() + }; + }; + + /** + * Continue a drag gesture; update x/y positions. + * @param {number[]} delta x/y pixel difference since drag + * started + */ + FixedDragHandle.prototype.continueDrag = function (delta) { + if (this.dragging) { + // Update x/y positions (snapping to grid) + this.elementHandle.x( + this.dragging.x + Math.round(delta[0] / this.gridSize[0]) + ); + this.elementHandle.y( + this.dragging.y + Math.round(delta[1] / this.gridSize[1]) + ); + // Invoke update callback + if (this.update) { + this.update(); + } + } + }; + + /** + * End a drag gesture. This should be callled when a drag + * concludes to trigger commit of changes. + */ + FixedDragHandle.prototype.endDrag = function () { + // Clear cached state + this.dragging = undefined; + // Mark change as complete + if (this.commit) { + this.commit("Dragged handle."); + } + }; + return FixedDragHandle; } ); diff --git a/platform/features/layout/src/FixedProxy.js b/platform/features/layout/src/FixedProxy.js index b84985f9d8..bb6c2f16c8 100644 --- a/platform/features/layout/src/FixedProxy.js +++ b/platform/features/layout/src/FixedProxy.js @@ -37,33 +37,41 @@ define( * when adding a new element will require user input */ function FixedProxy(addElementCallback, $q, dialogService) { - var factory = new ElementFactory(dialogService); - - return { - /** - * Add a new visual element to this view. - * @memberof platform/features/layout.FixedProxy# - */ - add: function (type) { - // Place a configured element into the view configuration - function addElement(element) { - // Configure common properties of the element - element.x = element.x || 0; - element.y = element.y || 0; - element.width = element.width || 1; - element.height = element.height || 1; - element.type = type; - - // Finally, add it to the view's configuration - addElementCallback(element); - } - - // Defer creation to the factory - $q.when(factory.createElement(type)).then(addElement); - } - }; + this.factory = new ElementFactory(dialogService); + this.$q = $q; + this.addElementCallback = addElementCallback; } + /** + * Add a new visual element to this view. Supported types are: + * + * * `fixed.image` + * * `fixed.box` + * * `fixed.text` + * * `fixed.line` + * + * @param {string} type the type of element to add + */ + FixedProxy.prototype.add = function (type) { + var addElementCallback = this.addElementCallback; + + // Place a configured element into the view configuration + function addElement(element) { + // Configure common properties of the element + element.x = element.x || 0; + element.y = element.y || 0; + element.width = element.width || 1; + element.height = element.height || 1; + element.type = type; + + // Finally, add it to the view's configuration + addElementCallback(element); + } + + // Defer creation to the factory + this.$q.when(this.factory.createElement(type)).then(addElement); + }; + return FixedProxy; } ); diff --git a/platform/features/layout/src/LayoutCompositionPolicy.js b/platform/features/layout/src/LayoutCompositionPolicy.js index 8f9b6ccf31..23d5a4f882 100644 --- a/platform/features/layout/src/LayoutCompositionPolicy.js +++ b/platform/features/layout/src/LayoutCompositionPolicy.js @@ -31,25 +31,20 @@ define( * They cannot contain folders. * @constructor * @memberof platform/features/layout + * @implements {Policy.} */ function LayoutCompositionPolicy() { - return { - /** - * Is the type identified by the candidate allowed to - * contain the type described by the context? - * @memberof platform/features/layout.LayoutCompositionPolicy# - */ - allow: function (candidate, context) { - var isFolderInLayout = - candidate && - context && - candidate.instanceOf('layout') && - context.instanceOf('folder'); - return !isFolderInLayout; - } - }; } + LayoutCompositionPolicy.prototype.allow = function (candidate, context) { + var isFolderInLayout = + candidate && + context && + candidate.instanceOf('layout') && + context.instanceOf('folder'); + return !isFolderInLayout; + }; + return LayoutCompositionPolicy; } ); diff --git a/platform/features/layout/src/LayoutController.js b/platform/features/layout/src/LayoutController.js index bf7c265de0..89364a1bb2 100644 --- a/platform/features/layout/src/LayoutController.js +++ b/platform/features/layout/src/LayoutController.js @@ -45,11 +45,7 @@ define( * @param {Scope} $scope the controller's Angular scope */ function LayoutController($scope) { - var gridSize = DEFAULT_GRID_SIZE, - activeDrag, - activeDragId, - rawPositions = {}, - positions = {}; + var self = this; // Utility function to copy raw positions from configuration, // without writing directly to configuration (to avoid triggering @@ -62,47 +58,6 @@ define( return copy; } - // Convert from { positions: ..., dimensions: ... } to an - // apropriate ng-style argument, to position frames. - function convertPosition(raw) { - // Multiply position/dimensions by grid size - return { - left: (gridSize[0] * raw.position[0]) + 'px', - top: (gridSize[1] * raw.position[1]) + 'px', - width: (gridSize[0] * raw.dimensions[0]) + 'px', - height: (gridSize[1] * raw.dimensions[1]) + 'px' - }; - } - - // Generate default positions for a new panel - function defaultDimensions() { - return MINIMUM_FRAME_SIZE.map(function (min, i) { - return Math.max( - Math.ceil(min / gridSize[i]), - DEFAULT_DIMENSIONS[i] - ); - }); - } - - // Generate a default position (in its raw format) for a frame. - // Use an index to ensure that default positions are unique. - function defaultPosition(index) { - return { - position: [index, index], - dimensions: defaultDimensions() - }; - } - - // Store a computed position for a contained frame by its - // domain object id. Called in a forEach loop, so arguments - // are as expected there. - function populatePosition(id, index) { - rawPositions[id] = - rawPositions[id] || defaultPosition(index || 0); - positions[id] = - convertPosition(rawPositions[id]); - } - // Compute panel positions based on the layout's object model function lookupPanels(ids) { var configuration = $scope.configuration || {}; @@ -112,27 +67,32 @@ define( ids = ids || []; // Pull panel positions from configuration - rawPositions = shallowCopy(configuration.panels || {}, ids); + self.rawPositions = + shallowCopy(configuration.panels || {}, ids); // Clear prior computed positions - positions = {}; + self.positions = {}; // Update width/height that we are tracking - gridSize = ($scope.model || {}).layoutGrid || DEFAULT_GRID_SIZE; + self.gridSize = + ($scope.model || {}).layoutGrid || DEFAULT_GRID_SIZE; // Compute positions and add defaults where needed - ids.forEach(populatePosition); + ids.forEach(function (id, index) { + self.populatePosition(id, index); + }); } // Update grid size when it changed function updateGridSize(layoutGrid) { - var oldSize = gridSize; + var oldSize = self.gridSize; - gridSize = layoutGrid || DEFAULT_GRID_SIZE; + self.gridSize = layoutGrid || DEFAULT_GRID_SIZE; // Only update panel positions if this actually changed things - if (gridSize[0] !== oldSize[0] || gridSize[1] !== oldSize[1]) { - lookupPanels(Object.keys(positions)); + if (self.gridSize[0] !== oldSize[0] || + self.gridSize[1] !== oldSize[1]) { + lookupPanels(Object.keys(self.positions)); } } @@ -150,8 +110,8 @@ define( // Store the position of this panel. $scope.configuration.panels[id] = { position: [ - Math.floor(position.x / gridSize[0]), - Math.floor(position.y / gridSize[1]) + Math.floor(position.x / self.gridSize[0]), + Math.floor(position.y / self.gridSize[1]) ], dimensions: DEFAULT_DIMENSIONS }; @@ -160,13 +120,39 @@ define( $scope.commit("Dropped a frame."); } // Populate template-facing position for this id - populatePosition(id); + self.populatePosition(id); // Layout may contain embedded views which will // listen for drops, so call preventDefault() so // that they can recognize that this event is handled. e.preventDefault(); } + // End drag; we don't want to put $scope into this + // because it triggers "cpws" (copy window or scope) + // errors in Angular. + this.endDragInScope = function () { + // Write to configuration; this is watched and + // saved by the EditRepresenter. + $scope.configuration = + $scope.configuration || {}; + // Make sure there is a "panels" field in the + // view configuration. + $scope.configuration.panels = + $scope.configuration.panels || {}; + // Store the position of this panel. + $scope.configuration.panels[self.activeDragId] = + self.rawPositions[self.activeDragId]; + // Mark this object as dirty to encourage persistence + if ($scope.commit) { + $scope.commit("Moved frame."); + } + }; + + this.positions = {}; + this.rawPositions = {}; + this.gridSize = DEFAULT_GRID_SIZE; + this.$scope = $scope; + // Watch for changes to the grid size in the model $scope.$watch("model.layoutGrid", updateGridSize); @@ -175,92 +161,116 @@ define( // Position panes where they are dropped $scope.$on("mctDrop", handleDrop); - - return { - /** - * Get a style object for a frame with the specified domain - * object identifier, suitable for use in an `ng-style` - * directive to position a frame as configured for this layout. - * @param {string} id the object identifier - * @returns {Object.} an object with - * appropriate left, width, etc fields for positioning - * @memberof platform/features/layout.LayoutController# - */ - getFrameStyle: function (id) { - // Called in a loop, so just look up; the "positions" - // object is kept up to date by a watch. - return positions[id]; - }, - /** - * Start a drag gesture to move/resize a frame. - * - * The provided position and dimensions factors will determine - * whether this is a move or a resize, and what type it - * will be. For instance, a position factor of [1, 1] - * will move a frame along with the mouse as the drag - * proceeds, while a dimension factor of [0, 0] will leave - * dimensions unchanged. Combining these in different - * ways results in different handles; a position factor of - * [1, 0] and a dimensions factor of [-1, 0] will implement - * a left-edge resize, as the horizontal position will move - * with the mouse while the horizontal dimensions shrink in - * kind (and vertical properties remain unmodified.) - * - * @param {string} id the identifier of the domain object - * in the frame being manipulated - * @param {number[]} posFactor the position factor - * @param {number[]} dimFactor the dimensions factor - * @memberof platform/features/layout.LayoutController# - */ - startDrag: function (id, posFactor, dimFactor) { - activeDragId = id; - activeDrag = new LayoutDrag( - rawPositions[id], - posFactor, - dimFactor, - gridSize - ); - }, - /** - * Continue an active drag gesture. - * @param {number[]} delta the offset, in pixels, - * of the current pointer position, relative - * to its position when the drag started - * @memberof platform/features/layout.LayoutController# - */ - continueDrag: function (delta) { - if (activeDrag) { - rawPositions[activeDragId] = - activeDrag.getAdjustedPosition(delta); - populatePosition(activeDragId); - } - }, - /** - * End the active drag gesture. This will update the - * view configuration. - * @memberof platform/features/layout.LayoutController# - */ - endDrag: function () { - // Write to configuration; this is watched and - // saved by the EditRepresenter. - $scope.configuration = - $scope.configuration || {}; - // Make sure there is a "panels" field in the - // view configuration. - $scope.configuration.panels = - $scope.configuration.panels || {}; - // Store the position of this panel. - $scope.configuration.panels[activeDragId] = - rawPositions[activeDragId]; - // Mark this object as dirty to encourage persistence - if ($scope.commit) { - $scope.commit("Moved frame."); - } - } - }; - } + // Convert from { positions: ..., dimensions: ... } to an + // apropriate ng-style argument, to position frames. + LayoutController.prototype.convertPosition = function (raw) { + var gridSize = this.gridSize; + // Multiply position/dimensions by grid size + return { + left: (gridSize[0] * raw.position[0]) + 'px', + top: (gridSize[1] * raw.position[1]) + 'px', + width: (gridSize[0] * raw.dimensions[0]) + 'px', + height: (gridSize[1] * raw.dimensions[1]) + 'px' + }; + }; + + // Generate default positions for a new panel + LayoutController.prototype.defaultDimensions = function () { + var gridSize = this.gridSize; + return MINIMUM_FRAME_SIZE.map(function (min, i) { + return Math.max( + Math.ceil(min / gridSize[i]), + DEFAULT_DIMENSIONS[i] + ); + }); + }; + + // Generate a default position (in its raw format) for a frame. + // Use an index to ensure that default positions are unique. + LayoutController.prototype.defaultPosition = function (index) { + return { + position: [index, index], + dimensions: this.defaultDimensions() + }; + }; + + // Store a computed position for a contained frame by its + // domain object id. Called in a forEach loop, so arguments + // are as expected there. + LayoutController.prototype.populatePosition = function (id, index) { + this.rawPositions[id] = + this.rawPositions[id] || this.defaultPosition(index || 0); + this.positions[id] = + this.convertPosition(this.rawPositions[id]); + }; + + /** + * Get a style object for a frame with the specified domain + * object identifier, suitable for use in an `ng-style` + * directive to position a frame as configured for this layout. + * @param {string} id the object identifier + * @returns {Object.} an object with + * appropriate left, width, etc fields for positioning + */ + LayoutController.prototype.getFrameStyle = function (id) { + // Called in a loop, so just look up; the "positions" + // object is kept up to date by a watch. + return this.positions[id]; + }; + + /** + * Start a drag gesture to move/resize a frame. + * + * The provided position and dimensions factors will determine + * whether this is a move or a resize, and what type it + * will be. For instance, a position factor of [1, 1] + * will move a frame along with the mouse as the drag + * proceeds, while a dimension factor of [0, 0] will leave + * dimensions unchanged. Combining these in different + * ways results in different handles; a position factor of + * [1, 0] and a dimensions factor of [-1, 0] will implement + * a left-edge resize, as the horizontal position will move + * with the mouse while the horizontal dimensions shrink in + * kind (and vertical properties remain unmodified.) + * + * @param {string} id the identifier of the domain object + * in the frame being manipulated + * @param {number[]} posFactor the position factor + * @param {number[]} dimFactor the dimensions factor + */ + LayoutController.prototype.startDrag = function (id, posFactor, dimFactor) { + this.activeDragId = id; + this.activeDrag = new LayoutDrag( + this.rawPositions[id], + posFactor, + dimFactor, + this.gridSize + ); + }; + /** + * Continue an active drag gesture. + * @param {number[]} delta the offset, in pixels, + * of the current pointer position, relative + * to its position when the drag started + */ + LayoutController.prototype.continueDrag = function (delta) { + if (this.activeDrag) { + this.rawPositions[this.activeDragId] = + this.activeDrag.getAdjustedPosition(delta); + this.populatePosition(this.activeDragId); + } + }; + + /** + * End the active drag gesture. This will update the + * view configuration. + */ + LayoutController.prototype.endDrag = function () { + this.endDragInScope(); + }; + return LayoutController; } ); diff --git a/platform/features/layout/src/LayoutDrag.js b/platform/features/layout/src/LayoutDrag.js index 45f1ad9856..0c0ef6b1a7 100644 --- a/platform/features/layout/src/LayoutDrag.js +++ b/platform/features/layout/src/LayoutDrag.js @@ -54,63 +54,63 @@ define( * @memberof platform/features/layout */ function LayoutDrag(rawPosition, posFactor, dimFactor, gridSize) { - // Convert a delta from pixel coordinates to grid coordinates, - // rounding to whole-number grid coordinates. - function toGridDelta(pixelDelta) { - return pixelDelta.map(function (v, i) { - return Math.round(v / gridSize[i]); - }); - } - - // Utility function to perform element-by-element multiplication - function multiply(array, factors) { - return array.map(function (v, i) { - return v * factors[i]; - }); - } - - // Utility function to perform element-by-element addition - function add(array, other) { - return array.map(function (v, i) { - return v + other[i]; - }); - } - - // Utility function to perform element-by-element max-choosing - function max(array, other) { - return array.map(function (v, i) { - return Math.max(v, other[i]); - }); - } - - function getAdjustedPosition(pixelDelta) { - var gridDelta = toGridDelta(pixelDelta); - return { - position: max(add( - rawPosition.position, - multiply(gridDelta, posFactor) - ), [0, 0]), - dimensions: max(add( - rawPosition.dimensions, - multiply(gridDelta, dimFactor) - ), [1, 1]) - }; - - } - - return { - /** - * Get a new position object in grid coordinates, with - * position and dimensions both offset appropriately - * according to the factors supplied in the constructor. - * @param {number[]} pixelDelta the offset from the - * original position, in pixels - * @memberof platform/features/layout.LayoutDrag# - */ - getAdjustedPosition: getAdjustedPosition - }; + this.rawPosition = rawPosition; + this.posFactor = posFactor; + this.dimFactor = dimFactor; + this.gridSize = gridSize; } + // Convert a delta from pixel coordinates to grid coordinates, + // rounding to whole-number grid coordinates. + function toGridDelta(gridSize, pixelDelta) { + return pixelDelta.map(function (v, i) { + return Math.round(v / gridSize[i]); + }); + } + + // Utility function to perform element-by-element multiplication + function multiply(array, factors) { + return array.map(function (v, i) { + return v * factors[i]; + }); + } + + // Utility function to perform element-by-element addition + function add(array, other) { + return array.map(function (v, i) { + return v + other[i]; + }); + } + + // Utility function to perform element-by-element max-choosing + function max(array, other) { + return array.map(function (v, i) { + return Math.max(v, other[i]); + }); + } + + + /** + * Get a new position object in grid coordinates, with + * position and dimensions both offset appropriately + * according to the factors supplied in the constructor. + * @param {number[]} pixelDelta the offset from the + * original position, in pixels + */ + LayoutDrag.prototype.getAdjustedPosition = function (pixelDelta) { + var gridDelta = toGridDelta(this.gridSize, pixelDelta); + return { + position: max(add( + this.rawPosition.position, + multiply(gridDelta, this.posFactor) + ), [0, 0]), + dimensions: max(add( + this.rawPosition.dimensions, + multiply(gridDelta, this.dimFactor) + ), [1, 1]) + }; + }; + return LayoutDrag; } diff --git a/platform/features/layout/src/elements/BoxProxy.js b/platform/features/layout/src/elements/BoxProxy.js index 46ec351b1f..a4b6bc4c09 100644 --- a/platform/features/layout/src/elements/BoxProxy.js +++ b/platform/features/layout/src/elements/BoxProxy.js @@ -48,7 +48,6 @@ define( * Get/set this element's fill color. (Omitting the * argument makes this act as a getter.) * @method - * @memberof BoxProxy * @param {string} fill the new fill color * @returns {string} the fill color * @memberof platform/features/layout.BoxProxy# diff --git a/platform/features/layout/src/elements/ElementFactory.js b/platform/features/layout/src/elements/ElementFactory.js index 01cc4be729..63dbb82757 100644 --- a/platform/features/layout/src/elements/ElementFactory.js +++ b/platform/features/layout/src/elements/ElementFactory.js @@ -89,29 +89,28 @@ define( * @constructor */ function ElementFactory(dialogService) { - return { - /** - * Create a new element for the fixed position view. - * @param {string} type the type of element to create - * @returns {Promise|object} the created element, or a promise - * for that element - * @memberof platform/features/layout.ElementFactory# - */ - createElement: function (type) { - var initialState = INITIAL_STATES[type] || {}; - - // Clone that state - initialState = JSON.parse(JSON.stringify(initialState)); - - // Show a dialog to configure initial state, if appropriate - return DIALOGS[type] ? dialogService.getUserInput( - DIALOGS[type], - initialState - ) : initialState; - } - }; + this.dialogService = dialogService; } + /** + * Create a new element for the fixed position view. + * @param {string} type the type of element to create + * @returns {Promise|object} the created element, or a promise + * for that element + */ + ElementFactory.prototype.createElement = function (type) { + var initialState = INITIAL_STATES[type] || {}; + + // Clone that state + initialState = JSON.parse(JSON.stringify(initialState)); + + // Show a dialog to configure initial state, if appropriate + return DIALOGS[type] ? this.dialogService.getUserInput( + DIALOGS[type], + initialState + ) : initialState; + }; + return ElementFactory; } ); diff --git a/platform/features/layout/src/elements/ElementProxy.js b/platform/features/layout/src/elements/ElementProxy.js index 1261104a9e..5f3ef17bcb 100644 --- a/platform/features/layout/src/elements/ElementProxy.js +++ b/platform/features/layout/src/elements/ElementProxy.js @@ -56,96 +56,108 @@ define( * @param {Array} elements the full array of elements */ function ElementProxy(element, index, elements) { - var handles = [ new ResizeHandle(element, 1, 1) ]; + this.resizeHandles = [ new ResizeHandle(element, 1, 1) ]; - return { - /** - * The element as stored in the view configuration. - * @memberof platform/features/layout.ElementProxy# - */ - element: element, - /** - * Get and/or set the x position of this element. - * Units are in fixed position grid space. - * @param {number} [x] the new x position (if setting) - * @returns {number} the x position - * @memberof platform/features/layout.ElementProxy# - */ - x: new AccessorMutator(element, 'x', clamp), - /** - * Get and/or set the y position of this element. - * Units are in fixed position grid space. - * @param {number} [y] the new y position (if setting) - * @returns {number} the y position - * @memberof platform/features/layout.ElementProxy# - */ - y: new AccessorMutator(element, 'y', clamp), - /** - * Get and/or set the stroke color of this element. - * @param {string} [stroke] the new stroke color (if setting) - * @returns {string} the stroke color - * @memberof platform/features/layout.ElementProxy# - */ - stroke: new AccessorMutator(element, 'stroke'), - /** - * Get and/or set the width of this element. - * Units are in fixed position grid space. - * @param {number} [w] the new width (if setting) - * @returns {number} the width - * @memberof platform/features/layout.ElementProxy# - */ - width: new AccessorMutator(element, 'width'), - /** - * Get and/or set the height of this element. - * Units are in fixed position grid space. - * @param {number} [h] the new height (if setting) - * @returns {number} the height - * @memberof platform/features/layout.ElementProxy# - */ - height: new AccessorMutator(element, 'height'), - /** - * Change the display order of this element. - * @param {string} o where to move this element; - * one of "top", "up", "down", or "bottom" - * @memberof platform/features/layout.ElementProxy# - */ - order: function (o) { - var delta = ORDERS[o] || 0, - desired = Math.max( - Math.min(index + delta, elements.length - 1), - 0 - ); - // Move to the desired index, if this is a change - if ((desired !== index) && (elements[index] === element)) { - // Splice out the current element - elements.splice(index, 1); - // Splice it back in at the correct index - elements.splice(desired, 0, element); - // Track change in index (proxy should be recreated - // anyway, but be consistent) - index = desired; - } - }, - /** - * Remove this element from the fixed position view. - * @memberof platform/features/layout.ElementProxy# - */ - remove: function () { - if (elements[index] === element) { - elements.splice(index, 1); - } - }, - /** - * Get handles to control specific features of this element, - * e.g. corner size. - * @memberof platform/features/layout.ElementProxy# - */ - handles: function () { - return handles; - } - }; + /** + * The element as stored in the view configuration. + * @memberof platform/features/layout.ElementProxy# + */ + this.element = element; + + /** + * Get and/or set the x position of this element. + * Units are in fixed position grid space. + * @param {number} [x] the new x position (if setting) + * @returns {number} the x position + * @memberof platform/features/layout.ElementProxy# + */ + this.x = new AccessorMutator(element, 'x', clamp); + + /** + * Get and/or set the y position of this element. + * Units are in fixed position grid space. + * @param {number} [y] the new y position (if setting) + * @returns {number} the y position + * @memberof platform/features/layout.ElementProxy# + */ + this.y = new AccessorMutator(element, 'y', clamp); + + /** + * Get and/or set the stroke color of this element. + * @param {string} [stroke] the new stroke color (if setting) + * @returns {string} the stroke color + * @memberof platform/features/layout.ElementProxy# + */ + this.stroke = new AccessorMutator(element, 'stroke'); + + /** + * Get and/or set the width of this element. + * Units are in fixed position grid space. + * @param {number} [w] the new width (if setting) + * @returns {number} the width + * @memberof platform/features/layout.ElementProxy# + */ + this.width = new AccessorMutator(element, 'width'); + + /** + * Get and/or set the height of this element. + * Units are in fixed position grid space. + * @param {number} [h] the new height (if setting) + * @returns {number} the height + * @memberof platform/features/layout.ElementProxy# + */ + this.height = new AccessorMutator(element, 'height'); + + this.index = index; + this.elements = elements; } + /** + * Change the display order of this element. + * @param {string} o where to move this element; + * one of "top", "up", "down", or "bottom" + */ + ElementProxy.prototype.order = function (o) { + var index = this.index, + elements = this.elements, + element = this.element, + delta = ORDERS[o] || 0, + desired = Math.max( + Math.min(index + delta, elements.length - 1), + 0 + ); + // Move to the desired index, if this is a change + if ((desired !== index) && (elements[index] === element)) { + // Splice out the current element + elements.splice(index, 1); + // Splice it back in at the correct index + elements.splice(desired, 0, element); + // Track change in index (proxy should be recreated + // anyway, but be consistent) + this.index = desired; + } + }; + + /** + * Remove this element from the fixed position view. + */ + ElementProxy.prototype.remove = function () { + var index = this.index; + if (this.elements[index] === this.element) { + this.elements.splice(index, 1); + } + }; + + /** + * Get handles to control specific features of this element, + * e.g. corner size. + * @return {platform/features/layout.ElementHandle[]} handles + * for moving/resizing this element + */ + ElementProxy.prototype.handles = function () { + return this.resizeHandles; + }; + return ElementProxy; } ); diff --git a/platform/features/layout/src/elements/ImageProxy.js b/platform/features/layout/src/elements/ImageProxy.js index 28d84a3fa4..22ef3ef0c3 100644 --- a/platform/features/layout/src/elements/ImageProxy.js +++ b/platform/features/layout/src/elements/ImageProxy.js @@ -38,6 +38,7 @@ define( * configuration * @param index the element's index within its array * @param {Array} elements the full array of elements + * @augments {platform/features/layout.ElementProxy} */ function ImageProxy(element, index, elements) { var proxy = new ElementProxy(element, index, elements); diff --git a/platform/features/layout/src/elements/LineHandle.js b/platform/features/layout/src/elements/LineHandle.js index f4178f107b..c61d1f3802 100644 --- a/platform/features/layout/src/elements/LineHandle.js +++ b/platform/features/layout/src/elements/LineHandle.js @@ -37,48 +37,54 @@ define( * @param {string} yProperty field which stores x position * @param {string} xOther field which stores x of other end * @param {string} yOther field which stores y of other end + * @implements {platform/features/layout.ElementHandle} */ function LineHandle(element, xProperty, yProperty, xOther, yOther) { - return { - /** - * Get/set the x position of the lower-right corner - * of the handle-controlled element, changing size - * as necessary. - * @memberof platform/features/layout.LineHandle# - */ - x: function (value) { - if (arguments.length > 0) { - // Ensure we stay in view - value = Math.max(value, 0); - // Make sure end points will still be different - if (element[yOther] !== element[yProperty] || - element[xOther] !== value) { - element[xProperty] = value; - } - } - return element[xProperty]; - }, - /** - * Get/set the y position of the lower-right corner - * of the handle-controlled element, changing size - * as necessary. - * @memberof platform/features/layout.LineHandle# - */ - y: function (value) { - if (arguments.length > 0) { - // Ensure we stay in view - value = Math.max(value, 0); - // Make sure end points will still be different - if (element[xOther] !== element[xProperty] || - element[yOther] !== value) { - element[yProperty] = value; - } - } - return element[yProperty]; - } - }; + this.element = element; + this.xProperty = xProperty; + this.yProperty = yProperty; + this.xOther = xOther; + this.yOther = yOther; } + LineHandle.prototype.x = function (value) { + var element = this.element, + xProperty = this.xProperty, + yProperty = this.yProperty, + xOther = this.xOther, + yOther = this.yOther; + + if (arguments.length > 0) { + // Ensure we stay in view + value = Math.max(value, 0); + // Make sure end points will still be different + if (element[yOther] !== element[yProperty] || + element[xOther] !== value) { + element[xProperty] = value; + } + } + return element[xProperty]; + }; + + LineHandle.prototype.y = function (value) { + var element = this.element, + xProperty = this.xProperty, + yProperty = this.yProperty, + xOther = this.xOther, + yOther = this.yOther; + + if (arguments.length > 0) { + // Ensure we stay in view + value = Math.max(value, 0); + // Make sure end points will still be different + if (element[xOther] !== element[xProperty] || + element[yOther] !== value) { + element[yProperty] = value; + } + } + return element[yProperty]; + }; + return LineHandle; } diff --git a/platform/features/layout/src/elements/LineProxy.js b/platform/features/layout/src/elements/LineProxy.js index 0051b1d3c1..44cf282993 100644 --- a/platform/features/layout/src/elements/LineProxy.js +++ b/platform/features/layout/src/elements/LineProxy.js @@ -35,6 +35,7 @@ define( * configuration * @param index the element's index within its array * @param {Array} elements the full array of elements + * @augments {platform/features/layout.ElementProxy} */ function LineProxy(element, index, elements) { var proxy = new ElementProxy(element, index, elements), diff --git a/platform/features/layout/src/elements/ResizeHandle.js b/platform/features/layout/src/elements/ResizeHandle.js index c8fe59682c..757bb6218e 100644 --- a/platform/features/layout/src/elements/ResizeHandle.js +++ b/platform/features/layout/src/elements/ResizeHandle.js @@ -25,6 +25,11 @@ define( function () { 'use strict'; + /** + * @interface platform/features/layout.ElementHandle + * @private + */ + /** * Handle for changing width/height properties of an element. * This is used to support drag handles for different @@ -33,44 +38,35 @@ define( * @constructor */ function ResizeHandle(element, minWidth, minHeight) { - // Ensure reasonable defaults - minWidth = minWidth || 0; - minHeight = minHeight || 0; + this.element = element; - return { - /** - * Get/set the x position of the lower-right corner - * of the handle-controlled element, changing size - * as necessary. - * @memberof platform/features/layout.ResizeHandle# - */ - x: function (value) { - if (arguments.length > 0) { - element.width = Math.max( - minWidth, - value - element.x - ); - } - return element.x + element.width; - }, - /** - * Get/set the y position of the lower-right corner - * of the handle-controlled element, changing size - * as necessary. - * @memberof platform/features/layout.ResizeHandle# - */ - y: function (value) { - if (arguments.length > 0) { - element.height = Math.max( - minHeight, - value - element.y - ); - } - return element.y + element.height; - } - }; + // Ensure reasonable defaults + this.minWidth = minWidth || 0; + this.minHeight = minHeight || 0; } + ResizeHandle.prototype.x = function (value) { + var element = this.element; + if (arguments.length > 0) { + element.width = Math.max( + this.minWidth, + value - element.x + ); + } + return element.x + element.width; + }; + + ResizeHandle.prototype.y = function (value) { + var element = this.element; + if (arguments.length > 0) { + element.height = Math.max( + this.minHeight, + value - element.y + ); + } + return element.y + element.height; + }; + return ResizeHandle; } diff --git a/platform/features/layout/src/elements/TelemetryProxy.js b/platform/features/layout/src/elements/TelemetryProxy.js index 058048eca8..dbb7044c51 100644 --- a/platform/features/layout/src/elements/TelemetryProxy.js +++ b/platform/features/layout/src/elements/TelemetryProxy.js @@ -41,6 +41,7 @@ define( * configuration * @param index the element's index within its array * @param {Array} elements the full array of elements + * @augments {platform/features/layout.ElementProxy} */ function TelemetryProxy(element, index, elements) { var proxy = new TextProxy(element, index, elements); diff --git a/platform/features/layout/src/elements/TextProxy.js b/platform/features/layout/src/elements/TextProxy.js index d3ff832833..c5b5247c4c 100644 --- a/platform/features/layout/src/elements/TextProxy.js +++ b/platform/features/layout/src/elements/TextProxy.js @@ -38,6 +38,7 @@ define( * configuration * @param index the element's index within its array * @param {Array} elements the full array of elements + * @augments {platform/features/layout.ElementProxy} */ function TextProxy(element, index, elements) { var proxy = new BoxProxy(element, index, elements);