From 6d58f23c0cd0ce77c1b309ab9cabeb9860e90c4d Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Wed, 13 Apr 2016 13:23:33 -0700 Subject: [PATCH] [Style] Switch to prototype Switch TimeRangeController to prototype style, and update tests and template to utilize new style. --- .../templates/controls/time-controller.html | 35 +- .../src/controllers/TimeRangeController.js | 490 +++++++++--------- .../controllers/TimeRangeControllerSpec.js | 28 +- 3 files changed, 287 insertions(+), 266 deletions(-) diff --git a/platform/commonUI/general/res/templates/controls/time-controller.html b/platform/commonUI/general/res/templates/controls/time-controller.html index bd07659f8e..281447e251 100644 --- a/platform/commonUI/general/res/templates/controls/time-controller.html +++ b/platform/commonUI/general/res/templates/controls/time-controller.html @@ -19,15 +19,18 @@ this source code distribution or the Licensing information page available at runtime from the About dialog for additional information. --> -
+
+ ng-submit="trCtrl.updateBoundsFromForm()"> C @@ -37,9 +40,12 @@   @@ -53,22 +59,25 @@
{{startInnerText}}
{{endInnerText}}
+ mct-drag-down="trCtrl.startMiddleDrag()" + mct-drag="trCtrl.middleDrag(delta[0])" + ng-style="{ + left: startInnerPct, + right: endInnerPct + }">
diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index f0e3da46d9..3ee05e1faf 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -19,247 +19,259 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -/*global define,Promise*/ +/*global define*/ -define( - ['moment'], - function (moment) { - "use strict"; +define([ - var TICK_SPACING_PX = 150; +], function () { + "use strict"; + var TICK_SPACING_PX = 150; - /** - * Controller used by the `time-controller` template. - * @memberof platform/commonUI/general - * @constructor - * @param $scope the Angular scope for this controller - * @param {FormatService} formatService the service to user to format - * domain values - * @param {string} defaultFormat the format to request when no - * format has been otherwise specified - * @param {Function} now a function to return current system time - */ - function TimeRangeController($scope, formatService, defaultFormat, now) { - var tickCount = 2, - innerMinimumSpan = 1000, // 1 second - outerMinimumSpan = 1000, // 1 second - initialDragValue, - formatter = formatService.getFormat(defaultFormat); - - function formatTimestamp(ts) { - return formatter.format(ts); - } - - // From 0.0-1.0 to "0%"-"100%" - function toPercent(p) { - return (100 * p) + "%"; - } - - function updateTicks() { - var i, p, ts, start, end, span; - end = $scope.ngModel.outer.end; - start = $scope.ngModel.outer.start; - span = end - start; - $scope.ticks = []; - for (i = 0; i < tickCount; i += 1) { - p = i / (tickCount - 1); - ts = p * span + start; - $scope.ticks.push(formatTimestamp(ts)); - } - } - - function updateSpanWidth(w) { - tickCount = Math.max(Math.floor(w / TICK_SPACING_PX), 2); - updateTicks(); - } - - function updateViewForInnerSpanFromModel(ngModel) { - var span = ngModel.outer.end - ngModel.outer.start; - - // Expose readable dates for the knobs - $scope.startInnerText = formatTimestamp(ngModel.inner.start); - $scope.endInnerText = formatTimestamp(ngModel.inner.end); - - // And positions for the knobs - $scope.startInnerPct = - toPercent((ngModel.inner.start - ngModel.outer.start) / span); - $scope.endInnerPct = - toPercent((ngModel.outer.end - ngModel.inner.end) / span); - } - - function defaultBounds() { - var t = now(); - return { - start: t - 24 * 3600 * 1000, // One day - end: t - }; - } - - function copyBounds(bounds) { - return { start: bounds.start, end: bounds.end }; - } - - function updateViewFromModel(ngModel) { - ngModel = ngModel || {}; - ngModel.outer = ngModel.outer || defaultBounds(); - ngModel.inner = ngModel.inner || copyBounds(ngModel.outer); - - // Stick it back is scope (in case we just set defaults) - $scope.ngModel = ngModel; - - updateViewForInnerSpanFromModel(ngModel); - updateTicks(); - } - - function startLeftDrag() { - initialDragValue = $scope.ngModel.inner.start; - } - - function startRightDrag() { - initialDragValue = $scope.ngModel.inner.end; - } - - function startMiddleDrag() { - initialDragValue = { - start: $scope.ngModel.inner.start, - end: $scope.ngModel.inner.end - }; - } - - function toMillis(pixels) { - var span = - $scope.ngModel.outer.end - $scope.ngModel.outer.start; - return (pixels / $scope.spanWidth) * span; - } - - function clamp(value, low, high) { - return Math.max(low, Math.min(high, value)); - } - - function leftDrag(pixels) { - var delta = toMillis(pixels); - $scope.ngModel.inner.start = clamp( - initialDragValue + delta, - $scope.ngModel.outer.start, - $scope.ngModel.inner.end - innerMinimumSpan - ); - updateViewFromModel($scope.ngModel); - } - - function rightDrag(pixels) { - var delta = toMillis(pixels); - $scope.ngModel.inner.end = clamp( - initialDragValue + delta, - $scope.ngModel.inner.start + innerMinimumSpan, - $scope.ngModel.outer.end - ); - updateViewFromModel($scope.ngModel); - } - - function middleDrag(pixels) { - var delta = toMillis(pixels), - edge = delta < 0 ? 'start' : 'end', - opposite = delta < 0 ? 'end' : 'start'; - - // Adjust the position of the edge in the direction of drag - $scope.ngModel.inner[edge] = clamp( - initialDragValue[edge] + delta, - $scope.ngModel.outer.start, - $scope.ngModel.outer.end - ); - // Adjust opposite knob to maintain span - $scope.ngModel.inner[opposite] = $scope.ngModel.inner[edge] + - initialDragValue[opposite] - initialDragValue[edge]; - - updateViewFromModel($scope.ngModel); - } - - function updateFormModel() { - $scope.formModel = { - start: (($scope.ngModel || {}).outer || {}).start, - end: (($scope.ngModel || {}).outer || {}).end - }; - } - - function updateOuterStart(t) { - var ngModel = $scope.ngModel; - - ngModel.inner.start = - Math.max(ngModel.outer.start, ngModel.inner.start); - ngModel.inner.end = Math.max( - ngModel.inner.start + innerMinimumSpan, - ngModel.inner.end - ); - - updateFormModel(); - updateViewForInnerSpanFromModel(ngModel); - updateTicks(); - } - - function updateOuterEnd(t) { - var ngModel = $scope.ngModel; - - ngModel.inner.end = - Math.min(ngModel.outer.end, ngModel.inner.end); - ngModel.inner.start = Math.min( - ngModel.inner.end - innerMinimumSpan, - ngModel.inner.start - ); - - updateFormModel(); - updateViewForInnerSpanFromModel(ngModel); - updateTicks(); - } - - function updateFormat(key) { - formatter = formatService.getFormat(key || defaultFormat); - updateViewForInnerSpanFromModel($scope.ngModel); - updateTicks(); - } - - function updateBoundsFromForm() { - var start = $scope.formModel.start, - end = $scope.formModel.end; - if (end >= start + outerMinimumSpan) { - $scope.ngModel = $scope.ngModel || {}; - $scope.ngModel.outer = { start: start, end: end }; - } - } - - function validateStart(startValue) { - return startValue <= $scope.formModel.end - outerMinimumSpan; - } - - function validateEnd(endValue) { - return endValue >= $scope.formModel.start + outerMinimumSpan; - } - - $scope.startLeftDrag = startLeftDrag; - $scope.startRightDrag = startRightDrag; - $scope.startMiddleDrag = startMiddleDrag; - $scope.leftDrag = leftDrag; - $scope.rightDrag = rightDrag; - $scope.middleDrag = middleDrag; - - $scope.updateBoundsFromForm = updateBoundsFromForm; - - $scope.validateStart = validateStart; - $scope.validateEnd = validateEnd; - - $scope.ticks = []; - - // Initialize scope to defaults - updateViewFromModel($scope.ngModel); - updateFormModel(); - - $scope.$watchCollection("ngModel", updateViewFromModel); - $scope.$watch("spanWidth", updateSpanWidth); - $scope.$watch("ngModel.outer.start", updateOuterStart); - $scope.$watch("ngModel.outer.end", updateOuterEnd); - $scope.$watch("parameters.format", updateFormat); - } - - return TimeRangeController; + /* format number as percent; 0.0-1.0 to "0%"-"100%" */ + function toPercent(p) { + return (100 * p) + "%"; } -); + + function clamp(value, low, high) { + return Math.max(low, Math.min(high, value)); + } + + function copyBounds(bounds) { + return { + start: bounds.start, + end: bounds.end + }; + } + + /** + * Controller used by the `time-controller` template. + * @memberof platform/commonUI/general + * @constructor + * @param $scope the Angular scope for this controller + * @param {FormatService} formatService the service to user to format + * domain values + * @param {string} defaultFormat the format to request when no + * format has been otherwise specified + * @param {Function} now a function to return current system time + */ + function TimeRangeController($scope, formatService, defaultFormat, now) { + this.$scope = $scope; + this.formatService = formatService; + this.defaultFormat = defaultFormat; + this.now = now; + + this.tickCount = 2; + this.innerMinimumSpan = 1000; // 1 second + this.outerMinimumSpan = 1000; // 1 second + this.initialDragValue = undefined; + this.formatter = formatService.getFormat(defaultFormat); + + this.$scope.ticks = []; + + this.updateViewFromModel(this.$scope.ngModel); + this.updateFormModel(); + + [ + 'updateViewFromModel', + 'updateSpanWidth', + 'updateOuterStart', + 'updateOuterEnd', + 'updateFormat', + 'validateStart', + 'validateEnd' + ].forEach(function (boundFn) { + this[boundFn] = this[boundFn].bind(this); + }, this); + + this.$scope.$watchCollection("ngModel", this.updateViewFromModel); + this.$scope.$watch("spanWidth", this.updateSpanWidth); + this.$scope.$watch("ngModel.outer.start", this.updateOuterStart); + this.$scope.$watch("ngModel.outer.end", this.updateOuterEnd); + this.$scope.$watch("parameters.format", this.updateFormat); + } + + TimeRangeController.prototype.formatTimestamp = function (ts) { + return this.formatter.format(ts); + }; + + TimeRangeController.prototype.updateTicks = function () { + var i, p, ts, start, end, span; + end = this.$scope.ngModel.outer.end; + start = this.$scope.ngModel.outer.start; + span = end - start; + this.$scope.ticks = []; + for (i = 0; i < this.tickCount; i += 1) { + p = i / (this.tickCount - 1); + ts = p * span + start; + this.$scope.ticks.push(this.formatTimestamp(ts)); + } + }; + + TimeRangeController.prototype.updateSpanWidth = function (w) { + this.tickCount = Math.max(Math.floor(w / TICK_SPACING_PX), 2); + this.updateTicks(); + }; + + TimeRangeController.prototype.updateViewForInnerSpanFromModel = function ( + ngModel + ) { + var span = ngModel.outer.end - ngModel.outer.start; + + // Expose readable dates for the knobs + this.$scope.startInnerText = this.formatTimestamp(ngModel.inner.start); + this.$scope.endInnerText = this.formatTimestamp(ngModel.inner.end); + + // And positions for the knobs + this.$scope.startInnerPct = + toPercent((ngModel.inner.start - ngModel.outer.start) / span); + this.$scope.endInnerPct = + toPercent((ngModel.outer.end - ngModel.inner.end) / span); + }; + + TimeRangeController.prototype.defaultBounds = function () { + var t = this.now(); + return { + start: t - 24 * 3600 * 1000, // One day + end: t + }; + }; + + + TimeRangeController.prototype.updateViewFromModel = function (ngModel) { + ngModel = ngModel || {}; + ngModel.outer = ngModel.outer || this.defaultBounds(); + ngModel.inner = ngModel.inner || copyBounds(ngModel.outer); + + // Stick it back is scope (in case we just set defaults) + this.$scope.ngModel = ngModel; + + this.updateViewForInnerSpanFromModel(ngModel); + this.updateTicks(); + }; + + TimeRangeController.prototype.startLeftDrag = function () { + this.initialDragValue = this.$scope.ngModel.inner.start; + }; + + TimeRangeController.prototype.startRightDrag = function () { + this.initialDragValue = this.$scope.ngModel.inner.end; + }; + + TimeRangeController.prototype.startMiddleDrag = function () { + this.initialDragValue = { + start: this.$scope.ngModel.inner.start, + end: this.$scope.ngModel.inner.end + }; + }; + + TimeRangeController.prototype.toMillis = function (pixels) { + var span = + this.$scope.ngModel.outer.end - this.$scope.ngModel.outer.start; + return (pixels / this.$scope.spanWidth) * span; + }; + + TimeRangeController.prototype.leftDrag = function (pixels) { + var delta = this.toMillis(pixels); + this.$scope.ngModel.inner.start = clamp( + this.initialDragValue + delta, + this.$scope.ngModel.outer.start, + this.$scope.ngModel.inner.end - this.innerMinimumSpan + ); + this.updateViewFromModel(this.$scope.ngModel); + }; + + TimeRangeController.prototype.rightDrag = function (pixels) { + var delta = this.toMillis(pixels); + this.$scope.ngModel.inner.end = clamp( + this.initialDragValue + delta, + this.$scope.ngModel.inner.start + this.innerMinimumSpan, + this.$scope.ngModel.outer.end + ); + this.updateViewFromModel(this.$scope.ngModel); + }; + + TimeRangeController.prototype.middleDrag = function (pixels) { + var delta = this.toMillis(pixels), + edge = delta < 0 ? 'start' : 'end', + opposite = delta < 0 ? 'end' : 'start'; + + // Adjust the position of the edge in the direction of drag + this.$scope.ngModel.inner[edge] = clamp( + this.initialDragValue[edge] + delta, + this.$scope.ngModel.outer.start, + this.$scope.ngModel.outer.end + ); + // Adjust opposite knob to maintain span + this.$scope.ngModel.inner[opposite] = + this.$scope.ngModel.inner[edge] + + this.initialDragValue[opposite] - + this.initialDragValue[edge]; + + this.updateViewFromModel(this.$scope.ngModel); + }; + + TimeRangeController.prototype.updateFormModel = function () { + this.$scope.formModel = { + start: ((this.$scope.ngModel || {}).outer || {}).start, + end: ((this.$scope.ngModel || {}).outer || {}).end + }; + }; + + TimeRangeController.prototype.updateOuterStart = function () { + var ngModel = this.$scope.ngModel; + + ngModel.inner.start = + Math.max(ngModel.outer.start, ngModel.inner.start); + ngModel.inner.end = Math.max( + ngModel.inner.start + this.innerMinimumSpan, + ngModel.inner.end + ); + + this.updateFormModel(); + this.updateViewForInnerSpanFromModel(ngModel); + this.updateTicks(); + }; + + TimeRangeController.prototype.updateOuterEnd = function () { + var ngModel = this.$scope.ngModel; + + ngModel.inner.end = + Math.min(ngModel.outer.end, ngModel.inner.end); + ngModel.inner.start = Math.min( + ngModel.inner.end - this.innerMinimumSpan, + ngModel.inner.start + ); + + this.updateFormModel(); + this.updateViewForInnerSpanFromModel(ngModel); + this.updateTicks(); + }; + + TimeRangeController.prototype.updateFormat = function (key) { + this.formatter = this.formatService.getFormat(key || this.defaultFormat); + this.updateViewForInnerSpanFromModel(this.$scope.ngModel); + this.updateTicks(); + }; + + TimeRangeController.prototype.updateBoundsFromForm = function () { + var start = this.$scope.formModel.start, + end = this.$scope.formModel.end; + if (end >= start + this.outerMinimumSpan) { + this.$scope.ngModel = this.$scope.ngModel || {}; + this.$scope.ngModel.outer = { start: start, end: end }; + } + }; + + TimeRangeController.prototype.validateStart = function (startValue) { + return startValue <= + this.$scope.formModel.end - this.outerMinimumSpan; + }; + + TimeRangeController.prototype.validateEnd = function (endValue) { + return endValue >= + this.$scope.formModel.start + this.outerMinimumSpan; + }; + + return TimeRangeController; +}); diff --git a/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js b/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js index efff87651f..086947950a 100644 --- a/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js +++ b/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js @@ -94,18 +94,18 @@ define( it("exposes start time validator", function () { var testValue = 42000000; mockScope.formModel = { end: testValue }; - expect(mockScope.validateStart(testValue + 1)) + expect(controller.validateStart(testValue + 1)) .toBe(false); - expect(mockScope.validateStart(testValue - 60 * 60 * 1000 - 1)) + expect(controller.validateStart(testValue - 60 * 60 * 1000 - 1)) .toBe(true); }); it("exposes end time validator", function () { var testValue = 42000000; mockScope.formModel = { start: testValue }; - expect(mockScope.validateEnd(testValue - 1)) + expect(controller.validateEnd(testValue - 1)) .toBe(false); - expect(mockScope.validateEnd(testValue + 60 * 60 * 1000 + 1)) + expect(controller.validateEnd(testValue + 60 * 60 * 1000 + 1)) .toBe(true); }); @@ -134,7 +134,7 @@ define( }); it("updates model bounds on request", function () { - mockScope.updateBoundsFromForm(); + controller.updateBoundsFromForm(); expect(mockScope.ngModel.outer.start) .toEqual(mockScope.formModel.start); expect(mockScope.ngModel.outer.end) @@ -160,27 +160,27 @@ define( }); it("updates the start time for left drags", function () { - mockScope.startLeftDrag(); - mockScope.leftDrag(250); + controller.startLeftDrag(); + controller.leftDrag(250); expect(mockScope.ngModel.inner.start) .toEqual(DAY * 1000 + HOUR * 9); }); it("updates the end time for right drags", function () { - mockScope.startRightDrag(); - mockScope.rightDrag(-250); + controller.startRightDrag(); + controller.rightDrag(-250); expect(mockScope.ngModel.inner.end) .toEqual(DAY * 1000 + HOUR * 15); }); it("updates both start and end for middle drags", function () { - mockScope.startMiddleDrag(); - mockScope.middleDrag(-125); + controller.startMiddleDrag(); + controller.middleDrag(-125); expect(mockScope.ngModel.inner).toEqual({ start: DAY * 1000, end: DAY * 1000 + HOUR * 18 }); - mockScope.middleDrag(250); + controller.middleDrag(250); expect(mockScope.ngModel.inner).toEqual({ start: DAY * 1000 + HOUR * 6, end: DAY * 1001 @@ -188,8 +188,8 @@ define( }); it("enforces a minimum inner span", function () { - mockScope.startRightDrag(); - mockScope.rightDrag(-9999999); + controller.startRightDrag(); + controller.rightDrag(-9999999); expect(mockScope.ngModel.inner.end) .toBeGreaterThan(mockScope.ngModel.inner.start); });