[Clocks/Timers] Bring in latest

Merge in latest from master branch to support review of
changes, nasa/openmctweb#207
This commit is contained in:
Victor Woeltjen 2015-10-26 10:42:41 -07:00
commit 20cb2ff239
17 changed files with 564 additions and 106 deletions

View File

@ -1 +1 @@
web: node app.js --port $PORT --include example/localstorage
web: node app.js --port $PORT

View File

@ -30,7 +30,8 @@
var CONSTANTS = {
DIAGRAM_WIDTH: 800,
DIAGRAM_HEIGHT: 500
};
},
TOC_HEAD = "# Table of Contents";
GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be defined
(function () {
@ -44,6 +45,7 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
split = require("split"),
stream = require("stream"),
nomnoml = require('nomnoml'),
toc = require("markdown-toc"),
Canvas = require('canvas'),
options = require("minimist")(process.argv.slice(2));
@ -110,6 +112,9 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define
done();
};
transform._flush = function (done) {
// Prepend table of contents
markdown =
[ TOC_HEAD, toc(markdown).content, "", markdown ].join("\n");
this.push("<html><body>\n");
this.push(marked(markdown));
this.push("\n</body></html>\n");

View File

@ -22,7 +22,8 @@
"split": "^1.0.0",
"mkdirp": "^0.5.1",
"nomnoml": "^0.0.3",
"canvas": "^1.2.7"
"canvas": "^1.2.7",
"markdown-toc": "^0.11.7"
},
"scripts": {
"start": "node app.js",

View File

@ -1,4 +1,9 @@
{
"configuration": {
"paths": {
"uuid": "uuid"
}
},
"extensions": {
"routes": [
{

View File

@ -25,7 +25,7 @@
* Module defining CreateService. Created by vwoeltje on 11/10/14.
*/
define(
["../../lib/uuid"],
["uuid"],
function (uuid) {
"use strict";

View File

@ -19,15 +19,17 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<!-- MINE -->
<div ng-controller="TimeRangeController">
<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-input" ng-controller="ToggleController as t1">
<!--<span class="lbl">Start</span>-->
<span class="s-btn time-range-start" ng-click="t1.toggle()">
<span class="val">{{startOuterText}}</span>
<a class="ui-symbol icon icon-calendar"></a>
<span class="s-btn time-range-start">
<input type="text"
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()">
<div mct-click-elsewhere="t1.setState(false)">
<mct-control key="'datetime-picker'"
@ -44,9 +46,13 @@
<span class="l-time-range-input" ng-controller="ToggleController as t2">
<!--<span class="lbl">End</span>-->
<span class="s-btn l-time-range-input" ng-click="t2.toggle()">
<span class="val">{{endOuterText}}</span>
<a class="ui-symbol icon icon-calendar"></a>
<span class="s-btn l-time-range-input">
<input type="text"
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()">
<div mct-click-elsewhere="t2.setState(false)">
<mct-control key="'datetime-picker'"

View File

@ -26,8 +26,7 @@ define(
function (moment) {
"use strict";
var
DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
TICK_SPACING_PX = 150;
/**
@ -44,6 +43,15 @@ define(
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%"
function toPercent(p) {
return (100 * p) + "%";
@ -93,6 +101,25 @@ define(
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) {
var t = now();
@ -101,8 +128,7 @@ define(
ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
// First, dates for the date pickers for outer bounds
$scope.startOuterDate = new Date(ngModel.outer.start);
$scope.endOuterDate = new Date(ngModel.outer.end);
updateBoundsText(ngModel);
// Then various updates for the inner span
updateViewForInnerSpanFromModel(ngModel);
@ -178,6 +204,8 @@ define(
function updateOuterStart(t) {
var ngModel = $scope.ngModel;
ngModel.outer.start = t;
ngModel.outer.end = Math.max(
ngModel.outer.start + outerMinimumSpan,
ngModel.outer.end
@ -190,14 +218,15 @@ define(
ngModel.inner.end
);
$scope.startOuterText = formatTimestamp(t);
updateViewForInnerSpanFromModel(ngModel);
updateTicks();
}
function updateOuterEnd(t) {
var ngModel = $scope.ngModel;
ngModel.outer.end = t;
ngModel.outer.start = Math.min(
ngModel.outer.end - outerMinimumSpan,
ngModel.outer.start
@ -210,9 +239,40 @@ define(
ngModel.inner.start
);
$scope.endOuterText = formatTimestamp(t);
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;
@ -224,14 +284,17 @@ define(
$scope.state = false;
$scope.ticks = [];
$scope.boundsModel = {};
// Initialize scope to defaults
updateViewFromModel($scope.ngModel);
$scope.$watchCollection("ngModel", updateViewFromModel);
$scope.$watch("spanWidth", updateSpanWidth);
$scope.$watch("ngModel.outer.start", updateOuterStart);
$scope.$watch("ngModel.outer.end", updateOuterEnd);
$scope.$watch("ngModel.outer.start", updateStartFromPicker);
$scope.$watch("ngModel.outer.end", updateEndFromPicker);
$scope.$watch("boundsModel.start", updateStartFromText);
$scope.$watch("boundsModel.end", updateEndFromText);
}
return TimeConductorController;

View File

@ -22,8 +22,8 @@
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../../src/controllers/TimeRangeController"],
function (TimeRangeController) {
["../../src/controllers/TimeRangeController", "moment"],
function (TimeRangeController, moment) {
"use strict";
var SEC = 1000,
@ -166,8 +166,72 @@ define(
expect(mockScope.ngModel.inner.end)
.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);
});
});
});
});
}
);

View File

@ -30,6 +30,14 @@
"category": "contextual",
"implementation": "actions/LinkAction.js",
"depends": ["locationService", "linkService"]
},
{
"key": "follow",
"name": "Go To Original",
"description": "Go to the original, un-linked instance of this object.",
"glyph": "\u00F4",
"category": "contextual",
"implementation": "actions/GoToOriginalAction.js"
}
],
"components": [
@ -52,7 +60,8 @@
"key": "location",
"name": "Location Capability",
"description": "Provides a capability for retrieving the location of an object based upon it's context.",
"implementation": "capabilities/LocationCapability"
"implementation": "capabilities/LocationCapability",
"depends": [ "$q", "$injector" ]
}
],
"services": [

View File

@ -0,0 +1,62 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define */
define(
function () {
"use strict";
/**
* Implements the "Go To Original" action, which follows a link back
* to an original instance of an object.
*
* @implements {Action}
* @constructor
* @private
* @memberof platform/entanglement
* @param {ActionContext} context the context in which the action
* will be performed
*/
function GoToOriginalAction(context) {
this.domainObject = context.domainObject;
}
GoToOriginalAction.prototype.perform = function () {
return this.domainObject.getCapability("location").getOriginal()
.then(function (originalObject) {
var actionCapability =
originalObject.getCapability("action");
return actionCapability &&
actionCapability.perform("navigate");
});
};
GoToOriginalAction.appliesTo = function (context) {
var domainObject = context.domainObject;
return domainObject && domainObject.hasCapability("location")
&& domainObject.getCapability("location").isLink();
};
return GoToOriginalAction;
}
);

View File

@ -1,3 +1,25 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define */
define(
@ -12,11 +34,41 @@ define(
*
* @constructor
*/
function LocationCapability(domainObject) {
function LocationCapability($q, $injector, domainObject) {
this.domainObject = domainObject;
this.$q = $q;
this.$injector = $injector;
return this;
}
/**
* Get an instance of this domain object in its original location.
*
* @returns {Promise.<DomainObject>} a promise for the original
* instance of this domain object
*/
LocationCapability.prototype.getOriginal = function () {
var id;
if (this.isOriginal()) {
return this.$q.when(this.domainObject);
}
id = this.domainObject.getId();
this.objectService =
this.objectService || this.$injector.get("objectService");
// Assume that an object will be correctly contextualized when
// loaded directly from the object service; this is true
// so long as LocatingObjectDecorator is present, and that
// decorator is also contained in this bundle.
return this.objectService.getObjects([id])
.then(function (objects) {
return objects[id];
});
};
/**
* Set the primary location (the parent id) of the current domain
* object.
@ -78,10 +130,6 @@ define(
return !this.isLink();
};
function createLocationCapability(domainObject) {
return new LocationCapability(domainObject);
}
return createLocationCapability;
return LocationCapability;
}
);

View File

@ -0,0 +1,95 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,describe,beforeEach,it,jasmine,expect */
define(
[
'../../src/actions/GoToOriginalAction',
'../DomainObjectFactory',
'../ControlledPromise'
],
function (GoToOriginalAction, domainObjectFactory, ControlledPromise) {
'use strict';
describe("The 'go to original' action", function () {
var testContext,
originalDomainObject,
mockLocationCapability,
mockOriginalActionCapability,
originalPromise,
action;
beforeEach(function () {
mockLocationCapability = jasmine.createSpyObj(
'location',
[ 'isLink', 'isOriginal', 'getOriginal' ]
);
mockOriginalActionCapability = jasmine.createSpyObj(
'action',
[ 'perform', 'getActions' ]
);
originalPromise = new ControlledPromise();
mockLocationCapability.getOriginal.andReturn(originalPromise);
mockLocationCapability.isLink.andReturn(true);
mockLocationCapability.isOriginal.andCallFake(function () {
return !mockLocationCapability.isLink();
});
testContext = {
domainObject: domainObjectFactory({
capabilities: {
location: mockLocationCapability
}
})
};
originalDomainObject = domainObjectFactory({
capabilities: {
action: mockOriginalActionCapability
}
});
action = new GoToOriginalAction(testContext);
});
it("is applicable to links", function () {
expect(GoToOriginalAction.appliesTo(testContext))
.toBeTruthy();
});
it("is not applicable to originals", function () {
mockLocationCapability.isLink.andReturn(false);
expect(GoToOriginalAction.appliesTo(testContext))
.toBeFalsy();
});
it("navigates to original objects when performed", function () {
expect(mockOriginalActionCapability.perform)
.not.toHaveBeenCalled();
action.perform();
originalPromise.resolve(originalDomainObject);
expect(mockOriginalActionCapability.perform)
.toHaveBeenCalledWith('navigate');
});
});
}
);

View File

@ -1,3 +1,25 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,describe,it,expect,beforeEach,jasmine */
define(
@ -7,6 +29,7 @@ define(
'../ControlledPromise'
],
function (LocationCapability, domainObjectFactory, ControlledPromise) {
'use strict';
describe("LocationCapability", function () {
@ -14,10 +37,14 @@ define(
var locationCapability,
persistencePromise,
mutationPromise,
mockQ,
mockInjector,
mockObjectService,
domainObject;
beforeEach(function () {
domainObject = domainObjectFactory({
id: "testObject",
capabilities: {
context: {
getParent: function () {
@ -35,6 +62,11 @@ define(
}
});
mockQ = jasmine.createSpyObj("$q", ["when"]);
mockInjector = jasmine.createSpyObj("$injector", ["get"]);
mockObjectService =
jasmine.createSpyObj("objectService", ["getObjects"]);
persistencePromise = new ControlledPromise();
domainObject.capabilities.persistence.persist.andReturn(
persistencePromise
@ -49,7 +81,11 @@ define(
}
);
locationCapability = new LocationCapability(domainObject);
locationCapability = new LocationCapability(
mockQ,
mockInjector,
domainObject
);
});
it("returns contextual location", function () {
@ -88,6 +124,57 @@ define(
expect(whenComplete).toHaveBeenCalled();
});
describe("when used to load an original instance", function () {
var objectPromise,
qPromise,
originalObjects,
mockCallback;
function resolvePromises() {
if (mockQ.when.calls.length > 0) {
qPromise.resolve(mockQ.when.mostRecentCall.args[0]);
}
if (mockObjectService.getObjects.calls.length > 0) {
objectPromise.resolve(originalObjects);
}
}
beforeEach(function () {
objectPromise = new ControlledPromise();
qPromise = new ControlledPromise();
originalObjects = {
testObject: domainObjectFactory()
};
mockInjector.get.andCallFake(function (key) {
return key === 'objectService' && mockObjectService;
});
mockObjectService.getObjects.andReturn(objectPromise);
mockQ.when.andReturn(qPromise);
mockCallback = jasmine.createSpy('callback');
});
it("provides originals directly", function () {
domainObject.model.location = 'root';
locationCapability.getOriginal().then(mockCallback);
expect(mockCallback).not.toHaveBeenCalled();
resolvePromises();
expect(mockCallback)
.toHaveBeenCalledWith(domainObject);
});
it("loads from the object service for links", function () {
domainObject.model.location = 'some-other-root';
locationCapability.getOriginal().then(mockCallback);
expect(mockCallback).not.toHaveBeenCalled();
resolvePromises();
expect(mockCallback)
.toHaveBeenCalledWith(originalObjects.testObject);
});
});
});
});
}

View File

@ -1,5 +1,9 @@
[
"actions/AbstractComposeAction",
"actions/CopyAction",
"actions/GoToOriginalAction",
"actions/LinkAction",
"actions/MoveAction",
"services/CopyService",
"services/LinkService",
"services/MoveService",

View File

@ -122,7 +122,7 @@ define([
mutationTopic.listen(function (mutatedObject) {
var id = mutatedObject.getId();
provider.indexed[id] = false;
provider.indexedIds[id] = false;
provider.scheduleForIndexing(id);
});
};

View File

@ -99,6 +99,14 @@ define([
.toHaveBeenCalledWith(jasmine.any(Function));
});
it('reschedules indexing when mutation occurs', function () {
var mockDomainObject =
jasmine.createSpyObj('domainObj', ['getId']);
mockDomainObject.getId.andReturn("some-id");
mutationTopic.listen.mostRecentCall.args[0](mockDomainObject);
expect(provider.scheduleForIndexing).toHaveBeenCalledWith('some-id');
});
it('starts indexing roots', function () {
expect(provider.scheduleForIndexing).toHaveBeenCalledWith('mine');
});

View File

@ -45,7 +45,8 @@ require.config({
paths: {
'es6-promise': 'platform/framework/lib/es6-promise-2.0.0.min',
'moment': 'platform/telemetry/lib/moment.min',
'moment-duration-format': 'platform/features/clock/lib/moment-duration-format'
'moment-duration-format': 'platform/features/clock/lib/moment-duration-format',
'uuid': 'platform/commonUI/browse/lib/uuid'
},
shim: {