Merge remote-tracking branch 'github-open/open181' into open-master

This commit is contained in:
Pete Richards 2015-10-23 16:23:29 -07:00
commit 90828ef63d
3 changed files with 222 additions and 89 deletions

View File

@ -19,15 +19,17 @@
this source code distribution or the Licensing information page available this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information. at runtime from the About dialog for additional information.
--> -->
<!-- MINE -->
<div ng-controller="TimeRangeController"> <div ng-controller="TimeRangeController">
<div class="l-time-range-inputs-holder"> <div class="l-time-range-inputs-holder">
<span class="l-time-range-inputs-elem ui-symbol type-icon">&#x43;</span> <span class="l-time-range-inputs-elem ui-symbol type-icon">&#x43;</span>
<span class="l-time-range-input" ng-controller="ToggleController as t1"> <span class="l-time-range-input" ng-controller="ToggleController as t1">
<!--<span class="lbl">Start</span>--> <!--<span class="lbl">Start</span>-->
<span class="s-btn time-range-start" ng-click="t1.toggle()"> <span class="s-btn time-range-start">
<span class="val">{{startOuterText}}</span> <input type="text"
<a class="ui-symbol icon icon-calendar"></a> ng-model="boundsModel.start"
ng-class="{ error: !boundsModel.startValid }">
</input>
<a class="ui-symbol icon icon-calendar" ng-click="t1.toggle()"></a>
<mct-popup ng-if="t1.isActive()"> <mct-popup ng-if="t1.isActive()">
<div mct-click-elsewhere="t1.setState(false)"> <div mct-click-elsewhere="t1.setState(false)">
<mct-control key="'datetime-picker'" <mct-control key="'datetime-picker'"
@ -44,9 +46,13 @@
<span class="l-time-range-input" ng-controller="ToggleController as t2"> <span class="l-time-range-input" ng-controller="ToggleController as t2">
<!--<span class="lbl">End</span>--> <!--<span class="lbl">End</span>-->
<span class="s-btn l-time-range-input" ng-click="t2.toggle()"> <span class="s-btn l-time-range-input">
<span class="val">{{endOuterText}}</span> <input type="text"
<a class="ui-symbol icon icon-calendar"></a> ng-model="boundsModel.end"
ng-class="{ error: !boundsModel.endValid }">
</input>
<a class="ui-symbol icon icon-calendar" ng-click="t2.toggle()">
</a>
<mct-popup ng-if="t2.isActive()"> <mct-popup ng-if="t2.isActive()">
<div mct-click-elsewhere="t2.setState(false)"> <div mct-click-elsewhere="t2.setState(false)">
<mct-control key="'datetime-picker'" <mct-control key="'datetime-picker'"

View File

@ -26,8 +26,7 @@ define(
function (moment) { function (moment) {
"use strict"; "use strict";
var var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
TICK_SPACING_PX = 150; TICK_SPACING_PX = 150;
/** /**
@ -44,6 +43,15 @@ define(
return moment.utc(ts).format(DATE_FORMAT); return moment.utc(ts).format(DATE_FORMAT);
} }
function parseTimestamp(text) {
var m = moment.utc(text, DATE_FORMAT);
if (m.isValid()) {
return m.valueOf();
} else {
throw new Error("Could not parse " + text);
}
}
// From 0.0-1.0 to "0%"-"1%" // From 0.0-1.0 to "0%"-"1%"
function toPercent(p) { function toPercent(p) {
return (100 * p) + "%"; return (100 * p) + "%";
@ -93,6 +101,25 @@ define(
return { start: bounds.start, end: bounds.end }; return { start: bounds.start, end: bounds.end };
} }
function updateBoundsTextForProperty(ngModel, property) {
try {
if (!$scope.boundsModel[property] ||
parseTimestamp($scope.boundsModel[property]) !==
ngModel.outer[property]) {
$scope.boundsModel[property] =
formatTimestamp(ngModel.outer[property]);
}
} catch (e) {
// User-entered text is invalid, so leave it be
// until they fix it.
}
}
function updateBoundsText(ngModel) {
updateBoundsTextForProperty(ngModel, 'start');
updateBoundsTextForProperty(ngModel, 'end');
}
function updateViewFromModel(ngModel) { function updateViewFromModel(ngModel) {
var t = now(); var t = now();
@ -101,8 +128,7 @@ define(
ngModel.inner = ngModel.inner || copyBounds(ngModel.outer); ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
// First, dates for the date pickers for outer bounds // First, dates for the date pickers for outer bounds
$scope.startOuterDate = new Date(ngModel.outer.start); updateBoundsText(ngModel);
$scope.endOuterDate = new Date(ngModel.outer.end);
// Then various updates for the inner span // Then various updates for the inner span
updateViewForInnerSpanFromModel(ngModel); updateViewForInnerSpanFromModel(ngModel);
@ -178,6 +204,8 @@ define(
function updateOuterStart(t) { function updateOuterStart(t) {
var ngModel = $scope.ngModel; var ngModel = $scope.ngModel;
ngModel.outer.start = t;
ngModel.outer.end = Math.max( ngModel.outer.end = Math.max(
ngModel.outer.start + outerMinimumSpan, ngModel.outer.start + outerMinimumSpan,
ngModel.outer.end ngModel.outer.end
@ -190,14 +218,15 @@ define(
ngModel.inner.end ngModel.inner.end
); );
$scope.startOuterText = formatTimestamp(t);
updateViewForInnerSpanFromModel(ngModel); updateViewForInnerSpanFromModel(ngModel);
updateTicks();
} }
function updateOuterEnd(t) { function updateOuterEnd(t) {
var ngModel = $scope.ngModel; var ngModel = $scope.ngModel;
ngModel.outer.end = t;
ngModel.outer.start = Math.min( ngModel.outer.start = Math.min(
ngModel.outer.end - outerMinimumSpan, ngModel.outer.end - outerMinimumSpan,
ngModel.outer.start ngModel.outer.start
@ -210,9 +239,40 @@ define(
ngModel.inner.start ngModel.inner.start
); );
$scope.endOuterText = formatTimestamp(t);
updateViewForInnerSpanFromModel(ngModel); updateViewForInnerSpanFromModel(ngModel);
updateTicks();
}
function updateStartFromText(value) {
try {
updateOuterStart(parseTimestamp(value));
updateBoundsTextForProperty($scope.ngModel, 'end');
$scope.boundsModel.startValid = true;
} catch (e) {
$scope.boundsModel.startValid = false;
return;
}
}
function updateEndFromText(value) {
try {
updateOuterEnd(parseTimestamp(value));
updateBoundsTextForProperty($scope.ngModel, 'start');
$scope.boundsModel.endValid = true;
} catch (e) {
$scope.boundsModel.endValid = false;
return;
}
}
function updateStartFromPicker(value) {
updateOuterStart(value);
updateBoundsText($scope.ngModel);
}
function updateEndFromPicker(value) {
updateOuterEnd(value);
updateBoundsText($scope.ngModel);
} }
$scope.startLeftDrag = startLeftDrag; $scope.startLeftDrag = startLeftDrag;
@ -224,14 +284,17 @@ define(
$scope.state = false; $scope.state = false;
$scope.ticks = []; $scope.ticks = [];
$scope.boundsModel = {};
// Initialize scope to defaults // Initialize scope to defaults
updateViewFromModel($scope.ngModel); updateViewFromModel($scope.ngModel);
$scope.$watchCollection("ngModel", updateViewFromModel); $scope.$watchCollection("ngModel", updateViewFromModel);
$scope.$watch("spanWidth", updateSpanWidth); $scope.$watch("spanWidth", updateSpanWidth);
$scope.$watch("ngModel.outer.start", updateOuterStart); $scope.$watch("ngModel.outer.start", updateStartFromPicker);
$scope.$watch("ngModel.outer.end", updateOuterEnd); $scope.$watch("ngModel.outer.end", updateEndFromPicker);
$scope.$watch("boundsModel.start", updateStartFromText);
$scope.$watch("boundsModel.end", updateEndFromText);
} }
return TimeConductorController; return TimeConductorController;

View File

@ -22,8 +22,8 @@
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ /*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define( define(
["../../src/controllers/TimeRangeController"], ["../../src/controllers/TimeRangeController", "moment"],
function (TimeRangeController) { function (TimeRangeController, moment) {
"use strict"; "use strict";
var SEC = 1000, var SEC = 1000,
@ -166,8 +166,72 @@ define(
expect(mockScope.ngModel.inner.end) expect(mockScope.ngModel.inner.end)
.toBeGreaterThan(mockScope.ngModel.inner.start); .toBeGreaterThan(mockScope.ngModel.inner.start);
}); });
describe("by typing", function () {
it("updates models", function () {
var newStart = "1977-05-25 17:30:00",
newEnd = "2015-12-18 03:30:00";
mockScope.boundsModel.start = newStart;
fireWatch("boundsModel.start", newStart);
expect(mockScope.ngModel.outer.start)
.toEqual(moment.utc(newStart).valueOf());
expect(mockScope.boundsModel.startValid)
.toBeTruthy();
mockScope.boundsModel.end = newEnd;
fireWatch("boundsModel.end", newEnd);
expect(mockScope.ngModel.outer.end)
.toEqual(moment.utc(newEnd).valueOf());
expect(mockScope.boundsModel.endValid)
.toBeTruthy();
}); });
it("displays error state", function () {
var newStart = "Not a date",
newEnd = "Definitely not a date",
oldStart = mockScope.ngModel.outer.start,
oldEnd = mockScope.ngModel.outer.end;
mockScope.boundsModel.start = newStart;
fireWatch("boundsModel.start", newStart);
expect(mockScope.ngModel.outer.start)
.toEqual(oldStart);
expect(mockScope.boundsModel.startValid)
.toBeFalsy();
mockScope.boundsModel.end = newEnd;
fireWatch("boundsModel.end", newEnd);
expect(mockScope.ngModel.outer.end)
.toEqual(oldEnd);
expect(mockScope.boundsModel.endValid)
.toBeFalsy();
});
it("does not modify user input", function () {
// Don't want the controller "fixing" bad or
// irregularly-formatted input out from under
// the user's fingertips.
var newStart = "Not a date",
newEnd = "2015-3-3 01:02:04",
oldStart = mockScope.ngModel.outer.start,
oldEnd = mockScope.ngModel.outer.end;
mockScope.boundsModel.start = newStart;
fireWatch("boundsModel.start", newStart);
expect(mockScope.boundsModel.start)
.toEqual(newStart);
mockScope.boundsModel.end = newEnd;
fireWatch("boundsModel.end", newEnd);
expect(mockScope.boundsModel.end)
.toEqual(newEnd);
});
});
});
}); });
} }
); );