From 60f2f9fb6c50e19348f264cb3aae4f3baeea15a5 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 6 Oct 2015 16:08:48 -0700 Subject: [PATCH 01/16] [Location] Add getOriginal method Add a getOriginal method to the location capability, to simplify loading of original versions of objects. nasa/openmctweb#147 --- platform/entanglement/bundle.json | 3 +- .../src/capabilities/LocationCapability.js | 38 ++++++++++++++++--- .../capabilities/LocationCapabilitySpec.js | 17 ++++++++- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/platform/entanglement/bundle.json b/platform/entanglement/bundle.json index 61c3d90539..52a65071a8 100644 --- a/platform/entanglement/bundle.json +++ b/platform/entanglement/bundle.json @@ -52,7 +52,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": [ diff --git a/platform/entanglement/src/capabilities/LocationCapability.js b/platform/entanglement/src/capabilities/LocationCapability.js index 17d678f57e..38974ecb0f 100644 --- a/platform/entanglement/src/capabilities/LocationCapability.js +++ b/platform/entanglement/src/capabilities/LocationCapability.js @@ -12,11 +12,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.} 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 +108,6 @@ define( return !this.isLink(); }; - function createLocationCapability(domainObject) { - return new LocationCapability(domainObject); - } - - return createLocationCapability; + return LocationCapability; } ); diff --git a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js index 9cbfcc1bea..497158a67e 100644 --- a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js +++ b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js @@ -7,6 +7,7 @@ define( '../ControlledPromise' ], function (LocationCapability, domainObjectFactory, ControlledPromise) { + 'use strict'; describe("LocationCapability", function () { @@ -14,13 +15,16 @@ define( var locationCapability, persistencePromise, mutationPromise, + mockQ, + mockInjector, + mockObjectService, domainObject; beforeEach(function () { domainObject = domainObjectFactory({ capabilities: { context: { - getParent: function() { + getParent: function () { return domainObjectFactory({id: 'root'}); } }, @@ -35,6 +39,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 +58,11 @@ define( } ); - locationCapability = new LocationCapability(domainObject); + locationCapability = new LocationCapability( + mockQ, + mockObjectService, + domainObject + ); }); it("returns contextual location", function () { From e3afaf0842afa5abb3f3991f65a20f39c937f0a5 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 6 Oct 2015 16:22:16 -0700 Subject: [PATCH 02/16] [Entanglement] Add Go To Original nasa/openmctweb#147 --- platform/entanglement/bundle.json | 8 +++ .../src/actions/GoToOriginalAction.js | 62 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 platform/entanglement/src/actions/GoToOriginalAction.js diff --git a/platform/entanglement/bundle.json b/platform/entanglement/bundle.json index 52a65071a8..d8cde0ada6 100644 --- a/platform/entanglement/bundle.json +++ b/platform/entanglement/bundle.json @@ -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": [ diff --git a/platform/entanglement/src/actions/GoToOriginalAction.js b/platform/entanglement/src/actions/GoToOriginalAction.js new file mode 100644 index 0000000000..9722915ad6 --- /dev/null +++ b/platform/entanglement/src/actions/GoToOriginalAction.js @@ -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; + } +); + From 70bbd3cf97f43914572df00783763b208cbf08fa Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 6 Oct 2015 16:37:37 -0700 Subject: [PATCH 03/16] [Entanglement] Add test cases for Go To Original --- .../test/actions/GoToOriginalActionSpec.js | 95 +++++++++++++++++++ platform/entanglement/test/suite.json | 1 + 2 files changed, 96 insertions(+) create mode 100644 platform/entanglement/test/actions/GoToOriginalActionSpec.js diff --git a/platform/entanglement/test/actions/GoToOriginalActionSpec.js b/platform/entanglement/test/actions/GoToOriginalActionSpec.js new file mode 100644 index 0000000000..40c2f213ce --- /dev/null +++ b/platform/entanglement/test/actions/GoToOriginalActionSpec.js @@ -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'); + }); + + }); + } +); diff --git a/platform/entanglement/test/suite.json b/platform/entanglement/test/suite.json index 12831b407a..2ce90499e8 100644 --- a/platform/entanglement/test/suite.json +++ b/platform/entanglement/test/suite.json @@ -1,5 +1,6 @@ [ "actions/AbstractComposeAction", + "actions/GoToOriginalAction", "services/CopyService", "services/LinkService", "services/MoveService", From a4944717a1eb1bcbe385dd3ddc7321ea8805ab34 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 6 Oct 2015 16:47:37 -0700 Subject: [PATCH 04/16] [Location] Test getOriginal method --- .../capabilities/LocationCapabilitySpec.js | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js index 497158a67e..938f0d843c 100644 --- a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js +++ b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js @@ -22,6 +22,7 @@ define( beforeEach(function () { domainObject = domainObjectFactory({ + id: "testObject", capabilities: { context: { getParent: function () { @@ -60,7 +61,7 @@ define( locationCapability = new LocationCapability( mockQ, - mockObjectService, + mockInjector, domainObject ); }); @@ -101,6 +102,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); + }); + }); + + }); }); } From bf41d82a78282fdac3acaed1845188bf69eed651 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 6 Oct 2015 16:50:35 -0700 Subject: [PATCH 05/16] [Entanglement] Restore missing specs Restore specs which had been omitted from suite.json (but currently succeed for the relevant scripts); done in the context of nasa/openmctweb#147 --- platform/entanglement/test/suite.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/entanglement/test/suite.json b/platform/entanglement/test/suite.json index 2ce90499e8..89c082f9c8 100644 --- a/platform/entanglement/test/suite.json +++ b/platform/entanglement/test/suite.json @@ -1,6 +1,9 @@ [ "actions/AbstractComposeAction", + "actions/CopyAction", "actions/GoToOriginalAction", + "actions/LinkAction", + "actions/MoveAction", "services/CopyService", "services/LinkService", "services/MoveService", From 87e317a6f5b4f4e0eeabfc9268699784764dec6e Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 16 Oct 2015 11:33:42 -0700 Subject: [PATCH 06/16] [CI] Remove non-existent bundle from procfile Remove the example/localstorage bundle from the procfile. Fixes #153. --- Procfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Procfile b/Procfile index aa6094edfe..1e13b4ae05 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: node app.js --port $PORT --include example/localstorage \ No newline at end of file +web: node app.js --port $PORT From 8e2a2eeba5db5c9c14769c75bfe67fcfb63491a9 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 19 Oct 2015 12:08:49 -0700 Subject: [PATCH 07/16] [Entanglement] Add license headers ...per code review feedback from nasa/openmctweb#175 --- .../src/capabilities/LocationCapability.js | 22 +++++++++++++++++++ .../capabilities/LocationCapabilitySpec.js | 22 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/platform/entanglement/src/capabilities/LocationCapability.js b/platform/entanglement/src/capabilities/LocationCapability.js index 38974ecb0f..27e1f74c74 100644 --- a/platform/entanglement/src/capabilities/LocationCapability.js +++ b/platform/entanglement/src/capabilities/LocationCapability.js @@ -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( diff --git a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js index 938f0d843c..442bfe20aa 100644 --- a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js +++ b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js @@ -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( From 21739fffd91c4892d427502e299550e49bb2e12a Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 20 Oct 2015 15:52:49 -0700 Subject: [PATCH 08/16] [Documentation] Add table of contents Add table of contents to generated documents, without modifying document sources; nasa/openmctweb#189. --- docs/gendocs.js | 11 ++++++++--- package.json | 3 ++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/gendocs.js b/docs/gendocs.js index 2fcda7214e..455937e8b9 100644 --- a/docs/gendocs.js +++ b/docs/gendocs.js @@ -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("\n"); this.push(marked(markdown)); this.push("\n\n"); @@ -133,8 +138,8 @@ GLOBAL.window = GLOBAL.window || GLOBAL; // nomnoml expects window to be define customRenderer.link = function (href, title, text) { // ...but only if they look like relative paths return (href || "").indexOf(":") === -1 && href[0] !== "/" ? - renderer.link(href.replace(/\.md/, ".html"), title, text) : - renderer.link.apply(renderer, arguments); + renderer.link(href.replace(/\.md/, ".html"), title, text) : + renderer.link.apply(renderer, arguments); }; return customRenderer; } diff --git a/package.json b/package.json index a1ac01f437..c96642129c 100644 --- a/package.json +++ b/package.json @@ -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", From 07818b0a6d69e521ee962a5e8c0351d08153ffc1 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 21 Oct 2015 14:35:18 -0700 Subject: [PATCH 09/16] [Time Controller] Show bounds in a text field Show bounds in a text field to allow user editing; supports manual editing of time controller bounds, nasa/openmctweb#181. --- .../templates/controls/time-controller.html | 152 +++++++++--------- .../src/controllers/TimeRangeController.js | 10 +- 2 files changed, 82 insertions(+), 80 deletions(-) diff --git a/platform/commonUI/general/res/templates/controls/time-controller.html b/platform/commonUI/general/res/templates/controls/time-controller.html index 61a4feedb8..7f1e8b8453 100644 --- a/platform/commonUI/general/res/templates/controls/time-controller.html +++ b/platform/commonUI/general/res/templates/controls/time-controller.html @@ -21,82 +21,84 @@ -->
-
- C - - - - {{startOuterText}} - - -
- - -
-
-
-
+
+ C + + + + + + + +
+ + +
+
+
+
- to + to - - - - {{endOuterText}} - - -
- - -
-
-
  -
-
+ + + + + + + +
+ + +
+
+
  +
+
-
-
-
-
-
{{startInnerText}}
-
-
-
{{endInnerText}}
-
-
-
-
-
-
-
-
-
+
+
+
+
+
{{startInnerText}}
+
+
+
{{endInnerText}}
+
+
+
+
+
+
+
+
+
-
-
-
- {{tick}} -
-
-
-
\ No newline at end of file +
+
+
+ {{tick}} +
+
+
+ diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index c979f03a47..92fc740034 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -26,9 +26,8 @@ define( function (moment) { "use strict"; - var - DATE_FORMAT = "YYYY-MM-DD HH:mm:ss", - TICK_SPACING_PX = 150; + var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss", + TICK_SPACING_PX = 150; /** * @memberof platform/commonUI/general @@ -99,8 +98,8 @@ 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); + $scope.boundsModel.start = formatTimestamp(ngModel.outer.start); + $scope.boundsModel.end = formatTimestamp(ngModel.outer.end); // Then various updates for the inner span updateViewForInnerSpanFromModel(ngModel); @@ -210,6 +209,7 @@ define( $scope.state = false; $scope.ticks = []; + $scope.boundsModel = {}; // Initialize scope to defaults updateViewFromModel($scope.ngModel); From 6d2b2fd81e0423faf7cfa0ea20bf55d30dd5e586 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 21 Oct 2015 14:46:12 -0700 Subject: [PATCH 10/16] [Time Controller] Parse user-entered timestamps nasa/openmctweb#181. --- .../src/controllers/TimeRangeController.js | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index 44db087a65..b5ff59ab1a 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -43,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) + "%"; @@ -214,6 +223,22 @@ define( updateViewForInnerSpanFromModel(ngModel); } + function updateStartFromText(value) { + try { + updateOuterStart(parseTimestamp(value)); + } catch (e) { + return; + } + } + + function updateEndFromText(value) { + try { + updateOuterEnd(parseTimestamp(value)); + } catch (e) { + return; + } + } + $scope.startLeftDrag = startLeftDrag; $scope.startRightDrag = startRightDrag; $scope.startMiddleDrag = startMiddleDrag; @@ -232,6 +257,8 @@ define( $scope.$watch("spanWidth", updateSpanWidth); $scope.$watch("ngModel.outer.start", updateOuterStart); $scope.$watch("ngModel.outer.end", updateOuterEnd); + $scope.$watch("boundsModel.start", updateStartFromText); + $scope.$watch("boundsModel.end", updateEndFromText); } return TimeConductorController; From f88e8ebb51feed773e95206674cbb83da142e139 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 21 Oct 2015 15:08:44 -0700 Subject: [PATCH 11/16] [Time Controller] Update model state for text entry --- .../general/src/controllers/TimeRangeController.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index b5ff59ab1a..a0c6b84dcf 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -186,6 +186,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 @@ -198,14 +200,14 @@ define( ngModel.inner.end ); - $scope.startOuterText = formatTimestamp(t); - updateViewForInnerSpanFromModel(ngModel); } function updateOuterEnd(t) { var ngModel = $scope.ngModel; + ngModel.outer.end = t; + ngModel.outer.start = Math.min( ngModel.outer.end - outerMinimumSpan, ngModel.outer.start @@ -218,8 +220,6 @@ define( ngModel.inner.start ); - $scope.endOuterText = formatTimestamp(t); - updateViewForInnerSpanFromModel(ngModel); } From 06bcd28558f5e9f6b3fd0e83c60a705a35702846 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 21 Oct 2015 15:22:00 -0700 Subject: [PATCH 12/16] [Time Controller] Keep inputs in sync Keep inputs in sync with displayed data in time controller, without overwriting user-entered text. nasa/openmctweb#181 --- .../src/controllers/TimeRangeController.js | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index a0c6b84dcf..1b567def99 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -101,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(); @@ -109,8 +128,7 @@ define( ngModel.inner = ngModel.inner || copyBounds(ngModel.outer); // First, dates for the date pickers for outer bounds - $scope.boundsModel.start = formatTimestamp(ngModel.outer.start); - $scope.boundsModel.end = formatTimestamp(ngModel.outer.end); + updateBoundsText(ngModel); // Then various updates for the inner span updateViewForInnerSpanFromModel(ngModel); @@ -201,6 +219,7 @@ define( ); updateViewForInnerSpanFromModel(ngModel); + updateTicks(); } function updateOuterEnd(t) { @@ -221,11 +240,13 @@ define( ); updateViewForInnerSpanFromModel(ngModel); + updateTicks(); } function updateStartFromText(value) { try { updateOuterStart(parseTimestamp(value)); + updateBoundsTextForProperty($scope.ngModel, 'end'); } catch (e) { return; } @@ -234,11 +255,22 @@ define( function updateEndFromText(value) { try { updateOuterEnd(parseTimestamp(value)); + updateBoundsTextForProperty($scope.ngModel, 'start'); } catch (e) { return; } } + function updateStartFromPicker(value) { + updateOuterStart(value); + updateBoundsText($scope.ngModel); + } + + function updateEndFromPicker(value) { + updateOuterEnd(value); + updateBoundsText($scope.ngModel); + } + $scope.startLeftDrag = startLeftDrag; $scope.startRightDrag = startRightDrag; $scope.startMiddleDrag = startMiddleDrag; @@ -255,8 +287,8 @@ define( $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); } From 847c3560630430cba3ef1a8bf0339280222097ce Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 21 Oct 2015 15:26:42 -0700 Subject: [PATCH 13/16] [Time Controller] Change color when input is invalid nasa/openmctweb#181 --- .../res/templates/controls/time-controller.html | 10 ++++++++-- .../general/src/controllers/TimeRangeController.js | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/platform/commonUI/general/res/templates/controls/time-controller.html b/platform/commonUI/general/res/templates/controls/time-controller.html index 8a9be523da..300e56c381 100644 --- a/platform/commonUI/general/res/templates/controls/time-controller.html +++ b/platform/commonUI/general/res/templates/controls/time-controller.html @@ -25,7 +25,10 @@ - + +
@@ -44,7 +47,10 @@ - + + diff --git a/platform/commonUI/general/src/controllers/TimeRangeController.js b/platform/commonUI/general/src/controllers/TimeRangeController.js index 1b567def99..d4fb21be08 100644 --- a/platform/commonUI/general/src/controllers/TimeRangeController.js +++ b/platform/commonUI/general/src/controllers/TimeRangeController.js @@ -247,7 +247,9 @@ define( try { updateOuterStart(parseTimestamp(value)); updateBoundsTextForProperty($scope.ngModel, 'end'); + $scope.boundsModel.startValid = true; } catch (e) { + $scope.boundsModel.startValid = false; return; } } @@ -256,7 +258,9 @@ define( try { updateOuterEnd(parseTimestamp(value)); updateBoundsTextForProperty($scope.ngModel, 'start'); + $scope.boundsModel.endValid = true; } catch (e) { + $scope.boundsModel.endValid = false; return; } } From dbebf085007be053a6f67d326b127cef16c93913 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 21 Oct 2015 15:38:58 -0700 Subject: [PATCH 14/16] [Time Controller] Add test cases ...to verify behavior on text entry of dates. --- .../controllers/TimeRangeControllerSpec.js | 68 ++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js b/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js index 9d7a6a9f52..91d3ecb9db 100644 --- a/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js +++ b/platform/commonUI/general/test/controllers/TimeRangeControllerSpec.js @@ -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); + }); + }); }); + + }); } ); From bf24ac7c933fb14cb9bd108e9b1b50070ac8a507 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 23 Oct 2015 12:14:46 -0700 Subject: [PATCH 15/16] [Search] Update field name Update field name in GenericSearchProvider to reflect changes from nasa/openmctweb#193. Avoids exceptions on mutation. Additionally, add test case exercising relevant code and verifying that reindexing is scheduled upon mutation as expected. --- platform/search/src/services/GenericSearchProvider.js | 2 +- .../search/test/services/GenericSearchProviderSpec.js | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/platform/search/src/services/GenericSearchProvider.js b/platform/search/src/services/GenericSearchProvider.js index 2b99abcae2..71dfe8c0ed 100644 --- a/platform/search/src/services/GenericSearchProvider.js +++ b/platform/search/src/services/GenericSearchProvider.js @@ -122,7 +122,7 @@ define([ mutationTopic.listen(function (mutatedObject) { var id = mutatedObject.getId(); - provider.indexed[id] = false; + provider.indexedIds[id] = false; provider.scheduleForIndexing(id); }); }; diff --git a/platform/search/test/services/GenericSearchProviderSpec.js b/platform/search/test/services/GenericSearchProviderSpec.js index 7b8e52274b..cc80e4210d 100644 --- a/platform/search/test/services/GenericSearchProviderSpec.js +++ b/platform/search/test/services/GenericSearchProviderSpec.js @@ -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'); }); From 3d996ac46696b983addd8c4696d5fee983eaf1a2 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 23 Oct 2015 16:32:05 -0700 Subject: [PATCH 16/16] [RequireJS] Specify path for uuid Specify path for uuid, making it available for any code that would require it, without that code needing to know the path to it. Fixes https://github.com/nasa/openmctweb/issues/211. --- platform/commonUI/browse/bundle.json | 5 +++++ platform/commonUI/browse/src/creation/CreationService.js | 2 +- test-main.js | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/platform/commonUI/browse/bundle.json b/platform/commonUI/browse/bundle.json index e3284d99b5..bc42a33c1f 100644 --- a/platform/commonUI/browse/bundle.json +++ b/platform/commonUI/browse/bundle.json @@ -1,4 +1,9 @@ { + "configuration": { + "paths": { + "uuid": "uuid" + } + }, "extensions": { "routes": [ { diff --git a/platform/commonUI/browse/src/creation/CreationService.js b/platform/commonUI/browse/src/creation/CreationService.js index 667863ef20..2b059724b3 100644 --- a/platform/commonUI/browse/src/creation/CreationService.js +++ b/platform/commonUI/browse/src/creation/CreationService.js @@ -25,7 +25,7 @@ * Module defining CreateService. Created by vwoeltje on 11/10/14. */ define( - ["../../lib/uuid"], + ["uuid"], function (uuid) { "use strict"; diff --git a/test-main.js b/test-main.js index 46740a93b2..18b5f2a0d4 100644 --- a/test-main.js +++ b/test-main.js @@ -44,7 +44,8 @@ require.config({ paths: { 'es6-promise': 'platform/framework/lib/es6-promise-2.0.0.min', - 'moment-duration-format': 'warp/clock/lib/moment-duration-format' + 'moment-duration-format': 'warp/clock/lib/moment-duration-format', + 'uuid': 'platform/commonUI/browse/lib/uuid' }, // dynamically load all test files