+
{{ngModel.name}}
-
\ No newline at end of file
diff --git a/platform/features/layout/res/templates/elements/text.html b/platform/features/layout/res/templates/elements/text.html
index 627468d3e2..c398f2d53f 100644
--- a/platform/features/layout/res/templates/elements/text.html
+++ b/platform/features/layout/res/templates/elements/text.html
@@ -1,4 +1,6 @@
-
+
{{ngModel.element.text}}
\ No newline at end of file
diff --git a/platform/features/layout/res/templates/fixed.html b/platform/features/layout/res/templates/fixed.html
index 9d6d741a62..39415b6090 100644
--- a/platform/features/layout/res/templates/fixed.html
+++ b/platform/features/layout/res/templates/fixed.html
@@ -1,26 +1,42 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
\ No newline at end of file
diff --git a/platform/features/layout/src/FixedController.js b/platform/features/layout/src/FixedController.js
index 60bc813c80..850441d15f 100644
--- a/platform/features/layout/src/FixedController.js
+++ b/platform/features/layout/src/FixedController.js
@@ -1,8 +1,8 @@
/*global define*/
define(
- ['./LayoutDrag', './FixedProxy', './elements/ElementProxies'],
- function (LayoutDrag, FixedProxy, ElementProxies) {
+ ['./FixedProxy', './elements/ElementProxies', './FixedDragHandle'],
+ function (FixedProxy, ElementProxies, FixedDragHandle) {
"use strict";
var DEFAULT_DIMENSIONS = [ 2, 1 ],
@@ -27,6 +27,8 @@ define(
names = {}, // Cache names by ID
values = {}, // Cache values by ID
elementProxiesById = {},
+ handles = [],
+ moveHandle,
selection;
// Refresh cell styles (e.g. because grid extent changed)
@@ -64,6 +66,40 @@ define(
};
}
+ // Update the style for a selected element
+ function updateSelectionStyle() {
+ var element = selection && selection.get();
+ if (element) {
+ element.style = convertPosition(element);
+ }
+ }
+
+ // Generate a specific drag handle
+ function generateDragHandle(elementHandle) {
+ return new FixedDragHandle(
+ elementHandle,
+ gridSize,
+ updateSelectionStyle,
+ $scope.commit
+ );
+ }
+
+ // Generate drag handles for an element
+ function generateDragHandles(element) {
+ 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,7 +157,7 @@ define(
if (selection) {
selection.deselect();
if (index > -1) {
- selection.select(elementProxies[index]);
+ select(elementProxies[index]);
}
}
@@ -183,9 +219,7 @@ define(
// Refresh displayed elements
refreshElements();
// Select the newly-added element
- if (selection) {
- selection.select(elementProxies[elementProxies.length - 1]);
- }
+ select(elementProxies[elementProxies.length - 1]);
// Mark change as persistable
if ($scope.commit) {
$scope.commit("Dropped an element.");
@@ -201,7 +235,7 @@ define(
y: Math.floor(position.y / gridSize[1]),
id: id,
stroke: "transparent",
- color: "#717171",
+ color: "#cccccc",
titled: true,
width: DEFAULT_DIMENSIONS[0],
height: DEFAULT_DIMENSIONS[1]
@@ -274,82 +308,42 @@ define(
return elementProxies;
},
/**
- * Check if the element is currently selected.
+ * Check if the element is currently selected, or (if no
+ * argument is supplied) get the currently selected element.
* @returns {boolean} true if selected
*/
selected: function (element) {
- return selection && selection.selected(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
*/
- select: function (element) {
- if (selection) {
- selection.select(element);
- }
- },
+ select: select,
/**
* Clear the current user selection.
*/
clearSelection: function () {
if (selection) {
selection.deselect();
+ handles = [];
+ moveHandle = undefined;
}
},
/**
- * 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 element the raw (undecorated) element to drag
+ * Get drag handles.
+ * @returns {Array} drag handles for the current selection
*/
- startDrag: function (element) {
- // Only allow dragging in edit mode
- if ($scope.domainObject &&
- $scope.domainObject.hasCapability('editor')) {
- dragging = {
- element: element,
- x: element.x(),
- y: element.y()
- };
- }
+ handles: function () {
+ return handles;
},
/**
- * 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
+ * Get the handle to handle dragging to reposition an element.
+ * @returns {FixedDragHandle} the drag handle
*/
- continueDrag: function (delta) {
- if (dragging) {
- // Update x/y values
- dragging.element.x(dragging.x + Math.round(delta[0] / gridSize[0]));
- dragging.element.y(dragging.y + Math.round(delta[1] / gridSize[1]));
- // Update display position
- dragging.element.style = convertPosition(dragging.element);
- }
- },
- /**
- * End the active drag gesture. This will update the
- * view configuration.
- */
- endDrag: function () {
- // Mark this object as dirty to encourage persistence
- if (dragging && $scope.commit) {
- dragging = undefined;
- $scope.commit("Moved element.");
- }
+ moveHandle: function () {
+ return moveHandle;
}
};
diff --git a/platform/features/layout/src/FixedDragHandle.js b/platform/features/layout/src/FixedDragHandle.js
new file mode 100644
index 0000000000..c20cfa74a7
--- /dev/null
+++ b/platform/features/layout/src/FixedDragHandle.js
@@ -0,0 +1,96 @@
+/*global define*/
+
+define(
+ [],
+ function () {
+ 'use strict';
+
+
+ // Drag handle dimensions
+ var DRAG_HANDLE_SIZE = [ 6, 6 ];
+
+ /**
+ * Template-displayable drag handle for an element in fixed
+ * position mode.
+ * @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`)
+ */
+ style: getStyle,
+ /**
+ * Start a drag gesture. This should be called when a drag
+ * begins to track initial state.
+ */
+ startDrag: startDrag,
+ /**
+ * Continue a drag gesture; update x/y positions.
+ * @param {number[]} delta x/y pixel difference since drag
+ * started
+ */
+ continueDrag: continueDrag,
+ /**
+ * End a drag gesture. This should be callled when a drag
+ * concludes to trigger commit of changes.
+ */
+ endDrag: endDrag
+ };
+ }
+
+ return FixedDragHandle;
+ }
+);
\ No newline at end of file
diff --git a/platform/features/layout/src/elements/AccessorMutator.js b/platform/features/layout/src/elements/AccessorMutator.js
index ac296ddd05..284bbd9b18 100644
--- a/platform/features/layout/src/elements/AccessorMutator.js
+++ b/platform/features/layout/src/elements/AccessorMutator.js
@@ -8,14 +8,26 @@ define(
/**
* Utility function for creating getter-setter functions,
* since these are frequently useful for element proxies.
+ *
+ * An optional third argument may be supplied in order to
+ * constrain or modify arguments when using as a setter;
+ * this argument is a function which takes two arguments
+ * (the current value for the property, and the requested
+ * new value.) This is useful when values need to be kept
+ * in certain ranges; specifically, to keep x/y positions
+ * non-negative in a fixed position view.
+ *
* @constructor
* @param {Object} object the object to get/set values upon
* @param {string} key the property to get/set
+ * @param {function} [updater] function used to process updates
*/
- function AccessorMutator(object, key) {
+ function AccessorMutator(object, key, updater) {
return function (value) {
if (arguments.length > 0) {
- object[key] = value;
+ object[key] = updater ?
+ updater(value, object[key]) :
+ value;
}
return object[key];
};
diff --git a/platform/features/layout/src/elements/ElementFactory.js b/platform/features/layout/src/elements/ElementFactory.js
index 3ae8cad80a..d80a8c420c 100644
--- a/platform/features/layout/src/elements/ElementFactory.js
+++ b/platform/features/layout/src/elements/ElementFactory.js
@@ -24,7 +24,7 @@ define(
"fixed.text": {
fill: "transparent",
stroke: "transparent",
- color: "#717171"
+ color: "#cccccc"
}
},
DIALOGS = {
diff --git a/platform/features/layout/src/elements/ElementProxy.js b/platform/features/layout/src/elements/ElementProxy.js
index 7d5fa3540b..f2e22f92de 100644
--- a/platform/features/layout/src/elements/ElementProxy.js
+++ b/platform/features/layout/src/elements/ElementProxy.js
@@ -1,8 +1,8 @@
/*global define*/
define(
- ['./AccessorMutator'],
- function (AccessorMutator) {
+ ['./AccessorMutator', './ResizeHandle'],
+ function (AccessorMutator, ResizeHandle) {
"use strict";
// Index deltas for changes in order
@@ -13,6 +13,11 @@ define(
bottom: Number.NEGATIVE_INFINITY
};
+ // Ensure a value is non-negative (for x/y setters)
+ function clamp(value) {
+ return Math.max(value, 0);
+ }
+
/**
* Abstract superclass for other classes which provide useful
* interfaces upon an elements in a fixed position view.
@@ -29,6 +34,8 @@ define(
* @param {Array} elements the full array of elements
*/
function ElementProxy(element, index, elements) {
+ var handles = [ new ResizeHandle(element, 1, 1) ];
+
return {
/**
* The element as stored in the view configuration.
@@ -40,14 +47,14 @@ define(
* @param {number} [x] the new x position (if setting)
* @returns {number} the x position
*/
- x: new AccessorMutator(element, 'x'),
+ 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
*/
- y: new AccessorMutator(element, 'y'),
+ 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)
@@ -97,6 +104,13 @@ define(
if (elements[index] === element) {
elements.splice(index, 1);
}
+ },
+ /**
+ * Get handles to control specific features of this element,
+ * e.g. corner size.
+ */
+ handles: function () {
+ return handles;
}
};
}
diff --git a/platform/features/layout/src/elements/LineHandle.js b/platform/features/layout/src/elements/LineHandle.js
new file mode 100644
index 0000000000..03474df1c1
--- /dev/null
+++ b/platform/features/layout/src/elements/LineHandle.js
@@ -0,0 +1,61 @@
+/*global define*/
+define(
+ [],
+ function () {
+ 'use strict';
+
+ /**
+ * Handle for changing x/y position of a line's end point.
+ * This is used to support drag handles for line elements
+ * in a fixed position view. Field names for opposite ends
+ * are provided to avoid zero-length lines.
+ * @constructor
+ * @param element the line element
+ * @param {string} xProperty field which stores x position
+ * @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
+ */
+ 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.
+ */
+ 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.
+ */
+ 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];
+ }
+ };
+ }
+
+ return LineHandle;
+
+ }
+);
\ No newline at end of file
diff --git a/platform/features/layout/src/elements/LineProxy.js b/platform/features/layout/src/elements/LineProxy.js
index 14671689dd..dc4729ef6e 100644
--- a/platform/features/layout/src/elements/LineProxy.js
+++ b/platform/features/layout/src/elements/LineProxy.js
@@ -1,8 +1,8 @@
/*global define*/
define(
- ['./ElementProxy'],
- function (ElementProxy) {
+ ['./ElementProxy', './LineHandle'],
+ function (ElementProxy, LineHandle) {
'use strict';
/**
@@ -15,7 +15,11 @@ define(
* @param {Array} elements the full array of elements
*/
function LineProxy(element, index, elements) {
- var proxy = new ElementProxy(element, index, elements);
+ var proxy = new ElementProxy(element, index, elements),
+ handles = [
+ new LineHandle(element, 'x', 'y', 'x2', 'y2'),
+ new LineHandle(element, 'x2', 'y2', 'x', 'y')
+ ];
/**
* Get the top-left x coordinate, in grid space, of
@@ -24,7 +28,7 @@ define(
*/
proxy.x = function (v) {
var x = Math.min(element.x, element.x2),
- delta = v - x;
+ delta = Math.max(v, 0) - x;
if (arguments.length > 0 && delta) {
element.x += delta;
element.x2 += delta;
@@ -39,7 +43,7 @@ define(
*/
proxy.y = function (v) {
var y = Math.min(element.y, element.y2),
- delta = v - y;
+ delta = Math.max(v, 0) - y;
if (arguments.length > 0 && delta) {
element.y += delta;
element.y2 += delta;
@@ -105,6 +109,15 @@ define(
return element.y2 - proxy.y();
};
+ /**
+ * Get element handles for changing the position of end
+ * points of this line.
+ * @returns {LineHandle[]} line handles for both end points
+ */
+ proxy.handles = function () {
+ return handles;
+ };
+
return proxy;
}
diff --git a/platform/features/layout/src/elements/ResizeHandle.js b/platform/features/layout/src/elements/ResizeHandle.js
new file mode 100644
index 0000000000..72c5963192
--- /dev/null
+++ b/platform/features/layout/src/elements/ResizeHandle.js
@@ -0,0 +1,53 @@
+/*global define*/
+define(
+ [],
+ function () {
+ 'use strict';
+
+ /**
+ * Handle for changing width/height properties of an element.
+ * This is used to support drag handles for different
+ * element types in a fixed position view.
+ * @constructor
+ */
+ function ResizeHandle(element, minWidth, minHeight) {
+ // Ensure reasonable defaults
+ minWidth = minWidth || 0;
+ minHeight = minHeight || 0;
+
+ return {
+ /**
+ * Get/set the x position of the lower-right corner
+ * of the handle-controlled element, changing size
+ * as necessary.
+ */
+ 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.
+ */
+ y: function (value) {
+ if (arguments.length > 0) {
+ element.height = Math.max(
+ minHeight,
+ value - element.y
+ );
+ }
+ return element.y + element.height;
+ }
+ };
+ }
+
+ return ResizeHandle;
+
+ }
+);
\ No newline at end of file
diff --git a/platform/features/layout/test/FixedControllerSpec.js b/platform/features/layout/test/FixedControllerSpec.js
index 68562a0e28..d94c93fc13 100644
--- a/platform/features/layout/test/FixedControllerSpec.js
+++ b/platform/features/layout/test/FixedControllerSpec.js
@@ -170,6 +170,20 @@ define(
.toHaveBeenCalledWith(elements[1]);
});
+ it("allows selection retrieval", function () {
+ // selected with no arguments should give the current
+ // selection
+ var elements;
+
+ testModel.modified = 1;
+ findWatch("model.modified")(testModel.modified);
+
+ elements = controller.getElements();
+ controller.select(elements[1]);
+ mockScope.selection.get.andReturn(elements[1]);
+ expect(controller.selected()).toEqual(elements[1]);
+ });
+
it("allows selections to be cleared", function () {
var elements;
@@ -303,6 +317,69 @@ define(
jasmine.any(Object)
);
});
+
+ it("exposes drag handles", function () {
+ var handles;
+
+ // Select something so that drag handles are expected
+ testModel.modified = 1;
+ findWatch("model.modified")(testModel.modified);
+ controller.select(controller.getElements()[1]);
+
+ // Should have a non-empty array of handles
+ handles = controller.handles();
+ expect(handles).toEqual(jasmine.any(Array));
+ expect(handles.length).not.toEqual(0);
+
+ // And they should have start/continue/end drag methods
+ handles.forEach(function (handle) {
+ expect(handle.startDrag).toEqual(jasmine.any(Function));
+ expect(handle.continueDrag).toEqual(jasmine.any(Function));
+ expect(handle.endDrag).toEqual(jasmine.any(Function));
+ });
+ });
+
+ it("exposes a move handle", function () {
+ var handle;
+
+ // Select something so that drag handles are expected
+ testModel.modified = 1;
+ findWatch("model.modified")(testModel.modified);
+ controller.select(controller.getElements()[1]);
+
+ // Should have a move handle
+ handle = controller.moveHandle();
+
+ // And it should have start/continue/end drag methods
+ expect(handle.startDrag).toEqual(jasmine.any(Function));
+ expect(handle.continueDrag).toEqual(jasmine.any(Function));
+ expect(handle.endDrag).toEqual(jasmine.any(Function));
+ });
+
+ it("updates selection style during drag", function () {
+ var oldStyle;
+
+ // Select something so that drag handles are expected
+ testModel.modified = 1;
+ findWatch("model.modified")(testModel.modified);
+ controller.select(controller.getElements()[1]);
+ mockScope.selection.get.andReturn(controller.getElements()[1]);
+
+ // Get style
+ oldStyle = controller.selected().style;
+
+ // Start a drag gesture
+ controller.moveHandle().startDrag();
+
+ // Haven't moved yet; style shouldn't have updated yet
+ expect(controller.selected().style).toEqual(oldStyle);
+
+ // Drag a little
+ controller.moveHandle().continueDrag([ 1000, 100 ]);
+
+ // Style should have been updated
+ expect(controller.selected().style).not.toEqual(oldStyle);
+ });
});
}
);
\ No newline at end of file
diff --git a/platform/features/layout/test/FixedDragHandleSpec.js b/platform/features/layout/test/FixedDragHandleSpec.js
new file mode 100644
index 0000000000..05040396d5
--- /dev/null
+++ b/platform/features/layout/test/FixedDragHandleSpec.js
@@ -0,0 +1,68 @@
+/*global define,describe,it,expect,beforeEach,jasmine,xit*/
+
+define(
+ ['../src/FixedDragHandle'],
+ function (FixedDragHandle) {
+ "use strict";
+
+ var TEST_GRID_SIZE = [ 13, 33 ];
+
+ describe("A fixed position drag handle", function () {
+ var mockElementHandle,
+ mockUpdate,
+ mockCommit,
+ handle;
+
+ beforeEach(function () {
+ mockElementHandle = jasmine.createSpyObj(
+ 'elementHandle',
+ [ 'x', 'y' ]
+ );
+ mockUpdate = jasmine.createSpy('update');
+ mockCommit = jasmine.createSpy('commit');
+
+ mockElementHandle.x.andReturn(6);
+ mockElementHandle.y.andReturn(8);
+
+ handle = new FixedDragHandle(
+ mockElementHandle,
+ TEST_GRID_SIZE,
+ mockUpdate,
+ mockCommit
+ );
+ });
+
+ it("provides a style for positioning", function () {
+ var style = handle.style();
+ // 6 grid coords * 13 pixels - 3 pixels for centering
+ expect(style.left).toEqual('75px');
+ // 8 grid coords * 33 pixels - 3 pixels for centering
+ expect(style.top).toEqual('261px');
+ });
+
+ it("allows handles to be dragged", function () {
+ handle.startDrag();
+ handle.continueDrag([ 16, 8 ]);
+
+ // Should update x/y, snapped to grid
+ expect(mockElementHandle.x).toHaveBeenCalledWith(7);
+ expect(mockElementHandle.y).toHaveBeenCalledWith(8);
+
+ handle.continueDrag([ -16, -35 ]);
+
+ // Should have interpreted relative to initial state
+ expect(mockElementHandle.x).toHaveBeenCalledWith(5);
+ expect(mockElementHandle.y).toHaveBeenCalledWith(7);
+
+ // Should have called update once per continueDrag
+ expect(mockUpdate.calls.length).toEqual(2);
+
+ // Finally, ending drag should commit
+ expect(mockCommit).not.toHaveBeenCalled();
+ handle.endDrag();
+ expect(mockCommit).toHaveBeenCalled();
+ });
+
+ });
+ }
+);
\ No newline at end of file
diff --git a/platform/features/layout/test/elements/ElementProxySpec.js b/platform/features/layout/test/elements/ElementProxySpec.js
index ab90204ce0..b05061594c 100644
--- a/platform/features/layout/test/elements/ElementProxySpec.js
+++ b/platform/features/layout/test/elements/ElementProxySpec.js
@@ -47,6 +47,13 @@ define(
proxy.order("top");
expect(testElements).toEqual([{}, {}, {}, testElement]);
});
+
+ it("ensures x/y values are non-negative", function () {
+ proxy.x(-1);
+ proxy.y(-400);
+ expect(proxy.x()).toEqual(0);
+ expect(proxy.y()).toEqual(0);
+ });
});
}
);
diff --git a/platform/features/layout/test/elements/LineHandleSpec.js b/platform/features/layout/test/elements/LineHandleSpec.js
new file mode 100644
index 0000000000..25d51f617e
--- /dev/null
+++ b/platform/features/layout/test/elements/LineHandleSpec.js
@@ -0,0 +1,54 @@
+/*global define,describe,it,expect,beforeEach,jasmine,xit*/
+
+define(
+ ['../../src/elements/LineHandle'],
+ function (LineHandle) {
+ "use strict";
+
+ describe("A fixed position drag handle", function () {
+ var testElement,
+ handle;
+
+ beforeEach(function () {
+ testElement = {
+ x: 3,
+ y: 42,
+ x2: 8,
+ y2: 11
+ };
+
+ handle = new LineHandle(testElement, 'x', 'y', 'x2', 'y2');
+ });
+
+ it("provides x/y grid coordinates for its corner", function () {
+ expect(handle.x()).toEqual(3);
+ expect(handle.y()).toEqual(42);
+ });
+
+ it("changes x and y positions", function () {
+ handle.x(30);
+ expect(testElement.x).toEqual(30);
+ handle.y(40);
+ expect(testElement.y).toEqual(40);
+ });
+
+ it("disallows values less than zero", function () {
+ handle.x(-1);
+ handle.y(-400);
+ expect(testElement.x).toEqual(0);
+ expect(testElement.y).toEqual(0);
+ });
+
+ it("ensures that end points remain different", function () {
+ handle.x(testElement.x2);
+ handle.y(testElement.y2);
+ // First change should have been fine, because y was different
+ expect(testElement.x).toEqual(testElement.x2);
+ // Second change should have been rejected
+ expect(testElement.y).not.toEqual(testElement.y2);
+ });
+
+
+ });
+ }
+);
\ No newline at end of file
diff --git a/platform/features/layout/test/elements/LineProxySpec.js b/platform/features/layout/test/elements/LineProxySpec.js
index fe38c7c3c9..9e2ed88b44 100644
--- a/platform/features/layout/test/elements/LineProxySpec.js
+++ b/platform/features/layout/test/elements/LineProxySpec.js
@@ -67,6 +67,10 @@ define(
expect(proxy.y2()).toEqual(0);
});
+ it("provides handles for both ends", function () {
+ expect(new LineProxy(diagonal).handles().length).toEqual(2);
+ });
+
});
}
);
\ No newline at end of file
diff --git a/platform/features/layout/test/elements/ResizeHandleSpec.js b/platform/features/layout/test/elements/ResizeHandleSpec.js
new file mode 100644
index 0000000000..49168aa5cd
--- /dev/null
+++ b/platform/features/layout/test/elements/ResizeHandleSpec.js
@@ -0,0 +1,59 @@
+/*global define,describe,it,expect,beforeEach,jasmine,xit*/
+
+define(
+ ['../../src/elements/ResizeHandle'],
+ function (ResizeHandle) {
+ "use strict";
+
+ var TEST_MIN_WIDTH = 4, TEST_MIN_HEIGHT = 2;
+
+ describe("A fixed position drag handle", function () {
+ var testElement,
+ handle;
+
+ beforeEach(function () {
+ testElement = {
+ x: 3,
+ y: 42,
+ width: 30,
+ height: 36
+ };
+
+ handle = new ResizeHandle(
+ testElement,
+ TEST_MIN_WIDTH,
+ TEST_MIN_HEIGHT
+ );
+ });
+
+ it("provides x/y grid coordinates for lower-right corner", function () {
+ expect(handle.x()).toEqual(33);
+ expect(handle.y()).toEqual(78);
+ });
+
+ it("changes width of an element", function () {
+ handle.x(30);
+ // Should change width, not x
+ expect(testElement.x).toEqual(3);
+ expect(testElement.width).toEqual(27);
+ });
+
+ it("changes height of an element", function () {
+ handle.y(60);
+ // Should change height, not y
+ expect(testElement.y).toEqual(42);
+ expect(testElement.height).toEqual(18);
+ });
+
+ it("enforces minimum width/height", function () {
+ handle.x(testElement.x);
+ handle.y(testElement.y);
+ expect(testElement.x).toEqual(3);
+ expect(testElement.y).toEqual(42);
+ expect(testElement.width).toEqual(TEST_MIN_WIDTH);
+ expect(testElement.height).toEqual(TEST_MIN_HEIGHT);
+ });
+
+ });
+ }
+);
\ No newline at end of file
diff --git a/platform/features/layout/test/suite.json b/platform/features/layout/test/suite.json
index 774cc00819..3bc1a259c1 100644
--- a/platform/features/layout/test/suite.json
+++ b/platform/features/layout/test/suite.json
@@ -1,5 +1,6 @@
[
"FixedController",
+ "FixedDragHandle",
"FixedProxy",
"LayoutController",
"LayoutDrag",
@@ -9,6 +10,7 @@
"elements/ElementProxies",
"elements/ElementProxy",
"elements/LineProxy",
+ "elements/ResizeHandle",
"elements/TelemetryProxy",
"elements/TextProxy"
]
\ No newline at end of file
diff --git a/platform/forms/res/templates/controls/button.html b/platform/forms/res/templates/controls/button.html
index 730a67dd8b..c826738641 100644
--- a/platform/forms/res/templates/controls/button.html
+++ b/platform/forms/res/templates/controls/button.html
@@ -1,5 +1,4 @@
-
diff --git a/platform/forms/res/templates/controls/checkbox.html b/platform/forms/res/templates/controls/checkbox.html
index 0152677ce6..c8d09f0303 100644
--- a/platform/forms/res/templates/controls/checkbox.html
+++ b/platform/forms/res/templates/controls/checkbox.html
@@ -3,5 +3,5 @@
name="mctControl"
ng-model="ngModel[field]"
ng-disabled="ngDisabled">
-
+
diff --git a/platform/forms/res/templates/controls/color.html b/platform/forms/res/templates/controls/color.html
index 31f9f3bec2..c4684a773d 100644
--- a/platform/forms/res/templates/controls/color.html
+++ b/platform/forms/res/templates/controls/color.html
@@ -1,36 +1,45 @@
-