From 0d312def27da85c56acd75c8017edd7ebb2a5ed5 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Apr 2016 16:37:44 -0700 Subject: [PATCH 01/78] [Persistence] Add PersistingMutationListener ...to automatically trigger persistence after mutation. #825 --- .../src/runs/PersistingMutationListener.js | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 platform/core/src/runs/PersistingMutationListener.js diff --git a/platform/core/src/runs/PersistingMutationListener.js b/platform/core/src/runs/PersistingMutationListener.js new file mode 100644 index 00000000000..54df8b4e56e --- /dev/null +++ b/platform/core/src/runs/PersistingMutationListener.js @@ -0,0 +1,42 @@ +/***************************************************************************** + * 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'; + + /** + * Listens for mutation on domain objects and triggers mutation when + * it occurs. + * @param {Topic} topic the `topic` service; used to listen for mutation + * @memberof platform/core + */ + function PersistingMutationListener(topic) { + var mutationTopic = topic('mutation'); + mutationTopic.listen(function (domainObject) { + var persistence = domainObject.getCapability('persistence'); + persistence.persist(); + }); + } + + return PersistingMutationListener; +}); From 4b641f87232c8864cccb105a7fd4e43860458f45 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Apr 2016 16:39:49 -0700 Subject: [PATCH 02/78] [Persistence] Expose PersistingMutationListener from bundle --- platform/core/bundle.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/platform/core/bundle.js b/platform/core/bundle.js index 1763ce88d43..057a6191198 100644 --- a/platform/core/bundle.js +++ b/platform/core/bundle.js @@ -47,6 +47,7 @@ define([ "./src/capabilities/MutationCapability", "./src/capabilities/DelegationCapability", "./src/capabilities/InstantiationCapability", + "./src/runs/PersistingMutationListener", "./src/services/Now", "./src/services/Throttle", "./src/services/Topic", @@ -79,6 +80,7 @@ define([ MutationCapability, DelegationCapability, InstantiationCapability, + PersistingMutationListener, Now, Throttle, Topic, @@ -411,6 +413,12 @@ define([ } } ], + "runs": [ + { + "implementation": PersistingMutationListener, + "depends": [ "topic" ] + } + ], "constants": [ { "key": "PERSISTENCE_SPACE", From 7fb7a5452fd07c57be767ecdb7acdfaaad445bee Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Apr 2016 16:43:52 -0700 Subject: [PATCH 03/78] [Persistence] Remove persistence usage from Edit Properties --- .../edit/src/actions/PropertiesAction.js | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/platform/commonUI/edit/src/actions/PropertiesAction.js b/platform/commonUI/edit/src/actions/PropertiesAction.js index 1134c23190a..288002386c6 100644 --- a/platform/commonUI/edit/src/actions/PropertiesAction.js +++ b/platform/commonUI/edit/src/actions/PropertiesAction.js @@ -52,12 +52,6 @@ define( domainObject = this.domainObject, dialogService = this.dialogService; - // Persist modifications to this domain object - function doPersist() { - var persistence = domainObject.getCapability('persistence'); - return persistence && persistence.persist(); - } - // Update the domain object model based on user input function updateModel(userInput, dialog) { return domainObject.useCapability('mutation', function (model) { @@ -75,11 +69,9 @@ define( dialog.getFormStructure(), dialog.getInitialFormValue() ).then(function (userInput) { - // Update the model, if user input was provided - return userInput && updateModel(userInput, dialog); - }).then(function (result) { - return result && doPersist(); - }); + // Update the model, if user input was provided + return userInput && updateModel(userInput, dialog); + }); } return type && showDialog(type); @@ -96,9 +88,7 @@ define( creatable = type && type.hasFeature('creation'); // Only allow creatable types to be edited - return domainObject && - domainObject.hasCapability("persistence") && - creatable; + return domainObject && creatable; }; return PropertiesAction; From ef815cf060c8ded711e7ce2395f6f2345bca5055 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Apr 2016 16:46:30 -0700 Subject: [PATCH 04/78] [Persistence] Remove persistence reference ...from composition capability (as persistence is now triggered, albeit indirectly.) --- platform/core/src/capabilities/CompositionCapability.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platform/core/src/capabilities/CompositionCapability.js b/platform/core/src/capabilities/CompositionCapability.js index 977d8cb4182..4be26bd1ede 100644 --- a/platform/core/src/capabilities/CompositionCapability.js +++ b/platform/core/src/capabilities/CompositionCapability.js @@ -51,8 +51,7 @@ define( } /** - * Add a domain object to the composition of the field. - * This mutates but does not persist the modified object. + * Add a domain object to the composition of this domain object. * * If no index is given, this is added to the end of the composition. * From c99ffcb1f48d2ed943c7466cd7a87d41fec68f7f Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Apr 2016 16:50:15 -0700 Subject: [PATCH 05/78] [Persistence] Remove persistence call from compose action --- platform/commonUI/edit/src/actions/LinkAction.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/platform/commonUI/edit/src/actions/LinkAction.js b/platform/commonUI/edit/src/actions/LinkAction.js index 95ed9a8082f..a3fdb1ba180 100644 --- a/platform/commonUI/edit/src/actions/LinkAction.js +++ b/platform/commonUI/edit/src/actions/LinkAction.js @@ -41,19 +41,11 @@ define( LinkAction.prototype.perform = function () { var self = this; - // Persist changes to the domain object - function doPersist() { - var persistence = - self.domainObject.getCapability('persistence'); - return persistence.persist(); - } - // Link these objects function doLink() { var composition = self.domainObject && self.domainObject.getCapability('composition'); - return composition && composition.add(self.selectedObject) - .then(doPersist); + return composition && composition.add(self.selectedObject); } return this.selectedObject && doLink(); From 433d26e703c20775730b784e530b6f6b1dcf00a2 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Apr 2016 16:53:12 -0700 Subject: [PATCH 06/78] [Persistence] Remove persistence usage from Remove action --- platform/commonUI/edit/bundle.js | 1 - .../commonUI/edit/src/actions/RemoveAction.js | 24 ++++--------------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/platform/commonUI/edit/bundle.js b/platform/commonUI/edit/bundle.js index e766d96e56b..ae0b2c6ba2b 100644 --- a/platform/commonUI/edit/bundle.js +++ b/platform/commonUI/edit/bundle.js @@ -159,7 +159,6 @@ define([ "name": "Remove", "description": "Remove this object from its containing object.", "depends": [ - "$q", "navigationService" ] }, diff --git a/platform/commonUI/edit/src/actions/RemoveAction.js b/platform/commonUI/edit/src/actions/RemoveAction.js index dd956162898..b2a8fd5128c 100644 --- a/platform/commonUI/edit/src/actions/RemoveAction.js +++ b/platform/commonUI/edit/src/actions/RemoveAction.js @@ -41,9 +41,8 @@ define( * @constructor * @implements {Action} */ - function RemoveAction($q, navigationService, context) { + function RemoveAction(navigationService, context) { this.domainObject = (context || {}).domainObject; - this.$q = $q; this.navigationService = navigationService; } @@ -53,8 +52,7 @@ define( * fulfilled when the action has completed. */ RemoveAction.prototype.perform = function () { - var $q = this.$q, - navigationService = this.navigationService, + var navigationService = this.navigationService, domainObject = this.domainObject; /* * Check whether an object ID matches the ID of the object being @@ -73,15 +71,6 @@ define( model.composition = model.composition.filter(isNotObject); } - /* - * Invoke persistence on a domain object. This will be called upon - * the removed object's parent (as its composition will have changed.) - */ - function doPersist(domainObject) { - var persistence = domainObject.getCapability('persistence'); - return persistence && persistence.persist(); - } - /* * Checks current object and ascendants of current * object with object being removed, if the current @@ -121,15 +110,10 @@ define( // navigates to existing object up tree checkObjectNavigation(object, parent); - return $q.when( - parent.useCapability('mutation', doMutate) - ).then(function () { - return doPersist(parent); - }); + return parent.useCapability('mutation', doMutate); } - return $q.when(domainObject) - .then(removeFromContext); + return removeFromContext(domainObject); }; // Object needs to have a parent for Remove to be applicable From 6418b0c634485985439775bb6b5418f746579557 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Apr 2016 16:58:10 -0700 Subject: [PATCH 07/78] [Persistence] Avoid redundant calls to persistenceService Mutation now triggers persistence; this means that some legacy code does or may issue redundant calls to persistence after mutating. To avoid issuing redundant calls all the way down to the persistenceService, bail out of persist calls early if there are no unsaved changes to persist. --- platform/core/src/capabilities/PersistenceCapability.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/platform/core/src/capabilities/PersistenceCapability.js b/platform/core/src/capabilities/PersistenceCapability.js index af21618ee31..186205aeb04 100644 --- a/platform/core/src/capabilities/PersistenceCapability.js +++ b/platform/core/src/capabilities/PersistenceCapability.js @@ -132,12 +132,17 @@ define( domainObject = this.domainObject, model = domainObject.getModel(), modified = model.modified, + persisted = model.persisted, cacheService = this.cacheService, persistenceService = this.persistenceService, persistenceFn = model.persisted !== undefined ? this.persistenceService.updateObject : this.persistenceService.createObject; + if (persisted !== undefined && persisted === modified) { + return this.$q.when(true); + } + // Update persistence timestamp... domainObject.useCapability("mutation", function (model) { model.persisted = modified; From b0cbb5a2b40d0aa60a0fd5ab0b17dad645c050fc Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 29 Apr 2016 11:31:36 -0700 Subject: [PATCH 08/78] [Persistence] Only auto-persist previously saved domain objects --- platform/core/src/capabilities/PersistenceCapability.js | 9 +++++++++ platform/core/src/runs/PersistingMutationListener.js | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/platform/core/src/capabilities/PersistenceCapability.js b/platform/core/src/capabilities/PersistenceCapability.js index 186205aeb04..35fd6d8de15 100644 --- a/platform/core/src/capabilities/PersistenceCapability.js +++ b/platform/core/src/capabilities/PersistenceCapability.js @@ -202,6 +202,15 @@ define( return this.identifierService.parse(id).getSpace(); }; + /** + * Check if this domain object has been persisted at some + * point. + * @returns {boolean} true if the object has been persisted + */ + PersistenceCapability.prototype.persisted = function () { + return this.domainObject.getModel().persisted !== undefined; + }; + return PersistenceCapability; } ); diff --git a/platform/core/src/runs/PersistingMutationListener.js b/platform/core/src/runs/PersistingMutationListener.js index 54df8b4e56e..c41934df024 100644 --- a/platform/core/src/runs/PersistingMutationListener.js +++ b/platform/core/src/runs/PersistingMutationListener.js @@ -34,7 +34,9 @@ define([], function () { var mutationTopic = topic('mutation'); mutationTopic.listen(function (domainObject) { var persistence = domainObject.getCapability('persistence'); - persistence.persist(); + if (persistence.persisted()) { + persistence.persist(); + } }); } From 8756b7213fb24b3351232b20d48868e6fa31fa2f Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 29 Apr 2016 11:35:00 -0700 Subject: [PATCH 09/78] [Persistence] Remove persistence usage from EditRepresenter --- .../edit/src/representers/EditRepresenter.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/platform/commonUI/edit/src/representers/EditRepresenter.js b/platform/commonUI/edit/src/representers/EditRepresenter.js index 689bb183d08..1c48269234d 100644 --- a/platform/commonUI/edit/src/representers/EditRepresenter.js +++ b/platform/commonUI/edit/src/representers/EditRepresenter.js @@ -52,17 +52,13 @@ define( this.listenHandle = undefined; // Mutate and persist a new version of a domain object's model. - function doPersist(model) { + function doMutate(model) { var domainObject = self.domainObject; // First, mutate; then, persist. return $q.when(domainObject.useCapability("mutation", function () { return model; - })).then(function (result) { - // Only persist when mutation was successful - return result && - domainObject.getCapability("persistence").persist(); - }); + })); } // Handle changes to model and/or view configuration @@ -82,14 +78,14 @@ define( ].join(" ")); // Update the configuration stored in the model, and persist. - if (domainObject && domainObject.hasCapability("persistence")) { + if (domainObject) { // Configurations for specific views are stored by // key in the "configuration" field of the model. if (self.key && configuration) { model.configuration = model.configuration || {}; model.configuration[self.key] = configuration; } - doPersist(model); + doMutate(model); } } From dd6414daf0bc38fe234c797496cc7fab0da6ed7b Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 29 Apr 2016 11:37:03 -0700 Subject: [PATCH 10/78] [Persistence] Remove persistence usage from LocationCapability --- .../entanglement/src/capabilities/LocationCapability.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/platform/entanglement/src/capabilities/LocationCapability.js b/platform/entanglement/src/capabilities/LocationCapability.js index 27e1f74c74e..2132240035d 100644 --- a/platform/entanglement/src/capabilities/LocationCapability.js +++ b/platform/entanglement/src/capabilities/LocationCapability.js @@ -78,17 +78,12 @@ define( * completes. */ LocationCapability.prototype.setPrimaryLocation = function (location) { - var capability = this; return this.domainObject.useCapability( 'mutation', function (model) { model.location = location; } - ).then(function () { - return capability.domainObject - .getCapability('persistence') - .persist(); - }); + ); }; /** From 0e683acde13d08e1d7f5a23e274aa907fc24a73a Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 29 Apr 2016 11:38:40 -0700 Subject: [PATCH 11/78] [Persistence] Remove persistence usage from LinkService --- platform/entanglement/src/services/LinkService.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/platform/entanglement/src/services/LinkService.js b/platform/entanglement/src/services/LinkService.js index 5989c3cf062..a092f52fe64 100644 --- a/platform/entanglement/src/services/LinkService.js +++ b/platform/entanglement/src/services/LinkService.js @@ -65,12 +65,7 @@ define( ); } - return parentObject.getCapability('composition').add(object) - .then(function (objectInNewContext) { - return parentObject.getCapability('persistence') - .persist() - .then(function () { return objectInNewContext; }); - }); + return parentObject.getCapability('composition').add(object); }; return LinkService; From f7f934f0e865db4068fae088ccd9db86ad719249 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 29 Apr 2016 11:40:06 -0700 Subject: [PATCH 12/78] [Persistence] Remove persistence usage from AbstractStartTimerAction --- .../clock/src/actions/AbstractStartTimerAction.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/platform/features/clock/src/actions/AbstractStartTimerAction.js b/platform/features/clock/src/actions/AbstractStartTimerAction.js index 116a5e51f42..8ec204e2271 100644 --- a/platform/features/clock/src/actions/AbstractStartTimerAction.js +++ b/platform/features/clock/src/actions/AbstractStartTimerAction.js @@ -51,17 +51,11 @@ define( var domainObject = this.domainObject, now = this.now; - function doPersist() { - var persistence = domainObject.getCapability('persistence'); - return persistence && persistence.persist(); - } - function setTimestamp(model) { model.timestamp = now(); } - return domainObject.useCapability('mutation', setTimestamp) - .then(doPersist); + return domainObject.useCapability('mutation', setTimestamp); }; return AbstractStartTimerAction; From 3f330dccf18e497cde26fc9b93015b68daf41b10 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 29 Apr 2016 11:50:02 -0700 Subject: [PATCH 13/78] [Persistence] Remove persistence usage from TimelineDragHandler --- .../src/controllers/drag/TimelineDragHandler.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/platform/features/timeline/src/controllers/drag/TimelineDragHandler.js b/platform/features/timeline/src/controllers/drag/TimelineDragHandler.js index ff1147bbae3..ce5b41b6c04 100644 --- a/platform/features/timeline/src/controllers/drag/TimelineDragHandler.js +++ b/platform/features/timeline/src/controllers/drag/TimelineDragHandler.js @@ -37,7 +37,6 @@ define( */ function TimelineDragHandler(domainObject, objectLoader) { var timespans = {}, - persists = {}, mutations = {}, compositions = {}, dirty = {}; @@ -73,19 +72,14 @@ define( } // Persist changes for objects by id (when dragging ends) - function doPersist(id) { - var persistence = persists[id], - mutation = mutations[id]; + function finalMutate(id) { + var mutation = mutations[id]; if (mutation) { // Mutate just to update the timestamp (since we // explicitly don't do this during the drag to // avoid firing a ton of refreshes.) mutation.mutate(function () {}); } - if (persistence) { - // Persist the changes - persistence.persist(); - } } // Use the object loader to get objects which have timespans @@ -107,7 +101,7 @@ define( */ persist: function () { // Persist every dirty object... - Object.keys(dirty).forEach(doPersist); + Object.keys(dirty).forEach(finalMutate); // Clear out the dirty list dirty = {}; }, @@ -255,4 +249,4 @@ define( return TimelineDragHandler; } -); \ No newline at end of file +); From bea9f22a0c5181e4ef611d329aaf16c1c815d738 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 29 Apr 2016 11:51:21 -0700 Subject: [PATCH 14/78] [Persistence] Remove persistence usage from TimelineSwimlaneDecorator --- .../src/controllers/swimlane/TimelineSwimlaneDecorator.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDecorator.js b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDecorator.js index 522197a873e..bc263de08a5 100644 --- a/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDecorator.js +++ b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDecorator.js @@ -37,7 +37,6 @@ define( var domainObject = swimlane && swimlane.domainObject, model = (domainObject && domainObject.getModel()) || {}, mutator = domainObject && domainObject.getCapability('mutation'), - persister = domainObject && domainObject.getCapability('persistence'), type = domainObject && domainObject.getCapability('type'), dropHandler = new TimelineSwimlaneDropHandler(swimlane); @@ -50,7 +49,7 @@ define( mutator.mutate(function (model) { model.relationships = model.relationships || {}; model.relationships[ACTIVITY_RELATIONSHIP] = value; - }).then(persister.persist); + }); } } // ...otherwise, use as a getter @@ -65,7 +64,7 @@ define( // Update the link mutator.mutate(function (model) { model.link = value; - }).then(persister.persist); + }); } return model.link; } @@ -86,7 +85,7 @@ define( } // Activities should have the Activity Modes and Activity Link dialog - if (type && type.instanceOf("activity") && mutator && persister) { + if (type && type.instanceOf("activity") && mutator) { swimlane.modes = modes; swimlane.link = link; } From f85ac79b4a6343f4d411677a9f33183e2eff98e1 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 29 Apr 2016 11:53:15 -0700 Subject: [PATCH 15/78] [Persistence] Remove persistence usage from TimelineSwimlaneDropHandler --- .../swimlane/TimelineSwimlaneDropHandler.js | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDropHandler.js b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDropHandler.js index 619232539fe..d24c634e3fe 100644 --- a/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDropHandler.js +++ b/platform/features/timeline/src/controllers/swimlane/TimelineSwimlaneDropHandler.js @@ -31,16 +31,6 @@ define( * @constructor */ function TimelineSwimlaneDropHandler(swimlane) { - // Utility function; like $q.when, but synchronous (to reduce - // performance impact when wrapping synchronous values) - function asPromise(value) { - return (value && value.then) ? value : { - then: function (callback) { - return asPromise(callback(value)); - } - }; - } - // Check if we are in edit mode function inEditMode() { return swimlane.domainObject.hasCapability("editor"); @@ -76,16 +66,7 @@ define( // Initiate mutation of a domain object function doMutate(domainObject, mutator) { - return asPromise( - domainObject.useCapability("mutation", mutator) - ).then(function () { - // Persist the results of mutation - var persistence = domainObject.getCapability("persistence"); - if (persistence) { - // Persist the changes - persistence.persist(); - } - }); + return domainObject.useCapability("mutation", mutator); } // Check if this swimlane is in a state where a drop-after will From d5914dfde9858ff89003da6eecf4bc1e27ca1cfe Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 11:42:58 -0700 Subject: [PATCH 16/78] [Persistence] Remove caching of persistence capability ...as this no longer needs to be invoked directly. --- .../timeline/src/controllers/drag/TimelineDragHandler.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/platform/features/timeline/src/controllers/drag/TimelineDragHandler.js b/platform/features/timeline/src/controllers/drag/TimelineDragHandler.js index 4216af0c369..ca5b17155e1 100644 --- a/platform/features/timeline/src/controllers/drag/TimelineDragHandler.js +++ b/platform/features/timeline/src/controllers/drag/TimelineDragHandler.js @@ -55,8 +55,6 @@ define( timespans[id] = timespan; // And its mutation capability mutations[id] = domainObject.getCapability('mutation'); - // Also cache the persistence capability for later - persists[id] = domainObject.getCapability('persistence'); // And the composition, for bulk moves compositions[id] = domainObject.getModel().composition || []; }); From 360fdeaec802504c57beeab87fa14da854509eba Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 11:44:26 -0700 Subject: [PATCH 17/78] [Persistence] Don't expect persist calls in spec --- .../swimlane/TimelineSwimlaneDecoratorSpec.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js index c4a059f7f72..f854a43b65b 100644 --- a/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js +++ b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDecoratorSpec.js @@ -50,10 +50,6 @@ define( 'mutation', ['mutate'] ); - mockCapabilities.persistence = jasmine.createSpyObj( - 'persistence', - ['persist'] - ); mockCapabilities.type = jasmine.createSpyObj( 'type', ['instanceOf'] @@ -115,11 +111,6 @@ define( .toHaveBeenCalledWith(jasmine.any(Function)); mockCapabilities.mutation.mutate.mostRecentCall.args[0](testModel); expect(testModel.relationships.modes).toEqual(['abc', 'xyz']); - - // Verify that persistence is called when promise resolves - expect(mockCapabilities.persistence.persist).not.toHaveBeenCalled(); - mockPromise.then.mostRecentCall.args[0](); - expect(mockCapabilities.persistence.persist).toHaveBeenCalled(); }); it("mutates modes when used as a setter", function () { @@ -128,11 +119,6 @@ define( .toHaveBeenCalledWith(jasmine.any(Function)); mockCapabilities.mutation.mutate.mostRecentCall.args[0](testModel); expect(testModel.link).toEqual("http://www.noaa.gov"); - - // Verify that persistence is called when promise resolves - expect(mockCapabilities.persistence.persist).not.toHaveBeenCalled(); - mockPromise.then.mostRecentCall.args[0](); - expect(mockCapabilities.persistence.persist).toHaveBeenCalled(); }); it("does not mutate modes when unchanged", function () { From 1e03f220863158113cf157bb33610f5791bb96fd Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 11:45:46 -0700 Subject: [PATCH 18/78] [Persistence] Don't expect persist calls in spec --- .../drag/TimelineDragHandlerSpec.js | 23 +------------------ 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/platform/features/timeline/test/controllers/drag/TimelineDragHandlerSpec.js b/platform/features/timeline/test/controllers/drag/TimelineDragHandlerSpec.js index 98e610f68b6..cab0cdde8bd 100644 --- a/platform/features/timeline/test/controllers/drag/TimelineDragHandlerSpec.js +++ b/platform/features/timeline/test/controllers/drag/TimelineDragHandlerSpec.js @@ -32,7 +32,6 @@ define( mockDomainObjects, mockTimespans, mockMutations, - mockPersists, mockCallback, handler; @@ -66,7 +65,6 @@ define( mockDomainObject.useCapability.andReturn(asPromise(mockTimespans[id])); mockDomainObject.getCapability.andCallFake(function (c) { return { - persistence: mockPersists[id], mutation: mockMutations[id] }[c]; }); @@ -76,17 +74,12 @@ define( beforeEach(function () { mockTimespans = {}; - mockPersists = {}; mockMutations = {}; ['a', 'b', 'c', 'd', 'e', 'f'].forEach(function (id, index) { mockTimespans[id] = jasmine.createSpyObj( 'timespan-' + id, [ 'getStart', 'getEnd', 'getDuration', 'setStart', 'setEnd', 'setDuration' ] ); - mockPersists[id] = jasmine.createSpyObj( - 'persistence-' + id, - [ 'persist' ] - ); mockMutations[id] = jasmine.createSpyObj( 'mutation-' + id, [ 'mutate' ] @@ -209,20 +202,6 @@ define( expect(mockTimespans.c.setStart).toHaveBeenCalledWith(1000); }); - it("persists mutated objects", function () { - handler.start('a', 20); - handler.end('b', 50); - handler.duration('c', 30); - handler.persist(); - expect(mockPersists.a.persist).toHaveBeenCalled(); - expect(mockPersists.b.persist).toHaveBeenCalled(); - expect(mockPersists.c.persist).toHaveBeenCalled(); - expect(mockPersists.d.persist).not.toHaveBeenCalled(); - expect(mockPersists.e.persist).not.toHaveBeenCalled(); - expect(mockPersists.f.persist).not.toHaveBeenCalled(); - }); - - }); } -); \ No newline at end of file +); From 390517145704d9c035d9b5fe35c0cb8266cd41e5 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 11:48:16 -0700 Subject: [PATCH 19/78] [Persistence] Don't expect persist calls in spec --- .../controllers/swimlane/TimelineSwimlaneDropHandlerSpec.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDropHandlerSpec.js b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDropHandlerSpec.js index 3eed051f454..0a45838e7dd 100644 --- a/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDropHandlerSpec.js +++ b/platform/features/timeline/test/controllers/swimlane/TimelineSwimlaneDropHandlerSpec.js @@ -29,7 +29,6 @@ define( mockOtherObject, mockActionCapability, mockEditorCapability, - mockPersistence, mockContext, mockAction, handler; @@ -76,7 +75,6 @@ define( [ "getId", "getCapability", "useCapability", "hasCapability" ] ); mockActionCapability = jasmine.createSpyObj("action", ["perform", "getActions"]); - mockPersistence = jasmine.createSpyObj("persistence", ["persist"]); mockContext = jasmine.createSpyObj('context', [ 'getParent' ]); mockActionCapability.getActions.andReturn([mockAction]); @@ -89,14 +87,12 @@ define( mockSwimlane.domainObject.getCapability.andCallFake(function (c) { return { action: mockActionCapability, - persistence: mockPersistence, editor: mockEditorCapability }[c]; }); mockSwimlane.parent.domainObject.getCapability.andCallFake(function (c) { return { action: mockActionCapability, - persistence: mockPersistence, editor: mockEditorCapability }[c]; }); @@ -162,8 +158,6 @@ define( mockSwimlane.domainObject.useCapability.mostRecentCall .args[1](testModel); expect(testModel.composition).toEqual(['c', 'd']); - // Finally, should also have persisted - expect(mockPersistence.persist).toHaveBeenCalled(); }); it("inserts after as a peer when highlighted at the bottom", function () { From ac3706dfb6f4bc48067c1f1e511072b0e2d11df4 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 11:50:12 -0700 Subject: [PATCH 20/78] [Persistence] Don't expect persist calls in specs --- .../test/actions/AbstractStartTimerActionSpec.js | 9 +-------- .../clock/test/actions/RestartTimerActionSpec.js | 11 +---------- .../clock/test/actions/StartTimerActionSpec.js | 10 +--------- 3 files changed, 3 insertions(+), 27 deletions(-) diff --git a/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js b/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js index 546c3487864..b09f4fba96e 100644 --- a/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js +++ b/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js @@ -27,7 +27,6 @@ define( describe("A timer's start/restart action", function () { var mockNow, mockDomainObject, - mockPersistence, testModel, action; @@ -45,10 +44,6 @@ define( 'domainObject', [ 'getCapability', 'useCapability' ] ); - mockPersistence = jasmine.createSpyObj( - 'persistence', - ['persist'] - ); mockDomainObject.getCapability.andCallFake(function (c) { return (c === 'persistence') && mockPersistence; @@ -67,18 +62,16 @@ define( }); }); - it("updates the model with a timestamp and persists", function () { + it("updates the model with a timestamp", function () { mockNow.andReturn(12000); action.perform(); expect(testModel.timestamp).toEqual(12000); - expect(mockPersistence.persist).toHaveBeenCalled(); }); it("does not truncate milliseconds", function () { mockNow.andReturn(42321); action.perform(); expect(testModel.timestamp).toEqual(42321); - expect(mockPersistence.persist).toHaveBeenCalled(); }); }); } diff --git a/platform/features/clock/test/actions/RestartTimerActionSpec.js b/platform/features/clock/test/actions/RestartTimerActionSpec.js index 72d955b79a0..84a4428c172 100644 --- a/platform/features/clock/test/actions/RestartTimerActionSpec.js +++ b/platform/features/clock/test/actions/RestartTimerActionSpec.js @@ -27,7 +27,6 @@ define( describe("A timer's restart action", function () { var mockNow, mockDomainObject, - mockPersistence, testModel, testContext, action; @@ -46,14 +45,7 @@ define( 'domainObject', [ 'getCapability', 'useCapability', 'getModel' ] ); - mockPersistence = jasmine.createSpyObj( - 'persistence', - ['persist'] - ); - mockDomainObject.getCapability.andCallFake(function (c) { - return (c === 'persistence') && mockPersistence; - }); mockDomainObject.useCapability.andCallFake(function (c, v) { if (c === 'mutation') { testModel = v(testModel) || testModel; @@ -70,11 +62,10 @@ define( action = new RestartTimerAction(mockNow, testContext); }); - it("updates the model with a timestamp and persists", function () { + it("updates the model with a timestamp", function () { mockNow.andReturn(12000); action.perform(); expect(testModel.timestamp).toEqual(12000); - expect(mockPersistence.persist).toHaveBeenCalled(); }); it("applies only to timers with a target time", function () { diff --git a/platform/features/clock/test/actions/StartTimerActionSpec.js b/platform/features/clock/test/actions/StartTimerActionSpec.js index 25e0fdfac7f..72278a849cd 100644 --- a/platform/features/clock/test/actions/StartTimerActionSpec.js +++ b/platform/features/clock/test/actions/StartTimerActionSpec.js @@ -46,14 +46,7 @@ define( 'domainObject', [ 'getCapability', 'useCapability', 'getModel' ] ); - mockPersistence = jasmine.createSpyObj( - 'persistence', - ['persist'] - ); - mockDomainObject.getCapability.andCallFake(function (c) { - return (c === 'persistence') && mockPersistence; - }); mockDomainObject.useCapability.andCallFake(function (c, v) { if (c === 'mutation') { testModel = v(testModel) || testModel; @@ -70,11 +63,10 @@ define( action = new StartTimerAction(mockNow, testContext); }); - it("updates the model with a timestamp and persists", function () { + it("updates the model with a timestamp", function () { mockNow.andReturn(12000); action.perform(); expect(testModel.timestamp).toEqual(12000); - expect(mockPersistence.persist).toHaveBeenCalled(); }); it("applies only to timers without a target time", function () { From 0aba9122a104cb30ba663a850c1f7a190b64043a Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 11:51:50 -0700 Subject: [PATCH 21/78] [Persistence] Don't expect persist calls in LinkService spec --- .../test/services/LinkServiceSpec.js | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/platform/entanglement/test/services/LinkServiceSpec.js b/platform/entanglement/test/services/LinkServiceSpec.js index e9e57d2c3e2..d98630ad477 100644 --- a/platform/entanglement/test/services/LinkServiceSpec.js +++ b/platform/entanglement/test/services/LinkServiceSpec.js @@ -139,20 +139,12 @@ define( parentModel, parentObject, compositionPromise, - persistencePromise, addPromise, - compositionCapability, - persistenceCapability; + compositionCapability; beforeEach(function () { compositionPromise = new ControlledPromise(); - persistencePromise = new ControlledPromise(); addPromise = new ControlledPromise(); - persistenceCapability = jasmine.createSpyObj( - 'persistenceCapability', - ['persist'] - ); - persistenceCapability.persist.andReturn(persistencePromise); compositionCapability = jasmine.createSpyObj( 'compositionCapability', ['invoke', 'add'] @@ -172,7 +164,6 @@ define( return new ControlledPromise(); } }, - persistence: persistenceCapability, composition: compositionCapability } }); @@ -197,15 +188,6 @@ define( .toHaveBeenCalledWith(object); }); - it("persists parent", function () { - linkService.perform(object, parentObject); - expect(addPromise.then).toHaveBeenCalled(); - addPromise.resolve(linkedObject); - expect(parentObject.getCapability) - .toHaveBeenCalledWith('persistence'); - expect(persistenceCapability.persist).toHaveBeenCalled(); - }); - it("returns object representing new link", function () { var returnPromise, whenComplete; returnPromise = linkService.perform(object, parentObject); @@ -213,7 +195,6 @@ define( returnPromise.then(whenComplete); addPromise.resolve(linkedObject); - persistencePromise.resolve(); compositionPromise.resolve([linkedObject]); expect(whenComplete).toHaveBeenCalledWith(linkedObject); }); From 9b273bc148277b9764b6b0e579fd9a5ed79ea31c Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 11:52:58 -0700 Subject: [PATCH 22/78] [Persistence] Don't expect persist calls in EditRepresenter spec --- .../edit/test/representers/EditRepresenterSpec.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js b/platform/commonUI/edit/test/representers/EditRepresenterSpec.js index fe0c17c5bdf..245b87cec37 100644 --- a/platform/commonUI/edit/test/representers/EditRepresenterSpec.js +++ b/platform/commonUI/edit/test/representers/EditRepresenterSpec.js @@ -30,7 +30,6 @@ define( mockScope, testRepresentation, mockDomainObject, - mockPersistence, mockStatusCapability, mockEditorCapability, mockCapabilities, @@ -56,15 +55,12 @@ define( "useCapability", "hasCapability" ]); - mockPersistence = - jasmine.createSpyObj("persistence", ["persist"]); mockStatusCapability = jasmine.createSpyObj("statusCapability", ["listen"]); mockEditorCapability = jasmine.createSpyObj("editorCapability", ["isEditContextRoot"]); mockCapabilities = { - 'persistence': mockPersistence, 'status': mockStatusCapability, 'editor': mockEditorCapability }; @@ -96,7 +92,7 @@ define( expect(representer.listenHandle).toHaveBeenCalled(); }); - it("mutates and persists upon observed changes", function () { + it("mutates upon observed changes", function () { mockScope.model = { someKey: "some value" }; mockScope.configuration = { someConfiguration: "something" }; @@ -108,9 +104,6 @@ define( jasmine.any(Function) ); - // ... and should have persisted the mutation - expect(mockPersistence.persist).toHaveBeenCalled(); - // Finally, check that the provided mutation function // includes both model and configuratioon expect( From a5aaa6f10395fee7dd5c3a2d2d074004577603d3 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 11:55:05 -0700 Subject: [PATCH 23/78] [Persistence] Don't expect persist calls in LocationCapability spec --- .../capabilities/LocationCapabilitySpec.js | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js index fb136a79883..2a929ba2bd6 100644 --- a/platform/entanglement/test/capabilities/LocationCapabilitySpec.js +++ b/platform/entanglement/test/capabilities/LocationCapabilitySpec.js @@ -33,7 +33,6 @@ define( describe("instantiated with domain object", function () { var locationCapability, - persistencePromise, mutationPromise, mockQ, mockInjector, @@ -49,10 +48,6 @@ define( return domainObjectFactory({id: 'root'}); } }, - persistence: jasmine.createSpyObj( - 'persistenceCapability', - ['persist'] - ), mutation: jasmine.createSpyObj( 'mutationCapability', ['invoke'] @@ -65,11 +60,6 @@ define( mockObjectService = jasmine.createSpyObj("objectService", ["getObjects"]); - persistencePromise = new ControlledPromise(); - domainObject.capabilities.persistence.persist.andReturn( - persistencePromise - ); - mutationPromise = new ControlledPromise(); domainObject.capabilities.mutation.invoke.andCallFake( function (mutator) { @@ -103,22 +93,17 @@ define( expect(locationCapability.isOriginal()).toBe(false); }); - it("can persist location", function () { - var persistResult = locationCapability + it("can mutate location", function () { + var result = locationCapability .setPrimaryLocation('root'), whenComplete = jasmine.createSpy('whenComplete'); - persistResult.then(whenComplete); + result.then(whenComplete); expect(domainObject.model.location).not.toBeDefined(); mutationPromise.resolve(); expect(domainObject.model.location).toBe('root'); - expect(whenComplete).not.toHaveBeenCalled(); - expect(domainObject.capabilities.persistence.persist) - .toHaveBeenCalled(); - - persistencePromise.resolve(); expect(whenComplete).toHaveBeenCalled(); }); From 467f4d2257e0e12ad607443ea6c5164f0b288d6c Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 12:00:51 -0700 Subject: [PATCH 24/78] [Persistence] Don't expect persist calls in RemoveAction spec --- .../commonUI/edit/test/actions/RemoveActionSpec.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/platform/commonUI/edit/test/actions/RemoveActionSpec.js b/platform/commonUI/edit/test/actions/RemoveActionSpec.js index f9f36e36a83..de036131b78 100644 --- a/platform/commonUI/edit/test/actions/RemoveActionSpec.js +++ b/platform/commonUI/edit/test/actions/RemoveActionSpec.js @@ -37,7 +37,6 @@ define( mockGrandchildContext, mockRootContext, mockMutation, - mockPersistence, mockType, actionContext, model, @@ -53,8 +52,6 @@ define( } beforeEach(function () { - - mockDomainObject = jasmine.createSpyObj( "domainObject", [ "getId", "getCapability" ] @@ -88,7 +85,6 @@ define( mockGrandchildContext = jasmine.createSpyObj("context", [ "getParent" ]); mockRootContext = jasmine.createSpyObj("context", [ "getParent" ]); mockMutation = jasmine.createSpyObj("mutation", [ "invoke" ]); - mockPersistence = jasmine.createSpyObj("persistence", [ "persist" ]); mockType = jasmine.createSpyObj("type", [ "hasFeature" ]); mockNavigationService = jasmine.createSpyObj( "navigationService", @@ -109,7 +105,6 @@ define( capabilities = { mutation: mockMutation, - persistence: mockPersistence, type: mockType }; model = { @@ -118,7 +113,7 @@ define( actionContext = { domainObject: mockDomainObject }; - action = new RemoveAction(mockQ, mockNavigationService, actionContext); + action = new RemoveAction(mockNavigationService, actionContext); }); it("only applies to objects with parents", function () { @@ -154,9 +149,6 @@ define( // Should have removed "test" - that was our // mock domain object's id. expect(result.composition).toEqual(["a", "b"]); - - // Finally, should have persisted - expect(mockPersistence.persist).toHaveBeenCalled(); }); it("removes parent of object currently navigated to", function () { @@ -214,4 +206,4 @@ define( }); } -); \ No newline at end of file +); From 63438ad9bad17583fd54358197aa5c6eb0b811e2 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 12:02:42 -0700 Subject: [PATCH 25/78] [Persistence] Don't expect persist calls in LinkAction spec --- platform/commonUI/edit/test/actions/LinkActionSpec.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/platform/commonUI/edit/test/actions/LinkActionSpec.js b/platform/commonUI/edit/test/actions/LinkActionSpec.js index 144dd4e3958..1580eb06153 100644 --- a/platform/commonUI/edit/test/actions/LinkActionSpec.js +++ b/platform/commonUI/edit/test/actions/LinkActionSpec.js @@ -30,7 +30,6 @@ define( mockParent, mockContext, mockComposition, - mockPersistence, mockType, actionContext, model, @@ -66,7 +65,6 @@ define( }; mockContext = jasmine.createSpyObj("context", [ "getParent" ]); mockComposition = jasmine.createSpyObj("composition", [ "invoke", "add" ]); - mockPersistence = jasmine.createSpyObj("persistence", [ "persist" ]); mockType = jasmine.createSpyObj("type", [ "hasFeature" ]); mockDomainObject.getId.andReturn("test"); @@ -78,7 +76,6 @@ define( capabilities = { composition: mockComposition, - persistence: mockPersistence, type: mockType }; model = { @@ -100,11 +97,6 @@ define( .toHaveBeenCalledWith(mockDomainObject); }); - it("persists changes afterward", function () { - action.perform(); - expect(mockPersistence.persist).toHaveBeenCalled(); - }); - }); } ); From 8279acc9a5c9f9d80e9798b0eaba7e396adaa5dc Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 12:04:08 -0700 Subject: [PATCH 26/78] [Persistence] Don't expect persist calls in PropertiesAction spec --- .../edit/test/actions/PropertiesActionSpec.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/platform/commonUI/edit/test/actions/PropertiesActionSpec.js b/platform/commonUI/edit/test/actions/PropertiesActionSpec.js index cad091cff5e..31036afaac6 100644 --- a/platform/commonUI/edit/test/actions/PropertiesActionSpec.js +++ b/platform/commonUI/edit/test/actions/PropertiesActionSpec.js @@ -41,7 +41,6 @@ define( getProperties: function () { return []; }, hasFeature: jasmine.createSpy('hasFeature') }, - persistence: jasmine.createSpyObj("persistence", ["persist"]), mutation: jasmine.createSpy("mutation") }; model = {}; @@ -66,23 +65,16 @@ define( action = new PropertiesAction(dialogService, context); }); - it("persists when an action is performed", function () { + it("mutates an object when performed", function () { action.perform(); - expect(capabilities.persistence.persist) - .toHaveBeenCalled(); + expect(capabilities.mutation).toHaveBeenCalled(); + capabilities.mutation.mostRecentCall.args[0]({}); }); - it("does not persist any changes upon cancel", function () { + it("does not muate object upon cancel", function () { input = undefined; action.perform(); - expect(capabilities.persistence.persist) - .not.toHaveBeenCalled(); - }); - - it("mutates an object when performed", function () { - action.perform(); - expect(capabilities.mutation).toHaveBeenCalled(); - capabilities.mutation.mostRecentCall.args[0]({}); + expect(capabilities.mutation).not.toHaveBeenCalled(); }); it("is only applicable when a domain object is in context", function () { From f1e81c359ecc8dfc22d0851f0a19155e07d873b2 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 12:17:20 -0700 Subject: [PATCH 27/78] [Persistence] Satisfy JSHint --- platform/core/src/runs/PersistingMutationListener.js | 2 -- .../clock/test/actions/AbstractStartTimerActionSpec.js | 3 --- platform/features/clock/test/actions/StartTimerActionSpec.js | 1 - 3 files changed, 6 deletions(-) diff --git a/platform/core/src/runs/PersistingMutationListener.js b/platform/core/src/runs/PersistingMutationListener.js index c41934df024..5ad7434d7cb 100644 --- a/platform/core/src/runs/PersistingMutationListener.js +++ b/platform/core/src/runs/PersistingMutationListener.js @@ -22,8 +22,6 @@ /*global define*/ define([], function () { - 'use strict'; - /** * Listens for mutation on domain objects and triggers mutation when * it occurs. diff --git a/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js b/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js index b09f4fba96e..35c70ecb761 100644 --- a/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js +++ b/platform/features/clock/test/actions/AbstractStartTimerActionSpec.js @@ -45,9 +45,6 @@ define( [ 'getCapability', 'useCapability' ] ); - mockDomainObject.getCapability.andCallFake(function (c) { - return (c === 'persistence') && mockPersistence; - }); mockDomainObject.useCapability.andCallFake(function (c, v) { if (c === 'mutation') { testModel = v(testModel) || testModel; diff --git a/platform/features/clock/test/actions/StartTimerActionSpec.js b/platform/features/clock/test/actions/StartTimerActionSpec.js index 72278a849cd..83816855fdf 100644 --- a/platform/features/clock/test/actions/StartTimerActionSpec.js +++ b/platform/features/clock/test/actions/StartTimerActionSpec.js @@ -27,7 +27,6 @@ define( describe("A timer's start action", function () { var mockNow, mockDomainObject, - mockPersistence, testModel, testContext, action; From bcf85db9c44c1561c21738d50d66b602d8b06c2c Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 19 May 2016 13:31:23 -0700 Subject: [PATCH 28/78] [Persistence] Delegate new 'persisted' method --- .../src/capabilities/TransactionalPersistenceCapability.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/commonUI/edit/src/capabilities/TransactionalPersistenceCapability.js b/platform/commonUI/edit/src/capabilities/TransactionalPersistenceCapability.js index 9dc7968d3ba..f96c56fd207 100644 --- a/platform/commonUI/edit/src/capabilities/TransactionalPersistenceCapability.js +++ b/platform/commonUI/edit/src/capabilities/TransactionalPersistenceCapability.js @@ -93,6 +93,10 @@ define( return this.persistenceCapability.getSpace(); }; + TransactionalPersistenceCapability.prototype.persisted = function () { + return this.persistenceCapability.persisted(); + }; + return TransactionalPersistenceCapability; } ); From 26839c7fd102ebc429dd26a60d55e47cdb82cdaf Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 20 May 2016 14:27:53 -0700 Subject: [PATCH 29/78] [Persistence] Fix code style after merge --- platform/core/bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/core/bundle.js b/platform/core/bundle.js index 4031592419b..5691a358bba 100644 --- a/platform/core/bundle.js +++ b/platform/core/bundle.js @@ -414,7 +414,7 @@ define([ "runs": [ { "implementation": PersistingMutationListener, - "depends": [ "topic" ] + "depends": ["topic"] } ], "constants": [ From fb8fe935292af7eca43bd2a95605f56090239b4c Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 25 May 2016 13:06:58 -0700 Subject: [PATCH 30/78] [Persistence] Fix mistake in JSDoc https://github.com/nasa/openmct/pull/874#discussion_r64639089 --- platform/core/src/runs/PersistingMutationListener.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/core/src/runs/PersistingMutationListener.js b/platform/core/src/runs/PersistingMutationListener.js index 5ad7434d7cb..b992ed3d8ab 100644 --- a/platform/core/src/runs/PersistingMutationListener.js +++ b/platform/core/src/runs/PersistingMutationListener.js @@ -23,7 +23,7 @@ define([], function () { /** - * Listens for mutation on domain objects and triggers mutation when + * Listens for mutation on domain objects and triggers persistence when * it occurs. * @param {Topic} topic the `topic` service; used to listen for mutation * @memberof platform/core From 6c4419fb72e30bf79d1037ac5d55d1f7f8a27b78 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 25 Jul 2016 16:45:32 -0700 Subject: [PATCH 31/78] [Persistence] Refactor out Transaction https://github.com/nasa/openmct/pull/874#issuecomment-233068178 --- .../commonUI/edit/src/services/Transaction.js | 63 ++++++++++++++++ .../edit/src/services/TransactionService.js | 71 ++++--------------- .../test/services/TransactionServiceSpec.js | 14 ++-- 3 files changed, 82 insertions(+), 66 deletions(-) create mode 100644 platform/commonUI/edit/src/services/Transaction.js diff --git a/platform/commonUI/edit/src/services/Transaction.js b/platform/commonUI/edit/src/services/Transaction.js new file mode 100644 index 00000000000..1583b7a5039 --- /dev/null +++ b/platform/commonUI/edit/src/services/Transaction.js @@ -0,0 +1,63 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ +define([], function () { + function Transaction($log) { + this.$log = $log; + this.callbacks = []; + } + + Transaction.prototype.add = function (commit, cancel) { + var callback = { commit: commit, cancel: cancel }; + this.callbacks.push(callback); + return function () { + this.callbacks = this.callbacks.filter(function (c) { + return c !== callback; + }); + }.bind(this); + }; + + Transaction.prototype.size = function () { + return this.callbacks.length; + }; + + ['commit', 'cancel'].forEach(function (method) { + Transaction.prototype[method] = function () { + var promises = []; + var callback; + + while (this.callbacks.length > 0) { + callback = this.callbacks.shift(); + try { + promises.push(callback[method]()); + } catch (e) { + this.$log + .error("Error trying to " + method + " transaction."); + } + } + + return Promise.all(promises); + }; + }); + + + return Transaction; +}); diff --git a/platform/commonUI/edit/src/services/TransactionService.js b/platform/commonUI/edit/src/services/TransactionService.js index 119e314b173..6de9cdbbc86 100644 --- a/platform/commonUI/edit/src/services/TransactionService.js +++ b/platform/commonUI/edit/src/services/TransactionService.js @@ -21,8 +21,8 @@ *****************************************************************************/ /*global define*/ define( - [], - function () { + ['./Transaction'], + function (Transaction) { /** * Implements an application-wide transaction state. Once a * transaction is started, calls to @@ -37,10 +37,7 @@ define( function TransactionService($q, $log) { this.$q = $q; this.$log = $log; - this.transaction = false; - - this.onCommits = []; - this.onCancels = []; + this.transaction = undefined; } /** @@ -54,14 +51,14 @@ define( //Log error because this is a programming error if it occurs. this.$log.error("Transaction already in progress"); } - this.transaction = true; + this.transaction = new Transaction(this.$log); }; /** * @returns {boolean} If true, indicates that a transaction is in progress */ TransactionService.prototype.isActive = function () { - return this.transaction; + return !!this.transaction; }; /** @@ -73,23 +70,11 @@ define( */ TransactionService.prototype.addToTransaction = function (onCommit, onCancel) { if (this.transaction) { - this.onCommits.push(onCommit); - if (onCancel) { - this.onCancels.push(onCancel); - } + return this.transaction.add(onCommit, onCancel); } else { //Log error because this is a programming error if it occurs. this.$log.error("No transaction in progress"); } - - return function () { - this.onCommits = this.onCommits.filter(function (callback) { - return callback !== onCommit; - }); - this.onCancels = this.onCancels.filter(function (callback) { - return callback !== onCancel; - }); - }.bind(this); }; /** @@ -100,24 +85,9 @@ define( * completed. Will reject if any commit operations fail */ TransactionService.prototype.commit = function () { - var self = this, - promises = [], - onCommit; - - while (this.onCommits.length > 0) { // ...using a while in case some onCommit adds to transaction - onCommit = this.onCommits.pop(); - try { // ...also don't want to fail mid-loop... - promises.push(onCommit()); - } catch (e) { - this.$log.error("Error committing transaction."); - } - } - return this.$q.all(promises).then(function () { - self.transaction = false; - - self.onCommits = []; - self.onCancels = []; - }); + var transaction = this.transaction; + this.transaction = undefined; + return transaction && transaction.commit(); }; /** @@ -129,28 +99,13 @@ define( * @returns {*} */ TransactionService.prototype.cancel = function () { - var self = this, - results = [], - onCancel; - - while (this.onCancels.length > 0) { - onCancel = this.onCancels.pop(); - try { - results.push(onCancel()); - } catch (error) { - this.$log.error("Error committing transaction."); - } - } - return this.$q.all(results).then(function () { - self.transaction = false; - - self.onCommits = []; - self.onCancels = []; - }); + var transaction = this.transaction; + this.transaction = undefined; + return transaction && transaction.cancel(); }; TransactionService.prototype.size = function () { - return this.onCommits.length; + return this.transaction ? this.transaction.size() : 0; }; return TransactionService; diff --git a/platform/commonUI/edit/test/services/TransactionServiceSpec.js b/platform/commonUI/edit/test/services/TransactionServiceSpec.js index 8c4d635a6fc..f05fb9df3d4 100644 --- a/platform/commonUI/edit/test/services/TransactionServiceSpec.js +++ b/platform/commonUI/edit/test/services/TransactionServiceSpec.js @@ -57,8 +57,7 @@ define( transactionService.startTransaction(); transactionService.addToTransaction(onCommit, onCancel); - expect(transactionService.onCommits.length).toBe(1); - expect(transactionService.onCancels.length).toBe(1); + expect(transactionService.size()).toBe(1); }); it("size function returns size of commit and cancel queues", function () { @@ -85,7 +84,7 @@ define( }); it("commit calls all queued commit functions", function () { - expect(transactionService.onCommits.length).toBe(3); + expect(transactionService.size()).toBe(3); transactionService.commit(); onCommits.forEach(function (spy) { expect(spy).toHaveBeenCalled(); @@ -95,8 +94,8 @@ define( it("commit resets active state and clears queues", function () { transactionService.commit(); expect(transactionService.isActive()).toBe(false); - expect(transactionService.onCommits.length).toBe(0); - expect(transactionService.onCancels.length).toBe(0); + expect(transactionService.size()).toBe(0); + expect(transactionService.size()).toBe(0); }); }); @@ -116,7 +115,7 @@ define( }); it("cancel calls all queued cancel functions", function () { - expect(transactionService.onCancels.length).toBe(3); + expect(transactionService.size()).toBe(3); transactionService.cancel(); onCancels.forEach(function (spy) { expect(spy).toHaveBeenCalled(); @@ -126,8 +125,7 @@ define( it("cancel resets active state and clears queues", function () { transactionService.cancel(); expect(transactionService.isActive()).toBe(false); - expect(transactionService.onCommits.length).toBe(0); - expect(transactionService.onCancels.length).toBe(0); + expect(transactionService.size()).toBe(0); }); }); From 0bedc227f44ccc28e8c28a9458f868f980c306f5 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Jul 2016 10:10:15 -0700 Subject: [PATCH 32/78] [Persistence] Allow nested transactions --- platform/commonUI/edit/src/services/TransactionService.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/commonUI/edit/src/services/TransactionService.js b/platform/commonUI/edit/src/services/TransactionService.js index 6de9cdbbc86..6dc8634dc4c 100644 --- a/platform/commonUI/edit/src/services/TransactionService.js +++ b/platform/commonUI/edit/src/services/TransactionService.js @@ -38,6 +38,7 @@ define( this.$q = $q; this.$log = $log; this.transaction = undefined; + this.transactionStack = []; } /** @@ -48,8 +49,7 @@ define( */ TransactionService.prototype.startTransaction = function () { if (this.transaction) { - //Log error because this is a programming error if it occurs. - this.$log.error("Transaction already in progress"); + this.transactionStack.push(this.transaction); } this.transaction = new Transaction(this.$log); }; @@ -86,7 +86,7 @@ define( */ TransactionService.prototype.commit = function () { var transaction = this.transaction; - this.transaction = undefined; + this.transaction = this.transactionStack.pop(); return transaction && transaction.commit(); }; @@ -100,7 +100,7 @@ define( */ TransactionService.prototype.cancel = function () { var transaction = this.transaction; - this.transaction = undefined; + this.transaction = this.transactionStack.pop(); return transaction && transaction.cancel(); }; From 0fe0b21eda76fefea4d28330671c379beb95a972 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Jul 2016 15:18:21 -0700 Subject: [PATCH 33/78] [Persistence] Add NestedTransaction --- .../edit/src/services/NestedTransaction.js | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 platform/commonUI/edit/src/services/NestedTransaction.js diff --git a/platform/commonUI/edit/src/services/NestedTransaction.js b/platform/commonUI/edit/src/services/NestedTransaction.js new file mode 100644 index 00000000000..33c33ee240b --- /dev/null +++ b/platform/commonUI/edit/src/services/NestedTransaction.js @@ -0,0 +1,38 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ +define(['./Transaction'], function (Transaction) { + function NestedTransaction(parent) { + this.parent = parent; + } + + NestedTransaction.prototype = Object.create(Transaction.prototype); + + NestedTransaction.prototype.commit = function () { + parent.add( + Transaction.prototype.commit.bind(this), + Transaction.prototype.cancel.bind(this) + ); + return Promise.resolve(true); + }; + + return NestedTransaction; +}); From 6f2c80bc2ebbc49e1340164d14249fe05ee50620 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Jul 2016 15:20:40 -0700 Subject: [PATCH 34/78] [Persistence] Use NestedTransaction --- platform/commonUI/edit/src/services/TransactionService.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/platform/commonUI/edit/src/services/TransactionService.js b/platform/commonUI/edit/src/services/TransactionService.js index 6dc8634dc4c..50b17f563ad 100644 --- a/platform/commonUI/edit/src/services/TransactionService.js +++ b/platform/commonUI/edit/src/services/TransactionService.js @@ -21,8 +21,8 @@ *****************************************************************************/ /*global define*/ define( - ['./Transaction'], - function (Transaction) { + ['./Transaction', './NestedTransaction'], + function (Transaction, NestedTransaction) { /** * Implements an application-wide transaction state. Once a * transaction is started, calls to @@ -50,8 +50,10 @@ define( TransactionService.prototype.startTransaction = function () { if (this.transaction) { this.transactionStack.push(this.transaction); + this.transaction = new NestedTransaction(this.transaction); + } else { + this.transaction = new Transaction(this.$log); } - this.transaction = new Transaction(this.$log); }; /** From f6d6cb929f4bd94def3d3586235bb270ba498b4d Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Jul 2016 15:24:16 -0700 Subject: [PATCH 35/78] [Persistence] Add to transaction on mutation --- ...tener.js => TransactingMutationListener.js} | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) rename platform/core/src/runs/{PersistingMutationListener.js => TransactingMutationListener.js} (75%) diff --git a/platform/core/src/runs/PersistingMutationListener.js b/platform/core/src/runs/TransactingMutationListener.js similarity index 75% rename from platform/core/src/runs/PersistingMutationListener.js rename to platform/core/src/runs/TransactingMutationListener.js index b992ed3d8ab..c534c6fae2e 100644 --- a/platform/core/src/runs/PersistingMutationListener.js +++ b/platform/core/src/runs/TransactingMutationListener.js @@ -28,15 +28,27 @@ define([], function () { * @param {Topic} topic the `topic` service; used to listen for mutation * @memberof platform/core */ - function PersistingMutationListener(topic) { + function TransactingMutationListener(topic, transactionService) { var mutationTopic = topic('mutation'); mutationTopic.listen(function (domainObject) { var persistence = domainObject.getCapability('persistence'); + var wasActive = transactionService.isActive(); if (persistence.persisted()) { - persistence.persist(); + if (!wasActive) { + transactionService.startTransaction(); + } + + transactionService.addToTransaction( + persistence.persist.bind(persistence), + persistence.refresh.bind(persistence) + ); + + if (!wasActive) { + transactionService.commit(); + } } }); } - return PersistingMutationListener; + return TransactingMutationListener; }); From b60eff2f5ecfc41a541429902f2a9bd15399cd23 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Jul 2016 15:26:43 -0700 Subject: [PATCH 36/78] [Persistence] Update bundle definition --- platform/core/bundle.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/core/bundle.js b/platform/core/bundle.js index 99df3c270c8..f340b8cbe18 100644 --- a/platform/core/bundle.js +++ b/platform/core/bundle.js @@ -46,7 +46,7 @@ define([ "./src/capabilities/MutationCapability", "./src/capabilities/DelegationCapability", "./src/capabilities/InstantiationCapability", - "./src/runs/PersistingMutationListener", + "./src/runs/TransactingMutationListener", "./src/services/Now", "./src/services/Throttle", "./src/services/Topic", @@ -79,7 +79,7 @@ define([ MutationCapability, DelegationCapability, InstantiationCapability, - PersistingMutationListener, + TransactingMutationListener, Now, Throttle, Topic, @@ -413,8 +413,8 @@ define([ ], "runs": [ { - "implementation": PersistingMutationListener, - "depends": ["topic"] + "implementation": TransactingMutationListener, + "depends": ["topic", "transactionService"] } ], "constants": [ From 771542ee5f7a7d60f2bb247d58c6e609b8dc7ca8 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 26 Jul 2016 15:30:40 -0700 Subject: [PATCH 37/78] [Persistence] Reliably return promises --- platform/commonUI/edit/src/services/TransactionService.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/commonUI/edit/src/services/TransactionService.js b/platform/commonUI/edit/src/services/TransactionService.js index 50b17f563ad..7697eed4363 100644 --- a/platform/commonUI/edit/src/services/TransactionService.js +++ b/platform/commonUI/edit/src/services/TransactionService.js @@ -89,7 +89,7 @@ define( TransactionService.prototype.commit = function () { var transaction = this.transaction; this.transaction = this.transactionStack.pop(); - return transaction && transaction.commit(); + return transaction ? transaction.commit() : Promise.reject(); }; /** @@ -103,7 +103,7 @@ define( TransactionService.prototype.cancel = function () { var transaction = this.transaction; this.transaction = this.transactionStack.pop(); - return transaction && transaction.cancel(); + return transaction ? transaction.cancel() : Promise.reject(); }; TransactionService.prototype.size = function () { From 02f34d1c04ff20028cd8f6143bac4c82f4451dc1 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 8 Aug 2016 12:28:13 -0700 Subject: [PATCH 38/78] [Persistence] Begin testing TransactingMutationListener --- .../runs/TransactingMutationListenerSpec.js | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 platform/core/test/runs/TransactingMutationListenerSpec.js diff --git a/platform/core/test/runs/TransactingMutationListenerSpec.js b/platform/core/test/runs/TransactingMutationListenerSpec.js new file mode 100644 index 00000000000..9bf5b41f768 --- /dev/null +++ b/platform/core/test/runs/TransactingMutationListenerSpec.js @@ -0,0 +1,63 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define( + ["../../src/runs/TransactingMutationListener"], + function (TransactingMutationListener) { + + describe("TransactingMutationListener", function () { + var mockTopic, + mockMutationTopic, + mockTransactionService; + + beforeEach(function () { + mockTopic = jasmine.createSpy('topic'); + mockMutationTopic = + jasmine.createSpyObj('mutation', [ 'listen' ]); + mockTransactionService = + jasmine.createSpyObj('transactionService', [ + 'isActive', + 'startTransaction', + 'addToTransaction', + 'commit' + ]); + + mockTopic.andCallFake(function (t) { + return (t === 'mutation') && mockMutationTopic; + }); + + + return new TransactingMutationListener( + mockTopic, + mockTransactionService + ); + }); + + it("listens for mutation", function () { + expect(mockMutationTopic.listen) + .toHaveBeenCalledWith(jasmine.any(Function)); + }); + + + }); + } +); From eb2fbcd8d03580ccfa0ba8aab427f18d69efa617 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 8 Aug 2016 12:34:46 -0700 Subject: [PATCH 39/78] [Persistence] Cover TransactingMutationListener Add test cases sufficient for line coverage of TransactingMutationListener. --- .../runs/TransactingMutationListenerSpec.js | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/platform/core/test/runs/TransactingMutationListenerSpec.js b/platform/core/test/runs/TransactingMutationListenerSpec.js index 9bf5b41f768..d523b2ecd15 100644 --- a/platform/core/test/runs/TransactingMutationListenerSpec.js +++ b/platform/core/test/runs/TransactingMutationListenerSpec.js @@ -27,7 +27,9 @@ define( describe("TransactingMutationListener", function () { var mockTopic, mockMutationTopic, - mockTransactionService; + mockTransactionService, + mockDomainObject, + mockPersistence; beforeEach(function () { mockTopic = jasmine.createSpy('topic'); @@ -40,11 +42,24 @@ define( 'addToTransaction', 'commit' ]); + mockDomainObject = jasmine.createSpyObj( + 'domainObject', + ['getId', 'getCapability', 'getModel'] + ); + mockPersistence = jasmine.createSpyObj( + 'persistence', + ['persist', 'refresh', 'persisted'] + ); mockTopic.andCallFake(function (t) { return (t === 'mutation') && mockMutationTopic; }); + mockDomainObject.getCapability.andCallFake(function (c) { + return (c === 'persistence') && mockPersistence; + }); + + mockPersistence.persisted.andReturn(true); return new TransactingMutationListener( mockTopic, @@ -58,6 +73,38 @@ define( }); + describe("when mutation occurs during a transaction", function () { + beforeEach(function () { + mockTransactionService.isActive.andReturn(true); + mockMutationTopic.listen.mostRecentCall + .args[0](mockDomainObject); + }); + + it("adds to the active transaction", function () { + expect(mockTransactionService.addToTransaction) + .toHaveBeenCalledWith( + jasmine.any(Function), + jasmine.any(Function) + ); + }); + }); + + describe("when mutation occurs outside a transaction", function () { + beforeEach(function () { + mockTransactionService.isActive.andReturn(false); + mockMutationTopic.listen.mostRecentCall + .args[0](mockDomainObject); + }); + + it("adds to the active transaction", function () { + expect(mockTransactionService.addToTransaction) + .toHaveBeenCalledWith( + jasmine.any(Function), + jasmine.any(Function) + ); + }); + }); + }); } ); From 70c072be0bb07f63a0d66bcf91c3649b39179430 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 8 Aug 2016 13:18:40 -0700 Subject: [PATCH 40/78] [Persistence] Test more cases Add more test cases for TransactingMutationListener to distinguish active-transaction case from no-transaction case. --- .../runs/TransactingMutationListenerSpec.js | 64 +++++++++++-------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/platform/core/test/runs/TransactingMutationListenerSpec.js b/platform/core/test/runs/TransactingMutationListenerSpec.js index d523b2ecd15..2529b04bcea 100644 --- a/platform/core/test/runs/TransactingMutationListenerSpec.js +++ b/platform/core/test/runs/TransactingMutationListenerSpec.js @@ -72,39 +72,49 @@ define( .toHaveBeenCalledWith(jasmine.any(Function)); }); + [false, true].forEach(function (isActive) { + var verb = isActive ? "is": "isn't"; - describe("when mutation occurs during a transaction", function () { - beforeEach(function () { - mockTransactionService.isActive.andReturn(true); - mockMutationTopic.listen.mostRecentCall - .args[0](mockDomainObject); - }); + function onlyWhenInactive(expectation) { + return isActive ? expectation.not : expectation; + } - it("adds to the active transaction", function () { - expect(mockTransactionService.addToTransaction) - .toHaveBeenCalledWith( - jasmine.any(Function), - jasmine.any(Function) - ); - }); - }); + describe("when a transaction " + verb + " active", function () { + var innerVerb = isActive ? "does" : "doesn't"; - describe("when mutation occurs outside a transaction", function () { - beforeEach(function () { - mockTransactionService.isActive.andReturn(false); - mockMutationTopic.listen.mostRecentCall - .args[0](mockDomainObject); - }); + beforeEach(function () { + mockTransactionService.isActive.andReturn(isActive); + }); + + describe("and mutation occurs", function () { + beforeEach(function () { + mockMutationTopic.listen.mostRecentCall + .args[0](mockDomainObject); + }); - it("adds to the active transaction", function () { - expect(mockTransactionService.addToTransaction) - .toHaveBeenCalledWith( - jasmine.any(Function), - jasmine.any(Function) - ); + + it(innerVerb + " start a new transaction", function () { + onlyWhenInactive( + expect(mockTransactionService.startTransaction) + ).toHaveBeenCalled(); + }); + + it("adds to the active transaction", function () { + expect(mockTransactionService.addToTransaction) + .toHaveBeenCalledWith( + jasmine.any(Function), + jasmine.any(Function) + ); + }); + + it(innerVerb + " immediately commit", function () { + onlyWhenInactive( + expect(mockTransactionService.commit) + ).toHaveBeenCalled(); + }); + }); }); }); - }); } ); From 668b7b6a39f97109c16d58ae189c8b7d196e3318 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 8 Aug 2016 13:21:48 -0700 Subject: [PATCH 41/78] [Persistence] Add empty specs for Transactions --- .../test/services/NestedTransactionSpec.js | 33 +++++++++++++++++++ .../edit/test/services/TransactionSpec.js | 33 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 platform/commonUI/edit/test/services/NestedTransactionSpec.js create mode 100644 platform/commonUI/edit/test/services/TransactionSpec.js diff --git a/platform/commonUI/edit/test/services/NestedTransactionSpec.js b/platform/commonUI/edit/test/services/NestedTransactionSpec.js new file mode 100644 index 00000000000..373debf6984 --- /dev/null +++ b/platform/commonUI/edit/test/services/NestedTransactionSpec.js @@ -0,0 +1,33 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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( + ["../../src/services/NestedTransaction"], + function (NestedTransaction) { + + describe("A NestedTransaction", function () { + + }); + } +); + diff --git a/platform/commonUI/edit/test/services/TransactionSpec.js b/platform/commonUI/edit/test/services/TransactionSpec.js new file mode 100644 index 00000000000..61d8134a41e --- /dev/null +++ b/platform/commonUI/edit/test/services/TransactionSpec.js @@ -0,0 +1,33 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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( + ["../../src/services/Transaction"], + function (Transaction) { + + describe("A Transaction", function () { + + }); + } +); + From 73d10ab093540e2bcdfd6f087fc37b921f04676d Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 8 Aug 2016 13:29:39 -0700 Subject: [PATCH 42/78] [Persistence] Test Transaction --- .../edit/test/services/TransactionSpec.js | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/platform/commonUI/edit/test/services/TransactionSpec.js b/platform/commonUI/edit/test/services/TransactionSpec.js index 61d8134a41e..3b555f3d059 100644 --- a/platform/commonUI/edit/test/services/TransactionSpec.js +++ b/platform/commonUI/edit/test/services/TransactionSpec.js @@ -26,6 +26,83 @@ define( function (Transaction) { describe("A Transaction", function () { + var mockLog, + transaction; + + beforeEach(function () { + mockLog = jasmine.createSpyObj( + '$log', + ['warn', 'info', 'error', 'debug'] + ); + transaction = new Transaction(mockLog); + }); + + it("initially has a size of zero", function () { + expect(transaction.size()).toEqual(0); + }); + + describe("when callbacks are added", function () { + var mockCommit, + mockCancel, + remove; + + beforeEach(function () { + mockCommit = jasmine.createSpy('commit'); + mockCancel = jasmine.createSpy('cancel'); + remove = transaction.add(mockCommit, mockCancel); + }); + + it("reports a new size", function () { + expect(transaction.size()).toEqual(1); + }); + + it("returns a function to remove those callbacks", function () { + expect(remove).toEqual(jasmine.any(Function)); + remove(); + expect(transaction.size()).toEqual(0); + }); + + describe("and the transaction is committed", function () { + beforeEach(function () { + transaction.commit(); + }); + + it("triggers the commit callback", function () { + expect(mockCommit).toHaveBeenCalled(); + }); + + it("does not trigger the cancel callback", function () { + expect(mockCancel).not.toHaveBeenCalled(); + }); + }); + + describe("and the transaction is cancelled", function () { + beforeEach(function () { + transaction.cancel(); + }); + + it("triggers the cancel callback", function () { + expect(mockCancel).toHaveBeenCalled(); + }); + + it("does not trigger the commit callback", function () { + expect(mockCommit).not.toHaveBeenCalled(); + }); + }); + + describe("and an exception is encountered during commit", function () { + beforeEach(function () { + mockCommit.andCallFake(function () { + throw new Error("test error"); + }); + transaction.commit(); + }); + + it("logs an error", function () { + expect(mockLog.error).toHaveBeenCalled(); + }); + }); + }); }); } From 353c24c70e0c661312d097a96108d7e3e57abf48 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 8 Aug 2016 13:39:03 -0700 Subject: [PATCH 43/78] [Persistence] Invoke parent constructor --- platform/commonUI/edit/src/services/NestedTransaction.js | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/commonUI/edit/src/services/NestedTransaction.js b/platform/commonUI/edit/src/services/NestedTransaction.js index 33c33ee240b..891829bc502 100644 --- a/platform/commonUI/edit/src/services/NestedTransaction.js +++ b/platform/commonUI/edit/src/services/NestedTransaction.js @@ -22,6 +22,7 @@ define(['./Transaction'], function (Transaction) { function NestedTransaction(parent) { this.parent = parent; + Transaction.call(this, parent.$log); } NestedTransaction.prototype = Object.create(Transaction.prototype); From 850da9de52e8cac677d9587d80b103025f77e7ec Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 8 Aug 2016 13:40:50 -0700 Subject: [PATCH 44/78] [Persistence] Fix member reference in NestedTransaction --- platform/commonUI/edit/src/services/NestedTransaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/commonUI/edit/src/services/NestedTransaction.js b/platform/commonUI/edit/src/services/NestedTransaction.js index 891829bc502..61a53048cf6 100644 --- a/platform/commonUI/edit/src/services/NestedTransaction.js +++ b/platform/commonUI/edit/src/services/NestedTransaction.js @@ -28,7 +28,7 @@ define(['./Transaction'], function (Transaction) { NestedTransaction.prototype = Object.create(Transaction.prototype); NestedTransaction.prototype.commit = function () { - parent.add( + this.parent.add( Transaction.prototype.commit.bind(this), Transaction.prototype.cancel.bind(this) ); From 2ebf05ae5bc03f56333643bbdc5ea2e6fafad0a8 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 8 Aug 2016 13:41:13 -0700 Subject: [PATCH 45/78] [Persistence] Test NestedTransaction --- .../test/services/NestedTransactionSpec.js | 57 +++++++++++++++++-- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/platform/commonUI/edit/test/services/NestedTransactionSpec.js b/platform/commonUI/edit/test/services/NestedTransactionSpec.js index 373debf6984..df82c12a78e 100644 --- a/platform/commonUI/edit/test/services/NestedTransactionSpec.js +++ b/platform/commonUI/edit/test/services/NestedTransactionSpec.js @@ -21,13 +21,58 @@ *****************************************************************************/ /*global define,describe,it,expect,beforeEach,jasmine*/ -define( - ["../../src/services/NestedTransaction"], - function (NestedTransaction) { +define(["../../src/services/NestedTransaction"], function (NestedTransaction) { + var TRANSACTION_METHODS = ['add', 'commit', 'cancel', 'size']; - describe("A NestedTransaction", function () { + describe("A NestedTransaction", function () { + var mockTransaction, + nestedTransaction; + beforeEach(function () { + mockTransaction = + jasmine.createSpyObj('transaction', TRANSACTION_METHODS); + nestedTransaction = new NestedTransaction(mockTransaction); }); - } -); + + it("exposes a Transaction's interface", function () { + TRANSACTION_METHODS.forEach(function (method) { + expect(nestedTransaction[method]) + .toEqual(jasmine.any(Function)); + }); + }); + + describe("when callbacks are added", function () { + var mockCommit, + mockCancel, + remove; + + beforeEach(function () { + mockCommit = jasmine.createSpy('commit'); + mockCancel = jasmine.createSpy('cancel'); + remove = nestedTransaction.add(mockCommit, mockCancel); + }); + + it("does not interact with its parent transaction", function () { + TRANSACTION_METHODS.forEach(function (method) { + expect(mockTransaction[method]) + .not.toHaveBeenCalled(); + }); + }); + + describe("and the transaction is committed", function () { + beforeEach(function () { + nestedTransaction.commit(); + }); + + it("adds to its parent transaction", function () { + expect(mockTransaction.add).toHaveBeenCalledWith( + jasmine.any(Function), + jasmine.any(Function) + ); + }); + }); + }); + }); +}); + From a35b158fd117f9d5fd448614002549b069713ff0 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 8 Aug 2016 13:48:19 -0700 Subject: [PATCH 46/78] [Persistence] Add JSDoc for Transaction --- .../commonUI/edit/src/services/Transaction.js | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/platform/commonUI/edit/src/services/Transaction.js b/platform/commonUI/edit/src/services/Transaction.js index 1583b7a5039..803536be4d7 100644 --- a/platform/commonUI/edit/src/services/Transaction.js +++ b/platform/commonUI/edit/src/services/Transaction.js @@ -20,11 +20,26 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ define([], function () { + /** + * A Transaction represents a set of changes that are intended to + * be kept or discarded as a unit. + * @param $log Angular's `$log` service, for logging messages + * @constructor + * @memberof platform/commonUI/edit/services + */ function Transaction($log) { this.$log = $log; this.callbacks = []; } + /** + * Add a change to the current transaction, as expressed by functions + * to either keep or discard the change. + * @param {Function} commit called when the transaction is committed + * @param {Function} cancel called when the transaction is cancelled + * @returns {Function) a function which may be called to remove this + * pair of callbacks from the transaction + */ Transaction.prototype.add = function (commit, cancel) { var callback = { commit: commit, cancel: cancel }; this.callbacks.push(callback); @@ -35,10 +50,28 @@ define([], function () { }.bind(this); }; + /** + * Get the number of changes in the current transaction. + * @returns {number} the size of the current transaction + */ Transaction.prototype.size = function () { return this.callbacks.length; }; + /** + * Keep all changes associated with this transaction. + * @method {platform/commonUI/edit/services.Transaction#commit} + * @returns {Promise} a promise which will resolve when all callbacks + * have been handled. + */ + + /** + * Discard all changes associated with this transaction. + * @method {platform/commonUI/edit/services.Transaction#cancel} + * @returns {Promise} a promise which will resolve when all callbacks + * have been handled. + */ + ['commit', 'cancel'].forEach(function (method) { Transaction.prototype[method] = function () { var promises = []; From ecf526aa034f6720fd9c7975bffabd4d5ab168c3 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 8 Aug 2016 13:49:34 -0700 Subject: [PATCH 47/78] [Persistence] Add JSDoc for NestedTransaction --- platform/commonUI/edit/src/services/NestedTransaction.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/platform/commonUI/edit/src/services/NestedTransaction.js b/platform/commonUI/edit/src/services/NestedTransaction.js index 61a53048cf6..c7fcee4d5eb 100644 --- a/platform/commonUI/edit/src/services/NestedTransaction.js +++ b/platform/commonUI/edit/src/services/NestedTransaction.js @@ -20,6 +20,15 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ define(['./Transaction'], function (Transaction) { + /** + * A nested transaction is a transaction which takes place in the context + * of a larger parent transaction. It becomes part of the parent + * transaction when (and only when) committed. + * @param parent + * @constructor + * @extends {platform/commonUI/edit/services.Transaction} + * @memberof platform/commonUI/edit/services + */ function NestedTransaction(parent) { this.parent = parent; Transaction.call(this, parent.$log); From 385d90b056e3724b6a3985a8ebfc57a436924f19 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 8 Aug 2016 13:51:10 -0700 Subject: [PATCH 48/78] [Persistence] Fix code style --- platform/core/test/runs/TransactingMutationListenerSpec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/core/test/runs/TransactingMutationListenerSpec.js b/platform/core/test/runs/TransactingMutationListenerSpec.js index 2529b04bcea..c371d25db95 100644 --- a/platform/core/test/runs/TransactingMutationListenerSpec.js +++ b/platform/core/test/runs/TransactingMutationListenerSpec.js @@ -34,7 +34,7 @@ define( beforeEach(function () { mockTopic = jasmine.createSpy('topic'); mockMutationTopic = - jasmine.createSpyObj('mutation', [ 'listen' ]); + jasmine.createSpyObj('mutation', ['listen']); mockTransactionService = jasmine.createSpyObj('transactionService', [ 'isActive', @@ -73,7 +73,7 @@ define( }); [false, true].forEach(function (isActive) { - var verb = isActive ? "is": "isn't"; + var verb = isActive ? "is" : "isn't"; function onlyWhenInactive(expectation) { return isActive ? expectation.not : expectation; From a93f41f1c3743f703e33948f3f136297ce4ad0e6 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 30 Sep 2016 10:25:22 -0700 Subject: [PATCH 49/78] [API] Miscellaneous platform updates Adds miscellaneous platform updates to include templates via the RequireJS text plugin; to support modification of asset paths; and to support priority order for gestures. Supports integration of new API, #1124. --- platform/commonUI/browse/bundle.js | 4 +++- platform/commonUI/general/bundle.js | 12 ++++++++++-- .../commonUI/general/src/StyleSheetLoader.js | 3 ++- platform/execution/src/WorkerService.js | 18 +++++++++++++----- platform/features/table/bundle.js | 12 +++++++++--- .../src/gestures/GestureProvider.js | 2 +- platform/search/bundle.js | 4 +++- 7 files changed, 41 insertions(+), 14 deletions(-) diff --git a/platform/commonUI/browse/bundle.js b/platform/commonUI/browse/bundle.js index 5ff13d789e4..b0aa7e42776 100644 --- a/platform/commonUI/browse/bundle.js +++ b/platform/commonUI/browse/bundle.js @@ -41,6 +41,7 @@ define([ "text!./res/templates/items/items.html", "text!./res/templates/browse/object-properties.html", "text!./res/templates/browse/inspector-region.html", + "text!./res/templates/view-object.html", 'legacyRegistry' ], function ( BrowseController, @@ -63,6 +64,7 @@ define([ itemsTemplate, objectPropertiesTemplate, inspectorRegionTemplate, + viewObjectTemplate, legacyRegistry ) { @@ -142,7 +144,7 @@ define([ "representations": [ { "key": "view-object", - "templateUrl": "templates/view-object.html" + "template": viewObjectTemplate }, { "key": "browse-object", diff --git a/platform/commonUI/general/bundle.js b/platform/commonUI/general/bundle.js index 15ea3076a62..4bd4a838731 100644 --- a/platform/commonUI/general/bundle.js +++ b/platform/commonUI/general/bundle.js @@ -48,6 +48,7 @@ define([ "./src/directives/MCTSplitPane", "./src/directives/MCTSplitter", "./src/directives/MCTTree", + "./src/filters/ReverseFilter.js", "text!./res/templates/bottombar.html", "text!./res/templates/controls/action-button.html", "text!./res/templates/controls/input-filter.html", @@ -96,6 +97,7 @@ define([ MCTSplitPane, MCTSplitter, MCTTree, + ReverseFilter, bottombarTemplate, actionButtonTemplate, inputFilterTemplate, @@ -146,7 +148,8 @@ define([ "depends": [ "stylesheets[]", "$document", - "THEME" + "THEME", + "ASSETS_PATH" ] }, { @@ -158,7 +161,7 @@ define([ ], "filters": [ { - "implementation": "filters/ReverseFilter.js", + "implementation": ReverseFilter, "key": "reverse" } ], @@ -405,6 +408,11 @@ define([ "key": "THEME", "value": "unspecified", "priority": "fallback" + }, + { + "key": "ASSETS_PATH", + "value": ".", + "priority": "fallback" } ], "containers": [ diff --git a/platform/commonUI/general/src/StyleSheetLoader.js b/platform/commonUI/general/src/StyleSheetLoader.js index 13c5075a8bc..29787e2ee83 100644 --- a/platform/commonUI/general/src/StyleSheetLoader.js +++ b/platform/commonUI/general/src/StyleSheetLoader.js @@ -38,7 +38,7 @@ define( * @param $document Angular's jqLite-wrapped document element * @param {string} activeTheme the theme in use */ - function StyleSheetLoader(stylesheets, $document, activeTheme) { + function StyleSheetLoader(stylesheets, $document, activeTheme, assetPath) { var head = $document.find('head'), document = $document[0]; @@ -47,6 +47,7 @@ define( // Create a link element, and construct full path var link = document.createElement('link'), path = [ + assetPath, stylesheet.bundle.path, stylesheet.bundle.resources, stylesheet.stylesheetUrl diff --git a/platform/execution/src/WorkerService.js b/platform/execution/src/WorkerService.js index d809a568f2d..87efd6f7da5 100644 --- a/platform/execution/src/WorkerService.js +++ b/platform/execution/src/WorkerService.js @@ -42,11 +42,19 @@ define( function addWorker(worker) { var key = worker.key; if (!workerUrls[key]) { - workerUrls[key] = [ - worker.bundle.path, - worker.bundle.sources, - worker.scriptUrl - ].join("/"); + if (worker.scriptUrl) { + workerUrls[key] = [ + worker.bundle.path, + worker.bundle.sources, + worker.scriptUrl + ].join("/"); + } else if (worker.scriptText) { + var blob = new Blob( + [worker.scriptText], + {type: 'application/javascript'} + ); + workerUrls[key] = URL.createObjectURL(blob); + } sharedWorkers[key] = worker.shared; } } diff --git a/platform/features/table/bundle.js b/platform/features/table/bundle.js index 4c7b77b47e2..8e820abef65 100644 --- a/platform/features/table/bundle.js +++ b/platform/features/table/bundle.js @@ -27,6 +27,9 @@ define([ "./src/controllers/TableOptionsController", '../../commonUI/regions/src/Region', '../../commonUI/browse/src/InspectorRegion', + "text!./res/templates/table-options-edit.html", + "text!./res/templates/rt-table.html", + "text!./res/templates/historical-table.html", "legacyRegistry" ], function ( MCTTable, @@ -35,6 +38,9 @@ define([ TableOptionsController, Region, InspectorRegion, + tableOptionsEditTemplate, + rtTableTemplate, + historicalTableTemplate, legacyRegistry ) { /** @@ -127,8 +133,8 @@ define([ { "name": "Historical Table", "key": "table", + "template": historicalTableTemplate, "cssclass": "icon-tabular", - "templateUrl": "templates/historical-table.html", "needs": [ "telemetry" ], @@ -139,7 +145,7 @@ define([ "name": "Real-time Table", "key": "rt-table", "cssclass": "icon-tabular-realtime", - "templateUrl": "templates/rt-table.html", + "templateUrl": rtTableTemplate, "needs": [ "telemetry" ], @@ -157,7 +163,7 @@ define([ "representations": [ { "key": "table-options-edit", - "templateUrl": "templates/table-options-edit.html" + "template": tableOptionsEditTemplate } ], "stylesheets": [ diff --git a/platform/representation/src/gestures/GestureProvider.js b/platform/representation/src/gestures/GestureProvider.js index 5e170f68ac1..051dafd5a7b 100644 --- a/platform/representation/src/gestures/GestureProvider.js +++ b/platform/representation/src/gestures/GestureProvider.js @@ -72,7 +72,7 @@ define( // Assemble all gestures into a map, for easy look up gestures.forEach(function (gesture) { - gestureMap[gesture.key] = gesture; + gestureMap[gesture.key] = gestureMap[gesture.key] || gesture; }); this.gestureMap = gestureMap; diff --git a/platform/search/bundle.js b/platform/search/bundle.js index 61a266d0497..7f8a64ab048 100644 --- a/platform/search/bundle.js +++ b/platform/search/bundle.js @@ -28,6 +28,7 @@ define([ "text!./res/templates/search-item.html", "text!./res/templates/search.html", "text!./res/templates/search-menu.html", + "text!./src/services/GenericSearchWorker.js", 'legacyRegistry' ], function ( SearchController, @@ -37,6 +38,7 @@ define([ searchItemTemplate, searchTemplate, searchMenuTemplate, + searchWorkerText, legacyRegistry ) { @@ -114,7 +116,7 @@ define([ "workers": [ { "key": "genericSearchWorker", - "scriptUrl": "services/GenericSearchWorker.js" + "scriptText": searchWorkerText } ] } From 901846e6c153690c9b6dd3620b91eca3199ff2ed Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 30 Sep 2016 10:29:03 -0700 Subject: [PATCH 50/78] [API] Use correct property for RT table template --- platform/features/table/bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/features/table/bundle.js b/platform/features/table/bundle.js index 8e820abef65..6f7d285a503 100644 --- a/platform/features/table/bundle.js +++ b/platform/features/table/bundle.js @@ -145,7 +145,7 @@ define([ "name": "Real-time Table", "key": "rt-table", "cssclass": "icon-tabular-realtime", - "templateUrl": rtTableTemplate, + "template": rtTableTemplate, "needs": [ "telemetry" ], From 230230aa943cd8f073e89cc13232d861b38984e7 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 30 Sep 2016 10:37:31 -0700 Subject: [PATCH 51/78] [API] Remove js extension from import ...to avoid confusing RequireJS and breaking test suite --- platform/commonUI/general/bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/commonUI/general/bundle.js b/platform/commonUI/general/bundle.js index 4bd4a838731..66d9b3de951 100644 --- a/platform/commonUI/general/bundle.js +++ b/platform/commonUI/general/bundle.js @@ -48,7 +48,7 @@ define([ "./src/directives/MCTSplitPane", "./src/directives/MCTSplitter", "./src/directives/MCTTree", - "./src/filters/ReverseFilter.js", + "./src/filters/ReverseFilter", "text!./res/templates/bottombar.html", "text!./res/templates/controls/action-button.html", "text!./res/templates/controls/input-filter.html", From 34e07b938dd3fdff413c18faa04903a11d6a519d Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 30 Sep 2016 10:42:48 -0700 Subject: [PATCH 52/78] [API] Update StyleSheetLoaderSpec ...to reflect asset path details --- platform/commonUI/general/src/StyleSheetLoader.js | 4 ++++ platform/commonUI/general/test/StyleSheetLoaderSpec.js | 7 ++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/platform/commonUI/general/src/StyleSheetLoader.js b/platform/commonUI/general/src/StyleSheetLoader.js index 29787e2ee83..7f5cabe7c1e 100644 --- a/platform/commonUI/general/src/StyleSheetLoader.js +++ b/platform/commonUI/general/src/StyleSheetLoader.js @@ -37,6 +37,8 @@ define( * @param {object[]} stylesheets stylesheet extension definitions * @param $document Angular's jqLite-wrapped document element * @param {string} activeTheme the theme in use + * @param {string} [assetPath] the directory relative to which + * stylesheets will be found */ function StyleSheetLoader(stylesheets, $document, activeTheme, assetPath) { var head = $document.find('head'), @@ -69,6 +71,8 @@ define( stylesheet.theme === activeTheme; } + assetPath = assetPath || "."; + // Add all stylesheets from extensions stylesheets.filter(matchesTheme).forEach(addStyleSheet); } diff --git a/platform/commonUI/general/test/StyleSheetLoaderSpec.js b/platform/commonUI/general/test/StyleSheetLoaderSpec.js index 272ad61bf97..4dfb20e82d5 100644 --- a/platform/commonUI/general/test/StyleSheetLoaderSpec.js +++ b/platform/commonUI/general/test/StyleSheetLoaderSpec.js @@ -69,7 +69,7 @@ define( it("adjusts link locations", function () { expect(mockElement.setAttribute) - .toHaveBeenCalledWith('href', "a/b/c/d.css"); + .toHaveBeenCalledWith('href', "./a/b/c/d.css"); }); describe("for themed stylesheets", function () { @@ -95,12 +95,13 @@ define( it("includes matching themes", function () { expect(mockElement.setAttribute) - .toHaveBeenCalledWith('href', "a/b/c/themed.css"); + .toHaveBeenCalledWith('href', "./a/b/c/themed.css"); }); it("excludes mismatching themes", function () { expect(mockElement.setAttribute) - .not.toHaveBeenCalledWith('href', "a/b/c/bad-theme.css"); + .not + .toHaveBeenCalledWith('href', "./a/b/c/bad-theme.css"); }); }); From b0e842863d951c26ea9ce1418ec70d6f038a0ad6 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 30 Sep 2016 17:31:33 -0700 Subject: [PATCH 53/78] Squashed commit of the following: commit 6b5528a4fc942920ace9d6bc2350a0c585ce752d Author: Victor Woeltjen Date: Fri Sep 30 17:22:27 2016 -0700 [API] Don't use new composition for legacy objects commit 2af993b0bc6e3c0948bcdb4dbb3b4f64bfcd9e28 Author: Victor Woeltjen Date: Fri Sep 30 17:19:52 2016 -0700 [API] Enable bundles from spec commit c762d58b5a7fadf701dc1c626c53813ea1a700e1 Author: Victor Woeltjen Date: Fri Sep 30 17:12:42 2016 -0700 [API] No, don't enable bundles by default commit 219c9348d9f9bd07331cc366e47cf6ab83d388f8 Merge: f8819ee 5b0fa90 Author: Victor Woeltjen Date: Fri Sep 30 17:11:05 2016 -0700 Merge remote-tracking branch 'origin/master' into api-1124b Conflicts: bower.json commit f8819ee946d80556a5bef5b25f0471a75bb9bd38 Author: Victor Woeltjen Date: Fri Sep 30 17:05:40 2016 -0700 [API] Fix failing specs commit ce6c6385c349781b3f6d6d1e826a5d00c9b6eb5b Author: Victor Woeltjen Date: Fri Sep 30 16:55:58 2016 -0700 [API] Remove failing spec commit ef0264d86485e4abc8972e53801456fe0995e20a Author: Victor Woeltjen Date: Fri Sep 30 16:23:17 2016 -0700 [API] Satisfy JSCS commit 044b1b627d6f2ff54cdbb214e406e9c4e23e017a Author: Victor Woeltjen Date: Fri Sep 30 16:17:35 2016 -0700 [API] Run gulp fixstyle commit 5b6f13f521c5d1407bbc52aac4db33c662d0dac6 Author: Victor Woeltjen Date: Fri Sep 30 16:15:30 2016 -0700 [API] Satisfy JSLint commit 5b2c0e9aee072584e0a74157bf8db60ba842e44f Author: Victor Woeltjen Date: Fri Sep 30 15:47:54 2016 -0700 [API] Adapt composition capability commit dd7d8d2642558ac3be4575b4f4f72223984812f9 Author: Victor Woeltjen Date: Fri Sep 30 15:18:51 2016 -0700 [API] Fix dependency issues with Composition commit 08e28018c1530318fd137e7dcea5a3f1505af74a Author: Victor Woeltjen Date: Fri Sep 30 15:09:09 2016 -0700 [API] Don't instantiate Error commit 3b1fe9319196e7b1dfb626396362d581acce15e7 Merge: 2b66a4d c874ae7 Author: Victor Woeltjen Date: Fri Sep 30 15:08:01 2016 -0700 Merge remote-tracking branch 'origin/api-platform-updates' into api-1124b commit 2b66a4d604b71c3117d56a1f96bada162d0e18b6 Author: Victor Woeltjen Date: Fri Sep 30 15:06:50 2016 -0700 [API] Wire in ObjectAPI appropriately commit 70c810b85d0cdd5a1bcdba82d8fb75ebd08b4443 Author: Victor Woeltjen Date: Fri Sep 30 14:51:26 2016 -0700 [API] Remove obsolete define parameter commit c874ae7afd92e703aedb47ebb66463ccc670dacf Merge: 34e07b9 c27c0c5 Author: Victor Woeltjen Date: Fri Sep 30 14:46:37 2016 -0700 Merge remote-tracking branch 'origin/master' into api-platform-updates commit fc5c07aeb6500154c244f02d3a13e4f7248ed59f Merge: 2976c9e ed10249 Author: Victor Woeltjen Date: Fri Sep 30 14:44:54 2016 -0700 Merge remote-tracking branch 'origin/fix-jspdf-version-1214' into api-1124b commit 2976c9e703ce687289a28ce9ac989b717f87a7a2 Author: Victor Woeltjen Date: Fri Sep 30 14:39:33 2016 -0700 [API] Really fix EventEmitter import commit 54d2b00e67411256748286964c68968372dcccc2 Author: Victor Woeltjen Date: Fri Sep 30 14:37:20 2016 -0700 [API] Update licenses correctly Instead of double-documenting Zepto, document eventemitter3 commit 061f9d6deb49a9ceb9fbb9f37d779e286e52292e Author: Victor Woeltjen Date: Fri Sep 30 14:32:10 2016 -0700 [API] Update EventEmitter dependency commit 1de9d182a7bd94dc083c697f9d8b1ffc044ff8b6 Author: Victor Woeltjen Date: Fri Sep 30 14:20:10 2016 -0700 [API] Add license info for new dependencies commit 810150d0d7bc4728975055427e9811c59f400160 Author: Victor Woeltjen Date: Fri Sep 30 13:41:28 2016 -0700 [API] Update documentation to reflect API status commit 1c0999b51249b3eadc4b15f2a7a7e55badb4e91c Author: Victor Woeltjen Date: Fri Sep 30 13:26:59 2016 -0700 [API] Include revision info in minified openmct commit 774ae03c3e2d2de96b5f863ae6742ce6dcfd6a7e Author: Victor Woeltjen Date: Fri Sep 30 13:23:11 2016 -0700 [API] Add license headers commit 1defee8953533ac84a82872a4950b7ec3891e6df Author: Victor Woeltjen Date: Fri Sep 30 13:16:36 2016 -0700 [API] Decruft README.md for objects ...as this content is now in JSDoc commit 7b9ac3e7fbb915e555b7649cf752aad769bd3524 Author: Victor Woeltjen Date: Fri Sep 30 13:11:03 2016 -0700 [API] Remove tutorial directories commit f2178e2b43155bf4c1f765b967fba86ca25ec265 Author: Victor Woeltjen Date: Fri Sep 30 13:08:33 2016 -0700 [API] Decruft unused view ...from context menu prototyping commit 0094fea3f73633098af90a555b6693f4c4bf7bb1 Author: Victor Woeltjen Date: Fri Sep 30 13:07:55 2016 -0700 [API] Decruft unused gesture commit 7f79abe8490ee9afcc33476d2c9265bf9afcf3ad Author: Victor Woeltjen Date: Fri Sep 30 13:07:25 2016 -0700 [API] Remove unused OverlayManager commit f43bb6e03d37edf229796c60dbf9766d7450f66d Author: Victor Woeltjen Date: Fri Sep 30 13:05:16 2016 -0700 [API] Decruft unused Region commit 8f3c3d910e35216f785bdd085c51d6b86099e23f Author: Victor Woeltjen Date: Fri Sep 30 13:04:23 2016 -0700 [API] Decruft unused gesture commit 0956811adc407968c72215ffdfc80e9f66ace2b7 Author: Victor Woeltjen Date: Fri Sep 30 13:00:00 2016 -0700 [API] Remove obsolete Selection.js commit bcc5a4e2ca8e38ca6617a3895bd885b7ddd72b85 Author: Victor Woeltjen Date: Fri Sep 30 12:57:55 2016 -0700 [API] Remove README to decruft Content is now provided in JSDoc commit 27b6a51887f525c325c4cc5a0d0f2dabb35c328d Author: Victor Woeltjen Date: Fri Sep 30 12:56:16 2016 -0700 [API] Remove README to decruft Content is now provided in JSDoc commit bb04d9db4ac6f02942be98477611fbd870084bd9 Author: Victor Woeltjen Date: Fri Sep 30 12:55:38 2016 -0700 [API] Remove obsolete method to decruft commit 7c3bfae0a64edc08098668c1b9f3186ad4ebc9db Author: Victor Woeltjen Date: Fri Sep 30 12:53:26 2016 -0700 [API] Remove obsolete script to decruft commit 471fe7453b32a303810ac382a6c108fdaf412d58 Author: Victor Woeltjen Date: Fri Sep 30 12:52:51 2016 -0700 [API] Remove empty View script ...and move API docs to ViewRegistry commit 0d0526627b66d84eefff90151223a92c28ad495d Author: Victor Woeltjen Date: Fri Sep 30 12:50:37 2016 -0700 [API] Decruft unused AngularView commit 43e35179a3cbfc2a1d9add40f8cffd42ba38b7fd Author: Victor Woeltjen Date: Fri Sep 30 12:49:08 2016 -0700 [API] Continue decrufting commit 59634be565777b45da7ae31f4b53e40c86498f0b Author: Victor Woeltjen Date: Fri Sep 30 12:46:36 2016 -0700 [API] Decruft unused script commit 3bd8dd99287cb44be92a5927ba705221e360e42a Author: Victor Woeltjen Date: Fri Sep 30 12:45:20 2016 -0700 [API] Decruft unused script commit b5f1f98555349f11cb67c2c92d38cb782076615b Author: Victor Woeltjen Date: Fri Sep 30 12:38:03 2016 -0700 [API] Minimize logic in openmct module commit 11965304fb4921fdf0442edd8023abaf219c5e8c Author: Victor Woeltjen Date: Fri Sep 30 12:35:15 2016 -0700 [API] Remove redundant openmct module commit 742d649d38e144b846028795c6f61f3fa5ac866d Author: Victor Woeltjen Date: Fri Sep 30 12:29:34 2016 -0700 [API] Decruft unused dependency commit 6a47df095b77d8920d6ac37034009001fe122035 Author: Victor Woeltjen Date: Fri Sep 30 12:27:53 2016 -0700 [API] Remove gulp api task commit 0012ca48c1115d8663a99c85c71362d67785c873 Author: Victor Woeltjen Date: Fri Sep 30 12:27:39 2016 -0700 [API] Simplify JSDoc build ...since extraneous documentation no longer needs to be worked-around. commit 34e07b938dd3fdff413c18faa04903a11d6a519d Author: Victor Woeltjen Date: Fri Sep 30 10:42:48 2016 -0700 [API] Update StyleSheetLoaderSpec ...to reflect asset path details commit 230230aa943cd8f073e89cc13232d861b38984e7 Author: Victor Woeltjen Date: Fri Sep 30 10:37:31 2016 -0700 [API] Remove js extension from import ...to avoid confusing RequireJS and breaking test suite commit 901846e6c153690c9b6dd3620b91eca3199ff2ed Author: Victor Woeltjen Date: Fri Sep 30 10:29:03 2016 -0700 [API] Use correct property for RT table template commit 04b832690074911148b6705e683709418cef1b9c Merge: aed01d3 a93f41f Author: Victor Woeltjen Date: Fri Sep 30 10:25:32 2016 -0700 Merge branch 'api-platform-updates' into api-1124b commit a93f41f1c3743f703e33948f3f136297ce4ad0e6 Author: Victor Woeltjen Date: Fri Sep 30 10:25:22 2016 -0700 [API] Miscellaneous platform updates Adds miscellaneous platform updates to include templates via the RequireJS text plugin; to support modification of asset paths; and to support priority order for gestures. Supports integration of new API, #1124. commit aed01d3a23a21334d45e3daedb73365aa2651158 Author: Victor Woeltjen Date: Fri Sep 30 10:20:09 2016 -0700 [API] Remove examples ...as these do not necessarily reflect API updates commit 09c73ef5f820a37e26b4425be87999a077d25032 Author: Victor Woeltjen Date: Fri Sep 30 10:09:42 2016 -0700 [API] Update MCT implementation commit b1b6080161a83ff4bc7bd758cda7ec85d6bb8513 Author: Victor Woeltjen Date: Fri Sep 30 09:56:50 2016 -0700 [API] Rename types in api module commit cb93da5e15fc2baffab45c964d0463c2308870fc Author: Victor Woeltjen Date: Fri Sep 30 09:56:06 2016 -0700 [API] Fix CompositionAPI module commit d0e7eb29a94aa264ed1f363e75c203ceb2c4e429 Author: Victor Woeltjen Date: Fri Sep 30 09:51:35 2016 -0700 [API] Mark MCT#selection as private commit 5e9e6099b06187d71a92b41a6c3c5e86670db83a Author: Victor Woeltjen Date: Thu Sep 29 21:59:06 2016 -0700 [API] Update TelemetryAPI to match docs commit 6bd1af5c033eb6246b8a99e5c5570a9fe40d9a36 Author: Victor Woeltjen Date: Thu Sep 29 21:42:09 2016 -0700 [API] Define LimitEvaluator interface commit 3f20c1bb945a4162b1367ab1d550c40110806566 Author: Victor Woeltjen Date: Thu Sep 29 17:34:41 2016 -0700 [API] Update ViewRegistry to match docs commit 91214f262322b179520b643af4715511575edea1 Author: Victor Woeltjen Date: Thu Sep 29 17:26:27 2016 -0700 [API] Implement TypeRegistry methods commit 1fde82e673dbef6e0375ef179ec30775a2342f37 Author: Victor Woeltjen Date: Thu Sep 29 17:22:29 2016 -0700 [API] Mark Selection as private commit 0ae0abcfc3bb84071312487fef33dcd73278223e Author: Victor Woeltjen Date: Thu Sep 29 17:20:27 2016 -0700 [API] Update ObjectAPI to match docs commit 6fe1e775e0f799666af87133654a6143aae5e6ae Author: Victor Woeltjen Date: Thu Sep 29 17:16:56 2016 -0700 [API] Update GestureAPI to match docs commit d4d9f9c4675c78b929db44c5a165c23f71cf9474 Author: Victor Woeltjen Date: Thu Sep 29 17:08:54 2016 -0700 [API] Update CompositionCollection to match docs commit 0cb1ec93665925686246cb8751c0cb5e8b080a8e Author: Victor Woeltjen Date: Thu Sep 29 17:04:15 2016 -0700 [API] Rewrite CompositionAPI to match docs commit 6ab27302f6a36c7fc2f9ed8877e1108f59f570a0 Author: Victor Woeltjen Date: Thu Sep 29 16:49:42 2016 -0700 [API] Document remaining Dialog method commit dfc5021e11cd5d5c15880a3e51a6b4fe7164c1c5 Author: Victor Woeltjen Date: Thu Sep 29 16:47:33 2016 -0700 [API] Fix JSDoc for Dialog commit 9e3a3529e0018a09599717a3460e0bd5589e0587 Author: Victor Woeltjen Date: Thu Sep 29 16:46:22 2016 -0700 [API] Rename main module commit 434ea5487a04cc1405f43485bf975eb8176b08b7 Merge: 987d98b 45ecc7b Author: Victor Woeltjen Date: Thu Sep 29 16:43:47 2016 -0700 Merge branch 'api-1110' into api-1124b commit 987d98b47e2aa955d5af66f2989683a1a0983dc4 Author: Victor Woeltjen Date: Thu Sep 29 16:37:57 2016 -0700 [API] Restore JSDoc for openmct module commit 53974bd69aaf9c2a4af672b578e301b7c433963a Merge: ed8d331 d61f446 Author: Victor Woeltjen Date: Thu Sep 29 10:54:08 2016 -0700 Merge branch 'subobject-selection-1126' into api-1124b Conflicts: bower.json index.html main.js src/openmct.js commit ed8d331cd36ed5fdbb6d0d34355521885cd7796b Merge: 7ce9bd9 7439d94 Author: Victor Woeltjen Date: Thu Sep 29 10:47:52 2016 -0700 [API] Merge in latest from master commit d61f4460029703a6f3d28245f506013490a21386 Author: Victor Woeltjen Date: Mon Sep 26 15:21:43 2016 -0700 [Selection] Add a click-elsewhere gesture commit 763f7dd02155955d2ef04770be5007a639f0c8c7 Author: Victor Woeltjen Date: Mon Sep 26 12:37:02 2016 -0700 [Selection] Begin adapting actions commit cad255ce8309a8b9aa48e669540d7902f9fd2651 Author: Victor Woeltjen Date: Mon Sep 26 11:52:14 2016 -0700 [Selection] Use variable name expected by template commit 3b4239fbd93a316e6c4914c863e797b59262bc80 Author: Victor Woeltjen Date: Mon Sep 26 11:43:38 2016 -0700 [Selection] Use AngularView for context menus commit 15ef89f4556ae260fe00aa6a5117841874000101 Author: Victor Woeltjen Date: Mon Sep 26 11:38:37 2016 -0700 [Selection] Bring over context menu template commit c9a0a469f690bf04bc826641c42d90b6c1487739 Author: Victor Woeltjen Date: Sun Sep 25 20:30:42 2016 -0700 [Selection] Position context menus commit f926bd9762959fef2de1a98e7d949169fa6c3a04 Author: Victor Woeltjen Date: Sun Sep 25 19:38:03 2016 -0700 [Selection] Position overlays commit d79392aeaa962f3d4d1ce41dd93ec640566e1af7 Author: Victor Woeltjen Date: Sun Sep 25 18:26:00 2016 -0700 [Selection] Begin integrating OverlayManager commit 9a5bda49178d2c0e6f209ef1d96765b32f1c55db Author: Victor Woeltjen Date: Sun Sep 25 18:22:07 2016 -0700 [Selection] Sketch in overlay manager commit 407550e6f448cb8aa3fda536f103d03a44bd2f24 Author: Victor Woeltjen Date: Fri Sep 23 15:48:43 2016 -0700 [Selection] Inject actionRegistry commit 8b44b44e381e3ce2dff8923194120c8f65baa24b Author: Victor Woeltjen Date: Fri Sep 23 15:46:45 2016 -0700 [Selection] Generalize Registry commit 8dfa8df28a6dc5cee7e7d51c721e9442a700dd66 Author: Victor Woeltjen Date: Fri Sep 23 15:45:46 2016 -0700 [Selection] Allow providers to return array ...to allow one-to-many providers for actions, as is useful for Create et al (and, in this specific case, to support adapters.) commit 9e19296b148d8cd0daadd1bf4573db1ccadd48e9 Author: Victor Woeltjen Date: Fri Sep 23 15:35:40 2016 -0700 [Selection] Add ActionRegistry commit 106632c21c35dc3e076b33462eb3c6a55188e891 Author: Victor Woeltjen Date: Fri Sep 23 15:32:40 2016 -0700 [Selection] Remove unused import commit 235032a57121b7ed26e81ec91911e19b81de36df Author: Victor Woeltjen Date: Fri Sep 23 15:27:39 2016 -0700 [Selection] Wire in new contextmenu commit db41f6e64fedaa9ced25bb05466d5a9f53460fad Author: Victor Woeltjen Date: Fri Sep 23 15:19:16 2016 -0700 [Selection] Obey priority order commit e83e0da521a938fa11957d8c1f412e39c1278f5b Author: Victor Woeltjen Date: Fri Sep 23 15:16:32 2016 -0700 [Selection] Export openmct from main.js commit c411f8fbe36c74c3f8641985c0cfd671260a92f3 Author: Victor Woeltjen Date: Fri Sep 23 15:07:03 2016 -0700 [Selection] Go through openmct API commit 4ce952846cf47b70ac3bb24a8785c54d7f1c9429 Author: Victor Woeltjen Date: Thu Sep 22 11:51:00 2016 -0700 [Selection] Expose context menu adapter commit fdab4a614f82e3125a6612572d800c95976f5c7c Author: Victor Woeltjen Date: Thu Sep 22 11:48:44 2016 -0700 [Selection] Expose contextual gesture commit 152f55652f745784838f52de7a78d3c167db25c4 Author: Victor Woeltjen Date: Thu Sep 22 11:46:37 2016 -0700 [Selection] Add adapter for context menu commit c46c42e5761243c5badf67f799f564c455843bf4 Author: Victor Woeltjen Date: Wed Sep 21 15:39:56 2016 -0700 [Selection] Sketch in AngularView ...for use in the adapter layer. commit 75bf956c3d42a362210d3f19c4e9f4261bdcc459 Author: Victor Woeltjen Date: Wed Sep 21 14:54:22 2016 -0700 [Selection] Use context typedef commit 0a44c48338299129974e57547586b92ede69d3c9 Author: Victor Woeltjen Date: Wed Sep 21 13:27:59 2016 -0700 [Selection] Sketch in context menu gesture commit e4c3412e8a74b3c6656ef71c9bbc676b26fe0066 Author: Victor Woeltjen Date: Wed Sep 21 13:16:24 2016 -0700 [Selection] Sketch in context menu view commit 4e1cfac4b9311a060427c246f488f7f81d21c7dd Author: Victor Woeltjen Date: Wed Sep 21 12:30:36 2016 -0700 [Selection] Expose inspector registry commit 0a64e9f515d271e162c5b71392941962a5f233d4 Author: Victor Woeltjen Date: Wed Sep 21 12:29:43 2016 -0700 [Selection] Add ViewRegistry commit 7341ed9d030bd6b491bbee23ae8e9c59b7a8fa9d Author: Victor Woeltjen Date: Wed Sep 21 12:26:57 2016 -0700 [Selection] Handle selection changes ...from the InspectorRegion commit e2631bdcfd14e13da0ff7dd2fd056365dd158d43 Author: Victor Woeltjen Date: Wed Sep 21 11:27:24 2016 -0700 [Selection] Listen/unlisten to selection ...from the inspector region commit 773a7c0c2423107d68bedd5a3d0cc75ebc941cfd Author: Victor Woeltjen Date: Wed Sep 21 11:25:21 2016 -0700 [Selection] Begin adding Inspector region commit fdcba665582a0c7e6c0f8001dd91310118d86038 Author: Victor Woeltjen Date: Wed Sep 14 11:54:19 2016 -0700 [Selection] Add legacy bundle ...to act as an adapter to legacy registration API commit 3b1ed7821fd70e942011122c69c7f778bebbabaf Author: Victor Woeltjen Date: Wed Sep 14 11:50:33 2016 -0700 [Selection] Add openmct module ...to expose selection APIs commit 1be517f3ea42dc6ddc52ca60efb39f8dc244035f Author: Victor Woeltjen Date: Wed Sep 14 11:46:06 2016 -0700 [Selection] Don't reuse old paths for new items commit 5e3dcadfa4debfc7135fb254c8b0781e1dc4d390 Merge: fd97f4d f732387 Author: Victor Woeltjen Date: Wed Sep 14 11:06:05 2016 -0700 Merge remote-tracking branch 'origin/master' into subobject-selection-1126 Conflicts: bower.json main.js test-main.js commit 45ecc7bb2bf5e712cc170e8c9c5445989f26938f Author: Victor Woeltjen Date: Wed Sep 7 14:10:51 2016 -0700 [API] Ascending/descending order commit a3c3f997cff1cae0718c06012b0dc53cefea7d2d Author: Victor Woeltjen Date: Wed Sep 7 13:25:05 2016 -0700 [API] Move down TC example ...to come after telemetry usage example commit 1ae3ce57d0ae3033af586b37e8308b97ca4996cf Author: Victor Woeltjen Date: Wed Sep 7 13:23:06 2016 -0700 [API] Telemetry registration example commit 616e2b4d775c529d098e9b7a2b86b7503b1d6c8f Author: Victor Woeltjen Date: Wed Sep 7 13:10:48 2016 -0700 [API] Exemplify mutate/observe commit 0e7d812db724e6bbac50f84363f87091f3a738b7 Author: Victor Woeltjen Date: Wed Sep 7 13:04:30 2016 -0700 [API] Document ObjectAPI registration commit c074f29a0789d6c86d73ddfca17a03940bf133fc Author: Victor Woeltjen Date: Wed Sep 7 11:26:13 2016 -0700 [API] Add gesture example commit 92118d353e9c57b608e12c34fa8ae747dec0021f Author: Victor Woeltjen Date: Wed Sep 7 11:24:01 2016 -0700 [API] Add Time Conductor example commit b111eeff070a19475670b210a8a2a93ee29f929e Author: Victor Woeltjen Date: Wed Sep 7 11:16:24 2016 -0700 [API] Update reference commit 8042e849119185bf50a4d7dced10b2c4ee07c477 Author: Victor Woeltjen Date: Wed Sep 7 11:15:26 2016 -0700 [API] Add composition-related content commit 631c4b5ddac95f8cf5a7cbc1d27ccd94abd8bd5c Author: Victor Woeltjen Date: Wed Sep 7 11:06:27 2016 -0700 [API] Describe composition property commit c6baf2dc1f74156777359a256339e26388a6b7c4 Author: Victor Woeltjen Date: Wed Sep 7 10:58:00 2016 -0700 [API] Make CompositionCollection an interface commit 4db7e12d459c1e1bd375d3cf5bf4f04798394637 Author: Victor Woeltjen Date: Wed Sep 7 10:55:34 2016 -0700 [API] Add example for views commit b1799c695e7ce137babf18d79b779f21f0a756d7 Author: Victor Woeltjen Date: Wed Sep 7 10:46:49 2016 -0700 [API] Add DomainObject commit 5761c889bcbfc5f6795c887ea48ce4e7cb1b0112 Author: Victor Woeltjen Date: Wed Sep 7 10:41:48 2016 -0700 [API] Move MutableObject methods to ObjectAPI commit 97cf26d4385110da99818acb38fa57fe80337a53 Author: Victor Woeltjen Date: Wed Sep 7 10:27:42 2016 -0700 [API] Telemetry provider commit c4b83b758941b81cb54ae257da0b27cd0693a1d0 Author: Victor Woeltjen Date: Wed Sep 7 10:18:43 2016 -0700 [API] Account for provider strategies commit d56f30c15acbd722fefecd7b4d811cbcc5706308 Author: Victor Woeltjen Date: Wed Sep 7 10:15:57 2016 -0700 [API] TelemetryRequest commit 7279c5d857c6cfd3ad89f8b9ab52f15bb271d822 Author: Victor Woeltjen Date: Wed Sep 7 10:12:47 2016 -0700 [API] Mark properties as optional commit 5d53ab83a3a69dc483df618aaa75500898abef0b Author: Victor Woeltjen Date: Wed Sep 7 10:11:52 2016 -0700 [API] More TelemetryProperty properties commit 4565e45b360f89bf79a601dee337e952eadd3897 Author: Victor Woeltjen Date: Wed Sep 7 10:08:07 2016 -0700 [API] Add TelemetryProperty for metadata commit 5950daa6cb175826f87276ae622c008302d7aaf2 Author: Victor Woeltjen Date: Wed Sep 7 09:33:21 2016 -0700 [API] Move out composition policy commit 60800c913eaf069c04d085cef5e78edf4734650d Author: Victor Woeltjen Date: Wed Sep 7 09:28:26 2016 -0700 [API] Gestures commit 649567176dd5a40113e66f56cddf0034b34ffae9 Author: Victor Woeltjen Date: Wed Sep 7 09:21:18 2016 -0700 [API] Notes on context commit 1df573b8c6989793112ee381f423a0f711669994 Author: Victor Woeltjen Date: Wed Sep 7 08:50:20 2016 -0700 [API] Move types into registry commit bc4ca10e53c6f83379da523b963d7b559ab681d4 Author: Victor Woeltjen Date: Wed Sep 7 08:45:18 2016 -0700 [API] Separate out ViewRegistry commit 1338f025414503ba104005263ac1185f0a92a0c7 Author: Victor Woeltjen Date: Wed Sep 7 08:32:21 2016 -0700 [API] Clean up telemetry API docs slightly commit 1cc6833c307bdfa6fb161bc9f7c01bf21403b504 Author: Victor Woeltjen Date: Wed Sep 7 08:26:32 2016 -0700 [API] Add request/subscribe JSDoc commit a7a47a36d64664d0e76841a4ba0968b23f542a75 Author: Victor Woeltjen Date: Wed Sep 7 08:10:40 2016 -0700 [API] Remove extraneous return jsdoc commit 851d0f0d6374a5622a7ad2cada5632f29b1ef509 Author: Victor Woeltjen Date: Wed Sep 7 08:09:37 2016 -0700 [API] Clean up Composition API commit 5a129de73d1df4552dd5ce50bba7bb2a455cca18 Author: Victor Woeltjen Date: Tue Sep 6 16:32:54 2016 -0700 [API] Clarify language commit 0cf634a412a04933a940b30a89635ebc65cd7f19 Author: Victor Woeltjen Date: Tue Sep 6 16:30:05 2016 -0700 [API] Add more front-page content commit 702d7acf6473d79ea2820bbdf800acacee6b1f95 Author: Victor Woeltjen Date: Tue Sep 6 16:18:42 2016 -0700 [API] Begin adding front page content commit 69a500bf4431efa864c76753242f638a37ca5ff0 Author: Victor Woeltjen Date: Tue Sep 6 16:07:49 2016 -0700 [API] Dedocument Selection ...and add a description to MCT#conductor commit 82e5b009e8272395fbfc8665e61796fcee2efedf Author: Victor Woeltjen Date: Tue Sep 6 13:58:19 2016 -0700 [API] Move Identifier typedef commit aafd0731ec054cc21ac3b644325e3223234d2f01 Author: Victor Woeltjen Date: Tue Sep 6 13:47:59 2016 -0700 [API] Remove 'the API' section ...as this is now generated from JSDoc, more or less commit c9705a5f2c7d4e918586d58cf545f609f7cf9135 Author: Victor Woeltjen Date: Tue Sep 6 13:46:02 2016 -0700 [API] Normalize line length commit 7cc4a1262c38d1a3c8ced4fd4c05947278609348 Author: Victor Woeltjen Date: Tue Sep 6 13:31:07 2016 -0700 [API] Proofread intro commit 02904a6081f828f59dc3141b41bb1e0bfd779a65 Author: Victor Woeltjen Date: Tue Sep 6 13:28:49 2016 -0700 [API] Link to openmct module in the preamble commit 7ce9bd969ad06a322e827da2b05081965c037d5d Author: Victor Woeltjen Date: Tue Sep 6 10:14:04 2016 -0700 [API] Telemetry JSdoc commit 8cafd2da7ebeb8b508df7b2222b810ad941077b8 Merge: 6264ab7 bccd018 Author: Victor Woeltjen Date: Tue Sep 6 10:06:30 2016 -0700 Merge remote-tracking branch 'origin/api-tutorial/telemetry' into api-1110 commit 6264ab75f3495b38646f79ddd5460a63587540d7 Merge: 7a5cad2 b4dc502 Author: Victor Woeltjen Date: Tue Sep 6 10:03:59 2016 -0700 Merge remote-tracking branch 'origin/api-tutorials' into api-1110 Conflicts: src/MCT.js src/api/composition/CompositionCollection.js src/api/composition/DefaultCompositionProvider.js src/api/objects/MutableObject.js commit 7a5cad20ec1ab3990aaa53a0815bcfeb3ae6a376 Author: Victor Woeltjen Date: Tue Sep 6 09:58:08 2016 -0700 [API] Add JSDoc for Dialog commit 4de069b3939171ae71d567b32884890cc3be50c9 Author: Victor Woeltjen Date: Tue Sep 6 09:54:51 2016 -0700 [API] JSDoc for Selection commit 70abd5c1f913ed7783f80b7d6202b5bd057f5c3f Author: Victor Woeltjen Date: Tue Sep 6 09:47:06 2016 -0700 [API] Document start event commit 2a3a61da863e797924f52e11bc3f016bc00bb92d Author: Victor Woeltjen Date: Tue Sep 6 09:45:24 2016 -0700 [API] Fix event memberofs in TimeConductor commit 018bd022ccebb2f7a54dea508c0468c37b531ce5 Author: Victor Woeltjen Date: Tue Sep 6 09:42:04 2016 -0700 [API] Document View API commit 4739b36bc3fcee0cf259be8ec4823b23954fb4a9 Author: Victor Woeltjen Date: Tue Sep 6 09:14:04 2016 -0700 [API] Add metadata to View jsdoc commit c9b1035a6d50e55e1e9b6d47e94768c883010dbd Author: Victor Woeltjen Date: Tue Sep 6 09:13:01 2016 -0700 [API] Document Type.check commit 67683284754cc936d5fca337324fae3a0da0826c Author: Victor Woeltjen Date: Tue Sep 6 08:43:01 2016 -0700 [API] Document MutableObject commit 60c179eac3adc89c220ba0c22e2c2118b1da37cf Author: Victor Woeltjen Date: Tue Sep 6 08:32:18 2016 -0700 [API] Add missing parameter names commit a20e8d69b5b7df97ea7dff6c2ca08814699bc870 Author: Victor Woeltjen Date: Tue Sep 6 08:31:42 2016 -0700 [API] More Objects doc commit 1abcb248fe61f27663e63ca4a28437be5ca721c7 Author: Victor Woeltjen Date: Tue Sep 6 08:13:26 2016 -0700 [API] Include API.md as main page commit a8151f5f2299100fe088d5f2f0b328a3ae59697f Author: Victor Woeltjen Date: Tue Sep 6 08:09:03 2016 -0700 [API] Document CompositionProvider commit cdf21f3763e32ea161560c69f66295caf4bebf46 Author: Victor Woeltjen Date: Tue Sep 6 07:58:52 2016 -0700 [API] Mark Composition as instance method commit 341bceb4e2a65368425194b1b4de07a8630418bb Author: Victor Woeltjen Date: Tue Sep 6 07:57:44 2016 -0700 [API] Document composition API commit 0470a022725b4dbd1736563a378e5c701a5db786 Author: Victor Woeltjen Date: Fri Sep 2 16:04:38 2016 -0700 [API] Reference ObjectAPI commit e3dc26c1304e9c7f1431ec7f20d8307f61bc4cfd Author: Victor Woeltjen Date: Fri Sep 2 15:25:28 2016 -0700 [API] Clean up docs commit 96c3d1cac2b2ef518c055385b4f2da7726516d39 Author: Victor Woeltjen Date: Fri Sep 2 15:19:20 2016 -0700 [API] Use JSDoc config commit 2af778145d2a2ef15e7031e35e3a4954d6428922 Author: Victor Woeltjen Date: Fri Sep 2 15:16:11 2016 -0700 [API] Clean up JSDoc slightly commit 5743eeb33a049a1ad92d7cc345b5f8e8b9d7b8b0 Author: Victor Woeltjen Date: Fri Sep 2 15:03:12 2016 -0700 [API] Reference EventEmitter commit f06f714bdcaa4a7f8153321b653a12e18affd831 Author: Victor Woeltjen Date: Fri Sep 2 15:02:18 2016 -0700 [API] Document some public fields commit d592bd1035b2db2f5179916fa1c16912ac6d9bb8 Author: Victor Woeltjen Date: Fri Sep 2 14:58:06 2016 -0700 [API] Give up on borrows-style documentation commit b5f62541ceed9fcc71ce06da4ec8626ab8dd1d62 Author: Victor Woeltjen Date: Fri Sep 2 14:54:27 2016 -0700 [API] Add watch for API docs commit 33ced4bccf42fc5542861047691448ed17f40b83 Author: Victor Woeltjen Date: Thu Sep 1 15:51:25 2016 -0700 [API] Expose MCT on openmct commit e37510dbab5eb86a7818cb468ce5a095049ed364 Author: Victor Woeltjen Date: Thu Sep 1 15:13:52 2016 -0700 [API] Ignore internal API after processing commit f27c41014dbc4471f6b0c66bcbf81381522c72f6 Author: Victor Woeltjen Date: Thu Sep 1 14:50:13 2016 -0700 [API] JSDoc for openmct.start() commit bd796f2bebf9ff97c44b1d1a6e374b0a1c38c1de Author: Victor Woeltjen Date: Thu Sep 1 14:44:24 2016 -0700 [API] Simple constructor documentation commit bcc32c76d06316d6ce718f5015ba365c85e8efd7 Author: Victor Woeltjen Date: Thu Sep 1 14:42:55 2016 -0700 [API] Module-level JSDoc commit ff2ec6690aa0756c780f682317a45244028089d0 Author: Victor Woeltjen Date: Thu Sep 1 14:35:52 2016 -0700 [API] Rename module to openmct commit 1e0fb3611d92b9a2c40540f99e418eb10bae6011 Author: Victor Woeltjen Date: Thu Sep 1 12:34:54 2016 -0700 [API] Render API docs to HTML commit 1d4f36a7d9f0cfd3f81d129fff306be22e55c8e8 Author: Victor Woeltjen Date: Fri Aug 26 11:48:27 2016 -0700 [API] Include JSDoc for mct namespace only commit 0f96fbdd623e487538fe283d0dbd0cc8eea568e5 Author: Victor Woeltjen Date: Fri Aug 26 11:31:56 2016 -0700 [API] Treat mct as a namespace commit e05fb57fe43a560f546bb9129828cca85e388770 Author: Victor Woeltjen Date: Fri Aug 26 09:47:50 2016 -0700 [API] Quasi-sensible JSDoc starting point commit 185cdcab083677b380b4f2dfc185bda3840b47af Author: Victor Woeltjen Date: Fri Aug 26 09:31:07 2016 -0700 [API] Begin adding mct.js ...which will provide an instance of OpenMCT at startup, as well as house documentation for entry point to public API. commit 50ccad5aaa682043215807e15ffe70ae742825d4 Author: Victor Woeltjen Date: Fri Aug 26 09:26:12 2016 -0700 [API] Rename MCT to OpenMCT commit 6a23df94542f21b59dabc13ce17a85d56da1c8df Author: Victor Woeltjen Date: Thu Aug 25 14:54:07 2016 -0700 [API] Add JSDoc for MCT commit ab5b1d3754994ca46ee856f069271f35198d7016 Author: Victor Woeltjen Date: Thu Aug 25 13:49:21 2016 -0700 [API] Add JSDoc task commit b309f26b56cd0381eee5328d6d32d412312ca67a Author: Victor Woeltjen Date: Thu Aug 25 13:38:44 2016 -0700 [API] Add gulp-jsdoc-to-markdown dep ...to aid in generating API docs as a measure of API completeness and consistency, for #1110 and #1111 commit b4dc50295c33cb1cf25d31ff580fabcde74111ec Merge: 02aa08a 382dde3 Author: Victor Woeltjen Date: Thu Aug 25 13:26:50 2016 -0700 Merge pull request #1131 from nasa/open1094 Resolve synchronization issues with MutableObject commit 382dde300a5129ffecc3f9015e3debd8712ec104 Merge: b1b8df4 02aa08a Author: Victor Woeltjen Date: Thu Aug 25 13:25:54 2016 -0700 Merge remote-tracking branch 'origin/api-tutorials' into open1094 Conflicts: index.html tutorials/todo/todo.js commit 02aa08a3ef9fb29c60fc6a2b3dfd1f5622642d38 Merge: c95d9c7 d73c505 Author: Victor Woeltjen Date: Thu Aug 25 13:19:14 2016 -0700 Merge pull request #1121 from nasa/api-containment [API] Containment commit c95d9c7956676c1887e85af4eaf556ae52ea65ae Merge: 1147f3a 2463e4d Author: Victor Woeltjen Date: Thu Aug 25 13:17:26 2016 -0700 Merge pull request #1107 from nasa/api-type-forms [API] Handle forms with a "properties" region commit fd97f4db41db28855b76253b1cf710205ec8f5f1 Author: Victor Woeltjen Date: Thu Aug 25 13:15:37 2016 -0700 [Selection] Begin implementing Hover gesture commit b1b8df4d8745b4520ae7887e80fec7fc5413aa6a Author: Andrew Henry Date: Tue Aug 23 13:57:12 2016 +0100 Use MutationCapability commit 3228a83802cb7bfe1f7db4df721cdf1f24371c53 Author: Victor Woeltjen Date: Mon Aug 22 16:31:27 2016 -0700 [Selection] Begin adding hover gesture commit 1dba551fc6c493739edf8ef48f54d17045312439 Author: Victor Woeltjen Date: Mon Aug 22 16:07:01 2016 -0700 [Selection] Force single select ...as a simplifying assumption for the initial prototype. commit 20f3b57dddcefa99a2cf5fb4f9407ba51700036d Author: Victor Woeltjen Date: Mon Aug 22 14:18:26 2016 -0700 [Selection] Release listeners commit 58787c4436bbf329befcdba042b4e60cce0fac21 Author: Victor Woeltjen Date: Mon Aug 22 14:13:46 2016 -0700 [Selection] Remove obsolete include commit 2a45893602daa7d32f859d0ef0d1ea1a0b0aa585 Author: Victor Woeltjen Date: Mon Aug 22 13:22:17 2016 -0700 [Selection] Begin adding context management commit a94ab44431d368609e5bcd563a2f47045e050913 Author: Victor Woeltjen Date: Mon Aug 22 12:24:40 2016 -0700 [Selection] Toggle selected class commit cc9efdff315b74f1374796ec267ba094ab9b3b45 Author: Victor Woeltjen Date: Mon Aug 22 12:17:29 2016 -0700 [Selection] At Path.toArray commit bd3c6665fbbde8a5f03f4269c80591f7af7b13af Author: Andrew Henry Date: Mon Aug 22 14:25:39 2016 +0100 Added bridge between old and new event models commit 10e90519c0293ce12c4a9d88170150b6c9b28126 Author: Pete Richards Date: Fri Aug 19 19:10:06 2016 -0700 Tidy todo views, remove unnecessary code commit d341a8be9766d88e4cc9b5f51281d18c628408b3 Author: Pete Richards Date: Fri Aug 19 19:07:53 2016 -0700 Selection changes include new selection commit 8c439d8109044e838f56e29182a8cb8b77aebafc Author: Andrew Henry Date: Sun Aug 21 23:01:48 2016 -0700 Adding compatibility between old and new style mutation events commit 9c88b7ce1d9d20eb3df34d538ab11bbb6c8879ab Author: Henry Date: Thu Aug 18 10:38:16 2016 -0700 Removed setters from MutableObject and fixed non-working todo list tutorial Refactoring MutableObject Fixed non-working todo example commit 6e664003e33af9796e17a98e7e62f7906affc138 Author: Victor Woeltjen Date: Thu Aug 18 13:11:09 2016 -0700 [Sub-object] Sketch in SelectGesture commit 70d804fc49c27381150fb4b2136dfadba2d10032 Author: Victor Woeltjen Date: Thu Aug 18 11:54:05 2016 -0700 [Sub-object] Emit change events commit 06c184821e1c20fe3b569b185caadbc4a47d08fc Author: Victor Woeltjen Date: Thu Aug 18 11:51:09 2016 -0700 [Sub-object] Add Path.append commit b76be3d2e55d1472a6eec4df6ee40fe53cab7871 Author: Victor Woeltjen Date: Thu Aug 18 11:45:13 2016 -0700 [Sub-object] Basic Path/Selection implementation commit 8934ba96f7302f71ac04dc609cb8054819070edc Author: Victor Woeltjen Date: Thu Aug 18 10:33:34 2016 -0700 [Sub-object] Add empty Selection class commit 3fb4ce78199d24292586fb35377f1cce049b20a1 Author: Victor Woeltjen Date: Fri May 27 11:43:35 2016 -0700 [Sub-object] Add EventEmitter dependency Selection state will be represented in #1126 by an EventEmitter. commit 2463e4d59fe01346a4f12ec4775d580a8025cae8 Author: Victor Woeltjen Date: Fri Aug 12 12:54:39 2016 -0700 [API] Update Dialog API usage commit d73c505bea3876bdc1954805ba50b5026b40fc5c Author: Victor Woeltjen Date: Fri Aug 12 11:17:00 2016 -0700 [API] Fix typo, add missing this commit 831ecc59d91ea29578aaa3524f3f411e8c9e2a12 Author: Victor Woeltjen Date: Fri Aug 12 10:24:59 2016 -0700 [API] Wire in canContain via policy commit 1de26d3c5d8daf4ab6d8a08cd0dd7155e1806d66 Author: Victor Woeltjen Date: Fri Aug 12 09:30:02 2016 -0700 [API] Throw error on containment violation commit 11409ce50954d1b1b8c537f471c706005af69184 Author: Victor Woeltjen Date: Fri Aug 12 09:27:46 2016 -0700 [API] Add containment methods commit 93872ce0744f46acc37e95cfa6cef95ca19e36a4 Author: Victor Woeltjen Date: Thu Aug 11 16:04:26 2016 -0700 [API] Expose Dialog as constructor ...and use it that way from todo plugin commit 8861644f2de09a0de26ce1fbf834f81b48237c61 Author: Victor Woeltjen Date: Thu Aug 11 16:02:04 2016 -0700 [API] Adjust Dialog API ...to allow OK button to be enabled/disabled. commit d4948f771b9b3f7db425f2db5edbb28cbfa52fcf Merge: 0656a29 8295a0b Author: Victor Woeltjen Date: Thu Aug 11 15:31:11 2016 -0700 Merge branch 'api-todo-update' into api-type-forms commit 8295a0bed13c85a260c85e03f7a5b6b8c946f5db Author: Victor Woeltjen Date: Thu Aug 11 15:29:46 2016 -0700 [API] Update todo tutorial ...to expect new domain object API (instead of explicitly wrapping it.) commit 0656a298daff730690476728b0af1c7e5703408e Author: Victor Woeltjen Date: Thu Aug 4 14:00:39 2016 -0700 [API] Remove test usage of properties region commit fe2ce91d50c06e5f618a7180787a19ca96bd5cfd Author: Victor Woeltjen Date: Thu Jul 28 16:16:22 2016 -0700 [API] Show a custom view in dialog commit 14f30b2489f9ac878b771257a6f86f00dcacd06f Author: Victor Woeltjen Date: Thu Jul 28 16:05:02 2016 -0700 [API] Restrict dialog overrides ...to those domain objects which have some view for the properties region registered. commit 62d90a8114d1c53be129aed4e3b881dde32420fd Author: Victor Woeltjen Date: Thu Jul 28 15:57:15 2016 -0700 [API] Show dialog via mct commit 87682607a589d4c63c2b1998bcdf6bdc0520f15f Author: Victor Woeltjen Date: Thu Jul 28 15:53:07 2016 -0700 [API] Rename dependency in adapter layer commit 7bf265b47877ff11dadbf34a47a29465547a240e Author: Victor Woeltjen Date: Thu Jul 28 15:52:52 2016 -0700 [API] Move mct service up commit 1d31fe8d0279c48d5048ad35f3015adc5c0663de Author: Victor Woeltjen Date: Thu Jul 28 15:48:28 2016 -0700 [API] Override dialogService in actions An ugly hack to allow dialogs to be shown for Save As and Edit Properties, without requiring form generation. This will permit views to be shown instead in certain cases, https://github.com/nasa/openmct/pull/999#issuecomment-236045158 commit bfdbc71e40f6df78369f22279ba2fa2bc302f6c0 Author: Victor Woeltjen Date: Thu Jul 28 15:34:03 2016 -0700 [API] Define a properties region commit 1147f3aa47c505ee7e1e56a7105d074c64408b23 Author: Pete Richards Date: Mon Jul 25 14:38:44 2016 -0700 tutorials: support arbitrary hosting directory (#1097) * Include all bundles in artifact change bundle registry such that all bundles are immediately registered, but must be specifically enabled. A default registry class enables bundles that make sense for demonstration purposes. Added methods to the registry to allow enabling and disabling of bundles without having to load additional files. * support alternate asset/worker paths Change the gulp glob for assets to copy over a more minimal set of files-- only css, fonts, and images. Results in a smaller distributable archive. Update stylesheet loader to use a constant for the assets path. This can be customized at run time via MCT.setAssetPath() to allow MCT to be hosted in various locations. Update worker loader to support loading workers from blobs to support packaging as standalone file. * Load templates via requirejs * [gulp] lazy-require where reasonable Require things right before starting tasks to reduce gulp start up time. * document setAssetPath commit 719f9f45e848e76c3af0925ba7b79fcb52e9e110 Author: Victor Woeltjen Date: Fri Jul 22 14:09:31 2016 -0700 [API] Add documentation for selection state (#1096) commit 95ef70a24ce060bd0018a847f1fec434a911fda7 Author: Victor Woeltjen Date: Fri Jul 22 13:56:45 2016 -0700 [API] Use selection state from toolbar (#1070) * [API] Allow selection * [API] Keep in sync using model * [API] Add selection as EventEmitter * [API] Use selection from ToDo tutorial * [API] Restore selection functionality commit d5aa998b4c40c91059497ef9564ee805097de040 Author: Pete Richards Date: Fri Jul 22 13:53:03 2016 -0700 [API] Draft Composition API (#1068) * [Objects] util for equality checking Add a method for checking object equality, useful for other services. * [Composition] Draft Composition API Draft composition API. Composition collections provide an observable for watching and mutating the composition of an object. Composition providers implement the loading and modification of composition. The default composition provider uses the composition attribute of domain objects, while allowing other providers to implement their own loading and mutation behavior. * add todo about event listener bindings * [Type] Add form property for defining form fields * [tutorial] Add Composition tutorial * provider doesn't have to implement events, load returns array of children * use new composition in old api * correct key name * Override instantiate to provide model ids Override instantiate in public API adapter to prevent making changes to platform code. Instantiate now passes the id of the domain object with the model so that capabilities can convert to a new-style domain object and use that to detect functionality. * Implement mutation capability with decorator Implementation mutation capability override with decorator to adapter code outside of platform. Capability override ensures that models are kept in sync even though they are no longer shared objects. * override composition cleanly Override composition capability without making changes inside platform. * cleanup after temporary collections * remove unused try/catch commit 7890fcae6991adfc69162a91f383ca685e99a8f3 Author: Pete Richards Date: Thu Jul 21 14:39:02 2016 -0700 tutorial consistency . (#1079) * [API] use new-style objects consistently * rewrite todo tutorial in test-api.html * [API] Add API doc, update object API * [Tutorials] Rename tutorials, remove old * Fix Links * updates * initial * hope this works * Object Utils always return new objects instead of mutating existing objects * keep domain object model in-sync when listening Keep the domain object model in sync with the latest version when listening for mutation events. * Remove old-style plugins * Update views to use new API * Tidy Code * Update API Docs * Add Plugin API and Example commit 18843cee48f233f2121580b3b120101459d672d1 Author: Victor Woeltjen Date: Wed Jul 20 13:46:03 2016 -0700 [API] Change approach to applies-to checking (#1072) * [API] Allow selection * [API] Keep in sync using model * [API] Add selection as EventEmitter * [API] Use selection from ToDo tutorial * [API] Add appliesTo-style method * [API] Remove destroy method, simplify show * [View] Return a no-op * [API] Use new applies-to checking * [API] Rename TodoView to TodoRenderer * [API] Rewire views * [API] Wire up so that things work * [API] Begin adding container ...to attempt to give views something to listen to for destroy-like events * [API] Begin using regions... * [API] Begin working through Region stuff * [API] Revise Region API ...for similarity with Marionette, https://github.com/nasa/openmct/pull/1072#issuecomment-230902986 * [API] Begin separating View, ViewDefinition * [API] Finish separating View/ViewDefinition * [API] Update MCTView ...to reflect updates to Region/View/ViewDefinition APIs * [API] Simplify View API ...merging closely-related populate/show methods, and restoring compatibility with todo tutorial * [API] Wire in from todo tutorial plugin * [API] Switch back to region constants * [API] Update method signature, add JSDoc * [API] Update variable name * [API] Remove unnecessary separate regions file * [API] Relocate Region; not external api * [API] Revert changes to api.js ...as these ended up becoming entirely superficial commit 1879c122c78518968518e9b312d8c9193acda351 Author: Andrew Henry Date: Thu Jul 7 14:30:45 2016 -0700 Mutation API (#1074) * [API] Allow selection * [API] Keep in sync using model * [API] Add selection as EventEmitter * [API] Use selection from ToDo tutorial * Object events prototype * Added examples * Transitional API * Modified todo list code to work with new setters * [API] Removed emitting of events on container when property changes value to remove ambiguity. Listeners must be listening to the same path used in the setter to catch changes commit d7ddb96c4ec5846c7f542ad9c4c1fe26e58658bc Author: Pete Richards Date: Thu Jul 7 14:25:23 2016 -0700 [API] UMD Packaging (#1078) * [Bundle] load filter with requirejs * [Build] Use almond, wrap in UMD Use almond for built version of application and wrap in UMD so that it supports requirejs, commonjs, and basic browser loading. * [Main] Can choose where to load app MCT.run allows you to specify a dom element to load application within. If element is not specified, will use body. * [MCT] set class on injected div Set class on injected div so extra markup is not required. * [Build] Re-enable optimize * Add minimal bootstrap example commit bccd018d97d431f101abcddd9ae7de5de1e72f58 Author: Pete Richards Date: Fri Jul 1 10:26:49 2016 -0700 Telemetry Draft commit b55668426d6ff8bb685e06e4e89bf38694ccb525 Merge: 47a543b 5b656fa Author: Andrew Henry Date: Fri Jul 1 10:22:16 2016 -0700 Merge pull request #1062 from nasa/tc-redux [Time Conductor] V2 Public API commit 5b656faa9d143a0bcb2d16ae8165ba9ccbbeedb4 Author: Henry Date: Thu Jun 30 20:50:03 2016 -0700 Added tests commit 8d2c489fa9a67ea44d44ca6e4230d0cb17ff4bb7 Author: Pete Richards Date: Thu Jun 30 16:54:56 2016 -0700 [TimeConductor] Set bounds on timeSystem Change Always set bounds on timeSystem change as not having valid bounds would put views in inconsistent states. commit 4366b0870dd3d1c9b879c0e48d6e264fbb6a4a5c Author: Henry Date: Wed Jun 29 12:51:02 2016 -0700 [Time Conductor] API redesign. Initial commit of V2 public API. Addresses #933 commit 47a543beb7fd2e66d4381aab5f0c74c8e85f8c14 Merge: 06f87c1 c944080 Author: Victor Woeltjen Date: Fri Jul 1 10:17:36 2016 -0700 Merge pull request #1067 from nasa/api-css [API] Remove stylesheet from example commit 06f87c1472fa4db72acb7c979511d8288c85b88f Merge: 14a56ea c9c41cd Author: Victor Woeltjen Date: Fri Jul 1 10:13:29 2016 -0700 Merge pull request #1029 from nasa/api-toolbar-add-only [API Prototype] Add toolbar commit c9c41cdcc8909103f672181f3d0b275798730a7e Merge: 370b515 14a56ea Author: Victor Woeltjen Date: Fri Jul 1 10:10:33 2016 -0700 Merge remote-tracking branch 'origin/api-tutorials' into api-toolbar-add-only Conflicts: src/MCT.js commit 14a56ea17e8a91a9f444f9390ce83dfe4246622c Merge: d51e6bf b2e7db7 Author: Victor Woeltjen Date: Fri Jul 1 10:09:29 2016 -0700 Merge pull request #1028 from nasa/api-view [API Prototype] Support imperative view registration commit b2e7db71cc629ac181f41b0d56cd7f3400e51618 Merge: 96316de d51e6bf Author: Victor Woeltjen Date: Fri Jul 1 10:08:05 2016 -0700 Merge remote-tracking branch 'origin/api-tutorials' into api-view Conflicts: src/MCT.js src/api/api.js commit d51e6bfd923d489433067782458b3ca900dfbad1 Merge: 5de7a96 d475d76 Author: Victor Woeltjen Date: Fri Jul 1 10:03:42 2016 -0700 Merge pull request #1030 from nasa/api-tutorial/objects Api tutorial/objects commit d475d767d5f6d43e5d7e24336add6c64a46bcd68 Author: Pete Richards Date: Fri Jun 17 17:05:05 2016 -0700 add grootprovider commit a63e0533993cf263e84cfda9f92298aafe45f7a8 Author: Pete Richards Date: Fri Jun 17 16:54:32 2016 -0700 [ObjectAPI] Draft new Object API Rought prototype of new object API. commit 370b515c23c36777a5f5b75754f3b290bbec87f8 Author: Victor Woeltjen Date: Fri Jun 17 14:21:37 2016 -0700 [API] Synchronize view to model commit 4a50f325cb7c596c8ae2ff0fa73d737bd8c71b28 Author: Victor Woeltjen Date: Fri Jun 17 14:18:51 2016 -0700 [API] Allow tasks to be added commit dbe6a4efc13b2697ab6eb4538b0915341ff83dc8 Author: Victor Woeltjen Date: Fri Jun 17 14:05:00 2016 -0700 [API] Title dialog commit 13920d88020f76244b98d706b3baf4f0e534800e Author: Victor Woeltjen Date: Fri Jun 17 14:00:45 2016 -0700 [API] Resolve/reject from dialog commit b6a8c514aa6fdd61a43c6c35f012b9757eabba29 Author: Victor Woeltjen Date: Fri Jun 17 13:51:15 2016 -0700 [API] Show dialog from toolbar commit e4a4704baabd5014a218056dea479f6d48de8117 Author: Victor Woeltjen Date: Fri Jun 17 13:41:59 2016 -0700 [API] Listen to add/remove buttons commit be0029e59a031f9821721a9e46bcccd632fcdc35 Author: Victor Woeltjen Date: Fri Jun 17 13:38:54 2016 -0700 [API] Get todo toolbar to look right commit 9cb273ef0ab1a3188f4c9a5f6aa482e00cd28d47 Author: Victor Woeltjen Date: Fri Jun 17 13:30:08 2016 -0700 [API] Get registered toolbar to appear commit eec9b1cf4cc8c4b6591c98909f54acc46ebe0bcf Author: Victor Woeltjen Date: Fri Jun 17 13:10:24 2016 -0700 [API] Support distinct region registration commit 1f96e845427cd0a6973872027e6d5e54a0508a5d Author: Victor Woeltjen Date: Fri Jun 17 12:16:46 2016 -0700 [API] Override template to allow toolbar injection commit c289a273059e0dd6055739a3a493af904bdb4729 Author: Victor Woeltjen Date: Fri Jun 17 11:43:18 2016 -0700 [API] Begin adding toolbar commit c9440807903bab585bc085efcabccffa6fa7ed43 Author: Victor Woeltjen Date: Fri Jun 17 11:27:04 2016 -0700 [API] Remove stylesheet from example No need to provide custom API for this. commit 96316de6e4143578e57eb4b16500adbff8c32f91 Author: Victor Woeltjen Date: Fri Jun 17 11:16:08 2016 -0700 [API] Update view API commit 2240a87ddc4a1788cb71acd7487d5cf2b8a87119 Author: Victor Woeltjen Date: Fri Jun 17 10:53:56 2016 -0700 [API] Move view off of type commit d891affe481efa2d8ef25bb44f32689d3ee218cc Author: Victor Woeltjen Date: Fri Jun 17 10:21:00 2016 -0700 [API] Move view off of type commit 21a618d1ce851e9a3538c5862034d122c8474d03 Merge: 580a4e5 09a833f Author: Victor Woeltjen Date: Fri Jun 17 10:19:44 2016 -0700 Merge branch 'api-type-proto' into api-view commit 5de7a96cccdd2abcd94844c5ce31aec64e3b2bb0 Merge: 9c4e17b 09a833f Author: Victor Woeltjen Date: Fri Jun 17 10:18:42 2016 -0700 Merge pull request #1010 from nasa/api-type-proto [API Prototype] Type registration commit 09a833f524dda0fcc8806a94c36ab53d4bcb7a09 Merge: c4fec1a 9c4e17b Author: Victor Woeltjen Date: Fri Jun 10 13:28:09 2016 -0700 Merge branch 'api-tutorials' into api-type-proto commit 580a4e52b59e316e3c776a3ccfd0e13030ece68f Merge: 4ca2f51 9c4e17b Author: Victor Woeltjen Date: Tue Jun 7 14:01:08 2016 -0700 Merge branch 'api-tutorials' into api-type-driven commit 9c4e17bfab56bf5295b068dfa2dabb067e893b58 Author: Victor Woeltjen Date: Tue Jun 7 13:14:36 2016 -0700 [Tutorials] Add telemetry tutorial commit d3e5d95d6bf1c40ec43da98fd6da2998fb6ec951 Author: Victor Woeltjen Date: Tue Jun 7 13:00:38 2016 -0700 [Tutorials] Add example server commit c70793ac2db0ff3b2aebe819a7457d3e8dd15a6a Author: Victor Woeltjen Date: Tue Jun 7 12:55:29 2016 -0700 [Tutorials] Add remainder of bargraph commit a6ef1d3423e7354eeb7ef0e94291618f6ace8f2d Author: Victor Woeltjen Date: Tue Jun 7 12:49:38 2016 -0700 [Tutorials] Add Bar Graph tutorial commit 4ca2f51d5e58236f928652a82ef3d94ac84bcb80 Author: Victor Woeltjen Date: Fri May 27 17:08:04 2016 -0700 [API] Use subclass style commit 86ac80ddbd103b3b00f4bd4b9e00db698e241117 Author: Victor Woeltjen Date: Fri May 27 16:56:08 2016 -0700 [API] Persist mutations commit 0525ba6b0b6862c724ce3a7cc2760552574de9e4 Author: Victor Woeltjen Date: Fri May 27 16:55:10 2016 -0700 [API] Check/uncheck todos commit a79e958ffc9cafdfbf558d4d5cef055c27452419 Author: Victor Woeltjen Date: Fri May 27 16:46:06 2016 -0700 [API] Show tasks from todo commit 03cb0ccb5743b90c112d4b6685dfddfc86315a42 Author: Victor Woeltjen Date: Fri May 27 16:36:55 2016 -0700 [API] Get View to render commit 7205faa6bb6cb131ba24bf282d582ad1ce266d42 Author: Victor Woeltjen Date: Fri May 27 16:27:47 2016 -0700 [API] Add adapter bundle commit 136f2ae785737ea831351307b955e44bd58bd757 Author: Victor Woeltjen Date: Fri May 27 16:19:20 2016 -0700 [API] Add MCTView directive as an adapter commit a07e2fb8e59ec9a46c9b9462add30bc473cecbc6 Author: Victor Woeltjen Date: Fri May 27 16:08:43 2016 -0700 [API] Implement View commit 55b531bdebe2115117821b93279462aa3e7fe647 Author: Victor Woeltjen Date: Fri May 27 15:49:16 2016 -0700 [API] Sketch in view instantiation commit 7ece5897e821f16d6e493ab9fdc728223cf792ec Author: Victor Woeltjen Date: Fri May 27 15:31:54 2016 -0700 [API] Begin adding View commit a29c7a6eab6d1f2f378b6745dcf8fe130756d4a8 Author: Victor Woeltjen Date: Fri May 27 13:46:30 2016 -0700 [API] Deangularize todo templates commit c4fec1af6aa12f225352813dcbf8a747a10fe23d Author: Victor Woeltjen Date: Fri May 27 13:31:30 2016 -0700 [API] Move type toward a newer API commit a6996df3df68964ee2dff7cf311ea1e7ec662b3f Author: Victor Woeltjen Date: Fri May 27 13:17:16 2016 -0700 [API] Begin moving out type commit 0c660238f2eb57d4bb271d9ded170c1725ec1c29 Author: Victor Woeltjen Date: Fri May 27 11:49:43 2016 -0700 [API] Add MCT class commit b73b824e55560a98ccc1e814284286a3bddeccc9 Author: Victor Woeltjen Date: Fri May 27 11:43:35 2016 -0700 [API] Add EventEmitter dep commit 1954d98628067fc9c626559e4b6389039f73b594 Author: Victor Woeltjen Date: Fri May 27 11:30:53 2016 -0700 [Tutorials] Remove diff markings in TodoController commit 7aa034ce2386d93a280a5770821382aae835f790 Author: Victor Woeltjen Date: Thu May 26 16:05:38 2016 -0700 Add todo tutorial commit 385dc5d298495a5b5bd8b439f3a3238d93579aac Author: Victor Woeltjen Date: Thu May 26 15:36:09 2016 -0700 Begin adding tutorials --- API.md | 285 ++++++++++++++++ LICENSES.md | 129 +++++++ README.md | 19 +- bower.json | 3 + docs/src/index.md | 32 +- example/msl/bundle.js | 2 +- gulpfile.js | 57 ++-- index.html | 6 +- jsdoc.json | 4 +- karma.conf.js | 2 + main.js => openmct.js | 64 ++-- package.json | 2 +- platform/commonUI/browse/bundle.js | 4 +- platform/commonUI/general/bundle.js | 12 +- .../commonUI/general/src/StyleSheetLoader.js | 7 +- .../general/test/StyleSheetLoaderSpec.js | 7 +- platform/execution/src/WorkerService.js | 18 +- platform/features/table/bundle.js | 12 +- .../src/gestures/GestureProvider.js | 2 +- platform/search/bundle.js | 4 +- src/BundleRegistry.js | 28 +- src/BundleRegistrySpec.js | 1 + src/MCT.js | 264 +++++++++++++++ src/adapter/actions/ActionDialogDecorator.js | 66 ++++ src/adapter/bundle.js | 127 +++++++ .../capabilities/APICapabilityDecorator.js | 59 ++++ .../AlternateCompositionCapability.js | 107 ++++++ .../synchronizeMutationCapability.js | 49 +++ src/adapter/directives/MCTView.js | 87 +++++ src/adapter/directives/Region.js | 45 +++ .../policies/AdapterCompositionPolicy.js | 48 +++ .../runs/AlternateCompositionInitializer.js | 36 ++ src/adapter/services/Instantiate.js | 49 +++ .../templates/edit-object-replacement.html | 46 +++ src/api/TimeConductor.js | 202 +++++++++++ src/api/TimeConductorSpec.js | 110 ++++++ src/api/Type.js | 82 +++++ src/api/api.js | 52 +++ src/api/composition/CompositionAPI.js | 136 ++++++++ src/api/composition/CompositionCollection.js | 231 +++++++++++++ .../composition/DefaultCompositionProvider.js | 150 +++++++++ src/api/objects/LegacyObjectAPIInterceptor.js | 128 +++++++ src/api/objects/MutableObject.js | 90 +++++ src/api/objects/ObjectAPI.js | 232 +++++++++++++ src/api/objects/bundle.js | 51 +++ src/api/objects/object-utils.js | 109 ++++++ src/api/objects/objectEventEmitter.js | 32 ++ src/api/telemetry/TelemetryAPI.js | 318 ++++++++++++++++++ src/api/telemetry/bundle.js | 45 +++ src/api/types/TypeRegistry.js | 51 +++ src/api/ui/Dialog.js | 107 ++++++ src/api/ui/GestureAPI.js | 68 ++++ src/api/ui/dialog.html | 21 ++ src/defaultRegistry.js | 135 ++++++++ src/end.frag | 3 + src/selection/ContextManager.js | 78 +++++ src/selection/HoverGesture.js | 58 ++++ src/selection/SelectGesture.js | 60 ++++ src/selection/Selection.js | 69 ++++ src/start.frag | 39 +++ src/ui/ViewRegistry.js | 145 ++++++++ test-main.js | 10 +- 62 files changed, 4382 insertions(+), 113 deletions(-) create mode 100644 API.md rename main.js => openmct.js (60%) create mode 100644 src/MCT.js create mode 100644 src/adapter/actions/ActionDialogDecorator.js create mode 100644 src/adapter/bundle.js create mode 100644 src/adapter/capabilities/APICapabilityDecorator.js create mode 100644 src/adapter/capabilities/AlternateCompositionCapability.js create mode 100644 src/adapter/capabilities/synchronizeMutationCapability.js create mode 100644 src/adapter/directives/MCTView.js create mode 100644 src/adapter/directives/Region.js create mode 100644 src/adapter/policies/AdapterCompositionPolicy.js create mode 100644 src/adapter/runs/AlternateCompositionInitializer.js create mode 100644 src/adapter/services/Instantiate.js create mode 100644 src/adapter/templates/edit-object-replacement.html create mode 100644 src/api/TimeConductor.js create mode 100644 src/api/TimeConductorSpec.js create mode 100644 src/api/Type.js create mode 100644 src/api/api.js create mode 100644 src/api/composition/CompositionAPI.js create mode 100644 src/api/composition/CompositionCollection.js create mode 100644 src/api/composition/DefaultCompositionProvider.js create mode 100644 src/api/objects/LegacyObjectAPIInterceptor.js create mode 100644 src/api/objects/MutableObject.js create mode 100644 src/api/objects/ObjectAPI.js create mode 100644 src/api/objects/bundle.js create mode 100644 src/api/objects/object-utils.js create mode 100644 src/api/objects/objectEventEmitter.js create mode 100644 src/api/telemetry/TelemetryAPI.js create mode 100644 src/api/telemetry/bundle.js create mode 100644 src/api/types/TypeRegistry.js create mode 100644 src/api/ui/Dialog.js create mode 100644 src/api/ui/GestureAPI.js create mode 100644 src/api/ui/dialog.html create mode 100644 src/defaultRegistry.js create mode 100644 src/end.frag create mode 100644 src/selection/ContextManager.js create mode 100644 src/selection/HoverGesture.js create mode 100644 src/selection/SelectGesture.js create mode 100644 src/selection/Selection.js create mode 100644 src/start.frag create mode 100644 src/ui/ViewRegistry.js diff --git a/API.md b/API.md new file mode 100644 index 00000000000..24083e1cf69 --- /dev/null +++ b/API.md @@ -0,0 +1,285 @@ +# Open MCT API + +The Open MCT framework public api can be utilized by building the application +(`gulp install`) and then copying the file from `dist/main.js` to your +directory of choice. + +Open MCT supports AMD, CommonJS, and loading via a script tag; it's easy to use +in your project. The [`openmct`]{@link module:openmct} module is exported +via AMD and CommonJS, and is also exposed as `openmct` in the global scope +if loaded via a script tag. + +## Overview + +Open MCT's goal is to allow you to browse, create, edit, and visualize all of +the domain knowledge you need on a daily basis. + +To do this, the main building block provided by Open MCT is the _domain object_. +The temperature sensor on the starboard solar panel, +an overlay plot comparing the results of all temperature sensor, +the command dictionary for a spacecraft, +the individual commands in that dictionary, your "my documents" folder: +All of these things are domain objects. + +Domain objects have Types, so a specific instrument temperature sensor is a +"Telemetry Point," and turning on a drill for a certain duration of time is +an "Activity". Types allow you to form an ontology of knowledge and provide +an abstraction for grouping, visualizing, and interpreting data. + +And then we have Views. Views allow you to visualize domain objects. Views can +apply to specific domain objects; they may also apply to certain types of +domain objects, or they may apply to everything. Views are simply a method +of visualizing domain objects. + +Regions allow you to specify what views are displayed for specific types of +domain objects in response to different user actions. For instance, you may +want to display a different view while editing, or you may want to update the +toolbar display when objects are selected. Regions allow you to map views to +specific user actions. + +Domain objects can be mutated and persisted, developers can create custom +actions and apply them to domain objects, and many more things can be done. +For more information, read on! + +## Running Open MCT + +Once the [`openmct`](@link module:openmct) module has been loaded, you can +simply invoke [`start`]{@link module:openmct.MCT#start} to run Open MCT: + + +``` +openmct.start(); +``` + +Generally, however, you will want to configure Open MCT by adding plugins +before starting it. It is important to install plugins and configure Open MCT +_before_ calling [`start`]{@link module:openmct.MCT#start}; Open MCT is not +designed to be reconfigured once started. + +## Configuring Open MCT + +The [`openmct`]{@link module:openmct} module (more specifically, the +[`MCT`]{@link module:openmct.MCT} class, of which `openmct` is an instance) +exposes a variety of methods to allow the application to be configured, +extended, and customized before running. + +Short examples follow; see the linked documentation for further details. + +### Adding Domain Object Types + +Custom types may be registered via +[`openmct.types`]{@link module:openmct.MCT#types}: + +``` +openmct.types.addType('my-type', new openmct.Type({ + label: "My Type", + description: "This is a type that I added!" +}); +``` + +### Adding Views + +Custom views may be registered based on the region in the application +where they should appear: + +* [`openmct.mainViews`]{@link module:openmct.MCT#mainViews} is a registry + of views of domain objects which should appear in the main viewing area. +* [`openmct.inspectors`]{@link module:openmct.MCT#inspectors} is a registry + of views of domain objects and/or active selections, which should appear in + the Inspector. +* [`openmct.toolbars`]{@link module:openmct.MCT#toolbars} is a registry + of views of domain objects and/or active selections, which should appear in + the toolbar area while editing. +* [`openmct.indicators`]{@link module:openmct.MCT#inspectors} is a registry + of views which should appear in the status area of the application. + +Example: + +``` +openmct.mainViews.addProvider({ + canView: function (domainObject) { + return domainObject.type === 'my-type'; + }, + view: function (domainObject) { + return new MyView(domainObject); + } +}); +``` + +### Adding a Root-level Object + +In many cases, you'd like a certain object (or a certain hierarchy of +objects) to be accessible from the top level of the application (the +tree on the left-hand side of Open MCT.) It is typical to expose a telemetry +dictionary as a hierarchy of telemetry-providing domain objects in this +fashion. + +To do so, use the [`addRoot`]{@link module:openmct.ObjectAPI#addRoot} method +of the [object API]{@link module:openmct.ObjectAPI}: + +``` +openmct.objects.addRoot({ + identifier: { key: "my-key", namespace: "my-namespace" } + name: "My Root-level Object", + type: "my-type" +}); +``` + +You can also remove this root-level object via its identifier: + +``` +openmct.objects.removeRoot({ key: "my-key", namespace: "my-namespace" }); +``` + +### Adding Composition Providers + +The "composition" of a domain object is the list of objects it contains, +as shown (for example) in the tree for browsing. Open MCT provides a +default solution for composition, but there may be cases where you want +to provide the composition of a certain object (or type of object) dynamically. +For instance, you may want to populate a hierarchy under a custom root-level +object based on the contents of a telemetry dictionary. +To do this, you can add a new CompositionProvider: + +``` +openmct.composition.addProvider({ + appliesTo: function (domainObject) { + return domainObject.type === 'my-type'; + }, + load: function (domainObject) { + return Promise.resolve(myDomainObjects); + } +}); +``` + +### Adding Telemetry Providers + +When connecting to a new telemetry source, you will want to register a new +[telemetry provider]{@link module:openmct.TelemetryAPI~TelemetryProvider} +with the [telemetry API]{@link module:openmct.TelemetryAPI#addProvider}: + +``` +openmct.telemetry.addProvider({ + canProvideTelemetry: function (domainObject) { + return domainObject.type === 'my-type'; + }, + properties: function (domainObject) { + return [ + { key: 'value', name: "Temperature", units: "degC" }, + { key: 'time', name: "UTC" } + ]; + }, + request: function (domainObject, options) { + var telemetryId = domainObject.myTelemetryId; + return myAdapter.request(telemetryId, options.start, options.end); + }, + subscribe: function (domainObject, callback) { + var telemetryId = domainObject.myTelemetryId; + myAdapter.subscribe(telemetryId, callback); + return myAdapter.unsubscribe.bind(myAdapter, telemetryId, callback); + } +}); +``` + +The implementations for `request` and `subscribe` can vary depending on the +nature of the endpoint which will provide telemetry. In the example above, +it is assumed that `myAdapter` contains the specific implementations +(HTTP requests, WebSocket connections, etc.) associated with some telemetry +source. + +## Using Open MCT + +When implementing new features, it is useful and sometimes necessary to +utilize functionality exposed by Open MCT. + +### Retrieving Composition + +To limit which objects are loaded at any given time, the composition of +a domain object must be requested asynchronously: + +``` +openmct.composition(myObject).load().then(function (childObjects) { + childObjects.forEach(doSomething); +}); +``` + +### Support Common Gestures + +Custom views may also want to support common gestures using the +[gesture API]{@link module:openmct.GestureAPI}. For instance, to make +a view (or part of a view) selectable: + +``` +openmct.gestures.selectable(myHtmlElement, myDomainObject); +``` + +### Working with Domain Objects + +The [object API]{@link module:openmct.ObjectAPI} provides useful methods +for working with domain objects. + +To make changes to a domain object, use the +[`mutate`]{@link module:openmct.ObjectAPI#mutate} method: + +``` +openmct.objects.mutate(myDomainObject, "name", "New name!"); +``` + +Making modifications in this fashion allows other usages of the domain +object to remain up to date using the +[`observe`]{@link module:openmct.ObjectAPI#observe} method: + +``` +openmct.objects.observe(myDomainObject, "name", function (newName) { + myLabel.textContent = newName; +}); +``` + +### Using Telemetry + +Very often in Open MCT, you wish to work with telemetry data (for instance, +to display it in a custom visualization.) + + +### Synchronizing with the Time Conductor + +Views which wish to remain synchronized with the state of Open MCT's +time conductor should utilize +[`openmct.conductor`]{@link module:openmct.TimeConductor}: + +``` +openmct.conductor.on('bounds', function (newBounds) { + requestTelemetry(newBounds.start, newBounds.end).then(displayTelemetry); +}); +``` + +## Plugins + +While you can register new features with Open MCT directly, it is generally +more useful to package these as a plugin. A plugin is a function that takes +[`openmct`]{@link module:openmct} as an argument, and performs configuration +upon `openmct` when invoked. + +### Installing Plugins + +To install plugins, use the [`install`]{@link module:openmct.MCT#install} +method: + +``` +openmct.install(myPlugin); +``` + +The plugin will be invoked to configure Open MCT before it is started. + +### Writing Plugins + +Plugins configure Open MCT, and should utilize the +[`openmct`]{@link module:openmct} module to do so, as summarized above in +"Configuring Open MCT" and "Using Open MCT" above. + +### Distributing Plugins + +Hosting or downloading plugins is outside of the scope of this documentation. +We recommend distributing plugins as UMD modules which export a single +function. + diff --git a/LICENSES.md b/LICENSES.md index 483a6e40729..94a7fecf13a 100644 --- a/LICENSES.md +++ b/LICENSES.md @@ -560,3 +560,132 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--- + +### Almond + +* Link: https://github.com/requirejs/almond + +* Version: 0.3.3 + +* Author: jQuery Foundation + +* Description: Lightweight RequireJS replacement for builds + +#### License + +Copyright jQuery Foundation and other contributors, https://jquery.org/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/requirejs/almond + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +Copyright and related rights for sample code are waived via CC0. Sample +code is defined as all source code displayed within the prose of the +documentation. + +CC0: http://creativecommons.org/publicdomain/zero/1.0/ + +==== + +Files located in the node_modules directory, and certain utilities used +to build or test the software in the test and dist directories, are +externally maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. + + +### Lodash + +* Link: https://lodash.com + +* Version: 3.10.1 + +* Author: Dojo Foundation + +* Description: Utility functions + +#### License + +Copyright 2012-2015 The Dojo Foundation +Based on Underscore.js, copyright 2009-2015 Jeremy Ashkenas, +DocumentCloud and Investigative Reporters & Editors + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +### EventEmitter3 + +* Link: https://github.com/primus/eventemitter3 + +* Version: 1.2.0 + +* Author: Arnout Kazemier + +* Description: Event-driven programming support + +#### License + +The MIT License (MIT) + +Copyright (c) 2014 Arnout Kazemier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index b9486dad3c8..8761314a0b4 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,24 @@ Try Open MCT now with our [live demo](https://openmct-demo.herokuapp.com/). ![Demo](https://nasa.github.io/openmct/static/res/images/Open-MCT.Browse.Layout.Mars-Weather-1.jpg) ## New API -A new API is currently under development that will deprecate a lot of the documentation currently in the docs directory, however Open MCT will remain compatible with the currently documented API. An updated set of tutorials is being developed with the new API, and progress on this task can be followed in the [associated pull request](https://github.com/nasa/openmct/pull/999). Any code in this branch should be considered experimental, and we welcome any feedback. -Differences between the two APIs include a move away from a declarative system of JSON configuration files towards an imperative system based on function calls. Developers will be able to extend and build on Open MCT by making direct function calls to a public API. Open MCT is also being refactored to minimize the dependencies that using Open MCT imposes on developers, such as the current requirement to use Angular JS. +A simpler, [easier-to-use API](https://nasa.github.io/openmct/docs/api/) +has been added to Open MCT. Changes in this +API include a move away from a declarative system of JSON configuration files +towards an imperative system based on function calls. Developers will be able +to extend and build on Open MCT by making direct function calls to a public +API. Open MCT is also being refactored to minimize the dependencies that using +Open MCT imposes on developers, such as the current requirement to use +AngularJS. + +This new API has not yet been heavily used and is likely to contain defects. +You can help by trying it out, and reporting any issues you encounter +using our GitHub issue tracker. Such issues may include bugs, suggestions, +missing documentation, or even just requests for help if you're having +trouble. + +We want Open MCT to be as easy to use, install, run, and develop for as +possible, and your feedback will help us get there! ## Building and Running Open MCT Locally diff --git a/bower.json b/bower.json index 419871fe575..ed3dbaf899d 100644 --- a/bower.json +++ b/bower.json @@ -19,6 +19,9 @@ "comma-separated-values": "^3.6.4", "FileSaver.js": "^0.0.2", "zepto": "^1.1.6", + "eventemitter3": "^1.2.0", + "lodash": "3.10.1", + "almond": "~0.3.2", "html2canvas": "^0.4.1" } } diff --git a/docs/src/index.md b/docs/src/index.md index 3b4d7671060..9c1a24aa023 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -9,26 +9,30 @@ Open MCT provides functionality out of the box, but it's also a platform for building rich mission operations applications based on modern web technology. - The platform is configured declaratively, and defines conventions for - building on the provided capabilities by creating modular 'bundles' that - extend the platform at a variety of extension points. The details of how to + The platform is configured by plugins which extend the platform at a variety + of extension points. The details of how to extend the platform are provided in the following documentation. ## Sections - - * The [Architecture Overview](architecture/) describes the concepts used - throughout Open MCT, and gives a high level overview of the platform's design. - - * The [Developer's Guide](guide/) goes into more detail about how to use the - platform and the functionality that it provides. - - * The [Tutorials](tutorials/) give examples of extending the platform to add - functionality, - and integrate with data sources. * The [API](api/) document is generated from inline documentation using [JSDoc](http://usejsdoc.org/), and describes the JavaScript objects and functions that make up the software platform. - * Finally, the [Development Process](process/) document describes the + * The [Development Process](process/) document describes the Open MCT software development cycle. + +## Legacy Documentation + +As we transition to a new API, the following documentation for the old API +(which is supported during the transtion) may be useful as well: + + * The [Architecture Overview](architecture/) describes the concepts used + throughout Open MCT, and gives a high level overview of the platform's design. + + * The [Developer's Guide](guide/) goes into more detail about how to use the + platform and the functionality that it provides. + + * The [Tutorials](tutorials/) give examples of extending the platform to add + functionality, + and integrate with data sources. diff --git a/example/msl/bundle.js b/example/msl/bundle.js index aefc3ffc168..fbd0843935f 100644 --- a/example/msl/bundle.js +++ b/example/msl/bundle.js @@ -36,7 +36,7 @@ define([ legacyRegistry ) { "use strict"; - legacyRegistry.register("example/notifications", { + legacyRegistry.register("example/msl-adapter", { "name" : "Mars Science Laboratory Data Adapter", "extensions" : { "types": [ diff --git a/gulpfile.js b/gulpfile.js index ff994c6a2c0..e9a5d0fcf00 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -21,41 +21,34 @@ *****************************************************************************/ /*global require,__dirname*/ + var gulp = require('gulp'), - requirejsOptimize = require('gulp-requirejs-optimize'), sourcemaps = require('gulp-sourcemaps'), - rename = require('gulp-rename'), - sass = require('gulp-sass'), - bourbon = require('node-bourbon'), - jshint = require('gulp-jshint'), - jscs = require('gulp-jscs'), - replace = require('gulp-replace-task'), - karma = require('karma'), path = require('path'), fs = require('fs'), git = require('git-rev-sync'), moment = require('moment'), - merge = require('merge-stream'), project = require('./package.json'), _ = require('lodash'), paths = { - main: 'main.js', + main: 'openmct.js', dist: 'dist', - assets: 'dist/assets', reports: 'dist/reports', scss: ['./platform/**/*.scss', './example/**/*.scss'], - scripts: [ 'main.js', 'platform/**/*.js', 'src/**/*.js' ], + assets: [ + './{example,platform}/**/*.{css,css.map,png,svg,ico,woff,eot,ttf}' + ], + scripts: [ 'openmct.js', 'platform/**/*.js', 'src/**/*.js' ], specs: [ 'platform/**/*Spec.js', 'src/**/*Spec.js' ], - static: [ - 'index.html', - 'platform/**/*', - 'example/**/*', - 'bower_components/**/*' - ] }, options = { requirejsOptimize: { - name: paths.main.replace(/\.js$/, ''), + name: 'bower_components/almond/almond.js', + include: paths.main.replace('.js', ''), + wrap: { + startFile: "src/start.frag", + endFile: "src/end.frag" + }, mainConfigFile: paths.main, wrapShim: true }, @@ -64,7 +57,6 @@ var gulp = require('gulp'), singleRun: true }, sass: { - includePaths: bourbon.includePaths, sourceComments: true }, replace: { @@ -78,6 +70,8 @@ var gulp = require('gulp'), }; gulp.task('scripts', function () { + var requirejsOptimize = require('gulp-requirejs-optimize'); + var replace = require('gulp-replace-task'); return gulp.src(paths.main) .pipe(sourcemaps.init()) .pipe(requirejsOptimize(options.requirejsOptimize)) @@ -87,10 +81,16 @@ gulp.task('scripts', function () { }); gulp.task('test', function (done) { + var karma = require('karma'); new karma.Server(options.karma, done).start(); }); gulp.task('stylesheets', function () { + var sass = require('gulp-sass'); + var rename = require('gulp-rename'); + var bourbon = require('node-bourbon'); + options.sass.includePaths = bourbon.includePaths; + return gulp.src(paths.scss, {base: '.'}) .pipe(sourcemaps.init()) .pipe(sass(options.sass).on('error', sass.logError)) @@ -104,6 +104,9 @@ gulp.task('stylesheets', function () { }); gulp.task('lint', function () { + var jshint = require('gulp-jshint'); + var merge = require('merge-stream'); + var nonspecs = paths.specs.map(function (glob) { return "!" + glob; }), @@ -122,6 +125,8 @@ gulp.task('lint', function () { }); gulp.task('checkstyle', function () { + var jscs = require('gulp-jscs'); + return gulp.src(paths.scripts) .pipe(jscs()) .pipe(jscs.reporter()) @@ -129,18 +134,20 @@ gulp.task('checkstyle', function () { }); gulp.task('fixstyle', function () { + var jscs = require('gulp-jscs'); + return gulp.src(paths.scripts, { base: '.' }) .pipe(jscs({ fix: true })) .pipe(gulp.dest('.')); }); -gulp.task('static', ['stylesheets'], function () { - return gulp.src(paths.static, { base: '.' }) +gulp.task('assets', ['stylesheets'], function () { + return gulp.src(paths.assets) .pipe(gulp.dest(paths.dist)); }); gulp.task('watch', function () { - gulp.watch(paths.scss, ['stylesheets']); + return gulp.watch(paths.scss, ['stylesheets', 'assets']); }); gulp.task('serve', function () { @@ -148,9 +155,9 @@ gulp.task('serve', function () { var app = require('./app.js'); }); -gulp.task('develop', ['serve', 'stylesheets', 'watch']); +gulp.task('develop', ['serve', 'install', 'watch']); -gulp.task('install', [ 'static', 'scripts' ]); +gulp.task('install', [ 'assets', 'scripts' ]); gulp.task('verify', [ 'lint', 'test', 'checkstyle' ]); diff --git a/index.html b/index.html index fcd37cfb0b3..3c80e1d8e5c 100644 --- a/index.html +++ b/index.html @@ -28,12 +28,12 @@ @@ -47,7 +47,5 @@
- -
diff --git a/jsdoc.json b/jsdoc.json index f913b650d14..ac485a5efaf 100644 --- a/jsdoc.json +++ b/jsdoc.json @@ -1,9 +1,9 @@ { "source": { "include": [ - "platform/" + "src/" ], - "includePattern": "platform/.+\\.js$", + "includePattern": "src/.+\\.js$", "excludePattern": ".+\\Spec\\.js$|lib/.+" }, "plugins": [ diff --git a/karma.conf.js b/karma.conf.js index e682ec6d86b..535c27b2a54 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -37,9 +37,11 @@ module.exports = function(config) { {pattern: 'bower_components/**/*.js', included: false}, {pattern: 'src/**/*.js', included: false}, {pattern: 'example/**/*.js', included: false}, + {pattern: 'example/**/*.json', included: false}, {pattern: 'platform/**/*.js', included: false}, {pattern: 'warp/**/*.js', included: false}, {pattern: 'platform/**/*.html', included: false}, + {pattern: 'src/**/*.html', included: false}, 'test-main.js' ], diff --git a/main.js b/openmct.js similarity index 60% rename from main.js rename to openmct.js index 6968d8ff26f..864bf243cd4 100644 --- a/main.js +++ b/openmct.js @@ -27,6 +27,7 @@ requirejs.config({ "angular": "bower_components/angular/angular.min", "angular-route": "bower_components/angular-route/angular-route.min", "csv": "bower_components/comma-separated-values/csv.min", + "EventEmitter": "bower_components/eventemitter3/index", "es6-promise": "bower_components/es6-promise/es6-promise.min", "html2canvas": "bower_components/html2canvas/build/html2canvas.min", "moment": "bower_components/moment/moment", @@ -35,7 +36,8 @@ requirejs.config({ "screenfull": "bower_components/screenfull/dist/screenfull.min", "text": "bower_components/text/text", "uuid": "bower_components/node-uuid/uuid", - "zepto": "bower_components/zepto/zepto.min" + "zepto": "bower_components/zepto/zepto.min", + "lodash": "bower_components/lodash/lodash" }, "shim": { "angular": { @@ -44,6 +46,9 @@ requirejs.config({ "angular-route": { "deps": ["angular"] }, + "EventEmitter": { + "exports": "EventEmitter" + }, "html2canvas": { "exports": "html2canvas" }, @@ -55,54 +60,25 @@ requirejs.config({ }, "zepto": { "exports": "Zepto" + }, + "lodash": { + "exports": "lodash" } } }); define([ './platform/framework/src/Main', - 'legacyRegistry', + './src/defaultRegistry', + './src/MCT' +], function (Main, defaultRegistry, MCT) { + var openmct = new MCT(); - './platform/framework/bundle', - './platform/core/bundle', - './platform/representation/bundle', - './platform/commonUI/about/bundle', - './platform/commonUI/browse/bundle', - './platform/commonUI/edit/bundle', - './platform/commonUI/dialog/bundle', - './platform/commonUI/formats/bundle', - './platform/commonUI/general/bundle', - './platform/commonUI/inspect/bundle', - './platform/commonUI/mobile/bundle', - './platform/commonUI/themes/espresso/bundle', - './platform/commonUI/notification/bundle', - './platform/containment/bundle', - './platform/execution/bundle', - './platform/exporters/bundle', - './platform/telemetry/bundle', - './platform/features/clock/bundle', - './platform/features/fixed/bundle', - './platform/features/imagery/bundle', - './platform/features/layout/bundle', - './platform/features/pages/bundle', - './platform/features/plot/bundle', - './platform/features/timeline/bundle', - './platform/features/table/bundle', - './platform/forms/bundle', - './platform/identity/bundle', - './platform/persistence/aggregator/bundle', - './platform/persistence/local/bundle', - './platform/persistence/queue/bundle', - './platform/policy/bundle', - './platform/entanglement/bundle', - './platform/search/bundle', - './platform/status/bundle', - './platform/commonUI/regions/bundle' -], function (Main, legacyRegistry) { - return { - legacyRegistry: legacyRegistry, - run: function () { - return new Main().run(legacyRegistry); - } - }; + openmct.legacyRegistry = defaultRegistry; + + openmct.on('start', function () { + return new Main().run(defaultRegistry); + }); + + return openmct; }); diff --git a/package.json b/package.json index e47a64e50e6..6e9a7c9801f 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "test": "karma start --single-run", "jshint": "jshint platform example", "watch": "karma start", - "jsdoc": "jsdoc -c jsdoc.json -r -d target/docs/api", + "jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api", "otherdoc": "node docs/gendocs.js --in docs/src --out target/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'", "docs": "npm run jsdoc ; npm run otherdoc", "prepublish": "node ./node_modules/bower/bin/bower install && node ./node_modules/gulp/bin/gulp.js install" diff --git a/platform/commonUI/browse/bundle.js b/platform/commonUI/browse/bundle.js index 5ff13d789e4..b0aa7e42776 100644 --- a/platform/commonUI/browse/bundle.js +++ b/platform/commonUI/browse/bundle.js @@ -41,6 +41,7 @@ define([ "text!./res/templates/items/items.html", "text!./res/templates/browse/object-properties.html", "text!./res/templates/browse/inspector-region.html", + "text!./res/templates/view-object.html", 'legacyRegistry' ], function ( BrowseController, @@ -63,6 +64,7 @@ define([ itemsTemplate, objectPropertiesTemplate, inspectorRegionTemplate, + viewObjectTemplate, legacyRegistry ) { @@ -142,7 +144,7 @@ define([ "representations": [ { "key": "view-object", - "templateUrl": "templates/view-object.html" + "template": viewObjectTemplate }, { "key": "browse-object", diff --git a/platform/commonUI/general/bundle.js b/platform/commonUI/general/bundle.js index 15ea3076a62..66d9b3de951 100644 --- a/platform/commonUI/general/bundle.js +++ b/platform/commonUI/general/bundle.js @@ -48,6 +48,7 @@ define([ "./src/directives/MCTSplitPane", "./src/directives/MCTSplitter", "./src/directives/MCTTree", + "./src/filters/ReverseFilter", "text!./res/templates/bottombar.html", "text!./res/templates/controls/action-button.html", "text!./res/templates/controls/input-filter.html", @@ -96,6 +97,7 @@ define([ MCTSplitPane, MCTSplitter, MCTTree, + ReverseFilter, bottombarTemplate, actionButtonTemplate, inputFilterTemplate, @@ -146,7 +148,8 @@ define([ "depends": [ "stylesheets[]", "$document", - "THEME" + "THEME", + "ASSETS_PATH" ] }, { @@ -158,7 +161,7 @@ define([ ], "filters": [ { - "implementation": "filters/ReverseFilter.js", + "implementation": ReverseFilter, "key": "reverse" } ], @@ -405,6 +408,11 @@ define([ "key": "THEME", "value": "unspecified", "priority": "fallback" + }, + { + "key": "ASSETS_PATH", + "value": ".", + "priority": "fallback" } ], "containers": [ diff --git a/platform/commonUI/general/src/StyleSheetLoader.js b/platform/commonUI/general/src/StyleSheetLoader.js index 13c5075a8bc..7f5cabe7c1e 100644 --- a/platform/commonUI/general/src/StyleSheetLoader.js +++ b/platform/commonUI/general/src/StyleSheetLoader.js @@ -37,8 +37,10 @@ define( * @param {object[]} stylesheets stylesheet extension definitions * @param $document Angular's jqLite-wrapped document element * @param {string} activeTheme the theme in use + * @param {string} [assetPath] the directory relative to which + * stylesheets will be found */ - function StyleSheetLoader(stylesheets, $document, activeTheme) { + function StyleSheetLoader(stylesheets, $document, activeTheme, assetPath) { var head = $document.find('head'), document = $document[0]; @@ -47,6 +49,7 @@ define( // Create a link element, and construct full path var link = document.createElement('link'), path = [ + assetPath, stylesheet.bundle.path, stylesheet.bundle.resources, stylesheet.stylesheetUrl @@ -68,6 +71,8 @@ define( stylesheet.theme === activeTheme; } + assetPath = assetPath || "."; + // Add all stylesheets from extensions stylesheets.filter(matchesTheme).forEach(addStyleSheet); } diff --git a/platform/commonUI/general/test/StyleSheetLoaderSpec.js b/platform/commonUI/general/test/StyleSheetLoaderSpec.js index 272ad61bf97..4dfb20e82d5 100644 --- a/platform/commonUI/general/test/StyleSheetLoaderSpec.js +++ b/platform/commonUI/general/test/StyleSheetLoaderSpec.js @@ -69,7 +69,7 @@ define( it("adjusts link locations", function () { expect(mockElement.setAttribute) - .toHaveBeenCalledWith('href', "a/b/c/d.css"); + .toHaveBeenCalledWith('href', "./a/b/c/d.css"); }); describe("for themed stylesheets", function () { @@ -95,12 +95,13 @@ define( it("includes matching themes", function () { expect(mockElement.setAttribute) - .toHaveBeenCalledWith('href', "a/b/c/themed.css"); + .toHaveBeenCalledWith('href', "./a/b/c/themed.css"); }); it("excludes mismatching themes", function () { expect(mockElement.setAttribute) - .not.toHaveBeenCalledWith('href', "a/b/c/bad-theme.css"); + .not + .toHaveBeenCalledWith('href', "./a/b/c/bad-theme.css"); }); }); diff --git a/platform/execution/src/WorkerService.js b/platform/execution/src/WorkerService.js index d809a568f2d..87efd6f7da5 100644 --- a/platform/execution/src/WorkerService.js +++ b/platform/execution/src/WorkerService.js @@ -42,11 +42,19 @@ define( function addWorker(worker) { var key = worker.key; if (!workerUrls[key]) { - workerUrls[key] = [ - worker.bundle.path, - worker.bundle.sources, - worker.scriptUrl - ].join("/"); + if (worker.scriptUrl) { + workerUrls[key] = [ + worker.bundle.path, + worker.bundle.sources, + worker.scriptUrl + ].join("/"); + } else if (worker.scriptText) { + var blob = new Blob( + [worker.scriptText], + {type: 'application/javascript'} + ); + workerUrls[key] = URL.createObjectURL(blob); + } sharedWorkers[key] = worker.shared; } } diff --git a/platform/features/table/bundle.js b/platform/features/table/bundle.js index 4c7b77b47e2..6f7d285a503 100644 --- a/platform/features/table/bundle.js +++ b/platform/features/table/bundle.js @@ -27,6 +27,9 @@ define([ "./src/controllers/TableOptionsController", '../../commonUI/regions/src/Region', '../../commonUI/browse/src/InspectorRegion', + "text!./res/templates/table-options-edit.html", + "text!./res/templates/rt-table.html", + "text!./res/templates/historical-table.html", "legacyRegistry" ], function ( MCTTable, @@ -35,6 +38,9 @@ define([ TableOptionsController, Region, InspectorRegion, + tableOptionsEditTemplate, + rtTableTemplate, + historicalTableTemplate, legacyRegistry ) { /** @@ -127,8 +133,8 @@ define([ { "name": "Historical Table", "key": "table", + "template": historicalTableTemplate, "cssclass": "icon-tabular", - "templateUrl": "templates/historical-table.html", "needs": [ "telemetry" ], @@ -139,7 +145,7 @@ define([ "name": "Real-time Table", "key": "rt-table", "cssclass": "icon-tabular-realtime", - "templateUrl": "templates/rt-table.html", + "template": rtTableTemplate, "needs": [ "telemetry" ], @@ -157,7 +163,7 @@ define([ "representations": [ { "key": "table-options-edit", - "templateUrl": "templates/table-options-edit.html" + "template": tableOptionsEditTemplate } ], "stylesheets": [ diff --git a/platform/representation/src/gestures/GestureProvider.js b/platform/representation/src/gestures/GestureProvider.js index 5e170f68ac1..051dafd5a7b 100644 --- a/platform/representation/src/gestures/GestureProvider.js +++ b/platform/representation/src/gestures/GestureProvider.js @@ -72,7 +72,7 @@ define( // Assemble all gestures into a map, for easy look up gestures.forEach(function (gesture) { - gestureMap[gesture.key] = gesture; + gestureMap[gesture.key] = gestureMap[gesture.key] || gesture; }); this.gestureMap = gestureMap; diff --git a/platform/search/bundle.js b/platform/search/bundle.js index 61a266d0497..7f8a64ab048 100644 --- a/platform/search/bundle.js +++ b/platform/search/bundle.js @@ -28,6 +28,7 @@ define([ "text!./res/templates/search-item.html", "text!./res/templates/search.html", "text!./res/templates/search-menu.html", + "text!./src/services/GenericSearchWorker.js", 'legacyRegistry' ], function ( SearchController, @@ -37,6 +38,7 @@ define([ searchItemTemplate, searchTemplate, searchMenuTemplate, + searchWorkerText, legacyRegistry ) { @@ -114,7 +116,7 @@ define([ "workers": [ { "key": "genericSearchWorker", - "scriptUrl": "services/GenericSearchWorker.js" + "scriptText": searchWorkerText } ] } diff --git a/src/BundleRegistry.js b/src/BundleRegistry.js index ccc1f04c602..9089d0ee9a2 100644 --- a/src/BundleRegistry.js +++ b/src/BundleRegistry.js @@ -24,10 +24,28 @@ define(function () { function BundleRegistry() { this.bundles = {}; + this.knownBundles = {}; } BundleRegistry.prototype.register = function (path, definition) { - this.bundles[path] = definition; + if (this.knownBundles.hasOwnProperty(path)) { + throw new Error('Cannot register bundle with duplicate path', path); + } + this.knownBundles[path] = definition; + }; + + BundleRegistry.prototype.enable = function (path) { + if (!this.knownBundles[path]) { + throw new Error('Unknown bundle ' + path); + } + this.bundles[path] = this.knownBundles[path]; + }; + + BundleRegistry.prototype.disable = function (path) { + if (!this.bundles[path]) { + throw new Error('Tried to disable inactive bundle ' + path); + } + delete this.bundles[path]; }; BundleRegistry.prototype.contains = function (path) { @@ -42,8 +60,14 @@ define(function () { return Object.keys(this.bundles); }; - BundleRegistry.prototype.remove = function (path) { + BundleRegistry.prototype.remove = BundleRegistry.prototype.disable; + + BundleRegistry.prototype.delete = function (path) { + if (!this.knownBundles[path]) { + throw new Error('Cannot remove Unknown Bundle ' + path); + } delete this.bundles[path]; + delete this.knownBundles[path]; }; return BundleRegistry; diff --git a/src/BundleRegistrySpec.js b/src/BundleRegistrySpec.js index 2dfa238d88c..b301db3d9c5 100644 --- a/src/BundleRegistrySpec.js +++ b/src/BundleRegistrySpec.js @@ -51,6 +51,7 @@ define(['./BundleRegistry'], function (BundleRegistry) { beforeEach(function () { testBundleDef = { someKey: "some value" }; bundleRegistry.register(testPath, testBundleDef); + bundleRegistry.enable(testPath); }); it("lists registered bundles", function () { diff --git a/src/MCT.js b/src/MCT.js new file mode 100644 index 00000000000..193e3fb29ac --- /dev/null +++ b/src/MCT.js @@ -0,0 +1,264 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + 'EventEmitter', + 'legacyRegistry', + 'uuid', + './api/api', + 'text!./adapter/templates/edit-object-replacement.html', + './selection/Selection', + './api/objects/object-utils', + './ui/ViewRegistry' +], function ( + EventEmitter, + legacyRegistry, + uuid, + api, + editObjectTemplate, + Selection, + objectUtils, + ViewRegistry +) { + /** + * Open MCT is an extensible web application for building mission + * control user interfaces. This module is itself an instance of + * [MCT]{@link module:openmct.MCT}, which provides an interface for + * configuring and executing the application. + * + * @exports openmct + */ + + /** + * The Open MCT application. This may be configured by installing plugins + * or registering extensions before the application is started. + * @class MCT + * @memberof module:openmct + * @augments {EventEmitter} + */ + function MCT() { + EventEmitter.call(this); + this.legacyBundle = { extensions: { + services: [ + { + key: "openmct", + implementation: function () { + return this; + }.bind(this) + } + ] + } }; + + /** + * Tracks current selection state of the application. + * @private + */ + this.selection = new Selection(); + + /** + * MCT's time conductor, which may be used to synchronize view contents + * for telemetry- or time-based views. + * @type {module:openmct.TimeConductor} + * @memberof module:openmct.MCT# + * @name conductor + */ + this.conductor = new api.TimeConductor(); + + /** + * An interface for interacting with the composition of domain objects. + * The composition of a domain object is the list of other domain + * objects it "contains" (for instance, that should be displayed + * beneath it in the tree.) + * + * `composition` may be called as a function, in which case it acts + * as [`composition.get`]{@link module:openmct.CompositionAPI#get}. + * + * @type {module:openmct.CompositionAPI} + * @memberof module:openmct.MCT# + * @name composition + */ + this.composition = new api.CompositionAPI(); + + /** + * Registry for views of domain objects which should appear in the + * main viewing area. + * + * @type {module:openmct.ViewRegistry} + * @memberof module:openmct.MCT# + * @name mainViews + */ + this.mainViews = new ViewRegistry(); + + /** + * Registry for views which should appear in the Inspector area. + * These views will be chosen based on selection state, so + * providers should be prepared to test arbitrary objects for + * viewability. + * + * @type {module:openmct.ViewRegistry} + * @memberof module:openmct.MCT# + * @name inspectors + */ + this.inspectors = new ViewRegistry(); + + /** + * Registry for views which should appear in the status indicator area. + * @type {module:openmct.ViewRegistry} + * @memberof module:openmct.MCT# + * @name indicators + */ + this.indicators = new ViewRegistry(); + + /** + * Registry for views which should appear in the toolbar area while + * editing. + * + * These views will be chosen based on selection state, so + * providers should be prepared to test arbitrary objects for + * viewability. + * + * @type {module:openmct.ViewRegistry} + * @memberof module:openmct.MCT# + * @name toolbars + */ + this.toolbars = new ViewRegistry(); + + /** + * Registry for domain object types which may exist within this + * instance of Open MCT. + * + * @type {module:openmct.TypeRegistry} + * @memberof module:openmct.MCT# + * @name types + */ + this.types = new api.TypeRegistry(); + + /** + * Utilities for attaching common behaviors to views. + * + * @type {module:openmct.GestureAPI} + * @memberof module:openmct.MCT# + * @name gestures + */ + this.gestures = new api.GestureAPI(); + + /** + * An interface for interacting with domain objects and the domain + * object hierarchy. + * + * @type {module:openmct.ObjectAPI} + * @memberof module:openmct.MCT# + * @name objects + */ + this.objects = new api.ObjectAPI(); + + /** + * An interface for retrieving and interpreting telemetry data associated + * with a domain object. + * + * @type {module:openmct.TelemetryAPI} + * @memberof module:openmct.MCT# + * @name telemetry + */ + this.telemetry = new api.TelemetryAPI(); + + this.TimeConductor = this.conductor; // compatibility for prototype + this.on('navigation', this.selection.clear.bind(this.selection)); + } + + MCT.prototype = Object.create(EventEmitter.prototype); + + Object.keys(api).forEach(function (k) { + MCT.prototype[k] = api[k]; + }); + MCT.prototype.MCT = MCT; + + MCT.prototype.legacyExtension = function (category, extension) { + this.legacyBundle.extensions[category] = + this.legacyBundle.extensions[category] || []; + this.legacyBundle.extensions[category].push(extension); + }; + + /** + * Set path to where assets are hosted. This should be the path to main.js. + * @memberof module:openmct.MCT# + * @method setAssetPath + */ + MCT.prototype.setAssetPath = function (path) { + this.legacyExtension('constants', { + key: "ASSETS_PATH", + value: path + }); + }; + + /** + * Start running Open MCT. This should be called only after any plugins + * have been installed. + * @fires module:openmct.MCT~start + * @memberof module:openmct.MCT# + * @method start + * @param {HTMLElement} [domElement] the DOM element in which to run + * MCT; if undefined, MCT will be run in the body of the document + */ + MCT.prototype.start = function (domElement) { + if (!domElement) { + domElement = document.body; + } + + var appDiv = document.createElement('div'); + appDiv.setAttribute('ng-view', ''); + appDiv.className = 'user-environ'; + domElement.appendChild(appDiv); + + this.legacyExtension('runs', { + depends: ['navigationService'], + implementation: function (navigationService) { + navigationService + .addListener(this.emit.bind(this, 'navigation')); + }.bind(this) + }); + + legacyRegistry.register('adapter', this.legacyBundle); + legacyRegistry.enable('adapter'); + /** + * Fired by [MCT]{@link module:openmct.MCT} when the application + * is started. + * @event start + * @memberof module:openmct.MCT~ + */ + this.emit('start'); + }; + + + /** + * Install a plugin in MCT. + * + * @param {Function} plugin a plugin install function which will be + * invoked with the mct instance. + * @memberof module:openmct.MCT# + */ + MCT.prototype.install = function (plugin) { + plugin(this); + }; + + return MCT; +}); diff --git a/src/adapter/actions/ActionDialogDecorator.js b/src/adapter/actions/ActionDialogDecorator.js new file mode 100644 index 00000000000..5a5426e0811 --- /dev/null +++ b/src/adapter/actions/ActionDialogDecorator.js @@ -0,0 +1,66 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + '../../api/objects/object-utils' +], function (objectUtils) { + function ActionDialogDecorator(mct, newViews, actionService) { + this.actionService = actionService; + this.mct = mct; + this.definitions = newViews.filter(function (newView) { + return newView.region === mct.regions.properties; + }).map(function (newView) { + return newView.factory; + }); + } + + ActionDialogDecorator.prototype.getActions = function (context) { + var mct = this.mct; + var definitions = this.definitions; + + return this.actionService.getActions(context).map(function (action) { + if (action.dialogService) { + var domainObject = objectUtils.toNewFormat( + context.domainObject.getModel(), + objectUtils.parseKeyString(context.domainObject.getId()) + ); + + definitions = definitions.filter(function (definition) { + return definition.canView(domainObject); + }); + + if (definitions.length > 0) { + action.dialogService = Object.create(action.dialogService); + action.dialogService.getUserInput = function (form, value) { + return new mct.Dialog( + definitions[0].view(context.domainObject), + form.title + ).show(); + }; + } + } + return action; + }); + }; + + return ActionDialogDecorator; +}); diff --git a/src/adapter/bundle.js b/src/adapter/bundle.js new file mode 100644 index 00000000000..a884897423d --- /dev/null +++ b/src/adapter/bundle.js @@ -0,0 +1,127 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + 'legacyRegistry', + './actions/ActionDialogDecorator', + './directives/MCTView', + './services/Instantiate', + './capabilities/APICapabilityDecorator', + './policies/AdapterCompositionPolicy', + './runs/AlternateCompositionInitializer' +], function ( + legacyRegistry, + ActionDialogDecorator, + MCTView, + Instantiate, + APICapabilityDecorator, + AdapterCompositionPolicy, + AlternateCompositionInitializer +) { + legacyRegistry.register('src/adapter', { + "extensions": { + "directives": [ + { + key: "mctView", + implementation: MCTView, + depends: [ + "newViews[]", + "openmct" + ] + } + ], + services: [ + { + key: "instantiate", + priority: "mandatory", + implementation: Instantiate, + depends: [ + "capabilityService", + "identifierService", + "cacheService" + ] + } + ], + components: [ + { + type: "decorator", + provides: "capabilityService", + implementation: APICapabilityDecorator, + depends: [ + "$injector" + ] + }, + { + type: "decorator", + provides: "actionService", + implementation: ActionDialogDecorator, + depends: ["openmct", "newViews[]"] + } + ], + policies: [ + { + category: "composition", + implementation: AdapterCompositionPolicy, + depends: ["openmct"] + } + ], + runs: [ + { + implementation: AlternateCompositionInitializer, + depends: ["openmct"] + } + ], + licenses: [ + { + "name": "almond", + "version": "0.3.3", + "description": "Lightweight RequireJS replacement for builds", + "author": "jQuery Foundation", + "website": "https://github.com/requirejs/almond", + "copyright": "Copyright jQuery Foundation and other contributors, https://jquery.org/", + "license": "license-mit", + "link": "https://github.com/requirejs/almond/blob/master/LICENSE" + }, + { + "name": "lodash", + "version": "3.10.1", + "description": "Utility functions", + "author": "Dojo Foundation", + "website": "https://lodash.com", + "copyright": "Copyright 2012-2015 The Dojo Foundation", + "license": "license-mit", + "link": "https://raw.githubusercontent.com/lodash/lodash/3.10.1/LICENSE" + }, + { + "name": "EventEmitter3", + "version": "1.2.0", + "description": "Event-driven programming support", + "author": "Arnout Kazemier", + "website": "https://github.com/primus/eventemitter3", + "copyright": "Copyright (c) 2014 Arnout Kazemier", + "license": "license-mit", + "link": "https://github.com/primus/eventemitter3/blob/1.2.0/LICENSE" + } + ] + } + }); +}); diff --git a/src/adapter/capabilities/APICapabilityDecorator.js b/src/adapter/capabilities/APICapabilityDecorator.js new file mode 100644 index 00000000000..01bd6a32efa --- /dev/null +++ b/src/adapter/capabilities/APICapabilityDecorator.js @@ -0,0 +1,59 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + './synchronizeMutationCapability', + './AlternateCompositionCapability' +], function ( + synchronizeMutationCapability, + AlternateCompositionCapability +) { + + /** + * Overrides certain capabilities to keep consistency between old API + * and new API. + */ + function APICapabilityDecorator($injector, capabilityService) { + this.$injector = $injector; + this.capabilityService = capabilityService; + } + + APICapabilityDecorator.prototype.getCapabilities = function ( + model + ) { + var capabilities = this.capabilityService.getCapabilities(model); + if (capabilities.mutation) { + capabilities.mutation = + synchronizeMutationCapability(capabilities.mutation); + } + if (AlternateCompositionCapability.appliesTo(model)) { + capabilities.composition = function (domainObject) { + return new AlternateCompositionCapability(this.$injector, domainObject); + }.bind(this); + } + + return capabilities; + }; + + return APICapabilityDecorator; + +}); diff --git a/src/adapter/capabilities/AlternateCompositionCapability.js b/src/adapter/capabilities/AlternateCompositionCapability.js new file mode 100644 index 00000000000..20cceb9d10e --- /dev/null +++ b/src/adapter/capabilities/AlternateCompositionCapability.js @@ -0,0 +1,107 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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. + *****************************************************************************/ + +/** + * Module defining AlternateCompositionCapability. Created by vwoeltje on 11/7/14. + */ +define([ + '../../api/objects/object-utils' +], function (objectUtils) { + function AlternateCompositionCapability($injector, domainObject) { + this.domainObject = domainObject; + this.getDependencies = function () { + this.instantiate = $injector.get("instantiate"); + this.contextualize = $injector.get("contextualize"); + this.getDependencies = undefined; + this.openmct = $injector.get("openmct"); + }.bind(this); + } + + AlternateCompositionCapability.prototype.add = function (child, index) { + if (typeof index !== 'undefined') { + // At first glance I don't see a location in the existing + // codebase where add is called with an index. Won't support. + throw new Error( + 'Composition Capability does not support adding at index' + ); + } + + function addChildToComposition(model) { + var existingIndex = model.composition.indexOf(child.getId()); + if (existingIndex === -1) { + model.composition.push(child.getId()); + } + } + + return this.domainObject.useCapability( + 'mutation', + addChildToComposition + ) + .then(this.invoke.bind(this)) + .then(function (children) { + return children.filter(function (c) { + return c.getId() === child.getId(); + })[0]; + }); + }; + + AlternateCompositionCapability.prototype.contextualizeChild = function ( + child + ) { + if (this.getDependencies) { + this.getDependencies(); + } + + var keyString = objectUtils.makeKeyString(child.key); + var oldModel = objectUtils.toOldFormat(child); + var newDO = this.instantiate(oldModel, keyString); + return this.contextualize(newDO, this.domainObject); + + }; + + AlternateCompositionCapability.prototype.invoke = function () { + var newFormatDO = objectUtils.toNewFormat( + this.domainObject.getModel(), + this.domainObject.getId() + ); + + if (this.getDependencies) { + this.getDependencies(); + } + + var collection = this.openmct.composition.get(newFormatDO); + return collection.load() + .then(function (children) { + collection.destroy(); + return children.map(this.contextualizeChild, this); + }.bind(this)); + }; + + AlternateCompositionCapability.appliesTo = function (model) { + // Will get replaced by a runs exception to properly + // bind to running openmct instance + return false; + }; + + return AlternateCompositionCapability; + } +); diff --git a/src/adapter/capabilities/synchronizeMutationCapability.js b/src/adapter/capabilities/synchronizeMutationCapability.js new file mode 100644 index 00000000000..e2aa14a96a8 --- /dev/null +++ b/src/adapter/capabilities/synchronizeMutationCapability.js @@ -0,0 +1,49 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + +], function ( + +) { + + /** + * Wraps the mutation capability and synchronizes the mutation + */ + function synchronizeMutationCapability(mutationConstructor) { + + return function makeCapability(domainObject) { + var capability = mutationConstructor(domainObject); + var oldListen = capability.listen.bind(capability); + capability.listen = function (listener) { + return oldListen(function (newModel) { + capability.domainObject.model = + JSON.parse(JSON.stringify(newModel)); + listener(newModel); + }); + }; + return capability; + }; + } + + return synchronizeMutationCapability; +}); diff --git a/src/adapter/directives/MCTView.js b/src/adapter/directives/MCTView.js new file mode 100644 index 00000000000..21306d0bf5d --- /dev/null +++ b/src/adapter/directives/MCTView.js @@ -0,0 +1,87 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + 'angular', + './Region', + '../../api/objects/object-utils' +], function ( + angular, + Region, + objectUtils +) { + function MCTView(newViews, PublicAPI) { + var definitions = {}; + + newViews.forEach(function (newView) { + definitions[newView.region] = definitions[newView.region] || {}; + definitions[newView.region][newView.key] = newView.factory; + }); + + return { + restrict: 'E', + link: function (scope, element, attrs) { + var key, mctObject, regionId, region; + + function maybeShow() { + if (!definitions[regionId] || !definitions[regionId][key] || !mctObject) { + return; + } + + region.show(definitions[regionId][key].view(mctObject)); + } + + function setKey(k) { + key = k; + maybeShow(); + } + + function setObject(obj) { + mctObject = undefined; + PublicAPI.Objects.get(objectUtils.parseKeyString(obj.getId())) + .then(function (mobj) { + mctObject = mobj; + maybeShow(); + }); + } + + function setRegionId(r) { + regionId = r; + maybeShow(); + } + + region = new Region(element[0]); + + scope.$watch('key', setKey); + scope.$watch('region', setRegionId); + scope.$watch('mctObject', setObject); + }, + scope: { + key: "=", + region: "=", + mctObject: "=" + } + }; + } + + return MCTView; +}); diff --git a/src/adapter/directives/Region.js b/src/adapter/directives/Region.js new file mode 100644 index 00000000000..dcd3305acd3 --- /dev/null +++ b/src/adapter/directives/Region.js @@ -0,0 +1,45 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([], function () { + function Region(element) { + this.activeView = undefined; + this.element = element; + } + + Region.prototype.clear = function () { + if (this.activeView) { + this.activeView.destroy(); + this.activeView = undefined; + } + }; + + Region.prototype.show = function (view) { + this.clear(); + this.activeView = view; + if (this.activeView) { + this.activeView.show(this.element); + } + }; + + return Region; +}); diff --git a/src/adapter/policies/AdapterCompositionPolicy.js b/src/adapter/policies/AdapterCompositionPolicy.js new file mode 100644 index 00000000000..50e9312d543 --- /dev/null +++ b/src/adapter/policies/AdapterCompositionPolicy.js @@ -0,0 +1,48 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([], function () { + function AdapterCompositionPolicy(mct) { + this.mct = mct; + } + + AdapterCompositionPolicy.prototype.allow = function ( + containerType, + childType + ) { + var containerObject = containerType.getInitialModel(); + var childObject = childType.getInitialModel(); + + containerObject.type = containerType.getKey(); + childObject.type = childType.getKey(); + + var composition = this.mct.Composition(containerObject); + + if (composition) { + return composition.canContain(childObject); + } + + return true; + }; + + return AdapterCompositionPolicy; +}); diff --git a/src/adapter/runs/AlternateCompositionInitializer.js b/src/adapter/runs/AlternateCompositionInitializer.js new file mode 100644 index 00000000000..c9169ab55b2 --- /dev/null +++ b/src/adapter/runs/AlternateCompositionInitializer.js @@ -0,0 +1,36 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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. + *****************************************************************************/ + +define([ + '../capabilities/AlternateCompositionCapability' +], function (AlternateCompositionCapability) { + // Present to work around the need for openmct to be used + // from AlternateCompositionCapability.appliesTo, even though it + // cannot be injected. + function AlternateCompositionInitializer(openmct) { + AlternateCompositionCapability.appliesTo = function (model) { + return !model.composition && !!openmct.composition.get(model); + }; + } + + return AlternateCompositionInitializer; +}); diff --git a/src/adapter/services/Instantiate.js b/src/adapter/services/Instantiate.js new file mode 100644 index 00000000000..3b4c1907050 --- /dev/null +++ b/src/adapter/services/Instantiate.js @@ -0,0 +1,49 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define( + ['../../../platform/core/src/objects/DomainObjectImpl'], + function (DomainObjectImpl) { + + /** + * Overrides platform version of instantiate, passes Id with model such + * that capability detection can utilize new format domain objects. + */ + function Instantiate( + capabilityService, + identifierService, + cacheService + ) { + return function (model, id) { + id = id || identifierService.generate(); + var old_id = model.id; + model.id = id; + var capabilities = capabilityService.getCapabilities(model); + model.id = old_id; + cacheService.put(id, model); + return new DomainObjectImpl(id, model, capabilities); + }; + } + + return Instantiate; + } +); diff --git a/src/adapter/templates/edit-object-replacement.html b/src/adapter/templates/edit-object-replacement.html new file mode 100644 index 00000000000..f8fc33ca07e --- /dev/null +++ b/src/adapter/templates/edit-object-replacement.html @@ -0,0 +1,46 @@ +
+
+
+ + + +
+
+ + + + + +
+
+
+
+ +
+ + + + +
+ + +
+
+
diff --git a/src/api/TimeConductor.js b/src/api/TimeConductor.js new file mode 100644 index 00000000000..135e29f1a53 --- /dev/null +++ b/src/api/TimeConductor.js @@ -0,0 +1,202 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define(['EventEmitter'], function (EventEmitter) { + + /** + * The public API for setting and querying time conductor state. The + * time conductor is the means by which the temporal bounds of a view + * are controlled. Time-sensitive views will typically respond to + * changes to bounds or other properties of the time conductor and + * update the data displayed based on the time conductor state. + * + * The TimeConductor extends the EventEmitter class. A number of events are + * fired when properties of the time conductor change, which are + * documented below. + * @interface + * @memberof module:openmct + */ + function TimeConductor() { + EventEmitter.call(this); + + //The Time System + this.system = undefined; + //The Time Of Interest + this.toi = undefined; + + this.boundsVal = { + start: undefined, + end: undefined + }; + + //Default to fixed mode + this.followMode = false; + } + + TimeConductor.prototype = Object.create(EventEmitter.prototype); + + /** + * Validate the given bounds. This can be used for pre-validation of + * bounds, for example by views validating user inputs. + * @param bounds The start and end time of the conductor. + * @returns {string | true} A validation error, or true if valid + * @memberof module:openmct.TimeConductor# + * @method validateBounds + */ + TimeConductor.prototype.validateBounds = function (bounds) { + if ((bounds.start === undefined) || + (bounds.end === undefined) || + isNaN(bounds.start) || + isNaN(bounds.end) + ) { + return "Start and end must be specified as integer values"; + } else if (bounds.start > bounds.end) { + return "Specified start date exceeds end bound"; + } + return true; + }; + + function throwOnError(validationResult) { + if (validationResult !== true) { + throw new Error(validationResult); + } + } + + /** + * Get or set the follow mode of the time conductor. In follow mode the + * time conductor ticks, regularly updating the bounds from a timing + * source appropriate to the selected time system and mode of the time + * conductor. + * @fires module:openmct.TimeConductor~follow + * @param {boolean} followMode + * @returns {boolean} + * @memberof module:openmct.TimeConductor# + * @method follow + */ + TimeConductor.prototype.follow = function (followMode) { + if (arguments.length > 0) { + this.followMode = followMode; + /** + * The TimeConductor has toggled into or out of follow mode. + * @event follow + * @memberof module:openmct.TimeConductor~ + * @property {boolean} followMode true if follow mode is + * enabled, otherwise false. + */ + this.emit('follow', this.followMode); + } + return this.followMode; + }; + + /** + * @typedef {Object} TimeConductorBounds + * @property {number} start The start time displayed by the time conductor in ms since epoch. Epoch determined by current time system + * @property {number} end The end time displayed by the time conductor in ms since epoch. + * @memberof module:openmct.TimeConductor~ + */ + + /** + * Get or set the start and end time of the time conductor. Basic validation + * of bounds is performed. + * + * @param {module:openmct.TimeConductorBounds~TimeConductorBounds} newBounds + * @throws {Error} Validation error + * @fires module:openmct.TimeConductor~bounds + * @returns {module:openmct.TimeConductorBounds~TimeConductorBounds} + * @memberof module:openmct.TimeConductor# + * @method bounds + */ + TimeConductor.prototype.bounds = function (newBounds) { + if (arguments.length > 0) { + throwOnError(this.validateBounds(newBounds)); + this.boundsVal = newBounds; + /** + * The start time, end time, or both have been updated. + * @event bounds + * @memberof module:openmct.TimeConductor~ + * @property {TimeConductorBounds} bounds + */ + this.emit('bounds', this.boundsVal); + } + return this.boundsVal; + }; + + /** + * Get or set the time system of the TimeConductor. Time systems determine + * units, epoch, and other aspects of time representation. When changing + * the time system in use, new valid bounds must also be provided. + * @param {TimeSystem} newTimeSystem + * @param {module:openmct.TimeConductor~TimeConductorBounds} bounds + * @fires module:openmct.TimeConductor~timeSystem + * @returns {TimeSystem} The currently applied time system + * @memberof module:openmct.TimeConductor# + * @method timeSystem + */ + TimeConductor.prototype.timeSystem = function (newTimeSystem, bounds) { + if (arguments.length >= 2) { + this.system = newTimeSystem; + /** + * The time system used by the time + * conductor has changed. A change in Time System will always be + * followed by a bounds event specifying new query bounds. + * + * @event module:openmct.TimeConductor~timeSystem + * @property {TimeSystem} The value of the currently applied + * Time System + * */ + this.emit('timeSystem', this.system); + // Do something with bounds here. Try and convert between + // time systems? Or just set defaults when time system changes? + // eg. + this.bounds(bounds); + } else if (arguments.length === 1) { + throw new Error('Must set bounds when changing time system'); + } + return this.system; + }; + + /** + * Get or set the Time of Interest. The Time of Interest is the temporal + * focus of the current view. It can be manipulated by the user from the + * time conductor or from other views. + * @fires module:openmct.TimeConductor~timeOfInterest + * @param newTOI + * @returns {number} the current time of interest + * @memberof module:openmct.TimeConductor# + * @method timeOfInterest + */ + TimeConductor.prototype.timeOfInterest = function (newTOI) { + if (arguments.length > 0) { + this.toi = newTOI; + /** + * The Time of Interest has moved. + * @event timeOfInterest + * @memberof module:openmct.TimeConductor~ + * @property {number} Current time of interest + */ + this.emit('timeOfInterest', this.toi); + } + return this.toi; + }; + + return TimeConductor; +}); diff --git a/src/api/TimeConductorSpec.js b/src/api/TimeConductorSpec.js new file mode 100644 index 00000000000..536667a6480 --- /dev/null +++ b/src/api/TimeConductorSpec.js @@ -0,0 +1,110 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define(['./TimeConductor'], function (TimeConductor) { + describe("The Time Conductor", function () { + var tc, + timeSystem, + bounds, + eventListener, + toi, + follow; + + beforeEach(function () { + tc = new TimeConductor(); + timeSystem = {}; + bounds = {start: 0, end: 0}; + eventListener = jasmine.createSpy("eventListener"); + toi = 111; + follow = true; + }); + + it("Supports setting and querying of time of interest and and follow mode", function () { + expect(tc.timeOfInterest()).not.toBe(toi); + tc.timeOfInterest(toi); + expect(tc.timeOfInterest()).toBe(toi); + + expect(tc.follow()).not.toBe(follow); + tc.follow(follow); + expect(tc.follow()).toBe(follow); + }); + + it("Allows setting of valid bounds", function () { + bounds = {start: 0, end: 1}; + expect(tc.bounds()).not.toBe(bounds); + expect(tc.bounds.bind(tc, bounds)).not.toThrow(); + expect(tc.bounds()).toBe(bounds); + }); + + it("Disallows setting of invalid bounds", function () { + bounds = {start: 1, end: 0}; + expect(tc.bounds()).not.toBe(bounds); + expect(tc.bounds.bind(tc, bounds)).toThrow(); + expect(tc.bounds()).not.toBe(bounds); + + bounds = {start: 1}; + expect(tc.bounds()).not.toBe(bounds); + expect(tc.bounds.bind(tc, bounds)).toThrow(); + expect(tc.bounds()).not.toBe(bounds); + }); + + it("Allows setting of time system with bounds", function () { + expect(tc.timeSystem()).not.toBe(timeSystem); + expect(tc.timeSystem.bind(tc, timeSystem, bounds)).not.toThrow(); + expect(tc.timeSystem()).toBe(timeSystem); + }); + + it("Disallows setting of time system without bounds", function () { + expect(tc.timeSystem()).not.toBe(timeSystem); + expect(tc.timeSystem.bind(tc, timeSystem)).toThrow(); + expect(tc.timeSystem()).not.toBe(timeSystem); + }); + + it("Emits an event when time system changes", function () { + expect(eventListener).not.toHaveBeenCalled(); + tc.on("timeSystem", eventListener); + tc.timeSystem(timeSystem, bounds); + expect(eventListener).toHaveBeenCalledWith(timeSystem); + }); + + it("Emits an event when time of interest changes", function () { + expect(eventListener).not.toHaveBeenCalled(); + tc.on("timeOfInterest", eventListener); + tc.timeOfInterest(toi); + expect(eventListener).toHaveBeenCalledWith(toi); + }); + + it("Emits an event when bounds change", function () { + expect(eventListener).not.toHaveBeenCalled(); + tc.on("bounds", eventListener); + tc.bounds(bounds); + expect(eventListener).toHaveBeenCalledWith(bounds); + }); + + it("Emits an event when follow mode changes", function () { + expect(eventListener).not.toHaveBeenCalled(); + tc.on("follow", eventListener); + tc.follow(follow); + expect(eventListener).toHaveBeenCalledWith(follow); + }); + }); +}); diff --git a/src/api/Type.js b/src/api/Type.js new file mode 100644 index 00000000000..d83e345b761 --- /dev/null +++ b/src/api/Type.js @@ -0,0 +1,82 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define(function () { + /** + * @typedef TypeDefinition + * @memberof module:openmct.Type~ + * @property {Metadata} metadata displayable metadata about this type + * @property {function (object)} [initialize] a function which initializes + * the model for new domain objects of this type + * @property {boolean} [creatable] true if users should be allowed to + * create this type (default: false) + */ + + /** + * A Type describes a kind of domain object that may appear or be + * created within Open MCT. + * + * @param {module:opemct.Type~TypeDefinition} definition + * @class Type + * @memberof module:openmct + */ + function Type(definition) { + this.definition = definition; + } + + /** + * Check if a domain object is an instance of this type. + * @param domainObject + * @returns {boolean} true if the domain object is of this type + * @memberof module:openmct.Type# + * @method check + */ + Type.prototype.check = function (domainObject) { + // Depends on assignment from MCT. + return domainObject.type === this.key; + }; + + /** + * Get a definition for this type that can be registered using the + * legacy bundle format. + * @private + */ + Type.prototype.toLegacyDefinition = function () { + var def = {}; + def.name = this.definition.metadata.label; + def.glyph = this.definition.metadata.glyph; + def.description = this.definition.metadata.description; + def.properties = this.definition.form; + + if (this.definition.initialize) { + def.model = {}; + this.definition.initialize(def.model); + } + + if (this.definition.creatable) { + def.features = ['creation']; + } + return def; + }; + + return Type; +}); diff --git a/src/api/api.js b/src/api/api.js new file mode 100644 index 00000000000..f68460578b3 --- /dev/null +++ b/src/api/api.js @@ -0,0 +1,52 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + './Type', + './TimeConductor', + './objects/ObjectAPI', + './composition/CompositionAPI', + './types/TypeRegistry', + './ui/Dialog', + './ui/GestureAPI', + './telemetry/TelemetryAPI' +], function ( + Type, + TimeConductor, + ObjectAPI, + CompositionAPI, + TypeRegistry, + Dialog, + GestureAPI, + TelemetryAPI +) { + return { + Type: Type, + TimeConductor: TimeConductor, + ObjectAPI: ObjectAPI, + CompositionAPI: CompositionAPI, + Dialog: Dialog, + TypeRegistry: TypeRegistry, + GestureAPI: GestureAPI, + TelemetryAPI: TelemetryAPI + }; +}); diff --git a/src/api/composition/CompositionAPI.js b/src/api/composition/CompositionAPI.js new file mode 100644 index 00000000000..31073bd956d --- /dev/null +++ b/src/api/composition/CompositionAPI.js @@ -0,0 +1,136 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + 'lodash', + 'EventEmitter', + './DefaultCompositionProvider', + './CompositionCollection' +], function ( + _, + EventEmitter, + DefaultCompositionProvider, + CompositionCollection +) { + /** + * An interface for interacting with the composition of domain objects. + * The composition of a domain object is the list of other domain objects + * it "contains" (for instance, that should be displayed beneath it + * in the tree.) + * + * @interface CompositionAPI + * @returns {module:openmct.CompositionCollection} + * @memberof module:openmct + */ + function CompositionAPI() { + this.registry = []; + this.policies = []; + this.addProvider(new DefaultCompositionProvider()); + } + + /** + * Add a composition provider. + * + * Plugins can add new composition providers to change the loading + * behavior for certain domain objects. + * + * @method addProvider + * @param {module:openmct.CompositionProvider} provider the provider to add + * @memberof module:openmct.CompositionAPI# + */ + CompositionAPI.prototype.addProvider = function (provider) { + this.registry.unshift(provider); + }; + + /** + * Retrieve the composition (if any) of this domain object. + * + * @method get + * @returns {module:openmct.CompositionCollection} + * @memberof module:openmct.CompositionAPI# + */ + CompositionAPI.prototype.get = function (domainObject) { + var provider = _.find(this.registry, function (p) { + return p.appliesTo(domainObject); + }); + + if (!provider) { + return; + } + + return new CompositionCollection(domainObject, provider); + }; + + /** + * A composition policy is a function which either allows or disallows + * placing one object in another's composition. + * + * Open MCT's policy model requires consensus, so any one policy may + * reject composition by returning false. As such, policies should + * generally be written to return true in the default case. + * + * @callback CompositionPolicy + * @memberof module:openmct.CompositionAPI~ + * @param {module:openmct.DomainObject} containingObject the object which + * would act as a container + * @param {module:openmct.DomainObject} containedObject the object which + * would be contained + * @returns {boolean} false if this composition should be disallowed + */ + + /** + * Add a composition policy. Composition policies may disallow domain + * objects from containing other domain objects. + * + * @method addPolicy + * @param {module:openmct.CompositionAPI~CompositionPolicy} policy + * the policy to add + * @memberof module:openmct.CompositionAPI# + */ + CompositionAPI.prototype.addPolicy = function (policy) { + this.policies.push(policy); + }; + + /** + * Check whether or not a domain object is allowed to contain another + * domain object. + * + * @private + * @method checkPolicy + * @param {module:openmct.DomainObject} containingObject the object which + * would act as a container + * @param {module:openmct.DomainObject} containedObject the object which + * would be contained + * @returns {boolean} false if this composition should be disallowed + + * @param {module:openmct.CompositionAPI~CompositionPolicy} policy + * the policy to add + * @memberof module:openmct.CompositionAPI# + */ + CompositionAPI.prototype.checkPolicy = function (container, containee) { + return this.policies.every(function (policy) { + return policy(container, containee); + }); + }; + + return CompositionAPI; +}); diff --git a/src/api/composition/CompositionCollection.js b/src/api/composition/CompositionCollection.js new file mode 100644 index 00000000000..1d0d0669d5e --- /dev/null +++ b/src/api/composition/CompositionCollection.js @@ -0,0 +1,231 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + 'EventEmitter', + 'lodash', + '../objects/object-utils' +], function ( + EventEmitter, + _, + objectUtils +) { + + + /** + * A CompositionCollection represents the list of domain objects contained + * by another domain object. It provides methods for loading this + * list asynchronously, and for modifying this list. + * + * @interface CompositionCollection + * @param {module:openmct.DomainObject} domainObject the domain object + * whose composition will be contained + * @param {module:openmct.CompositionProvider} provider the provider + * to use to retrieve other domain objects + * @param {module:openmct.CompositionAPI} api the composition API, for + * policy checks + * @memberof module:openmct + * @augments EventEmitter + */ + function CompositionCollection(domainObject, provider, api) { + EventEmitter.call(this); + this.domainObject = domainObject; + this.provider = provider; + this.api = api; + if (this.provider.on) { + this.provider.on( + this.domainObject, + 'add', + this.onProviderAdd, + this + ); + this.provider.on( + this.domainObject, + 'remove', + this.onProviderRemove, + this + ); + } + } + + CompositionCollection.prototype = Object.create(EventEmitter.prototype); + + CompositionCollection.prototype.onProviderAdd = function (child) { + this.add(child, true); + }; + + CompositionCollection.prototype.onProviderRemove = function (child) { + this.remove(child, true); + }; + + /** + * Get the index of a domain object within this composition. If the + * domain object is not contained here, -1 will be returned. + * + * A call to [load]{@link module:openmct.CompositionCollection#load} + * must have resolved before using this method. + * + * @param {module:openmct.DomainObject} child the domain object for which + * an index should be retrieved + * @returns {number} the index of that domain object + * @memberof module:openmct.CompositionCollection# + * @name indexOf + */ + CompositionCollection.prototype.indexOf = function (child) { + return _.findIndex(this.loadedChildren, function (other) { + return objectUtils.equals(child, other); + }); + }; + + /** + * Get the index of a domain object within this composition. + * + * A call to [load]{@link module:openmct.CompositionCollection#load} + * must have resolved before using this method. + * + * @param {module:openmct.DomainObject} child the domain object for which + * containment should be checked + * @returns {boolean} true if the domain object is contained here + * @memberof module:openmct.CompositionCollection# + * @name contains + */ + CompositionCollection.prototype.contains = function (child) { + return this.indexOf(child) !== -1; + }; + + /** + * Check if a domain object can be added to this composition. + * + * @param {module:openmct.DomainObject} child the domain object to add + * @memberof module:openmct.CompositionCollection# + * @name canContain + */ + CompositionCollection.prototype.canContain = function (domainObject) { + return this.api.checkPolicy(this.domainObject, domainObject); + }; + + /** + * Add a domain object to this composition. + * + * A call to [load]{@link module:openmct.CompositionCollection#load} + * must have resolved before using this method. + * + * @param {module:openmct.DomainObject} child the domain object to add + * @param {boolean} skipMutate true if the underlying provider should + * not be updated + * @memberof module:openmct.CompositionCollection# + * @name add + */ + CompositionCollection.prototype.add = function (child, skipMutate) { + if (!this.loadedChildren) { + throw new Error("Must load composition before you can add!"); + } + if (!this.canContain(child)) { + throw new Error("This object cannot contain that object."); + } + if (this.contains(child)) { + if (skipMutate) { + return; // don't add twice, don't error. + } + throw new Error("Unable to add child: already in composition"); + } + this.loadedChildren.push(child); + this.emit('add', child); + if (!skipMutate) { + // add after we have added. + this.provider.add(this.domainObject, child); + } + }; + + /** + * Load the domain objects in this composition. + * + * @returns {Promise.>} a promise for + * the domain objects in this composition + * @memberof {module:openmct.CompositionCollection#} + * @name load + */ + CompositionCollection.prototype.load = function () { + return this.provider.load(this.domainObject) + .then(function (children) { + this.loadedChildren = []; + children.map(function (c) { + this.add(c, true); + }, this); + this.emit('load'); + return this.loadedChildren.slice(); + }.bind(this)); + }; + + /** + * Remove a domain object from this composition. + * + * A call to [load]{@link module:openmct.CompositionCollection#load} + * must have resolved before using this method. + * + * @param {module:openmct.DomainObject} child the domain object to remove + * @param {boolean} skipMutate true if the underlying provider should + * not be updated + * @memberof module:openmct.CompositionCollection# + * @name remove + */ + CompositionCollection.prototype.remove = function (child, skipMutate) { + if (!this.contains(child)) { + if (skipMutate) { + return; + } + throw new Error("Unable to remove child: not found in composition"); + } + var index = this.indexOf(child); + var removed = this.loadedChildren.splice(index, 1)[0]; + this.emit('remove', index, child); + if (!skipMutate) { + // trigger removal after we have internally removed it. + this.provider.remove(this.domainObject, removed); + } + }; + + /** + * Stop using this composition collection. This will release any resources + * associated with this collection. + * @name destroy + * @memberof module:openmct.CompositionCollection# + */ + CompositionCollection.prototype.destroy = function () { + if (this.provider.off) { + this.provider.off( + this.domainObject, + 'add', + this.onProviderAdd, + this + ); + this.provider.off( + this.domainObject, + 'remove', + this.onProviderRemove, + this + ); + } + }; + + return CompositionCollection; +}); diff --git a/src/api/composition/DefaultCompositionProvider.js b/src/api/composition/DefaultCompositionProvider.js new file mode 100644 index 00000000000..df048379a5f --- /dev/null +++ b/src/api/composition/DefaultCompositionProvider.js @@ -0,0 +1,150 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + 'lodash', + 'EventEmitter', + '../objects/ObjectAPI', + '../objects/object-utils' +], function ( + _, + EventEmitter, + ObjectAPI, + objectUtils +) { + /** + * A CompositionProvider provides the underlying implementation of + * composition-related behavior for certain types of domain object. + * + * @interface CompositionProvider + * @memberof module:openmct + * @augments EventEmitter + */ + + function makeEventName(domainObject, event) { + return event + ':' + objectUtils.makeKeyString(domainObject.key); + } + + function DefaultCompositionProvider() { + EventEmitter.call(this); + } + + DefaultCompositionProvider.prototype = + Object.create(EventEmitter.prototype); + + /** + * Check if this provider should be used to load composition for a + * particular domain object. + * @param {module:openmct.DomainObject} domainObject the domain object + * to check + * @returns {boolean} true if this provider can provide + * composition for a given domain object + * @memberof module:openmct.CompositionProvider# + * @method appliesTo + */ + DefaultCompositionProvider.prototype.appliesTo = function (domainObject) { + return !!domainObject.composition; + }; + + /** + * Load any domain objects contained in the composition of this domain + * object. + * @param {module:openmct.DomainObjcet} domainObject the domain object + * for which to load composition + * @returns {Promise.>} a promise for + * the domain objects in this composition + * @memberof module:openmct.CompositionProvider# + * @method load + */ + DefaultCompositionProvider.prototype.load = function (domainObject) { + return Promise.all(domainObject.composition.map(ObjectAPI.get)); + }; + + DefaultCompositionProvider.prototype.on = function ( + domainObject, + event, + listener, + context + ) { + // these can likely be passed through to the mutation service instead + // of using an eventemitter. + this.addListener( + makeEventName(domainObject, event), + listener, + context + ); + }; + + DefaultCompositionProvider.prototype.off = function ( + domainObject, + event, + listener, + context + ) { + // these can likely be passed through to the mutation service instead + // of using an eventemitter. + this.removeListener( + makeEventName(domainObject, event), + listener, + context + ); + }; + + /** + * Remove a domain object from another domain object's composition. + * + * This method is optional; if not present, adding to a domain object's + * composition using this provider will be disallowed. + * + * @param {module:openmct.DomainObject} domainObject the domain object + * which should have its composition modified + * @param {module:openmct.DomainObject} child the domain object to remove + * @memberof module:openmct.CompositionProvider# + * @method remove + */ + DefaultCompositionProvider.prototype.remove = function (domainObject, child) { + // TODO: this needs to be synchronized via mutation + var index = domainObject.composition.indexOf(child); + domainObject.composition.splice(index, 1); + this.emit(makeEventName(domainObject, 'remove'), child); + }; + + /** + * Add a domain object to another domain object's composition. + * + * This method is optional; if not present, adding to a domain object's + * composition using this provider will be disallowed. + * + * @param {module:openmct.DomainObject} domainObject the domain object + * which should have its composition modified + * @param {module:openmct.DomainObject} child the domain object to add + * @memberof module:openmct.CompositionProvider# + * @method add + */ + DefaultCompositionProvider.prototype.add = function (domainObject, child) { + // TODO: this needs to be synchronized via mutation + domainObject.composition.push(child.key); + this.emit(makeEventName(domainObject, 'add'), child); + }; + + return DefaultCompositionProvider; +}); diff --git a/src/api/objects/LegacyObjectAPIInterceptor.js b/src/api/objects/LegacyObjectAPIInterceptor.js new file mode 100644 index 00000000000..88758710e4c --- /dev/null +++ b/src/api/objects/LegacyObjectAPIInterceptor.js @@ -0,0 +1,128 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + './object-utils', + './objectEventEmitter' +], function ( + utils, + objectEventEmitter +) { + function ObjectServiceProvider(objectService, instantiate, topic) { + this.objectService = objectService; + this.instantiate = instantiate; + + this.generalTopic = topic('mutation'); + this.bridgeEventBuses(); + } + + /** + * Bridges old and new style mutation events to provide compatibility between the two APIs + * @private + */ + ObjectServiceProvider.prototype.bridgeEventBuses = function () { + var removeGeneralTopicListener; + var handleLegacyMutation; + + var handleMutation = function (newStyleObject) { + var keyString = utils.makeKeyString(newStyleObject.key); + var oldStyleObject = this.instantiate(utils.toOldFormat(newStyleObject), keyString); + + // Don't trigger self + removeGeneralTopicListener(); + + oldStyleObject.getCapability('mutation').mutate(function () { + return utils.toOldFormat(newStyleObject); + }); + + removeGeneralTopicListener = this.generalTopic.listen(handleLegacyMutation); + }.bind(this); + + handleLegacyMutation = function (legacyObject) { + var newStyleObject = utils.toNewFormat(legacyObject.getModel(), legacyObject.getId()); + + //Don't trigger self + objectEventEmitter.off('mutation', handleMutation); + objectEventEmitter.emit(newStyleObject.key.identifier + ":*", newStyleObject); + objectEventEmitter.on('mutation', handleMutation); + }.bind(this); + + objectEventEmitter.on('mutation', handleMutation); + removeGeneralTopicListener = this.generalTopic.listen(handleLegacyMutation); + }; + + ObjectServiceProvider.prototype.save = function (object) { + var key = object.key; + + return object.getCapability('persistence') + .persist() + .then(function () { + return utils.toNewFormat(object, key); + }); + }; + + ObjectServiceProvider.prototype.delete = function (object) { + // TODO! + }; + + ObjectServiceProvider.prototype.get = function (key) { + var keyString = utils.makeKeyString(key); + return this.objectService.getObjects([keyString]) + .then(function (results) { + var model = results[keyString].getModel(); + return utils.toNewFormat(model, key); + }); + }; + + // Injects new object API as a decorator so that it hijacks all requests. + // Object providers implemented on new API should just work, old API should just work, many things may break. + function LegacyObjectAPIInterceptor(openmct, ROOTS, instantiate, topic, objectService) { + this.getObjects = function (keys) { + var results = {}, + promises = keys.map(function (keyString) { + var key = utils.parseKeyString(keyString); + return openmct.objects.get(key) + .then(function (object) { + object = utils.toOldFormat(object); + results[keyString] = instantiate(object, keyString); + }); + }); + + return Promise.all(promises) + .then(function () { + return results; + }); + }; + + openmct.objects.supersecretSetFallbackProvider( + new ObjectServiceProvider(objectService, instantiate, topic) + ); + + ROOTS.forEach(function (r) { + openmct.objects.addRoot(utils.parseKeyString(r.id)); + }); + + return this; + } + + return LegacyObjectAPIInterceptor; +}); diff --git a/src/api/objects/MutableObject.js b/src/api/objects/MutableObject.js new file mode 100644 index 00000000000..003bc2c5b90 --- /dev/null +++ b/src/api/objects/MutableObject.js @@ -0,0 +1,90 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + 'lodash', + './objectEventEmitter' +], function ( + _, + objectEventEmitter +) { + var ANY_OBJECT_EVENT = "mutation"; + + /** + * The MutableObject wraps a DomainObject and provides getters and + * setters for + * @param eventEmitter + * @param object + * @interface MutableObject + */ + function MutableObject(object) { + this.object = object; + this.unlisteners = []; + } + + function qualifiedEventName(object, eventName) { + return [object.key.identifier, eventName].join(':'); + } + + MutableObject.prototype.stopListening = function () { + this.unlisteners.forEach(function (unlisten) { + unlisten(); + }); + }; + + /** + * Observe changes to this domain object. + * @param {string} path the property to observe + * @param {Function} callback a callback to invoke when new values for + * this property are observed + * @method on + * @memberof module:openmct.MutableObject# + */ + MutableObject.prototype.on = function (path, callback) { + var fullPath = qualifiedEventName(this.object, path); + objectEventEmitter.on(fullPath, callback); + this.unlisteners.push(objectEventEmitter.off.bind(objectEventEmitter, fullPath, callback)); + }; + + /** + * Modify this domain object. + * @param {string} path the property to modify + * @param {*} value the new value for this property + * @method set + * @memberof module:openmct.MutableObject# + */ + MutableObject.prototype.set = function (path, value) { + + _.set(this.object, path, value); + _.set(this.object, 'modified', Date.now()); + + //Emit event specific to property + objectEventEmitter.emit(qualifiedEventName(this.object, path), value); + //Emit wildcare event + objectEventEmitter.emit(qualifiedEventName(this.object, '*'), this.object); + + //Emit a general "any object" event + objectEventEmitter.emit(ANY_OBJECT_EVENT, this.object); + }; + + return MutableObject; +}); diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js new file mode 100644 index 00000000000..97c1b630c85 --- /dev/null +++ b/src/api/objects/ObjectAPI.js @@ -0,0 +1,232 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + 'lodash', + './object-utils', + './MutableObject' +], function ( + _, + utils, + MutableObject +) { + + + /** + * Utilities for loading, saving, and manipulating domain objects. + * @interface ObjectAPI + * @memberof module:openmct + * @implements {module:openmct.ObjectProvider} + */ + + function ObjectAPI() { + this.providers = {}; + this.rootRegistry = []; + this.rootProvider = { + 'get': function () { + return Promise.resolve({ + name: 'The root object', + type: 'root', + composition: this.rootRegistry + }); + }.bind(this) + }; + } + + ObjectAPI.prototype.supersecretSetFallbackProvider = function (p) { + this.fallbackProvider = p; + }; + + // Retrieve the provider for a given key. + ObjectAPI.prototype.getProvider = function (key) { + if (key.identifier === 'ROOT') { + return this.rootProvider; + } + return this.providers[key.namespace] || this.fallbackProvider; + }; + + + /** + * Register a new object provider for a particular namespace. + * + * @param {string} namespace the namespace for which to provide objects + * @param {module:openmct.ObjectProvider} provider the provider which + * will handle loading domain objects from this namespace + * @memberof {module:openmct.ObjectAPI#} + * @name addProvider + */ + ObjectAPI.prototype.addProvider = function (namespace, provider) { + this.providers[namespace] = provider; + }; + + /** + * Provides the ability to read, write, and delete domain objects. + * + * When registering a new object provider, all methods on this interface + * are optional. + * + * @interface ObjectProvider + * @memberof module:openmct + */ + + /** + * Save this domain object in its current state. + * + * @method save + * @memberof module:openmct.ObjectProvider# + * @param {module:openmct.DomainObject} domainObject the domain object to + * save + * @returns {Promise} a promise which will resolve when the domain object + * has been saved, or be rejected if it cannot be saved + */ + + /** + * Delete this domain object. + * + * @method delete + * @memberof module:openmct.ObjectProvider# + * @param {module:openmct.DomainObject} domainObject the domain object to + * delete + * @returns {Promise} a promise which will resolve when the domain object + * has been deleted, or be rejected if it cannot be deleted + */ + + /** + * Get a domain object. + * + * @method get + * @memberof module:openmct.ObjectProvider# + * @param {string} key the key for the domain object to load + * @returns {Promise} a promise which will resolve when the domain object + * has been saved, or be rejected if it cannot be saved + */ + + [ + 'save', + 'delete', + 'get' + ].forEach(function (method) { + ObjectAPI.prototype[method] = function () { + var key = arguments[0], + provider = this.getProvider(key); + + if (!provider) { + throw new Error('No Provider Matched'); + } + + if (!provider[method]) { + throw new Error('Provider does not support [' + method + '].'); + } + + return provider[method].apply(provider, arguments); + }; + }); + + /** + * Add a root-level object. + * @param {module:openmct.DomainObject} domainObject the root-level object + * to add. + * @method addRoot + * @memberof module:openmct.ObjectAPI# + */ + ObjectAPI.prototype.addRoot = function (key) { + this.rootRegistry.unshift(key); + }; + + /** + * Remove a root-level object. + * @param {module:openmct.ObjectAPI~Identifier} id the identifier of the + * root-level object to remove. + * @method removeRoot + * @memberof module:openmct.ObjectAPI# + */ + ObjectAPI.prototype.removeRoot = function (key) { + this.rootRegistry = this.rootRegistry.filter(function (k) { + return ( + k.identifier !== key.identifier || + k.namespace !== key.namespace + ); + }); + }; + + /** + * Modify a domain object. + * @param {module:openmct.DomainObject} object the object to mutate + * @param {string} path the property to modify + * @param {*} value the new value for this property + * @method mutate + * @memberof module:openmct.ObjectAPI# + */ + ObjectAPI.prototype.mutate = function (domainObject, path, value) { + return new MutableObject(domainObject).set(path, value); + }; + + /** + * Observe changes to a domain object. + * @param {module:openmct.DomainObject} object the object to observe + * @param {string} path the property to observe + * @param {Function} callback a callback to invoke when new values for + * this property are observed + * @method observe + * @memberof module:openmct.ObjectAPI# + */ + ObjectAPI.prototype.observe = function (domainObject, path, callback) { + return new MutableObject(domainObject).on(path, callback); + }; + + /** + * Uniquely identifies a domain object. + * + * @typedef Identifier + * @memberof module:openmct.ObjectAPI~ + * @property {string} namespace the namespace to/from which this domain + * object should be loaded/stored. + * @property {string} key a unique identifier for the domain object + * within that namespace + */ + + /** + * A domain object is an entity of relevance to a user's workflow, that + * should appear as a distinct and meaningful object within the user + * interface. Examples of domain objects are folders, telemetry sensors, + * and so forth. + * + * A few common properties are defined for domain objects. Beyond these, + * individual types of domain objects may add more as they see fit. + * + * @property {module:openmct.ObjectAPI~Identifier} identifier a key/namespace pair which + * uniquely identifies this domain object + * @property {string} type the type of domain object + * @property {string} name the human-readable name for this domain object + * @property {string} [creator] the user name of the creator of this domain + * object + * @property {number} [modified] the time, in milliseconds since the UNIX + * epoch, at which this domain object was last modified + * @property {module:openmct.ObjectAPI~Identifier[]} [composition] if + * present, this will be used by the default composition provider + * to load domain objects + * @typedef DomainObject + * @memberof module:openmct + */ + + return ObjectAPI; +}); diff --git a/src/api/objects/bundle.js b/src/api/objects/bundle.js new file mode 100644 index 00000000000..83a865b84ed --- /dev/null +++ b/src/api/objects/bundle.js @@ -0,0 +1,51 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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([ + './LegacyObjectAPIInterceptor', + 'legacyRegistry' +], function ( + LegacyObjectAPIInterceptor, + legacyRegistry +) { + legacyRegistry.register('src/api/objects', { + name: 'Object API', + description: 'The public Objects API', + extensions: { + components: [ + { + provides: "objectService", + type: "decorator", + priority: "mandatory", + implementation: LegacyObjectAPIInterceptor, + depends: [ + "openmct", + "roots[]", + "instantiate", + "topic" + ] + } + ] + } + }); +}); diff --git a/src/api/objects/object-utils.js b/src/api/objects/object-utils.js new file mode 100644 index 00000000000..f749a8110b4 --- /dev/null +++ b/src/api/objects/object-utils.js @@ -0,0 +1,109 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + +], function ( + +) { + + // take a key string and turn it into a key object + // 'scratch:root' ==> {namespace: 'scratch', identifier: 'root'} + var parseKeyString = function (key) { + if (typeof key === 'object') { + return key; + } + var namespace = '', + identifier = key; + for (var i = 0, escaped = false; i < key.length; i++) { + if (escaped) { + escaped = false; + namespace += key[i]; + } else { + if (key[i] === "\\") { + escaped = true; + } else if (key[i] === ":") { + // namespace = key.slice(0, i); + identifier = key.slice(i + 1); + break; + } + namespace += key[i]; + } + } + + if (key === namespace) { + namespace = ''; + } + + return { + namespace: namespace, + identifier: identifier + }; + }; + + // take a key and turn it into a key string + // {namespace: 'scratch', identifier: 'root'} ==> 'scratch:root' + var makeKeyString = function (key) { + if (typeof key === 'string') { + return key; + } + if (!key.namespace) { + return key.identifier; + } + return [ + key.namespace.replace(':', '\\:'), + key.identifier.replace(':', '\\:') + ].join(':'); + }; + + // Converts composition to use key strings instead of keys + var toOldFormat = function (model) { + model = JSON.parse(JSON.stringify(model)); + delete model.key; + if (model.composition) { + model.composition = model.composition.map(makeKeyString); + } + return model; + }; + + // converts composition to use keys instead of key strings + var toNewFormat = function (model, key) { + model = JSON.parse(JSON.stringify(model)); + model.key = key; + if (model.composition) { + model.composition = model.composition.map(parseKeyString); + } + return model; + }; + + var equals = function (a, b) { + return makeKeyString(a.key) === makeKeyString(b.key); + }; + + return { + toOldFormat: toOldFormat, + toNewFormat: toNewFormat, + makeKeyString: makeKeyString, + parseKeyString: parseKeyString, + equals: equals + }; +}); diff --git a/src/api/objects/objectEventEmitter.js b/src/api/objects/objectEventEmitter.js new file mode 100644 index 00000000000..8f8bf5d3258 --- /dev/null +++ b/src/api/objects/objectEventEmitter.js @@ -0,0 +1,32 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + "EventEmitter" +], function ( + EventEmitter +) { + /** + * Provides a singleton event bus for sharing between objects. + */ + return new EventEmitter(); +}); diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js new file mode 100644 index 00000000000..65f603ffa28 --- /dev/null +++ b/src/api/telemetry/TelemetryAPI.js @@ -0,0 +1,318 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +/** + +var key = '114ced6c-deb7-4169-ae71-68c571665514'; +MCT.objects.getObject([key]) + .then(function (results) { + console.log('got results'); + return results[key]; + }) + .then(function (domainObject) { + console.log('got object'); + MCT.telemetry.subscribe(domainObject, function (datum) { + console.log('gotData!', datum); + }); + }); +}); + + +*/ + +define([ + 'lodash', + 'EventEmitter' +], function ( + _, + EventEmitter +) { + /** + * A LimitEvaluator may be used to detect when telemetry values + * have exceeded nominal conditions. + * + * @interface LimitEvaluator + * @memberof module:openmct.TelemetryAPI~ + */ + + /** + * Check for any limit violations associated with a telemetry datum. + * @method evaluate + * @param {*} datum the telemetry datum to evaluate + * @param {TelemetryProperty} the property to check for limit violations + * @memberof module:openmct.TelemetryAPI~LimitEvaluator + * @returns {module:openmct.TelemetryAPI~LimitViolation} metadata about + * the limit violation, or undefined if a value is within limits + */ + + /** + * A violation of limits defined for a telemetry property. + * @typedef LimitViolation + * @memberof {module:openmct.TelemetryAPI~} + * @property {string} cssclass the class (or space-separated classes) to + * apply to display elements for values which violate this limit + * @property {string} name the human-readable name for the limit violation + */ + + /** + * A TelemetryFormatter converts telemetry values for purposes of + * display as text. + * + * @interface TelemetryFormatter + * @memberof module:openmct.TelemetryAPI~ + */ + + /** + * Retrieve the 'key' from the datum and format it accordingly to + * telemetry metadata in domain object. + * + * @method format + * @memberof module:openmct.TelemetryAPI~TelemetryFormatter# + */ + + + + + // format map is a placeholder until we figure out format service. + var FORMAT_MAP = { + generic: function (range) { + return function (datum) { + return datum[range.key]; + }; + }, + enum: function (range) { + var enumMap = _.indexBy(range.enumerations, 'value'); + return function (datum) { + try { + return enumMap[datum[range.valueKey]].text; + } catch (e) { + return datum[range.valueKey]; + } + }; + } + }; + + FORMAT_MAP.number = + FORMAT_MAP.float = + FORMAT_MAP.integer = + FORMAT_MAP.ascii = + FORMAT_MAP.generic; + + /** + * Describes a property which would be found in a datum of telemetry + * associated with a particular domain object. + * + * @typedef TelemetryProperty + * @memberof module:openmct.TelemetryAPI~ + * @property {string} key the name of the property in the datum which + * contains this telemetry value + * @property {string} name the human-readable name for this property + * @property {string} [units] the units associated with this property + * @property {boolean} [temporal] true if this property is a timestamp, or + * may be otherwise used to order telemetry in a time-like + * fashion; default is false + * @property {boolean} [numeric] true if the values for this property + * can be interpreted plainly as numbers; default is true + * @property {boolean} [enumerated] true if this property may have only + * certain specific values; default is false + * @property {string} [values] for enumerated states, an ordered list + * of possible values + */ + + /** + * Describes and bounds requests for telemetry data. + * + * @typedef TelemetryRequest + * @memberof module:openmct.TelemetryAPI~ + * @property {string} sort the key of the property to sort by. This may + * be prefixed with a "+" or a "-" sign to sort in ascending + * or descending order respectively. If no prefix is present, + * ascending order will be used. + * @property {*} start the lower bound for values of the sorting property + * @property {*} end the upper bound for values of the sorting property + * @property {string[]} strategies symbolic identifiers for strategies + * (such as `minmax`) which may be recognized by providers; + * these will be tried in order until an appropriate provider + * is found + */ + + /** + * Provides telemetry data. To connect to new data sources, new + * TelemetryProvider implementations should be + * [registered]{@link module:openmct.TelemetryAPI#addProvider}. + * + * @interface TelemetryProvider + * @memberof module:openmct.TelemetryAPI~ + */ + + + + /** + * An interface for retrieving telemetry data associated with a domain + * object. + * + * @interface TelemetryAPI + * @augments module:openmct.TelemetryAPI~TelemetryProvider + * @memberof module:openmct + */ + function TelemetryAPI() { + this.providers = []; + } + + /** + * Check if this provider can supply telemetry data associated with + * this domain object. + * + * @method canProvideTelemetry + * @param {module:openmct.DomainObject} domainObject the object for + * which telemetry would be provided + * @returns {boolean} true if telemetry can be provided + * @memberof module:openmct.TelemetryAPI~TelemetryProvider# + */ + TelemetryAPI.prototype.canProvideTelemetry = function (domainObject) { + return this.providers.some(function (provider) { + return provider.canProvideTelemetry(domainObject); + }); + }; + + /** + * Register a telemetry provider with the telemetry service. This + * allows you to connect alternative telemetry sources. + * @method addProvider + * @memberof module:openmct.TelemetryAPI# + * @param {module:openmct.TelemetryAPI~TelemetryProvider} provider the new + * telemetry provider + * @param {string} [strategy] the request strategy supported by + * this provider. If omitted, this will be used as a + * default provider (when no strategy is requested or no + * matching strategy is found.) + */ + TelemetryAPI.prototype.addProvider = function (provider) { + this.providers.push(provider); + }; + + /** + * Request historical telemetry for a domain object. + * The `options` argument allows you to specify filters + * (start, end, etc.), sort order, and strategies for retrieving + * telemetry (aggregation, latest available, etc.). + * + * @method request + * @memberof module:openmct.TelemetryAPI~TelemetryProvider# + * @param {module:openmct.DomainObject} domainObject the object + * which has associated telemetry + * @param {module:openmct.TelemetryAPI~TelemetryRequest} options + * options for this historical request + * @returns {Promise.} a promise for an array of + * telemetry data + */ + TelemetryAPI.prototype.request = function (domainObject, options) { + var provider = this.providers.reduce(function (result, p) { + return (p.canProvideTelemetry(domainObject)) ? + p : result; + }, undefined); + return provider ? + provider.request(domainObject, options) : + Promise.reject([]); + }; + + /** + * Subscribe to realtime telemetry for a specific domain object. + * The callback will be called whenever data is received from a + * realtime provider. + * + * @method subscribe + * @memberof module:openmct.TelemetryAPI~TelemetryProvider# + * @param {module:openmct.DomainObject} domainObject the object + * which has associated telemetry + * @param {Function} callback the callback to invoke with new data, as + * it becomes available + * @param {module:openmct.TelemetryAPI~TelemetryRequest} options + * options for this request + * @returns {Function} a function which may be called to terminate + * the subscription + */ + + /** + * Get a list of all telemetry properties defined for this + * domain object. + * + * @param {module:openmct.DomainObject} domainObject the domain + * object for which to request telemetry + * @returns {module:openmct.TelemetryAPI~TelemetryProperty[]} + * telemetry metadata + * @method properties + * @memberof module:openmct.TelemetryAPI~TelemetryProvider# + */ + + /** + * Telemetry formatters help you format telemetry values for + * display. Under the covers, they use telemetry metadata to + * interpret your telemetry data, and then they use the format API + * to format that data for display. + * + * This method is optional. + * If a provider does not implement this method, it is presumed + * that all telemetry associated with this domain object can + * be formatted correctly by string coercion. + * + * @param {module:openmct.DomainObject} domainObject the domain + * object for which to format telemetry + * @returns {module:openmct.TelemetryAPI~TelemetryFormatter} + * @method formatter + * @memberof module:openmct.TelemetryAPI~TelemetryProvider# + */ + + /** + * Get a limit evaluator for this domain object. + * Limit Evaluators help you evaluate limit and alarm status of individual telemetry datums for display purposes without having to interact directly with the Limit API. + * + * This method is optional. + * If a provider does not implement this method, it is presumed + * that no limits are defined for this domain object's telemetry. + * + * @param {module:openmct.DomainObject} domainObject the domain + * object for which to evaluate limits + * @returns {module:openmct.TelemetryAPI~LimitEvaluator} + * @method limitEvaluator + * @memberof module:openmct.TelemetryAPI~TelemetryProvider# + */ + _.forEach({ + request: undefined, + subscribe: undefined, + properties: [], + formatter: undefined, + limitEvaluator: undefined + }, function (defaultValue, method) { + TelemetryAPI.prototype[method] = function (domainObject) { + var provider = this.providers.reduce(function (result, p) { + return (p.canProvideTelemetry(domainObject)) ? + p : result; + }, undefined); + return provider ? + provider[method].apply(provider, arguments) : + defaultValue; + }; + }); + + return TelemetryAPI; +}); diff --git a/src/api/telemetry/bundle.js b/src/api/telemetry/bundle.js new file mode 100644 index 00000000000..a389ebf804f --- /dev/null +++ b/src/api/telemetry/bundle.js @@ -0,0 +1,45 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + './TelemetryAPI', + 'legacyRegistry' +], function ( + TelemetryAPI, + legacyRegistry +) { + legacyRegistry.register('api/telemetry-api', { + name: 'Telemetry API', + description: 'The public Telemetry API', + extensions: { + runs: [ + { + key: "TelemetryAPI", + implementation: TelemetryAPI, + depends: [ + 'formatService' + ] + } + ] + } + }); +}); diff --git a/src/api/types/TypeRegistry.js b/src/api/types/TypeRegistry.js new file mode 100644 index 00000000000..e01bea4a9d8 --- /dev/null +++ b/src/api/types/TypeRegistry.js @@ -0,0 +1,51 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([], function () { + + /** + * A TypeRegistry maintains the definitions for different types + * that domain objects may have. + * @interface TypeRegistry + * @memberof module:openmct + */ + function TypeRegistry() { + this.types = {}; + } + + /** + * Register a new type of view. + * + * @param {string} typeKey a string identifier for this type + * @param {module:openmct.Type} type the type to add + * @method addProvider + * @memberof module:openmct.TypeRegistry# + */ + TypeRegistry.prototype.addType = function (typeKey, type) { + this.types[typeKey] = type; + }; + + + return TypeRegistry; +}); + + diff --git a/src/api/ui/Dialog.js b/src/api/ui/Dialog.js new file mode 100644 index 00000000000..63408f31abe --- /dev/null +++ b/src/api/ui/Dialog.js @@ -0,0 +1,107 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define(['text!./dialog.html', 'zepto'], function (dialogTemplate, $) { + + /** + * A dialog may be displayed to show blocking content to users. + * @param {module:openmct.View} view the view to show in the dialog + * @param {string} [title] the title for this dialog + * @constructor + * @memberof module:openmct + */ + function Dialog(view, title) { + this.view = view; + this.title = title; + this.showing = false; + this.enabledState = true; + } + + /** + * Display this dialog. + * @returns {Promise} a promise that will be resolved if the user + * chooses "OK", an rejected if the user chooses "cancel" + * @method show + * @memberof module:openmct.Dialog# + */ + Dialog.prototype.show = function () { + if (this.showing) { + throw new Error("Dialog already showing."); + } + + var $body = $('body'); + var $dialog = $(dialogTemplate); + var $contents = $dialog.find('.contents .editor'); + var $close = $dialog.find('.close'); + + var $ok = $dialog.find('.ok'); + var $cancel = $dialog.find('.cancel'); + + if (this.title) { + $dialog.find('.title').text(this.title); + } + + $body.append($dialog); + this.view.show($contents[0]); + this.$dialog = $dialog; + this.$ok = $ok; + this.showing = true; + + [$ok, $cancel, $close].forEach(function ($button) { + $button.on('click', this.hide.bind(this)); + }.bind(this)); + + return new Promise(function (resolve, reject) { + $ok.on('click', resolve); + $cancel.on('click', reject); + $close.on('click', reject); + }); + }; + + Dialog.prototype.hide = function () { + if (!this.showing) { + return; + } + this.$dialog.remove(); + this.view.destroy(); + this.showing = false; + }; + + /** + * Get or set the "enabled" state of the OK button for this dialog. + * @param {boolean} [state] true to enable, false to disable + * @returns {boolean} true if enabled, false if disabled + * @method enabled + * @memberof module:openmct.Dialog# + */ + Dialog.prototype.enabled = function (state) { + if (state !== undefined) { + this.enabledState = state; + if (this.showing) { + this.$ok.toggleClass('disabled', !state); + } + } + return this.enabledState; + }; + + return Dialog; +}); diff --git a/src/api/ui/GestureAPI.js b/src/api/ui/GestureAPI.js new file mode 100644 index 00000000000..211318673af --- /dev/null +++ b/src/api/ui/GestureAPI.js @@ -0,0 +1,68 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([], function () { + /** + * Allows support for common user actions to be attached to views. + * @interface GestureAPI + * @memberof module:openmct + */ + function GestureAPI(selectGesture, contextMenuGesture) { + this.selectGesture = selectGesture; + this.contextMenuGesture = contextMenuGesture; + } + + /** + * Designate an HTML element as selectable, and associated with a + * particular object. + * + * @param {HTMLElement} htmlElement the element to make selectable + * @param {*} item the object which should become selected when this + * element is clicked. + * @returns {Function} a function to remove selectability from this + * HTML element. + * @method selectable + * @memberof module:openmct.GestureAPI# + */ + GestureAPI.prototype.selectable = function (htmlElement, item) { + return this.selectGesture.apply(htmlElement, item); + }; + + + /** + * Designate an HTML element as having a context menu associated with + * the provided item. + * + * @private + * @param {HTMLElement} htmlElement the element to make selectable + * @param {*} item the object for which a context menu should appear + * @returns {Function} a function to remove this geture from this + * HTML element. + * @method selectable + * @memberof module:openmct.GestureAPI# + */ + GestureAPI.prototype.contextMenu = function (htmlElement, item) { + return this.contextMenuGesture.apply(htmlElement, item); + }; + + return GestureAPI; +}); diff --git a/src/api/ui/dialog.html b/src/api/ui/dialog.html new file mode 100644 index 00000000000..83181c5c780 --- /dev/null +++ b/src/api/ui/dialog.html @@ -0,0 +1,21 @@ +
+
+
+ x +
+
+
+
+
+
+
+
+ OK + Cancel +
+
+
+
+ + + diff --git a/src/defaultRegistry.js b/src/defaultRegistry.js new file mode 100644 index 00000000000..e75b513a3ca --- /dev/null +++ b/src/defaultRegistry.js @@ -0,0 +1,135 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + 'legacyRegistry', + + '../src/adapter/bundle', + '../src/api/objects/bundle', + + '../example/builtins/bundle', + '../example/composite/bundle', + '../example/eventGenerator/bundle', + '../example/export/bundle', + '../example/extensions/bundle', + '../example/forms/bundle', + '../example/generator/bundle', + '../example/identity/bundle', + '../example/imagery/bundle', + '../example/mobile/bundle', + '../example/msl/bundle', + '../example/notifications/bundle', + '../example/persistence/bundle', + '../example/plotOptions/bundle', + '../example/policy/bundle', + '../example/profiling/bundle', + '../example/scratchpad/bundle', + '../example/taxonomy/bundle', + '../example/worker/bundle', + + '../platform/commonUI/about/bundle', + '../platform/commonUI/browse/bundle', + '../platform/commonUI/dialog/bundle', + '../platform/commonUI/edit/bundle', + '../platform/commonUI/formats/bundle', + '../platform/commonUI/general/bundle', + '../platform/commonUI/inspect/bundle', + '../platform/commonUI/mobile/bundle', + '../platform/commonUI/notification/bundle', + '../platform/commonUI/regions/bundle', + '../platform/commonUI/themes/espresso/bundle', + '../platform/commonUI/themes/snow/bundle', + '../platform/containment/bundle', + '../platform/core/bundle', + '../platform/entanglement/bundle', + '../platform/execution/bundle', + '../platform/exporters/bundle', + '../platform/features/clock/bundle', + '../platform/features/conductor/bundle', + '../platform/features/imagery/bundle', + '../platform/features/layout/bundle', + '../platform/features/pages/bundle', + '../platform/features/plot/bundle', + '../platform/features/static-markup/bundle', + '../platform/features/table/bundle', + '../platform/features/timeline/bundle', + '../platform/forms/bundle', + '../platform/framework/bundle', + '../platform/framework/src/load/Bundle', + '../platform/identity/bundle', + '../platform/persistence/aggregator/bundle', + '../platform/persistence/couch/bundle', + '../platform/persistence/elastic/bundle', + '../platform/persistence/local/bundle', + '../platform/persistence/queue/bundle', + '../platform/policy/bundle', + '../platform/representation/bundle', + '../platform/search/bundle', + '../platform/status/bundle', + '../platform/telemetry/bundle' +], function (legacyRegistry) { + + var DEFAULTS = [ + 'src/adapter', + 'src/api/objects', + 'platform/framework', + 'platform/core', + 'platform/representation', + 'platform/commonUI/about', + 'platform/commonUI/browse', + 'platform/commonUI/edit', + 'platform/commonUI/dialog', + 'platform/commonUI/formats', + 'platform/commonUI/general', + 'platform/commonUI/inspect', + 'platform/commonUI/mobile', + 'platform/commonUI/themes/espresso', + 'platform/commonUI/notification', + 'platform/containment', + 'platform/execution', + 'platform/exporters', + 'platform/telemetry', + 'platform/features/clock', + 'platform/features/imagery', + 'platform/features/layout', + 'platform/features/pages', + 'platform/features/plot', + 'platform/features/timeline', + 'platform/features/table', + 'platform/forms', + 'platform/identity', + 'platform/persistence/aggregator', + 'platform/persistence/local', + 'platform/persistence/queue', + 'platform/policy', + 'platform/entanglement', + 'platform/search', + 'platform/status', + 'platform/commonUI/regions' + ]; + + DEFAULTS.forEach(function (bundlePath) { + legacyRegistry.enable(bundlePath); + }); + + return legacyRegistry; +}); diff --git a/src/end.frag b/src/end.frag new file mode 100644 index 00000000000..5ef11daae2a --- /dev/null +++ b/src/end.frag @@ -0,0 +1,3 @@ + + return require('main'); +})); \ No newline at end of file diff --git a/src/selection/ContextManager.js b/src/selection/ContextManager.js new file mode 100644 index 00000000000..1bd524dcde2 --- /dev/null +++ b/src/selection/ContextManager.js @@ -0,0 +1,78 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define(['zepto'], function ($) { + /** + * @typedef Context + * @property {*} item + * @property {HTMLElement} element + * @property {Context} parent the containing context (may be undefined) + * @memberof module:openmct + */ + + + function ContextManager() { + this.counter = 0; + this.contexts = {}; + } + + ContextManager.prototype.nextId = function () { + this.counter += 1; + return "context-" + this.counter; + }; + + ContextManager.prototype.context = function (item, htmlElement) { + var $element = $(htmlElement); + var id = $element.attr('data-context') || this.nextId(); + + $element.attr('data-context', id); + + if (this.contexts[id] && this.contexts[id].item !== item) { + this.release(htmlElement); + } + + if (!this.contexts[id]) { + var $parent = $element.closest('[data-context]'); + var parentId = $parent.attr('data-context'); + var parentContext = parentId ? this.contexts[parentId] : undefined; + this.contexts[id] = { + item: item, + element: htmlElement, + parent: parentContext + }; + } + + return this.contexts[id]; + }; + + ContextManager.prototype.release = function (htmlElement) { + var $element = $(htmlElement); + var id = $element.attr('data-context'); + + if (id) { + delete this.contexts[id]; + $element.removeAttr('data-context'); + } + }; + + return ContextManager; +}); diff --git a/src/selection/HoverGesture.js b/src/selection/HoverGesture.js new file mode 100644 index 00000000000..5c1c7ae2ad9 --- /dev/null +++ b/src/selection/HoverGesture.js @@ -0,0 +1,58 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define(['zepto'], function ($) { + function HoverGesture(hoverManager) { + this.hoverManager = hoverManager; + } + + HoverGesture.prototype.apply = function (htmlElement) { + var $element = $(htmlElement); + var hoverManager = this.hoverManager; + + function update() { + $(hoverManager.all()).removeClass('hovering'); + $(hoverManager.top()).addClass('hovering'); + } + + function enter() { + hoverManager.add(htmlElement); + update(); + } + + function leave() { + hoverManager.remove(htmlElement); + update(); + } + + $element.on('mouseenter', enter); + $element.on('mouseleave', leave); + + return function () { + leave(); + $element.off('mouseenter', enter); + $element.off('mouseleave', leave); + }.bind(this); + }; + + return HoverGesture; +}); diff --git a/src/selection/SelectGesture.js b/src/selection/SelectGesture.js new file mode 100644 index 00000000000..aaea15a4841 --- /dev/null +++ b/src/selection/SelectGesture.js @@ -0,0 +1,60 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define(['zepto'], function ($) { + function SelectGesture(selection, contextManager) { + this.selection = selection; + this.contextManager = contextManager; + } + + SelectGesture.prototype.apply = function (htmlElement, item) { + var $element = $(htmlElement); + var contextManager = this.contextManager; + var selection = this.selection; + var path = contextManager.path(item, htmlElement); + + function select() { + selection.add(path); + } + + function change() { + var selected = selection.primary(); + $element.toggleClass( + 'selected', + selected && path.matches(selected) + ); + } + + $element.addClass('selectable'); + $element.on('click', select); + selection.on('change', change); + change(); // Initialize + + return function () { + contextManager.release(htmlElement); + $element.off('click', select); + selection.off('change', change); + }; + }; + + return SelectGesture; +}); diff --git a/src/selection/Selection.js b/src/selection/Selection.js new file mode 100644 index 00000000000..e6e3c19287a --- /dev/null +++ b/src/selection/Selection.js @@ -0,0 +1,69 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define(['EventEmitter'], function (EventEmitter) { + + /** + * Manages selection state for Open MCT + * @private + */ + function Selection() { + EventEmitter.call(this); + this.selected = []; + } + + Selection.prototype = Object.create(EventEmitter.prototype); + + Selection.prototype.add = function (context) { + this.clear(); // Only allow single select as initial simplification + this.selected.push(context); + this.emit('change'); + }; + + Selection.prototype.remove = function (path) { + this.selected = this.selected.filter(function (otherPath) { + return !path.matches(otherPath); + }); + this.emit('change'); + }; + + Selection.prototype.contains = function (path) { + return this.selected.some(function (otherPath) { + return path.matches(otherPath); + }); + }; + + Selection.prototype.clear = function () { + this.selected = []; + this.emit('change'); + }; + + Selection.prototype.primary = function () { + return this.selected[this.selected.length - 1]; + }; + + Selection.prototype.all = function () { + return this.selected; + }; + + return Selection; +}); diff --git a/src/start.frag b/src/start.frag new file mode 100644 index 00000000000..5cd7cf2114a --- /dev/null +++ b/src/start.frag @@ -0,0 +1,39 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +/** + * Open MCT https://nasa.github.io/openmct/ + * Version @@version + * Built @@timestamp + * Revision @@revision + * Branch @@branch + */ + +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define([], factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.MCT = factory(); + } +}(this, function() { diff --git a/src/ui/ViewRegistry.js b/src/ui/ViewRegistry.js new file mode 100644 index 00000000000..074a9fbf2ea --- /dev/null +++ b/src/ui/ViewRegistry.js @@ -0,0 +1,145 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([], function () { + /** + * A ViewRegistry maintains the definitions for different kinds of views + * that may occur in different places in the user interface. + * @interface ViewRegistry + * @memberof module:openmct + */ + function ViewRegistry() { + this.providers = []; + } + + ViewRegistry.prototype.get = function (item) { + return this.providers.filter(function (provider) { + return provider.canView(item); + }); + }; + + /** + * Register a new type of view. + * + * @param {module:openmct.ViewProvider} provider the provider for this view + * @method addProvider + * @memberof module:openmct.ViewRegistry# + */ + ViewRegistry.prototype.addProvider = function (provider) { + this.providers.push(provider); + }; + + /** + * A View is used to provide displayable content, and to react to + * associated life cycle events. + * + * @name View + * @interface + * @memberof module:openmct + */ + + /** + * Populate the supplied DOM element with the contents of this view. + * + * View implementations should use this method to attach any + * listeners or acquire other resources that are necessary to keep + * the contents of this view up-to-date. + * + * @param {HTMLElement} container the DOM element to populate + * @method show + * @memberof module:openmct.View# + */ + + /** + * Release any resources associated with this view. + * + * View implementations should use this method to detach any + * listeners or release other resources that are no longer necessary + * once a view is no longer used. + * + * @method destroy + * @memberof module:openmct.View# + */ + + /** + * Exposes types of views in Open MCT. + * + * @interface ViewProvider + * @memberof module:openmct + */ + + /** + * Check if this provider can supply views for a domain object. + * + * When called by Open MCT, this may include additional arguments + * which are on the path to the object to be viewed; for instance, + * when viewing "A Folder" within "My Items", this method will be + * invoked with "A Folder" (as a domain object) as the first argument, + * and "My Items" as the second argument. + * + * @method canView + * @memberof module:openmct.ViewProvider# + * @param {module:openmct.DomainObject} domainObject the domain object + * to be viewed + * @returns {boolean} true if this domain object can be viewed using + * this provider + */ + + /** + * Provide a view of this object. + * + * When called by Open MCT, this may include additional arguments + * which are on the path to the object to be viewed; for instance, + * when viewing "A Folder" within "My Items", this method will be + * invoked with "A Folder" (as a domain object) as the first argument, + * and "My Items" as the second argument. + * + * @method view + * @memberof module:openmct.ViewProvider# + * @param {*} object the object to be viewed + * @returns {module:openmct.View} a view of this domain object + */ + + /** + * Get metadata associated with this view provider. This may be used + * to populate the user interface with options associated with this + * view provider. + * + * @method metadata + * @memberof module:openmct.ViewProvider# + * @returns {module:openmct.ViewProvider~ViewMetadata} view metadata + */ + + /** + * @typedef ViewMetadata + * @memberof module:openmct.ViewProvider~ + * @property {string} name the human-readable name of this view + * @property {string} key a machine-readable name for this view + * @property {string} [description] a longer-form description (typically + * a single sentence or short paragraph) of this kind of view + * @property {string} cssclass the CSS class to apply to labels for this + * view (to add icons, for instance) + */ + + return ViewRegistry; + +}); diff --git a/test-main.js b/test-main.js index e006094a248..74d554701bd 100644 --- a/test-main.js +++ b/test-main.js @@ -53,6 +53,7 @@ requirejs.config({ "angular": "bower_components/angular/angular.min", "angular-route": "bower_components/angular-route/angular-route.min", "csv": "bower_components/comma-separated-values/csv.min", + "EventEmitter": "bower_components/eventemitter3/index", "es6-promise": "bower_components/es6-promise/es6-promise.min", "html2canvas": "bower_components/html2canvas/build/html2canvas.min", "moment": "bower_components/moment/moment", @@ -61,7 +62,8 @@ requirejs.config({ "screenfull": "bower_components/screenfull/dist/screenfull.min", "text": "bower_components/text/text", "uuid": "bower_components/node-uuid/uuid", - "zepto": "bower_components/zepto/zepto.min" + "zepto": "bower_components/zepto/zepto.min", + "lodash": "bower_components/lodash/lodash" }, "shim": { @@ -71,6 +73,9 @@ requirejs.config({ "angular-route": { "deps": [ "angular" ] }, + "EventEmitter": { + "exports": "EventEmitter" + }, "moment-duration-format": { "deps": [ "moment" ] }, @@ -79,6 +84,9 @@ requirejs.config({ }, "zepto": { "exports": "Zepto" + }, + "lodash": { + "exports": "lodash" } }, From 772d24b173b0039972783e3075329e1ee3d772c2 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 6 Oct 2016 17:46:26 -0700 Subject: [PATCH 54/78] [API] Avoid memory leak https://github.com/nasa/openmct/pull/1212#discussion_r81882955 --- src/api/objects/ObjectAPI.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index 97c1b630c85..eb67c7adbdb 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -190,7 +190,9 @@ define([ * @memberof module:openmct.ObjectAPI# */ ObjectAPI.prototype.observe = function (domainObject, path, callback) { - return new MutableObject(domainObject).on(path, callback); + var mutableObject = new MutableObject(domainObject); + mutableObject.on(path, callback); + return mutableObject.stopListening.bind(mutableObject); }; /** From 09d59f00e7af7592f848562143a9bc7100b45fef Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 6 Oct 2016 17:48:08 -0700 Subject: [PATCH 55/78] [API] Remove silly old comment https://github.com/nasa/openmct/pull/1212#discussion_r82086807 --- src/api/telemetry/TelemetryAPI.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js index 65f603ffa28..cc3a265dd9a 100644 --- a/src/api/telemetry/TelemetryAPI.js +++ b/src/api/telemetry/TelemetryAPI.js @@ -20,25 +20,6 @@ * at runtime from the About dialog for additional information. *****************************************************************************/ -/** - -var key = '114ced6c-deb7-4169-ae71-68c571665514'; -MCT.objects.getObject([key]) - .then(function (results) { - console.log('got results'); - return results[key]; - }) - .then(function (domainObject) { - console.log('got object'); - MCT.telemetry.subscribe(domainObject, function (datum) { - console.log('gotData!', datum); - }); - }); -}); - - -*/ - define([ 'lodash', 'EventEmitter' From 13b5e7c00eff3b055555ddeec18851173c83cd92 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 6 Oct 2016 17:50:01 -0700 Subject: [PATCH 56/78] [API] Remove toLegacyDefinition https://github.com/nasa/openmct/pull/1212#discussion_r81666469 --- src/api/Type.js | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/api/Type.js b/src/api/Type.js index d83e345b761..717e417b218 100644 --- a/src/api/Type.js +++ b/src/api/Type.js @@ -55,28 +55,5 @@ define(function () { return domainObject.type === this.key; }; - /** - * Get a definition for this type that can be registered using the - * legacy bundle format. - * @private - */ - Type.prototype.toLegacyDefinition = function () { - var def = {}; - def.name = this.definition.metadata.label; - def.glyph = this.definition.metadata.glyph; - def.description = this.definition.metadata.description; - def.properties = this.definition.form; - - if (this.definition.initialize) { - def.model = {}; - this.definition.initialize(def.model); - } - - if (this.definition.creatable) { - def.features = ['creation']; - } - return def; - }; - return Type; }); From fbe9621387946405c11097f53d1d08a277c003cc Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Thu, 6 Oct 2016 18:00:32 -0700 Subject: [PATCH 57/78] [API] Support telemetry request strategies https://github.com/nasa/openmct/pull/1212#discussion_r81880741 --- src/api/telemetry/TelemetryAPI.js | 48 ++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js index cc3a265dd9a..8b5ed95f6b2 100644 --- a/src/api/telemetry/TelemetryAPI.js +++ b/src/api/telemetry/TelemetryAPI.js @@ -156,7 +156,8 @@ define([ * @memberof module:openmct */ function TelemetryAPI() { - this.providers = []; + this.providersByStrategy = {}; + this.defaultProviders = []; } /** @@ -170,7 +171,7 @@ define([ * @memberof module:openmct.TelemetryAPI~TelemetryProvider# */ TelemetryAPI.prototype.canProvideTelemetry = function (domainObject) { - return this.providers.some(function (provider) { + return this.defaultProviders.some(function (provider) { return provider.canProvideTelemetry(domainObject); }); }; @@ -187,8 +188,36 @@ define([ * default provider (when no strategy is requested or no * matching strategy is found.) */ - TelemetryAPI.prototype.addProvider = function (provider) { - this.providers.push(provider); + TelemetryAPI.prototype.addProvider = function (provider, strategy) { + if (!strategy) { + this.defaultProviders.push(provider); + } else { + this.providersByStrategy[strategy] = + this.providersByStrategy[strategy] || []; + this.providersByStrategy[strategy].push(provider); + } + }; + + /** + * @private + */ + TelemetryAPI.prototype.findProvider = function (domainObject, options) { + var strategy = options.strategy; + + function supportsDomainObject(provider) { + return provider.canProvideTelemetry(domainObject); + } + + if (strategy) { + var eligibleProviders = + (this.providersByStrategy[strategy] || []) + .filter(supportsDomainObject); + if (eligibleProviders.length > 0) { + return eligibleProviders[0]; + } + } + + return this.defaultProviders.filter(supportsDomainObject)[0]; }; /** @@ -207,10 +236,7 @@ define([ * telemetry data */ TelemetryAPI.prototype.request = function (domainObject, options) { - var provider = this.providers.reduce(function (result, p) { - return (p.canProvideTelemetry(domainObject)) ? - p : result; - }, undefined); + var provider = this.findProvider(domainObject, options); return provider ? provider.request(domainObject, options) : Promise.reject([]); @@ -278,17 +304,13 @@ define([ * @memberof module:openmct.TelemetryAPI~TelemetryProvider# */ _.forEach({ - request: undefined, subscribe: undefined, properties: [], formatter: undefined, limitEvaluator: undefined }, function (defaultValue, method) { TelemetryAPI.prototype[method] = function (domainObject) { - var provider = this.providers.reduce(function (result, p) { - return (p.canProvideTelemetry(domainObject)) ? - p : result; - }, undefined); + var provider = this.findProvider(domainObject, options); return provider ? provider[method].apply(provider, arguments) : defaultValue; From 580e10b024232aa76112750c759a5ecf0be8fc45 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 7 Oct 2016 10:15:02 -0700 Subject: [PATCH 58/78] [API] Only use strategy where needed Satisfies JSHint. --- src/api/telemetry/TelemetryAPI.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/api/telemetry/TelemetryAPI.js b/src/api/telemetry/TelemetryAPI.js index 8b5ed95f6b2..6e310b1aa8f 100644 --- a/src/api/telemetry/TelemetryAPI.js +++ b/src/api/telemetry/TelemetryAPI.js @@ -201,9 +201,7 @@ define([ /** * @private */ - TelemetryAPI.prototype.findProvider = function (domainObject, options) { - var strategy = options.strategy; - + TelemetryAPI.prototype.findProvider = function (domainObject, strategy) { function supportsDomainObject(provider) { return provider.canProvideTelemetry(domainObject); } @@ -236,7 +234,7 @@ define([ * telemetry data */ TelemetryAPI.prototype.request = function (domainObject, options) { - var provider = this.findProvider(domainObject, options); + var provider = this.findProvider(domainObject, options.strategy); return provider ? provider.request(domainObject, options) : Promise.reject([]); @@ -310,7 +308,7 @@ define([ limitEvaluator: undefined }, function (defaultValue, method) { TelemetryAPI.prototype[method] = function (domainObject) { - var provider = this.findProvider(domainObject, options); + var provider = this.findProvider(domainObject); return provider ? provider[method].apply(provider, arguments) : defaultValue; From 00d0b71080f4435da8a50f409fb7334b3948301a Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 7 Oct 2016 11:07:53 -0700 Subject: [PATCH 59/78] [API] Use ViewRegistry from dialog adapter https://github.com/nasa/openmct/pull/1212#pullrequestreview-2458991 --- src/MCT.js | 11 +++++++++++ src/adapter/actions/ActionDialogDecorator.js | 19 +++++-------------- src/ui/ViewRegistry.js | 7 +++++++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/MCT.js b/src/MCT.js index 193e3fb29ac..c1b328b736a 100644 --- a/src/MCT.js +++ b/src/MCT.js @@ -120,6 +120,17 @@ define([ */ this.inspectors = new ViewRegistry(); + /** + * Registry for views which should appear in Edit Properties + * dialogs, and similar user interface elements used for + * modifying domain objects external to its regular views. + * + * @type {module:openmct.ViewRegistry} + * @memberof module:openmct.MCT# + * @name propertyEditors + */ + this.propertyEditors = new ViewRegistry(); + /** * Registry for views which should appear in the status indicator area. * @type {module:openmct.ViewRegistry} diff --git a/src/adapter/actions/ActionDialogDecorator.js b/src/adapter/actions/ActionDialogDecorator.js index 5a5426e0811..d80e72741ae 100644 --- a/src/adapter/actions/ActionDialogDecorator.js +++ b/src/adapter/actions/ActionDialogDecorator.js @@ -23,19 +23,13 @@ define([ '../../api/objects/object-utils' ], function (objectUtils) { - function ActionDialogDecorator(mct, newViews, actionService) { - this.actionService = actionService; + function ActionDialogDecorator(mct, actionService) { this.mct = mct; - this.definitions = newViews.filter(function (newView) { - return newView.region === mct.regions.properties; - }).map(function (newView) { - return newView.factory; - }); + this.actionService = actionService; } ActionDialogDecorator.prototype.getActions = function (context) { var mct = this.mct; - var definitions = this.definitions; return this.actionService.getActions(context).map(function (action) { if (action.dialogService) { @@ -43,16 +37,13 @@ define([ context.domainObject.getModel(), objectUtils.parseKeyString(context.domainObject.getId()) ); + var providers = mct.propertyEditors.get(domainObject); - definitions = definitions.filter(function (definition) { - return definition.canView(domainObject); - }); - - if (definitions.length > 0) { + if (providers.length > 0) { action.dialogService = Object.create(action.dialogService); action.dialogService.getUserInput = function (form, value) { return new mct.Dialog( - definitions[0].view(context.domainObject), + providers[0].view(context.domainObject), form.title ).show(); }; diff --git a/src/ui/ViewRegistry.js b/src/ui/ViewRegistry.js index 074a9fbf2ea..67bd494f4d6 100644 --- a/src/ui/ViewRegistry.js +++ b/src/ui/ViewRegistry.js @@ -31,6 +31,13 @@ define([], function () { this.providers = []; } + + /** + * @private for platform-internal use + * @param {*} item the object to be viewed + * @returns {module:openmct.ViewProvider[]} any providers + * which can provide views of this object + */ ViewRegistry.prototype.get = function (item) { return this.providers.filter(function (provider) { return provider.canView(item); From 567bb6fa2a4c1fe7e8fa2aa9c5effeff83120097 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 7 Oct 2016 11:10:41 -0700 Subject: [PATCH 60/78] [API] Fix ActionDialogDecorator's dependencies --- src/adapter/bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/adapter/bundle.js b/src/adapter/bundle.js index a884897423d..0e2a4cf3040 100644 --- a/src/adapter/bundle.js +++ b/src/adapter/bundle.js @@ -74,7 +74,7 @@ define([ type: "decorator", provides: "actionService", implementation: ActionDialogDecorator, - depends: ["openmct", "newViews[]"] + depends: ["openmct"] } ], policies: [ From 947b54555a07ed69cdb4e6b7a458485cbdcfa6a1 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 7 Oct 2016 11:35:35 -0700 Subject: [PATCH 61/78] [Edit Mode] Simplify transaction stack https://github.com/nasa/openmct/pull/874#r76593588 --- .../edit/src/services/TransactionService.js | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/platform/commonUI/edit/src/services/TransactionService.js b/platform/commonUI/edit/src/services/TransactionService.js index 7697eed4363..00aa91e6e49 100644 --- a/platform/commonUI/edit/src/services/TransactionService.js +++ b/platform/commonUI/edit/src/services/TransactionService.js @@ -37,8 +37,7 @@ define( function TransactionService($q, $log) { this.$q = $q; this.$log = $log; - this.transaction = undefined; - this.transactionStack = []; + this.transactions = []; } /** @@ -48,19 +47,18 @@ define( * #cancel} are called */ TransactionService.prototype.startTransaction = function () { - if (this.transaction) { - this.transactionStack.push(this.transaction); - this.transaction = new NestedTransaction(this.transaction); - } else { - this.transaction = new Transaction(this.$log); - } + var transaction = this.isActive() ? + new NestedTransaction(this.transactions[0]) : + new Transaction(this.$log); + + this.transactions.push(transaction); }; /** * @returns {boolean} If true, indicates that a transaction is in progress */ TransactionService.prototype.isActive = function () { - return !!this.transaction; + return this.transactions.length > 0; }; /** @@ -71,14 +69,22 @@ define( * @param onCancel A function to call on cancel */ TransactionService.prototype.addToTransaction = function (onCommit, onCancel) { - if (this.transaction) { - return this.transaction.add(onCommit, onCancel); + if (this.isActive()) { + return this.activeTransaction().add(onCommit, onCancel); } else { //Log error because this is a programming error if it occurs. this.$log.error("No transaction in progress"); } }; + /** + * Get the transaction at the top of the stack. + * @private + */ + TransactionService.prototype.activeTransaction = function () { + return this.transactions[this.transactions.length - 1]; + }; + /** * All persist calls deferred since the beginning of the transaction * will be committed. @@ -87,8 +93,7 @@ define( * completed. Will reject if any commit operations fail */ TransactionService.prototype.commit = function () { - var transaction = this.transaction; - this.transaction = this.transactionStack.pop(); + var transaction = this.transactions.pop(); return transaction ? transaction.commit() : Promise.reject(); }; @@ -101,13 +106,17 @@ define( * @returns {*} */ TransactionService.prototype.cancel = function () { - var transaction = this.transaction; - this.transaction = this.transactionStack.pop(); + var transaction = this.transactions.pop(); return transaction ? transaction.cancel() : Promise.reject(); }; + /** + * Get the size (the number of commit/cancel callbacks) of + * the active transaction. + * @returns {number} size of the active transaction + */ TransactionService.prototype.size = function () { - return this.transaction ? this.transaction.size() : 0; + return this.isActive() ? this.activeTransaction.size() : 0; }; return TransactionService; From 0833674b9150ecd626440846cefb7e4ab061855c Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Fri, 7 Oct 2016 11:54:49 -0700 Subject: [PATCH 62/78] [Persistence] Address refactoring error ...caught by unit tests --- platform/commonUI/edit/src/services/TransactionService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/commonUI/edit/src/services/TransactionService.js b/platform/commonUI/edit/src/services/TransactionService.js index 00aa91e6e49..3c234ca8829 100644 --- a/platform/commonUI/edit/src/services/TransactionService.js +++ b/platform/commonUI/edit/src/services/TransactionService.js @@ -116,7 +116,7 @@ define( * @returns {number} size of the active transaction */ TransactionService.prototype.size = function () { - return this.isActive() ? this.activeTransaction.size() : 0; + return this.isActive() ? this.activeTransaction().size() : 0; }; return TransactionService; From 1bf7c06b1e0a0dd72e2a258c1146b07e6d9c8ef0 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Wed, 12 Oct 2016 13:13:47 -0700 Subject: [PATCH 63/78] [API] Enable example bundles Enable example bundles in the default build. Addresses regression in development environments introduced by #1212. --- index.html | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index 3c80e1d8e5c..aa5d79cfdc7 100644 --- a/index.html +++ b/index.html @@ -29,11 +29,14 @@ From 65043d0ff325c7e5a3d9d0bb374e90739182cccd Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Wed, 12 Oct 2016 13:47:56 -0700 Subject: [PATCH 64/78] squash merge open933 into integration-1089, resolve merge conflicts. --- bower.json | 1 + example/localTimeSystem/bundle.js | 48 ++ example/localTimeSystem/src/LADTickSource.js | 43 ++ .../localTimeSystem/src/LocalTimeFormat.js | 112 +++++ .../localTimeSystem/src/LocalTimeSystem.js | 79 ++++ openmct.js | 9 +- .../browse/res/templates/browse-object.html | 3 +- .../res/templates/browse/object-header.html | 3 +- .../edit/res/templates/edit-object.html | 5 + platform/commonUI/formats/bundle.js | 17 + .../commonUI/formats/src/DurationFormat.js | 62 +++ .../commonUI/formats/src/FormatProvider.js | 4 + .../commonUI/formats/src/UTCTimeFormat.js | 64 ++- .../commonUI/formats/src/UTCTimeFormatSpec.js | 62 +++ .../general/res/sass/_animations.scss | 91 ++++ .../general/res/sass/_archetypes.scss | 3 + .../commonUI/general/res/sass/_constants.scss | 2 +- .../general/res/sass/_data-status.scss | 1 - .../commonUI/general/res/sass/_effects.scss | 10 +- .../commonUI/general/res/sass/_global.scss | 2 +- .../commonUI/general/res/sass/_icons.scss | 18 +- platform/commonUI/general/res/sass/_main.scss | 2 +- .../commonUI/general/res/sass/_mixins.scss | 17 - .../general/res/sass/controls/_controls.scss | 63 ++- .../general/res/sass/controls/_menus.scss | 73 +-- .../general/res/sass/controls/_messages.scss | 26 ++ .../res/sass/controls/_time-controller.scss | 266 ----------- .../general/res/sass/features/_imagery.scss | 2 +- .../res/sass/helpers/_wait-spinner.scss | 9 - .../res/sass/user-environ/_layout.scss | 4 +- .../templates/controls/datetime-field.html | 2 + .../themes/espresso/res/sass/_constants.scss | 1 + .../themes/snow/res/sass/_constants.scss | 1 + .../conductor-v2/compatibility/bundle.js | 66 +++ .../compatibility/src/ConductorRepresenter.js | 94 ++++ .../compatibility/src/ConductorService.js | 72 +++ .../src/ConductorTelemetryDecorator.js | 87 ++++ .../features/conductor-v2/conductor/bundle.js | 134 ++++++ .../conductor/res/sass/_constants.scss | 3 + .../res/sass/_time-conductor-base.scss | 431 ++++++++++++++++++ .../res/sass/time-conductor-espresso.scss | 39 ++ .../res/sass/time-conductor-snow.scss | 39 ++ .../templates/mode-selector/mode-menu.html | 45 ++ .../mode-selector/mode-selector.html | 34 ++ .../res/templates/time-conductor.html | 108 +++++ .../conductor/src/TimeConductor.js | 179 ++++++++ .../conductor/src/TimeConductorSpec.js | 110 +++++ .../conductor/src/timeSystems/LocalClock.js | 89 ++++ .../src/timeSystems/LocalClockSpec.js | 50 ++ .../conductor/src/timeSystems/TickSource.js | 47 ++ .../conductor/src/timeSystems/TimeSystem.js | 93 ++++ .../conductor/src/ui/MctConductorAxis.js | 146 ++++++ .../conductor/src/ui/MctConductorAxisSpec.js | 146 ++++++ .../conductor/src/ui/NumberFormat.js | 53 +++ .../conductor/src/ui/NumberFormatSpec.js | 49 ++ .../src/ui/TimeConductorController.js | 243 ++++++++++ .../src/ui/TimeConductorControllerSpec.js | 335 ++++++++++++++ .../conductor/src/ui/TimeConductorMode.js | 201 ++++++++ .../conductor/src/ui/TimeConductorModeSpec.js | 210 +++++++++ .../src/ui/TimeConductorValidation.js | 69 +++ .../src/ui/TimeConductorValidationSpec.js | 73 +++ .../src/ui/TimeConductorViewService.js | 202 ++++++++ .../src/ui/TimeConductorViewServiceSpec.js | 185 ++++++++ .../conductor-v2/utcTimeSystem/bundle.js | 40 ++ .../utcTimeSystem/src/UTCTimeSystem.js | 78 ++++ .../utcTimeSystem/src/UTCTimeSystemSpec.js | 46 ++ platform/features/conductor/bundle.js | 5 + .../conductor/res/sass/time-conductor.scss | 300 ++++++++++++ .../features/layout/res/templates/layout.html | 9 +- platform/features/plot/src/PlotController.js | 26 +- .../features/plot/test/PlotControllerSpec.js | 37 +- .../controllers/HistoricalTableController.js | 22 + .../controllers/TelemetryTableController.js | 5 - .../res/templates/controls/textfield.html | 1 + platform/forms/src/MCTControl.js | 3 + test-main.js | 6 +- 76 files changed, 4956 insertions(+), 359 deletions(-) create mode 100644 example/localTimeSystem/bundle.js create mode 100644 example/localTimeSystem/src/LADTickSource.js create mode 100644 example/localTimeSystem/src/LocalTimeFormat.js create mode 100644 example/localTimeSystem/src/LocalTimeSystem.js create mode 100644 platform/commonUI/formats/src/DurationFormat.js create mode 100644 platform/commonUI/formats/src/UTCTimeFormatSpec.js create mode 100644 platform/commonUI/general/res/sass/_animations.scss delete mode 100644 platform/commonUI/general/res/sass/controls/_time-controller.scss create mode 100644 platform/features/conductor-v2/compatibility/bundle.js create mode 100644 platform/features/conductor-v2/compatibility/src/ConductorRepresenter.js create mode 100644 platform/features/conductor-v2/compatibility/src/ConductorService.js create mode 100644 platform/features/conductor-v2/compatibility/src/ConductorTelemetryDecorator.js create mode 100644 platform/features/conductor-v2/conductor/bundle.js create mode 100644 platform/features/conductor-v2/conductor/res/sass/_constants.scss create mode 100644 platform/features/conductor-v2/conductor/res/sass/_time-conductor-base.scss create mode 100644 platform/features/conductor-v2/conductor/res/sass/time-conductor-espresso.scss create mode 100644 platform/features/conductor-v2/conductor/res/sass/time-conductor-snow.scss create mode 100644 platform/features/conductor-v2/conductor/res/templates/mode-selector/mode-menu.html create mode 100644 platform/features/conductor-v2/conductor/res/templates/mode-selector/mode-selector.html create mode 100644 platform/features/conductor-v2/conductor/res/templates/time-conductor.html create mode 100644 platform/features/conductor-v2/conductor/src/TimeConductor.js create mode 100644 platform/features/conductor-v2/conductor/src/TimeConductorSpec.js create mode 100644 platform/features/conductor-v2/conductor/src/timeSystems/LocalClock.js create mode 100644 platform/features/conductor-v2/conductor/src/timeSystems/LocalClockSpec.js create mode 100644 platform/features/conductor-v2/conductor/src/timeSystems/TickSource.js create mode 100644 platform/features/conductor-v2/conductor/src/timeSystems/TimeSystem.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/MctConductorAxis.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/MctConductorAxisSpec.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/NumberFormat.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/NumberFormatSpec.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/TimeConductorController.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/TimeConductorControllerSpec.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/TimeConductorMode.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/TimeConductorModeSpec.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/TimeConductorValidation.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/TimeConductorValidationSpec.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/TimeConductorViewService.js create mode 100644 platform/features/conductor-v2/conductor/src/ui/TimeConductorViewServiceSpec.js create mode 100644 platform/features/conductor-v2/utcTimeSystem/bundle.js create mode 100644 platform/features/conductor-v2/utcTimeSystem/src/UTCTimeSystem.js create mode 100644 platform/features/conductor-v2/utcTimeSystem/src/UTCTimeSystemSpec.js create mode 100644 platform/features/conductor/res/sass/time-conductor.scss diff --git a/bower.json b/bower.json index ed3dbaf899d..161ee041869 100644 --- a/bower.json +++ b/bower.json @@ -22,6 +22,7 @@ "eventemitter3": "^1.2.0", "lodash": "3.10.1", "almond": "~0.3.2", + "d3": "~4.1.0", "html2canvas": "^0.4.1" } } diff --git a/example/localTimeSystem/bundle.js b/example/localTimeSystem/bundle.js new file mode 100644 index 00000000000..1d1ff2cf398 --- /dev/null +++ b/example/localTimeSystem/bundle.js @@ -0,0 +1,48 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define([ + "./src/LocalTimeSystem", + "./src/LocalTimeFormat", + 'legacyRegistry' +], function ( + LocalTimeSystem, + LocalTimeFormat, + legacyRegistry +) { + legacyRegistry.register("example/localTimeSystem", { + "extensions": { + "formats": [ + { + "key": "local-format", + "implementation": LocalTimeFormat + } + ], + "timeSystems": [ + { + "implementation": LocalTimeSystem, + "depends": ["$timeout"] + } + ] + } + }); +}); diff --git a/example/localTimeSystem/src/LADTickSource.js b/example/localTimeSystem/src/LADTickSource.js new file mode 100644 index 00000000000..35fff32f547 --- /dev/null +++ b/example/localTimeSystem/src/LADTickSource.js @@ -0,0 +1,43 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(['../../../platform/features/conductor-v2/conductor/src/timeSystems/LocalClock'], function (LocalClock) { + /** + * @implements TickSource + * @constructor + */ + function LADTickSource ($timeout, period) { + LocalClock.call(this, $timeout, period); + + this.metadata = { + key: 'test-lad', + mode: 'lad', + cssclass: 'icon-clock', + label: 'Latest Available Data', + name: 'Latest available data', + description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.' + }; + } + LADTickSource.prototype = Object.create(LocalClock.prototype); + + return LADTickSource; +}); diff --git a/example/localTimeSystem/src/LocalTimeFormat.js b/example/localTimeSystem/src/LocalTimeFormat.js new file mode 100644 index 00000000000..a9b26ed4efb --- /dev/null +++ b/example/localTimeSystem/src/LocalTimeFormat.js @@ -0,0 +1,112 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define([ + 'moment' +], function ( + moment +) { + + var DATE_FORMAT = "YYYY-MM-DD h:mm:ss.SSS a", + DATE_FORMATS = [ + DATE_FORMAT, + "YYYY-MM-DD h:mm:ss a", + "YYYY-MM-DD h:mm a", + "YYYY-MM-DD" + ]; + + /** + * @typedef Scale + * @property {number} min the minimum scale value, in ms + * @property {number} max the maximum scale value, in ms + */ + + /** + * Formatter for UTC timestamps. Interprets numeric values as + * milliseconds since the start of 1970. + * + * @implements {Format} + * @constructor + * @memberof platform/commonUI/formats + */ + function LocalTimeFormat() { + } + + /** + * Returns an appropriate time format based on the provided value and + * the threshold required. + * @private + */ + function getScaledFormat (d) { + var m = moment.utc(d); + /** + * Uses logic from d3 Time-Scales, v3 of the API. See + * https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Scales.md + * + * Licensed + */ + return [ + [".SSS", function(m) { return m.milliseconds(); }], + [":ss", function(m) { return m.seconds(); }], + ["hh:mma", function(m) { return m.minutes(); }], + ["hha", function(m) { return m.hours(); }], + ["ddd DD", function(m) { + return m.days() && + m.date() != 1; + }], + ["MMM DD", function(m) { return m.date() != 1; }], + ["MMMM", function(m) { + return m.month(); + }], + ["YYYY", function() { return true; }] + ].filter(function (row){ + return row[1](m); + })[0][0]; + }; + + /** + * + * @param value + * @param {Scale} [scale] Optionally provides context to the + * format request, allowing for scale-appropriate formatting. + * @returns {string} the formatted date + */ + LocalTimeFormat.prototype.format = function (value, scale) { + if (scale !== undefined){ + var scaledFormat = getScaledFormat(value, scale); + if (scaledFormat) { + return moment.utc(value).format(scaledFormat); + } + } + return moment(value).format(DATE_FORMAT); + }; + + LocalTimeFormat.prototype.parse = function (text) { + return moment(text, DATE_FORMATS).valueOf(); + }; + + LocalTimeFormat.prototype.validate = function (text) { + return moment(text, DATE_FORMATS).isValid(); + }; + + return LocalTimeFormat; +}); diff --git a/example/localTimeSystem/src/LocalTimeSystem.js b/example/localTimeSystem/src/LocalTimeSystem.js new file mode 100644 index 00000000000..95485b7e69d --- /dev/null +++ b/example/localTimeSystem/src/LocalTimeSystem.js @@ -0,0 +1,79 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define([ + '../../../platform/features/conductor-v2/conductor/src/timeSystems/TimeSystem', + '../../../platform/features/conductor-v2/conductor/src/timeSystems/LocalClock', + './LADTickSource' +], function (TimeSystem, LocalClock, LADTickSource) { + var THIRTY_MINUTES = 30 * 60 * 1000, + DEFAULT_PERIOD = 1000; + + /** + * This time system supports UTC dates and provides a ticking clock source. + * @implements TimeSystem + * @constructor + */ + function LocalTimeSystem ($timeout) { + TimeSystem.call(this); + + /** + * Some metadata, which will be used to identify the time system in + * the UI + * @type {{key: string, name: string, glyph: string}} + */ + this.metadata = { + 'key': 'local', + 'name': 'Local', + 'glyph': '\u0043' + }; + + this.fmts = ['local-format']; + this.sources = [new LocalClock($timeout, DEFAULT_PERIOD), new LADTickSource($timeout, DEFAULT_PERIOD)]; + } + + LocalTimeSystem.prototype = Object.create(TimeSystem.prototype); + + LocalTimeSystem.prototype.formats = function () { + return this.fmts; + }; + + LocalTimeSystem.prototype.deltaFormat = function () { + return 'duration'; + }; + + LocalTimeSystem.prototype.tickSources = function () { + return this.sources; + }; + + LocalTimeSystem.prototype.defaults = function (key) { + var now = Math.ceil(Date.now() / 1000) * 1000; + return { + key: 'local-default', + name: 'Local 12 hour time system defaults', + deltas: {start: THIRTY_MINUTES, end: 0}, + bounds: {start: now - THIRTY_MINUTES, end: now} + }; + }; + + return LocalTimeSystem; +}); diff --git a/openmct.js b/openmct.js index 864bf243cd4..f1bb593860d 100644 --- a/openmct.js +++ b/openmct.js @@ -37,7 +37,8 @@ requirejs.config({ "text": "bower_components/text/text", "uuid": "bower_components/node-uuid/uuid", "zepto": "bower_components/zepto/zepto.min", - "lodash": "bower_components/lodash/lodash" + "lodash": "bower_components/lodash/lodash", + "d3": "bower_components/d3/d3.min" }, "shim": { "angular": { @@ -52,6 +53,9 @@ requirejs.config({ "html2canvas": { "exports": "html2canvas" }, + "EventEmitter": { + "exports": "EventEmitter" + }, "moment-duration-format": { "deps": ["moment"] }, @@ -63,6 +67,9 @@ requirejs.config({ }, "lodash": { "exports": "lodash" + }, + "d3": { + "exports": "d3" } } }); diff --git a/platform/commonUI/browse/res/templates/browse-object.html b/platform/commonUI/browse/res/templates/browse-object.html index eb32a156487..f431003e476 100644 --- a/platform/commonUI/browse/res/templates/browse-object.html +++ b/platform/commonUI/browse/res/templates/browse-object.html @@ -43,7 +43,7 @@ -
+
@@ -59,4 +59,5 @@
+
diff --git a/platform/commonUI/browse/res/templates/browse/object-header.html b/platform/commonUI/browse/res/templates/browse/object-header.html index 48a64796da8..62391caac58 100644 --- a/platform/commonUI/browse/res/templates/browse/object-header.html +++ b/platform/commonUI/browse/res/templates/browse/object-header.html @@ -22,7 +22,8 @@ {{parameters.mode}} - {{model.name}} + {{model.name}} +
+ + + diff --git a/platform/commonUI/formats/bundle.js b/platform/commonUI/formats/bundle.js index 040c7f57fb0..c685151149f 100644 --- a/platform/commonUI/formats/bundle.js +++ b/platform/commonUI/formats/bundle.js @@ -23,10 +23,12 @@ define([ "./src/FormatProvider", "./src/UTCTimeFormat", + "./src/DurationFormat", 'legacyRegistry' ], function ( FormatProvider, UTCTimeFormat, + DurationFormat, legacyRegistry ) { @@ -48,6 +50,10 @@ define([ { "key": "utc", "implementation": UTCTimeFormat + }, + { + "key": "duration", + "implementation": DurationFormat } ], "constants": [ @@ -55,6 +61,17 @@ define([ "key": "DEFAULT_TIME_FORMAT", "value": "utc" } + ], + "licenses": [ + { + "name": "d3", + "version": "3.0.0", + "description": "Incorporates modified code from d3 Time Scales", + "author": "Mike Bostock", + "copyright": "Copyright 2010-2016 Mike Bostock. " + + "All rights reserved.", + "link": "https://github.com/d3/d3/blob/master/LICENSE" + } ] } }); diff --git a/platform/commonUI/formats/src/DurationFormat.js b/platform/commonUI/formats/src/DurationFormat.js new file mode 100644 index 00000000000..12ce3b3c6b1 --- /dev/null +++ b/platform/commonUI/formats/src/DurationFormat.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. + *****************************************************************************/ + +define([ + 'moment' +], function ( + moment +) { + + var DATE_FORMAT = "HH:mm:ss", + DATE_FORMATS = [ + DATE_FORMAT + ]; + + + /** + * Formatter for duration. Uses moment to produce a date from a given + * value, but output is formatted to display only time. Can be used for + * specifying a time duration. For specifying duration, it's best to + * specify a date of January 1, 1970, as the ms offset will equal the + * duration represented by the time. + * + * @implements {Format} + * @constructor + * @memberof platform/commonUI/formats + */ + function DurationFormat() { + } + + DurationFormat.prototype.format = function (value) { + return moment.utc(value).format(DATE_FORMAT); + }; + + DurationFormat.prototype.parse = function (text) { + return moment.duration(text).asMilliseconds(); + }; + + DurationFormat.prototype.validate = function (text) { + return moment.utc(text, DATE_FORMATS).isValid(); + }; + + return DurationFormat; +}); diff --git a/platform/commonUI/formats/src/FormatProvider.js b/platform/commonUI/formats/src/FormatProvider.js index f8f126001da..4700260fd80 100644 --- a/platform/commonUI/formats/src/FormatProvider.js +++ b/platform/commonUI/formats/src/FormatProvider.js @@ -58,6 +58,10 @@ define([ * @method format * @memberof Format# * @param {number} value the numeric value to format + * @param {number} [threshold] Optionally provides context to the + * format request, allowing for scale-appropriate formatting. This value + * should be the minimum unit to be represented by this format, in ms. For + * example, to display seconds, a threshold of 1 * 1000 should be provided. * @returns {string} the text representation of the value */ diff --git a/platform/commonUI/formats/src/UTCTimeFormat.js b/platform/commonUI/formats/src/UTCTimeFormat.js index 8c4277f9b8b..3faab7a620e 100644 --- a/platform/commonUI/formats/src/UTCTimeFormat.js +++ b/platform/commonUI/formats/src/UTCTimeFormat.js @@ -34,6 +34,11 @@ define([ "YYYY-MM-DD" ]; + /** + * @typedef Scale + * @property {number} min the minimum scale value, in ms + * @property {number} max the maximum scale value, in ms + */ /** * Formatter for UTC timestamps. Interprets numeric values as @@ -46,7 +51,64 @@ define([ function UTCTimeFormat() { } - UTCTimeFormat.prototype.format = function (value) { + /** + * Returns an appropriate time format based on the provided value and + * the threshold required. + * @private + */ + function getScaledFormat(d) { + var momentified = moment.utc(d); + /** + * Uses logic from d3 Time-Scales, v3 of the API. See + * https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Scales.md + * + * Licensed + */ + return [ + [".SSS", function (m) { + return m.milliseconds(); + }], + [":ss", function (m) { + return m.seconds(); + }], + ["HH:mm", function (m) { + return m.minutes(); + }], + ["HH", function (m) { + return m.hours(); + }], + ["ddd DD", function (m) { + return m.days() && + m.date() !== 1; + }], + ["MMM DD", function (m) { + return m.date() !== 1; + }], + ["MMMM", function (m) { + return m.month(); + }], + ["YYYY", function () { + return true; + }] + ].filter(function (row) { + return row[1](momentified); + })[0][0]; + } + + /** + * + * @param value + * @param {Scale} [scale] Optionally provides context to the + * format request, allowing for scale-appropriate formatting. + * @returns {string} the formatted date + */ + UTCTimeFormat.prototype.format = function (value, scale) { + if (scale !== undefined) { + var scaledFormat = getScaledFormat(value, scale); + if (scaledFormat) { + return moment.utc(value).format(scaledFormat); + } + } return moment.utc(value).format(DATE_FORMAT) + "Z"; }; diff --git a/platform/commonUI/formats/src/UTCTimeFormatSpec.js b/platform/commonUI/formats/src/UTCTimeFormatSpec.js new file mode 100644 index 00000000000..c4111709a33 --- /dev/null +++ b/platform/commonUI/formats/src/UTCTimeFormatSpec.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. + *****************************************************************************/ + +define([ + "./UTCTimeFormat", + "moment" +], function ( + UTCTimeFormat, + moment +) { + describe("The UTCTimeFormat class", function () { + var format; + var scale; + + beforeEach(function () { + format = new UTCTimeFormat(); + scale = {min: 0, max: 0}; + }); + + it("Provides an appropriately scaled time format based on the input" + + " time", function () { + var TWO_HUNDRED_MS = 200; + var THREE_SECONDS = 3000; + var FIVE_MINUTES = 5 * 60 * 1000; + var ONE_HOUR_TWENTY_MINS = (1 * 60 * 60 * 1000) + (20 * 60 * 1000); + var TEN_HOURS = (10 * 60 * 60 * 1000); + + var JUNE_THIRD = moment.utc("2016-06-03", "YYYY-MM-DD"); + var APRIL = moment.utc("2016-04", "YYYY-MM"); + var TWENTY_SIXTEEN = moment.utc("2016", "YYYY"); + + expect(format.format(TWO_HUNDRED_MS, scale)).toBe(".200"); + expect(format.format(THREE_SECONDS, scale)).toBe(":03"); + expect(format.format(FIVE_MINUTES, scale)).toBe("00:05"); + expect(format.format(ONE_HOUR_TWENTY_MINS, scale)).toBe("01:20"); + expect(format.format(TEN_HOURS, scale)).toBe("10"); + + expect(format.format(JUNE_THIRD, scale)).toBe("Fri 03"); + expect(format.format(APRIL, scale)).toBe("April"); + expect(format.format(TWENTY_SIXTEEN, scale)).toBe("2016"); + }); + }); +}); diff --git a/platform/commonUI/general/res/sass/_animations.scss b/platform/commonUI/general/res/sass/_animations.scss new file mode 100644 index 00000000000..fb1c0f1bfbb --- /dev/null +++ b/platform/commonUI/general/res/sass/_animations.scss @@ -0,0 +1,91 @@ +@include keyframes(rotation) { + 100% { @include transform(rotate(360deg)); } +} + +@include keyframes(rotation-centered) { + 0% { @include transform(translate(-50%, -50%) rotate(0deg)); } + 100% { @include transform(translate(-50%, -50%) rotate(360deg)); } +} + +@include keyframes(clock-hands) { + 0% { @include transform(translate(-50%, -50%) rotate(0deg)); } + 100% { @include transform(translate(-50%, -50%) rotate(360deg)); } +} + +@include keyframes(clock-hands-sticky) { + 0% { + @include transform(translate(-50%, -50%) rotate(0deg)); + } + 7% { + @include transform(translate(-50%, -50%) rotate(0deg)); + } + 8% { + @include transform(translate(-50%, -50%) rotate(30deg)); + } + 15% { + @include transform(translate(-50%, -50%) rotate(30deg)); + } + 16% { + @include transform(translate(-50%, -50%) rotate(60deg)); + } + 24% { + @include transform(translate(-50%, -50%) rotate(60deg)); + } + 25% { + @include transform(translate(-50%, -50%) rotate(90deg)); + } + 32% { + @include transform(translate(-50%, -50%) rotate(90deg)); + } + 33% { + @include transform(translate(-50%, -50%) rotate(120deg)); + } + 40% { + @include transform(translate(-50%, -50%) rotate(120deg)); + } + 41% { + @include transform(translate(-50%, -50%) rotate(150deg)); + } + 49% { + @include transform(translate(-50%, -50%) rotate(150deg)); + } + 50% { + @include transform(translate(-50%, -50%) rotate(180deg)); + } + 57% { + @include transform(translate(-50%, -50%) rotate(180deg)); + } + 58% { + @include transform(translate(-50%, -50%) rotate(210deg)); + } + 65% { + @include transform(translate(-50%, -50%) rotate(210deg)); + } + 66% { + @include transform(translate(-50%, -50%) rotate(240deg)); + } + 74% { + @include transform(translate(-50%, -50%) rotate(240deg)); + } + 75% { + @include transform(translate(-50%, -50%) rotate(270deg)); + } + 82% { + @include transform(translate(-50%, -50%) rotate(270deg)); + } + 83% { + @include transform(translate(-50%, -50%) rotate(300deg)); + } + 90% { + @include transform(translate(-50%, -50%) rotate(300deg)); + } + 91% { + @include transform(translate(-50%, -50%) rotate(330deg)); + } + 99% { + @include transform(translate(-50%, -50%) rotate(330deg)); + } + 100% { + @include transform(translate(-50%, -50%) rotate(360deg)); + } +} \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/_archetypes.scss b/platform/commonUI/general/res/sass/_archetypes.scss index d51944e2f42..206f126fde2 100644 --- a/platform/commonUI/general/res/sass/_archetypes.scss +++ b/platform/commonUI/general/res/sass/_archetypes.scss @@ -108,6 +108,9 @@ &.grows { @include flex(1 1 auto); } + &.contents-align-right { + text-align: right; + } } .flex-container { // Apply to wrapping elements, mct-includes, etc. diff --git a/platform/commonUI/general/res/sass/_constants.scss b/platform/commonUI/general/res/sass/_constants.scss index 1e609cc0381..efa87c6a08f 100644 --- a/platform/commonUI/general/res/sass/_constants.scss +++ b/platform/commonUI/general/res/sass/_constants.scss @@ -49,7 +49,6 @@ $uePaneMiniTabFontSize: 8px; $uePaneMiniTabCollapsedW: 18px; $ueEditLeftPaneW: 75%; $treeSearchInputBarH: 25px; -$ueTimeControlH: (33px, 18px, 20px); /*************** Panes */ $ueBrowseLeftPaneTreeMinW: 150px; $ueBrowseLeftPaneTreeMaxW: 35%; @@ -112,6 +111,7 @@ $bubbleMaxW: 300px; $reqSymbolW: 15px; $reqSymbolM: $interiorMargin * 2; $reqSymbolFontSize: 0.75em; +$inputTextP: 3px 5px; /*************** Wait Spinner Defaults */ $waitSpinnerD: 32px; $waitSpinnerTreeD: 20px; diff --git a/platform/commonUI/general/res/sass/_data-status.scss b/platform/commonUI/general/res/sass/_data-status.scss index 24a15db7943..d058885cf19 100644 --- a/platform/commonUI/general/res/sass/_data-status.scss +++ b/platform/commonUI/general/res/sass/_data-status.scss @@ -4,4 +4,3 @@ @include s-stale(); } } - diff --git a/platform/commonUI/general/res/sass/_effects.scss b/platform/commonUI/general/res/sass/_effects.scss index 22a54432943..acdda74b552 100644 --- a/platform/commonUI/general/res/sass/_effects.scss +++ b/platform/commonUI/general/res/sass/_effects.scss @@ -39,20 +39,20 @@ @include pulse($animName: pulse-subtle, $dur: 500ms, $opacity0: 0.7); } -@mixin animTo($animName, $propName, $propValStart, $propValEnd, $dur: 500ms, $delay: 0) { +@mixin animTo($animName, $propName, $propValStart, $propValEnd, $dur: 500ms, $delay: 0, $dir: normal, $count: 1) { @include keyframes($animName) { from { #{propName}: $propValStart; } to { #{$propName}: $propValEnd; } } - @include animToParams($animName, $dur: 500ms, $delay: 0) + @include animToParams($animName, $dur: $dur, $delay: $delay, $dir: $dir, $count: $count) } -@mixin animToParams($animName, $dur: 500ms, $delay: 0) { +@mixin animToParams($animName, $dur: 500ms, $delay: 0, $dir: normal, $count: 1) { @include animation-name($animName); @include animation-duration($dur); @include animation-delay($delay); @include animation-fill-mode(both); - @include animation-direction(normal); - @include animation-iteration-count(1); + @include animation-direction($dir); + @include animation-iteration-count($count); @include animation-timing-function(ease-in-out); } \ No newline at end of file diff --git a/platform/commonUI/general/res/sass/_global.scss b/platform/commonUI/general/res/sass/_global.scss index 9d3627f3aa1..964d609c4fe 100644 --- a/platform/commonUI/general/res/sass/_global.scss +++ b/platform/commonUI/general/res/sass/_global.scss @@ -82,7 +82,7 @@ input, textarea { input[type="text"], input[type="search"] { vertical-align: baseline; - padding: 3px 5px; + padding: $inputTextP; } h1, h2, h3 { diff --git a/platform/commonUI/general/res/sass/_icons.scss b/platform/commonUI/general/res/sass/_icons.scss index a8990d4fe8b..d15ec1d362b 100644 --- a/platform/commonUI/general/res/sass/_icons.scss +++ b/platform/commonUI/general/res/sass/_icons.scss @@ -30,6 +30,7 @@ .ui-symbol { font-family: 'symbolsfont'; + -webkit-font-smoothing: antialiased; } .ui-symbol.icon { @@ -70,10 +71,21 @@ line-height: inherit; position: relative; &.l-icon-link { - .t-item-icon-glyph { + &:after { + color: $colorIconLink; + content: $glyph-icon-link; + height: auto; width: auto; + position: absolute; + left: 0; top: 0; right: 0; bottom: 20%; + @include transform-origin(bottom left); + @include transform(scale(0.3)); + z-index: 2; + } + +/* .t-item-icon-glyph { &:after { color: $colorIconLink; - content: $glyph-icon-link; + content: '\e921'; //$glyph-icon-link; height: auto; width: auto; position: absolute; left: 0; top: 0; right: 0; bottom: 20%; @@ -81,6 +93,6 @@ @include transform(scale(0.3)); z-index: 2; } - } + }*/ } } diff --git a/platform/commonUI/general/res/sass/_main.scss b/platform/commonUI/general/res/sass/_main.scss index e092f1474a6..3ecb2304ab8 100644 --- a/platform/commonUI/general/res/sass/_main.scss +++ b/platform/commonUI/general/res/sass/_main.scss @@ -22,6 +22,7 @@ @import "effects"; @import "global"; @import "glyphs"; +@import "animations"; @import "archetypes"; @import "about"; @import "text"; @@ -41,7 +42,6 @@ @import "controls/lists"; @import "controls/menus"; @import "controls/messages"; -@import "controls/time-controller"; @import "mobile/controls/menus"; /********************************* FORMS */ diff --git a/platform/commonUI/general/res/sass/_mixins.scss b/platform/commonUI/general/res/sass/_mixins.scss index 318bfff4958..91720348b9f 100644 --- a/platform/commonUI/general/res/sass/_mixins.scss +++ b/platform/commonUI/general/res/sass/_mixins.scss @@ -185,21 +185,15 @@ } @mixin sliderTrack($bg: $scrollbarTrackColorBg) { - //$b: 1px solid lighten($bg, 30%); border-radius: 2px; box-sizing: border-box; @include boxIncised(0.7); background-color: $bg; - //border-bottom: $b; - //border-right: $b; } @mixin controlGrippy($b, $direction: horizontal, $w: 1px, $style: dotted) { - //&:before { - //@include trans-prop-nice("border-color", 25ms); content: ''; display: block; - //height: auto; pointer-events: none; position: absolute; z-index: 2; @@ -274,16 +268,6 @@ text-shadow: rgba(black, $sVal) 0 3px 7px; } -@function pullForward($c, $p: 20%) { - // For dark interfaces, lighter things come forward - @return lighten($c, $p); -} - -@function pushBack($c, $p: 20%) { - // For dark interfaces, darker things move back - @return darken($c, $p); -} - @function percentToDecimal($p) { @return $p / 100%; } @@ -304,7 +288,6 @@ border-radius: $controlCr; box-sizing: border-box; color: $fg; - //display: inline-block; } @mixin btnBase($bg: $colorBtnBg, $bgHov: $colorBtnBgHov, $fg: $colorBtnFg, $fgHov: $colorBtnFgHov, $ic: $colorBtnIcon, $icHov: $colorBtnIconHov) { diff --git a/platform/commonUI/general/res/sass/controls/_controls.scss b/platform/commonUI/general/res/sass/controls/_controls.scss index d1935d8ce09..ad32ea05151 100644 --- a/platform/commonUI/general/res/sass/controls/_controls.scss +++ b/platform/commonUI/general/res/sass/controls/_controls.scss @@ -296,8 +296,6 @@ input[type="search"] { .title-label { color: $colorObjHdrTxt; @include ellipsize(); - @include webkitProp(flex, '0 1 auto'); - padding-right: 0.35em; // For context arrow. Done with em's so pad is relative to the scale of the text. } .context-available-w { @@ -308,6 +306,10 @@ input[type="search"] { font-size: 0.7em; @include flex(0 0 1); } + + .t-object-alert { + display: none; + } } /******************************************************** PROGRESS BAR */ @@ -441,6 +443,63 @@ input[type="search"] { } } +@mixin sliderKnob() { + $h: 16px; + cursor: pointer; + width: floor($h/1.75); + height: $h; + margin-top: 1 + floor($h/2) * -1; + @include btnSubtle(pullForward($colorBtnBg, 10%)); + //border-radius: 50% !important; +} + +@mixin sliderKnobRound() { + $h: 12px; + cursor: pointer; + width: $h; + height: $h; + margin-top: 1 + floor($h/2) * -1; + @include btnSubtle(pullForward($colorBtnBg, 10%)); + border-radius: 50% !important; +} + +input[type="range"] { + // HTML5 range inputs + + -webkit-appearance: none; /* Hides the slider so that custom slider can be made */ + background: transparent; /* Otherwise white in Chrome */ + &:focus { + outline: none; /* Removes the blue border. */ + } + + // Thumb + &::-webkit-slider-thumb { + -webkit-appearance: none; + @include sliderKnobRound(); + } + &::-moz-range-thumb { + border: none; + @include sliderKnobRound(); + } + &::-ms-thumb { + border: none; + @include sliderKnobRound(); + } + + // Track + &::-webkit-slider-runnable-track { + width: 100%; + height: 3px; + @include sliderTrack(); + } + + &::-moz-range-track { + width: 100%; + height: 3px; + @include sliderTrack(); + } +} + /******************************************************** DATETIME PICKER */ .l-datetime-picker { $r1H: 15px; diff --git a/platform/commonUI/general/res/sass/controls/_menus.scss b/platform/commonUI/general/res/sass/controls/_menus.scss index 61e87b5b2b0..def9c3d3bb6 100644 --- a/platform/commonUI/general/res/sass/controls/_menus.scss +++ b/platform/commonUI/general/res/sass/controls/_menus.scss @@ -178,7 +178,7 @@ } .pane { box-sizing: border-box; - &.left { + &.menu-items { border-right: 1px solid pullForward($colorMenuBg, 10%); left: 0; padding-right: $interiorMargin; @@ -194,38 +194,53 @@ } } } - &.right { + &.menu-item-description { left: auto; right: 0; padding: $interiorMargin * 5; width: $prw; + .desc-area { + &.icon { + color: $colorCreateMenuLgIcon; + font-size: 8em; + margin-bottom: $interiorMargin * 3; + position: relative; + text-align: center; + } + &.title { + color: $colorCreateMenuText; + font-size: 1.2em; + margin-bottom: $interiorMargin * 2; + } + &.description { + color: pushBack($colorCreateMenuText, 20%); + font-size: 0.8em; + line-height: 1.5em; + } + } } } - .menu-item-description { - .desc-area { - &.icon { - $h: 150px; - color: $colorCreateMenuLgIcon; - position: relative; - font-size: 8em; - left: 0; - height: $h; - line-height: $h; - margin-bottom: $interiorMargin * 5; - text-align: center; - } - &.title { - color: $colorCreateMenuText; - font-size: 1.2em; - margin-bottom: 0.5em; - } - &.description { - color: $colorCreateMenuText; - font-size: 0.8em; - line-height: 1.5em; - } - } - } + + &.mini { + width: 400px; + height: 300px; + .pane { + &.menu-items { + font-size: 0.8em; + } + &.menu-item-description { + padding: $interiorMargin * 3; + .desc-area { + &.icon { + font-size: 4em; + } + &.title { + font-size: 1em; + } + } + } + } + } } .context-menu { font-size: 0.80rem; @@ -262,3 +277,7 @@ right: 0; width: auto; } + +.menus-up .menu { + bottom: $btnStdH; top: auto; +} diff --git a/platform/commonUI/general/res/sass/controls/_messages.scss b/platform/commonUI/general/res/sass/controls/_messages.scss index 2604d498bdc..485202c2521 100644 --- a/platform/commonUI/general/res/sass/controls/_messages.scss +++ b/platform/commonUI/general/res/sass/controls/_messages.scss @@ -345,3 +345,29 @@ body.desktop .t-message-single { body.desktop .t-message-list { .message-contents .l-message { margin-right: $interiorMarginLg; } } + +// Alert elements in views +.s-unsynced { + $c: $colorPausedBg; + border: 1px solid $c; + @include animTo($animName: pulsePaused, $propName: border-color, $propValStart: rgba($c, 0.8), $propValEnd: rgba($c, 0.5), $dur: $animPausedPulseDur, $dir: alternate, $count: infinite); +} + +.s-status-timeconductor-unsynced { + // Plot areas + .gl-plot .gl-plot-display-area { + @extend .s-unsynced; + } + + // Object headers + .object-header { + .t-object-alert { + display: inline; + &.t-alert-unsynced { + @extend .icon-alert-triangle; + color: $colorPausedBg; + } + } + } +} + diff --git a/platform/commonUI/general/res/sass/controls/_time-controller.scss b/platform/commonUI/general/res/sass/controls/_time-controller.scss deleted file mode 100644 index ba2cf5b3eee..00000000000 --- a/platform/commonUI/general/res/sass/controls/_time-controller.scss +++ /dev/null @@ -1,266 +0,0 @@ -@mixin toiLineHovEffects() { - &:before, - &:after { - background-color: $timeControllerToiLineColorHov; - } -} - -.l-time-controller { - $minW: 500px; - $knobHOffset: 0px; - $knobM: ($sliderKnobW + $knobHOffset) * -1; - $rangeValPad: $interiorMargin; - $rangeValOffset: $sliderKnobW + $interiorMargin; - $timeRangeSliderLROffset: 150px + ($sliderKnobW * 2); - $r1H: nth($ueTimeControlH,1); // Not currently used - $r2H: nth($ueTimeControlH,2); - $r3H: nth($ueTimeControlH,3); - - min-width: $minW; - font-size: 0.8rem; - - .l-time-range-inputs-holder, - .l-time-range-slider-holder, - .l-time-range-ticks-holder - { - box-sizing: border-box; - position: relative; - &:not(:first-child) { - margin-top: $interiorMargin; - } - } - .l-time-range-slider, - .l-time-range-ticks { - @include absPosDefault(0, visible); - left: $timeRangeSliderLROffset; right: $timeRangeSliderLROffset; - } - - .l-time-range-inputs-holder { - border-top: 1px solid $colorInteriorBorder; - padding-top: $interiorMargin; - &.l-flex-row, - .l-flex-row { - @include align-items(center); - .flex-elem { - height: auto; - line-height: normal; - } - } - .type-icon { - font-size: 120%; - vertical-align: middle; - } - .l-time-range-input-w, - .l-time-range-inputs-elem { - margin-right: $interiorMargin; - .lbl { - color: $colorPlotLabelFg; - } - .ui-symbol.icon { - font-size: 11px; - } - } - .l-time-range-input-w { - // Wraps a datetime text input field - position: relative; - input[type="text"] { - width: 200px; - &.picker-icon { - padding-right: 20px; - } - } - .icon-calendar { - position: absolute; - right: 5px; - top: 5px; - } - } - } - - .l-time-range-slider-holder { - height: $r2H; - .range-holder { - box-shadow: none; - background: none; - border: none; - .range { - .toi-line { - $myC: $timeControllerToiLineColor; - $myW: 8px; - @include transform(translateX(50%)); - position: absolute; - top: 0; right: 0; bottom: 0px; left: auto; - width: $myW; - height: auto; - z-index: 2; - &:before { - // Vert line - background-color: $myC; - position: absolute; - content: ""; - top: 0; right: auto; bottom: -10px; left: floor($myW/2) - 1; - width: 1px; - } - } - &:hover .toi-line { - @include toiLineHovEffects; - } - } - } - &:not(:active) { - .knob, - .range { - @include transition-property(left, right); - @include transition-duration(500ms); - @include transition-timing-function(ease-in-out); - } - } - } - - .l-time-range-ticks-holder { - height: $r3H; - .l-time-range-ticks { - border-top: 1px solid $colorTick; - .tick { - background-color: $colorTick; - border:none; - height: 5px; - width: 1px; - margin-left: -1px; - position: absolute; - &:first-child { - margin-left: 0; - } - .l-time-range-tick-label { - @include webkitProp(transform, translateX(-50%)); - color: $colorPlotLabelFg; - display: inline-block; - font-size: 0.7rem; - position: absolute; - top: 5px; - white-space: nowrap; - z-index: 2; - } - } - } - } - - .knob { - z-index: 2; - &:before { - $mTB: 2px; - $grippyW: 3px; - $mLR: ($sliderKnobW - $grippyW)/2; - @include bgStripes($c: pullForward($sliderColorKnob, 20%), $a: 1, $bgsize: 4px, $angle: 0deg); - content: ''; - display: block; - position: absolute; - top: $mTB; right: $mLR; bottom: $mTB; left: $mLR; - } - .range-value { - @include trans-prop-nice-fade(.25s); - font-size: 0.7rem; - position: absolute; - height: $r2H; - line-height: $r2H; - white-space: nowrap; - z-index: 1; - } - &:hover { - .range-value { - color: $sliderColorKnobHov; - } - } - &.knob-l { - margin-left: $knobM; - .range-value { - text-align: right; - right: $rangeValOffset; - } - } - &.knob-r { - margin-right: $knobM; - .range-value { - left: $rangeValOffset; - } - &:hover + .range-holder .range .toi-line { - @include toiLineHovEffects; - } - } - } - - .l-time-domain-selector { - position: absolute; - right: 0px; - top: $interiorMargin; - } - -} - -.s-time-range-val { - border-radius: $controlCr; - background-color: $colorInputBg; - padding: 1px 1px 0 $interiorMargin; -} - -/******************************************************************** MOBILE */ - -@include phoneandtablet { - .l-time-controller { - min-width: 0; - .l-time-range-slider-holder, - .l-time-range-ticks-holder { - display: none; - } - } -} - -@include phone { - .l-time-controller { - .l-time-range-inputs-holder { - &.l-flex-row, - .l-flex-row { - @include align-items(flex-start); - } - .l-time-range-inputs-elem { - &.type-icon { - margin-top: 3px; - } - } - .t-inputs-w { - @include flex-direction(column); - .l-time-range-input-w:not(:first-child) { - &:not(:first-child) { - margin-top: $interiorMargin; - } - margin-right: 0; - } - .l-time-range-inputs-elem { - &.lbl { display: none; } - } - } - } - } -} - -@include phonePortrait { - .l-time-controller { - .l-time-range-inputs-holder { - .t-inputs-w { - @include flex(1 1 auto); - padding-top: 25px; // Make room for the ever lovin' Time Domain Selector - .flex-elem { - @include flex(1 1 auto); - width: 100%; - } - input[type="text"] { - width: 100%; - } - } - } - } - .l-time-domain-selector { - right: auto; - left: 20px; - } -} diff --git a/platform/commonUI/general/res/sass/features/_imagery.scss b/platform/commonUI/general/res/sass/features/_imagery.scss index cd04568e021..e33970b0e90 100644 --- a/platform/commonUI/general/res/sass/features/_imagery.scss +++ b/platform/commonUI/general/res/sass/features/_imagery.scss @@ -74,7 +74,7 @@ .s-image-main { border: 1px solid transparent; &.paused { - border-color: $colorPausedBg; + @extend .s-unsynced; } } diff --git a/platform/commonUI/general/res/sass/helpers/_wait-spinner.scss b/platform/commonUI/general/res/sass/helpers/_wait-spinner.scss index aecf42e02f4..93c4fa093f8 100644 --- a/platform/commonUI/general/res/sass/helpers/_wait-spinner.scss +++ b/platform/commonUI/general/res/sass/helpers/_wait-spinner.scss @@ -19,15 +19,6 @@ * this source code distribution or the Licensing information page available * at runtime from the About dialog for additional information. *****************************************************************************/ -@include keyframes(rotation) { - 100% { @include transform(rotate(360deg)); } -} - -@include keyframes(rotation-centered) { - 0% { @include transform(translate(-50%, -50%) rotate(0deg)); } - 100% { @include transform(translate(-50%, -50%) rotate(360deg)); } -} - @mixin spinner($b: 5px, $c: $colorKey) { @include transform-origin(center); @include animation-name(rotation-centered); diff --git a/platform/commonUI/general/res/sass/user-environ/_layout.scss b/platform/commonUI/general/res/sass/user-environ/_layout.scss index 9740171ff08..d9ef67bb0a4 100644 --- a/platform/commonUI/general/res/sass/user-environ/_layout.scss +++ b/platform/commonUI/general/res/sass/user-environ/_layout.scss @@ -128,7 +128,7 @@ line-height: $ueTopBarH; } - .primary-pane { + .t-object.primary-pane { // Need to lift up this pane to ensure that 'collapsed' panes don't block user interactions z-index: 4; } @@ -212,6 +212,8 @@ body.desktop .pane .mini-tab-icon.toggle-pane { .holder-object { top: $bodyMargin; bottom: $interiorMargin; + // Clip element that have min-widths + overflow: hidden; } .holder-inspector { top: $bodyMargin; diff --git a/platform/commonUI/general/res/templates/controls/datetime-field.html b/platform/commonUI/general/res/templates/controls/datetime-field.html index 1cb5e76f6de..684ac0b6842 100644 --- a/platform/commonUI/general/res/templates/controls/datetime-field.html +++ b/platform/commonUI/general/res/templates/controls/datetime-field.html @@ -23,6 +23,8 @@ .l-row-elem { + // First order row elements + box-sizing: border-box; + width: 100%; + position: relative; + } + + .mode-selector .s-menu-button, + .time-delta { + &:before { + @extend .ui-symbol; + } + } + + .time-delta { + &:before { + color: $colorTimeCondKeyBg; + } + } + + .l-time-conductor-inputs-holder, + .l-time-conductor-inputs-and-ticks, + .l-time-conductor-zoom-w { + font-size: 0.8rem; + } + + .l-time-conductor-inputs-holder { + $ticksBlockerFadeW: 50px; + $iconCalendarW: 16px; + $wBgColor: $colorBodyBg; + + height: $r1H; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + .l-time-range-w { + // Wraps a datetime text input field + height: 100%; + position: absolute; + .title { + display: inline-block; + margin-right: $interiorMarginSm; + } + &.start-w { + @include background-image(linear-gradient(270deg, transparent, $wBgColor $ticksBlockerFadeW)); + padding-right: $ticksBlockerFadeW; + .title:before { + content: 'Start'; + } + } + &.end-w { + @include background-image(linear-gradient(90deg, transparent, $wBgColor $ticksBlockerFadeW)); + padding-left: $ticksBlockerFadeW; + right: 0; + .title:before { + content: 'End'; + } + } + input[type="text"] { + @include trans-prop-nice(padding, 250ms); + } + .time-range-input input[type="text"] { + width: $timeCondInputTimeSysDefW; + } + .hrs-min-input input[type="text"] { + width: $timeCondInputDeltaDefW; + } + .icon-calendar { + margin-top: 4px; + } + } + } + + .l-time-conductor-inputs-and-ticks { + $c: $colorTimeCondTicks; //$colorTick; + height: $r1H; + mct-conductor-axis { + display: block; + position: relative; + width: 100%; + } + .l-axis-holder { + height: $r1H; + position: relative; + width: 100%; + svg { + text-rendering: geometricPrecision; + width: 100%; + height: 100%; + > g { + font-size: 0.9em; + } + path { + // Line beneath ticks + display: none; + } + line { + // Tick marks + stroke: $c; + } + text { + // Tick labels + fill: $c; + } + } + } + } + .l-data-visualization { + background: $colorTimeCondDataVisBg; + height: $r2H; + } + + .l-time-conductor-controls { + align-items: center; + margin-top: $interiorMargin; + .l-time-conductor-zoom-w { + @include justify-content(flex-end); + .time-conductor-zoom { + display: none; // TEMP per request from Andrew 8/1/16 + height: $r3H; + min-width: 100px; + width: 20%; + } + .time-conductor-zoom-current-range { + display: none; // TEMP per request from Andrew 8/1/16 + color: $colorTick; + } + } + } + + // Real-time, latest modes + &.realtime-mode, + &.lad-mode { + .time-conductor-icon { + &:before { color: $colorTimeCondKeyBg; } + div[class*="hand"] { + @include animation-name(clock-hands); + &:before { + background: $colorTimeCondKeyBg; + } + } + } + + .l-time-conductor-inputs-holder { + .l-time-range-input-w { + input[type="text"]:not(.error) { + background: transparent; + box-shadow: none; + border-radius: 0; + padding-left: 0; + padding-right: 0; + &:hover, + &:focus { + @include nice-input(); + padding: $inputTextP; + } + } + .icon-calendar { + display: none; + } + &.start-date { + display: none; + } + &.end-date { + pointer-events: none; + input[type="text"] { + color: pullForward($colorTimeCondKeyBg, 5%); + margin-right: $interiorMargin; + tab-index: -1; + } + } + } + } + + .l-data-visualization { + background: $colorTimeCondDataVisRtBg !important + } + + .mode-selector .s-menu-button { + $fg: $colorTimeCondKeyFg; + @include btnSubtle($bg: $colorTimeCondKeyBg, $bgHov: pullForward($colorTimeCondKeyBg, $ltGamma), $fg: $colorTimeCondKeyFg); + &:before { color: $fg !important; }; + color: $fg !important; + } + } + + // Fixed mode + &.fixed-mode { + $i: $glyph-icon-calendar; + .time-conductor-icon div[class*="hand"] { + &.hand-little { + @include transform(rotate(120deg)); + } + } + .mode-selector .s-menu-button:before { + content: $i; + } + } + + // Realtime mode + &.realtime-mode { + $i: $glyph-icon-clock; + .time-conductor-icon div[class*="hand"] { + @include animation-name(clock-hands); + } + .time-delta:before { + content: $i; + } + .l-time-conductor-inputs-holder .l-time-range-w.end-w .title:before { + content: 'Now'; + } + .mode-selector .s-menu-button:before { + content: $i; + } + } + + // LAD mode + &.lad-mode { + $i: $glyph-icon-database; + .time-conductor-icon div[class*="hand"] { + @include animation-name(clock-hands-sticky); + &.hand-big { + @include animation-duration(5s); + } + &.hand-little { + @include animation-duration(60s); + } + } + .time-delta:before { + content: $i; + } + .l-time-conductor-inputs-holder .l-time-range-w.end-w .title:before { + content: 'LAD'; + } + .mode-selector .s-menu-button:before { + content: $i; + } + } +} + +/******************************************************************** MOBILE */ + +@include phoneandtablet { + .l-time-conductor-holder { min-width: 0 !important; } + .super-menu.mini { + width: 200px; + height: 100px; + .pane.menu-item-description { + display: none; + } + } +} + +@include phone { + .l-time-conductor { + min-width: 0; + .l-time-conductor-inputs-and-ticks { + .l-time-conductor-inputs-holder { + .l-time-range-w { + background-image: none !important; + } + } + mct-conductor-axis { + display: none; + } + } + } +} + +@include phonePortrait { + .l-time-conductor { + .l-data-visualization, + .l-time-conductor-zoom-w, + .time-delta { + display: none; + } + + .l-time-conductor-inputs-and-ticks { + height: auto !important; + .l-time-conductor-inputs-holder { + position: relative; + height: auto !important; + + .l-time-range-w { + background-image: none !important; + display: block; + height: auto !important; + padding: 0 !important; + position: relative; + text-align: left; + &:not(:first-child) { + margin-top: $interiorMargin; + } + } + } + } + + // Fixed mode + &.fixed-mode { + .l-time-conductor-inputs-and-ticks { + .l-time-range-w { + .title { + width: 30px; + } + } + } + } + + // Real-time, latest modes + &.realtime-mode, + &.lad-mode { + .l-time-conductor-inputs-and-ticks { + .l-time-range-w { + &.start-w { + display: none; + } + &.end-w { + margin-top: 0; + .end-date input[type="text"] { + margin: 0; + text-align: left; + } + } + } + } + } + } +} diff --git a/platform/features/conductor-v2/conductor/res/sass/time-conductor-espresso.scss b/platform/features/conductor-v2/conductor/res/sass/time-conductor-espresso.scss new file mode 100644 index 00000000000..a47f4c07a76 --- /dev/null +++ b/platform/features/conductor-v2/conductor/res/sass/time-conductor-espresso.scss @@ -0,0 +1,39 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ +@import "bourbon"; +@import "../../../../../commonUI/general/res/sass/constants"; +@import "../../../../../commonUI/general/res/sass/mixins"; +@import "../../../../../commonUI/general/res/sass/mobile/constants"; +@import "../../../../../commonUI/general/res/sass/mobile/mixins"; +@import "../../../../../commonUI/themes/espresso/res/sass/constants"; +@import "../../../../../commonUI/themes/espresso/res/sass/mixins"; +@import "../../../../../commonUI/general/res/sass/glyphs"; +@import "../../../../../commonUI/general/res/sass/icons"; +@import "constants"; + +// Thematic constants +$colorTimeCondTicks: pullForward($colorBodyBg, 30%); +$colorTimeCondKeyBg: #4e70dc; +$colorTimeCondKeyFg: #fff; +$colorTimeCondDataVisBg: pullForward($colorBodyBg, 10%); +$colorTimeCondDataVisRtBg: pushBack($colorTimeCondKeyBg, 10%); +@import "time-conductor-base"; \ No newline at end of file diff --git a/platform/features/conductor-v2/conductor/res/sass/time-conductor-snow.scss b/platform/features/conductor-v2/conductor/res/sass/time-conductor-snow.scss new file mode 100644 index 00000000000..626ceb0e514 --- /dev/null +++ b/platform/features/conductor-v2/conductor/res/sass/time-conductor-snow.scss @@ -0,0 +1,39 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2015, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ +@import "bourbon"; +@import "../../../../../commonUI/general/res/sass/constants"; +@import "../../../../../commonUI/general/res/sass/mixins"; +@import "../../../../../commonUI/general/res/sass/mobile/constants"; +@import "../../../../../commonUI/general/res/sass/mobile/mixins"; +@import "../../../../../commonUI/themes/snow/res/sass/constants"; +@import "../../../../../commonUI/themes/snow/res/sass/mixins"; +@import "../../../../../commonUI/general/res/sass/glyphs"; +@import "../../../../../commonUI/general/res/sass/icons"; +@import "constants"; + +// Thematic constants +$colorTimeCondTicks: pullForward($colorBodyBg, 30%); +$colorTimeCondKeyBg: #6178dc; +$colorTimeCondKeyFg: #fff; +$colorTimeCondDataVisBg: pullForward($colorBodyBg, 10%); +$colorTimeCondDataVisRtBg: pushBack($colorTimeCondKeyBg, 30%); +@import "time-conductor-base"; \ No newline at end of file diff --git a/platform/features/conductor-v2/conductor/res/templates/mode-selector/mode-menu.html b/platform/features/conductor-v2/conductor/res/templates/mode-selector/mode-menu.html new file mode 100644 index 00000000000..990c6de4824 --- /dev/null +++ b/platform/features/conductor-v2/conductor/res/templates/mode-selector/mode-menu.html @@ -0,0 +1,45 @@ + +
+ + +
diff --git a/platform/features/conductor-v2/conductor/res/templates/mode-selector/mode-selector.html b/platform/features/conductor-v2/conductor/res/templates/mode-selector/mode-selector.html new file mode 100644 index 00000000000..1f346f2bef6 --- /dev/null +++ b/platform/features/conductor-v2/conductor/res/templates/mode-selector/mode-selector.html @@ -0,0 +1,34 @@ + + +
+ {{ngModel.options[ngModel.selectedKey] + .label}} +
+ +
\ No newline at end of file diff --git a/platform/features/conductor-v2/conductor/res/templates/time-conductor.html b/platform/features/conductor-v2/conductor/res/templates/time-conductor.html new file mode 100644 index 00000000000..19c86b2b746 --- /dev/null +++ b/platform/features/conductor-v2/conductor/res/templates/time-conductor.html @@ -0,0 +1,108 @@ + +
+ +
+
+
+
+ +
+ +
+
+ + + + + + + + - + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+ + +
+ + + + + +
+ + +
+
+ +
+
diff --git a/platform/features/conductor-v2/conductor/src/TimeConductor.js b/platform/features/conductor-v2/conductor/src/TimeConductor.js new file mode 100644 index 00000000000..2c2194b5a01 --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/TimeConductor.js @@ -0,0 +1,179 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(['EventEmitter'], function (EventEmitter) { + + /** + * The public API for setting and querying time conductor state. The + * time conductor is the means by which the temporal bounds of a view + * are controlled. Time-sensitive views will typically respond to + * changes to bounds or other properties of the time conductor and + * update the data displayed based on the time conductor state. + * + * The TimeConductor extends the EventEmitter class. A number of events are + * fired when properties of the time conductor change, which are + * documented below. + * @constructor + */ + function TimeConductor() { + EventEmitter.call(this); + + //The Time System + this.system = undefined; + //The Time Of Interest + this.toi = undefined; + + this.boundsVal = { + start: undefined, + end: undefined + }; + + //Default to fixed mode + this.followMode = false; + } + + TimeConductor.prototype = Object.create(EventEmitter.prototype); + + /** + * Validate the given bounds. This can be used for pre-validation of + * bounds, for example by views validating user inputs. + * @param bounds The start and end time of the conductor. + * @returns {string | true} A validation error, or true if valid + */ + TimeConductor.prototype.validateBounds = function (bounds) { + if ((bounds.start === undefined) || + (bounds.end === undefined) || + isNaN(bounds.start) || + isNaN(bounds.end) + ) { + return "Start and end must be specified as integer values"; + } else if (bounds.start > bounds.end) { + return "Specified start date exceeds end bound"; + } + return true; + }; + + /** + * Get or set the follow mode of the time conductor. In follow mode the + * time conductor ticks, regularly updating the bounds from a timing + * source appropriate to the selected time system and mode of the time + * conductor. + * @fires TimeConductor#follow + * @param {boolean} followMode + * @returns {boolean} + */ + TimeConductor.prototype.follow = function (followMode) { + if (arguments.length > 0) { + this.followMode = followMode; + /** + * @event TimeConductor#follow The TimeConductor has toggled + * into or out of follow mode. + * @property {boolean} followMode true if follow mode is + * enabled, otherwise false. + */ + this.emit('follow', this.followMode); + } + return this.followMode; + }; + + /** + * @typedef {Object} TimeConductorBounds + * @property {number} start The start time displayed by the time conductor in ms since epoch. Epoch determined by current time system + * @property {number} end The end time displayed by the time conductor in ms since epoch. + */ + /** + * Get or set the start and end time of the time conductor. Basic validation + * of bounds is performed. + * + * @param {TimeConductorBounds} newBounds + * @throws {Error} Validation error + * @fires TimeConductor#bounds + * @returns {TimeConductorBounds} + */ + TimeConductor.prototype.bounds = function (newBounds) { + if (arguments.length > 0) { + var validationResult = this.validateBounds(newBounds); + if (validationResult !== true) { + throw new Error(validationResult); + } + //Create a copy to avoid direct mutation of conductor bounds + this.boundsVal = JSON.parse(JSON.stringify(newBounds)); + /** + * @event TimeConductor#bounds The start time, end time, or + * both have been updated + * @property {TimeConductorBounds} bounds + */ + this.emit('bounds', this.boundsVal); + } + //Return a copy to prevent direct mutation of time conductor bounds. + return JSON.parse(JSON.stringify(this.boundsVal)); + }; + + /** + * Get or set the time system of the TimeConductor. Time systems determine + * units, epoch, and other aspects of time representation. When changing + * the time system in use, new valid bounds must also be provided. + * @param {TimeSystem} newTimeSystem + * @param {TimeConductorBounds} bounds + * @fires TimeConductor#timeSystem + * @returns {TimeSystem} The currently applied time system + */ + TimeConductor.prototype.timeSystem = function (newTimeSystem, bounds) { + if (arguments.length >= 2) { + this.system = newTimeSystem; + /** + * @event TimeConductor#timeSystem The time system used by the time + * conductor has changed. A change in Time System will always be + * followed by a bounds event specifying new query bounds + * @property {TimeSystem} The value of the currently applied + * Time System + * */ + this.emit('timeSystem', this.system); + this.bounds(bounds); + } else if (arguments.length === 1) { + throw new Error('Must set bounds when changing time system'); + } + return this.system; + }; + + /** + * Get or set the Time of Interest. The Time of Interest is the temporal + * focus of the current view. It can be manipulated by the user from the + * time conductor or from other views. + * @fires TimeConductor#timeOfInterest + * @param newTOI + * @returns {number} the current time of interest + */ + TimeConductor.prototype.timeOfInterest = function (newTOI) { + if (arguments.length > 0) { + this.toi = newTOI; + /** + * @event TimeConductor#timeOfInterest The Time of Interest has moved. + * @property {number} Current time of interest + */ + this.emit('timeOfInterest', this.toi); + } + return this.toi; + }; + + return TimeConductor; +}); diff --git a/platform/features/conductor-v2/conductor/src/TimeConductorSpec.js b/platform/features/conductor-v2/conductor/src/TimeConductorSpec.js new file mode 100644 index 00000000000..7701f5e9b4e --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/TimeConductorSpec.js @@ -0,0 +1,110 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(['./TimeConductor'], function (TimeConductor) { + describe("The Time Conductor", function () { + var tc, + timeSystem, + bounds, + eventListener, + toi, + follow; + + beforeEach(function () { + tc = new TimeConductor(); + timeSystem = {}; + bounds = {start: 0, end: 0}; + eventListener = jasmine.createSpy("eventListener"); + toi = 111; + follow = true; + }); + + it("Supports setting and querying of time of interest and and follow mode", function () { + expect(tc.timeOfInterest()).not.toBe(toi); + tc.timeOfInterest(toi); + expect(tc.timeOfInterest()).toBe(toi); + + expect(tc.follow()).not.toBe(follow); + tc.follow(follow); + expect(tc.follow()).toBe(follow); + }); + + it("Allows setting of valid bounds", function () { + bounds = {start: 0, end: 1}; + expect(tc.bounds()).not.toEqual(bounds); + expect(tc.bounds.bind(tc, bounds)).not.toThrow(); + expect(tc.bounds()).toEqual(bounds); + }); + + it("Disallows setting of invalid bounds", function () { + bounds = {start: 1, end: 0}; + expect(tc.bounds()).not.toEqual(bounds); + expect(tc.bounds.bind(tc, bounds)).toThrow(); + expect(tc.bounds()).not.toEqual(bounds); + + bounds = {start: 1}; + expect(tc.bounds()).not.toEqual(bounds); + expect(tc.bounds.bind(tc, bounds)).toThrow(); + expect(tc.bounds()).not.toEqual(bounds); + }); + + it("Allows setting of time system with bounds", function () { + expect(tc.timeSystem()).not.toBe(timeSystem); + expect(tc.timeSystem.bind(tc, timeSystem, bounds)).not.toThrow(); + expect(tc.timeSystem()).toBe(timeSystem); + }); + + it("Disallows setting of time system without bounds", function () { + expect(tc.timeSystem()).not.toBe(timeSystem); + expect(tc.timeSystem.bind(tc, timeSystem)).toThrow(); + expect(tc.timeSystem()).not.toBe(timeSystem); + }); + + it("Emits an event when time system changes", function () { + expect(eventListener).not.toHaveBeenCalled(); + tc.on("timeSystem", eventListener); + tc.timeSystem(timeSystem, bounds); + expect(eventListener).toHaveBeenCalledWith(timeSystem); + }); + + it("Emits an event when time of interest changes", function () { + expect(eventListener).not.toHaveBeenCalled(); + tc.on("timeOfInterest", eventListener); + tc.timeOfInterest(toi); + expect(eventListener).toHaveBeenCalledWith(toi); + }); + + it("Emits an event when bounds change", function () { + expect(eventListener).not.toHaveBeenCalled(); + tc.on("bounds", eventListener); + tc.bounds(bounds); + expect(eventListener).toHaveBeenCalledWith(bounds); + }); + + it("Emits an event when follow mode changes", function () { + expect(eventListener).not.toHaveBeenCalled(); + tc.on("follow", eventListener); + tc.follow(follow); + expect(eventListener).toHaveBeenCalledWith(follow); + }); + }); +}); diff --git a/platform/features/conductor-v2/conductor/src/timeSystems/LocalClock.js b/platform/features/conductor-v2/conductor/src/timeSystems/LocalClock.js new file mode 100644 index 00000000000..09ddd356120 --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/timeSystems/LocalClock.js @@ -0,0 +1,89 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(['./TickSource'], function (TickSource) { + /** + * @implements TickSource + * @constructor + */ + function LocalClock($timeout, period) { + TickSource.call(this); + + this.metadata = { + key: 'local', + mode: 'realtime', + cssclass: 'icon-clock', + label: 'Real-time', + name: 'Real-time Mode', + description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.' + }; + + this.period = period; + this.$timeout = $timeout; + this.timeoutHandle = undefined; + } + + LocalClock.prototype = Object.create(TickSource.prototype); + + LocalClock.prototype.start = function () { + this.timeoutHandle = this.$timeout(this.tick.bind(this), this.period); + }; + + LocalClock.prototype.stop = function () { + if (this.timeoutHandle) { + this.$timeout.cancel(this.timeoutHandle); + } + }; + + LocalClock.prototype.tick = function () { + var now = Date.now(); + this.listeners.forEach(function (listener) { + listener(now); + }); + this.timeoutHandle = this.$timeout(this.tick.bind(this), this.period); + }; + + /** + * Register a listener for the local clock. When it ticks, the local + * clock will provide the current local system time + * + * @param listener + * @returns {function} a function for deregistering the provided listener + */ + LocalClock.prototype.listen = function (listener) { + var listeners = this.listeners; + listeners.push(listener); + + if (listeners.length === 1) { + this.start(); + } + + return function () { + listeners.splice(listeners.indexOf(listener)); + if (listeners.length === 0) { + this.stop(); + } + }.bind(this); + }; + + return LocalClock; +}); diff --git a/platform/features/conductor-v2/conductor/src/timeSystems/LocalClockSpec.js b/platform/features/conductor-v2/conductor/src/timeSystems/LocalClockSpec.js new file mode 100644 index 00000000000..b34b625f430 --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/timeSystems/LocalClockSpec.js @@ -0,0 +1,50 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(["./LocalClock"], function (LocalClock) { + describe("The LocalClock class", function () { + var clock, + mockTimeout, + timeoutHandle = {}; + + beforeEach(function () { + mockTimeout = jasmine.createSpy("timeout"); + mockTimeout.andReturn(timeoutHandle); + mockTimeout.cancel = jasmine.createSpy("cancel"); + + clock = new LocalClock(mockTimeout, 0); + clock.start(); + }); + + it("calls listeners on tick with current time", function () { + var mockListener = jasmine.createSpy("listener"); + clock.listen(mockListener); + clock.tick(); + expect(mockListener).toHaveBeenCalledWith(jasmine.any(Number)); + }); + + it("stops ticking when stop is called", function () { + clock.stop(); + expect(mockTimeout.cancel).toHaveBeenCalledWith(timeoutHandle); + }); + }); +}); diff --git a/platform/features/conductor-v2/conductor/src/timeSystems/TickSource.js b/platform/features/conductor-v2/conductor/src/timeSystems/TickSource.js new file mode 100644 index 00000000000..8cb9fd308d5 --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/timeSystems/TickSource.js @@ -0,0 +1,47 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define([], function () { + /** + * A tick source is an event generator such as a timing signal, or + * indicator of data availability, which can be used to advance the Time + * Conductor. Usage is simple, a listener registers a callback which is + * invoked when this source 'ticks'. + * + * @interface + * @constructor + */ + function TickSource() { + this.listeners = []; + } + + /** + * @param callback Function to be called when this tick source ticks. + * @returns an 'unlisten' function that will remove the callback from + * the registered listeners + */ + TickSource.prototype.listen = function (callback) { + throw new Error('Not implemented'); + }; + + return TickSource; +}); diff --git a/platform/features/conductor-v2/conductor/src/timeSystems/TimeSystem.js b/platform/features/conductor-v2/conductor/src/timeSystems/TimeSystem.js new file mode 100644 index 00000000000..652ea9ed0f0 --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/timeSystems/TimeSystem.js @@ -0,0 +1,93 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define([], function () { + /** + * @interface + * @constructor + */ + function TimeSystem() { + /** + * @typedef TimeSystemMetadata + * @property {string} key + * @property {string} name + * @property {string} description + * + * @type {TimeSystemMetadata} + */ + this.metadata = undefined; + } + + /** + * Time formats are defined as extensions. Time systems that implement + * this interface should provide an array of format keys supported by them. + * + * @returns {string[]} An array of time format keys + */ + TimeSystem.prototype.formats = function () { + throw new Error('Not implemented'); + }; + + /** + * @typedef DeltaFormat + * @property {string} type the type of MctControl used to represent this + * field. Typically 'datetime-field' for UTC based dates, or 'textfield' + * otherwise + * @property {string} [format] An optional field specifying the + * Format to use for delta fields in this time system. + */ + /** + * Specifies a format for deltas in this time system. + * + * @returns {DeltaFormat} a delta format specifier + */ + TimeSystem.prototype.deltaFormat = function () { + throw new Error('Not implemented'); + }; + + /** + * Returns the tick sources supported by this time system. Tick sources + * are event generators that can be used to advance the time conductor + * @returns {TickSource[]} The tick sources supported by this time system. + */ + TimeSystem.prototype.tickSources = function () { + throw new Error('Not implemented'); + }; + + /** + * + * @returns {TimeSystemDefault[]} At least one set of default values for + * this time system. + */ + TimeSystem.prototype.defaults = function () { + throw new Error('Not implemented'); + }; + + /** + * @return {boolean} + */ + TimeSystem.prototype.isUTCBased = function () { + return true; + }; + + return TimeSystem; +}); diff --git a/platform/features/conductor-v2/conductor/src/ui/MctConductorAxis.js b/platform/features/conductor-v2/conductor/src/ui/MctConductorAxis.js new file mode 100644 index 00000000000..58cb60befcf --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/MctConductorAxis.js @@ -0,0 +1,146 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define( + [ + "d3" + ], + function (d3) { + var PADDING = 1; + + /** + * The mct-conductor-axis renders a horizontal axis with regular + * labelled 'ticks'. It requires 'start' and 'end' integer values to + * be specified as attributes. + */ + function MCTConductorAxis(conductor, formatService) { + // Dependencies + this.d3 = d3; + this.conductor = conductor; + this.formatService = formatService; + + // Runtime properties (set by 'link' function) + this.target = undefined; + this.xScale = undefined; + this.xAxis = undefined; + this.axisElement = undefined; + + // Angular Directive interface + this.link = this.link.bind(this); + this.restrict = "E"; + this.template = + "
"; + this.priority = 1000; + + //Bind all class functions to 'this' + Object.keys(MCTConductorAxis.prototype).filter(function (key) { + return typeof MCTConductorAxis.prototype[key] === 'function'; + }).forEach(function (key) { + this[key] = this[key].bind(this); + }.bind(this)); + } + + MCTConductorAxis.prototype.setScale = function () { + var width = this.target.offsetWidth; + var timeSystem = this.conductor.timeSystem(); + var bounds = this.conductor.bounds(); + + if (timeSystem.isUTCBased()) { + this.xScale = this.xScale || this.d3.scaleUtc(); + this.xScale.domain([new Date(bounds.start), new Date(bounds.end)]); + } else { + this.xScale = this.xScale || this.d3.scaleLinear(); + this.xScale.domain([bounds.start, bounds.end]); + } + + this.xScale.range([PADDING, width - PADDING * 2]); + this.axisElement.call(this.xAxis); + }; + + MCTConductorAxis.prototype.changeTimeSystem = function (timeSystem) { + var key = timeSystem.formats()[0]; + if (key !== undefined) { + var format = this.formatService.getFormat(key); + var bounds = this.conductor.bounds(); + + if (timeSystem.isUTCBased()) { + this.xScale = this.d3.scaleUtc(); + } else { + this.xScale = this.d3.scaleLinear(); + } + + this.xAxis.scale(this.xScale); + //Define a custom format function + this.xAxis.tickFormat(function (tickValue) { + // Normalize date representations to numbers + if (tickValue instanceof Date) { + tickValue = tickValue.getTime(); + } + return format.format(tickValue, { + min: bounds.start, + max: bounds.end + }); + }); + this.axisElement.call(this.xAxis); + } + }; + + MCTConductorAxis.prototype.destroy = function () { + this.conductor.off('timeSystem', this.changeTimeSystem); + this.conductor.off('bounds', this.setScale); + }; + + MCTConductorAxis.prototype.link = function (scope, element) { + var conductor = this.conductor; + this.target = element[0].firstChild; + var height = this.target.offsetHeight; + var vis = this.d3.select(this.target) + .append('svg:svg') + .attr('width', '100%') + .attr('height', height); + + this.xAxis = this.d3.axisTop(); + + // draw x axis with labels and move to the bottom of the chart area + this.axisElement = vis.append("g") + .attr("transform", "translate(0," + (height - PADDING) + ")"); + + scope.resize = this.setScale; + + conductor.on('timeSystem', this.changeTimeSystem); + + //On conductor bounds changes, redraw ticks + conductor.on('bounds', this.setScale); + + scope.$on("$destroy", this.destroy); + + if (conductor.timeSystem() !== undefined) { + this.changeTimeSystem(conductor.timeSystem()); + this.setScale(); + } + }; + + return function (conductor, formatService) { + return new MCTConductorAxis(conductor, formatService); + }; + } +); diff --git a/platform/features/conductor-v2/conductor/src/ui/MctConductorAxisSpec.js b/platform/features/conductor-v2/conductor/src/ui/MctConductorAxisSpec.js new file mode 100644 index 00000000000..0fe3c4cf3fc --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/MctConductorAxisSpec.js @@ -0,0 +1,146 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(['./MctConductorAxis'], function (MctConductorAxis) { + describe("The MctConductorAxis directive", function () { + var directive, + mockConductor, + mockFormatService, + mockScope, + mockElement, + mockTarget, + mockBounds, + d3; + + beforeEach(function () { + mockScope = jasmine.createSpyObj("scope", [ + "$on" + ]); + + //Add some HTML elements + mockTarget = { + offsetWidth: 0, + offsetHeight: 0 + }; + mockElement = { + firstChild: mockTarget + }; + mockBounds = { + start: 100, + end: 200 + }; + mockConductor = jasmine.createSpyObj("conductor", [ + "timeSystem", + "bounds", + "on", + "off" + ]); + mockConductor.bounds.andReturn(mockBounds); + + mockFormatService = jasmine.createSpyObj("formatService", [ + "getFormat" + ]); + + var d3Functions = [ + "scale", + "scaleUtc", + "scaleLinear", + "select", + "append", + "attr", + "axisTop", + "call", + "tickFormat", + "domain", + "range" + ]; + d3 = jasmine.createSpyObj("d3", d3Functions); + d3Functions.forEach(function (func) { + d3[func].andReturn(d3); + }); + + directive = new MctConductorAxis(mockConductor, mockFormatService); + directive.d3 = d3; + directive.link(mockScope, [mockElement]); + }); + + it("listens for changes to time system and bounds", function () { + expect(mockConductor.on).toHaveBeenCalledWith("timeSystem", directive.changeTimeSystem); + expect(mockConductor.on).toHaveBeenCalledWith("bounds", directive.setScale); + }); + + it("on scope destruction, deregisters listeners", function () { + expect(mockScope.$on).toHaveBeenCalledWith("$destroy", directive.destroy); + directive.destroy(); + expect(mockConductor.off).toHaveBeenCalledWith("timeSystem", directive.changeTimeSystem); + expect(mockConductor.off).toHaveBeenCalledWith("bounds", directive.setScale); + }); + + describe("when the time system changes", function () { + var mockTimeSystem; + var mockFormat; + + beforeEach(function () { + mockTimeSystem = jasmine.createSpyObj("timeSystem", [ + "formats", + "isUTCBased" + ]); + mockFormat = jasmine.createSpyObj("format", [ + "format" + ]); + + mockTimeSystem.formats.andReturn(["mockFormat"]); + mockFormatService.getFormat.andReturn(mockFormat); + }); + + it("uses a UTC scale for UTC time systems", function () { + mockTimeSystem.isUTCBased.andReturn(true); + directive.changeTimeSystem(mockTimeSystem); + expect(d3.scaleUtc).toHaveBeenCalled(); + expect(d3.scaleLinear).not.toHaveBeenCalled(); + }); + + it("uses a linear scale for non-UTC time systems", function () { + mockTimeSystem.isUTCBased.andReturn(false); + directive.changeTimeSystem(mockTimeSystem); + expect(d3.scaleLinear).toHaveBeenCalled(); + expect(d3.scaleUtc).not.toHaveBeenCalled(); + }); + + it("sets axis domain to time conductor bounds", function () { + mockTimeSystem.isUTCBased.andReturn(false); + mockConductor.timeSystem.andReturn(mockTimeSystem); + + directive.setScale(); + expect(d3.domain).toHaveBeenCalledWith([mockBounds.start, mockBounds.end]); + }); + + it("uses the format specified by the time system to format tick" + + " labels", function () { + directive.changeTimeSystem(mockTimeSystem); + expect(d3.tickFormat).toHaveBeenCalled(); + d3.tickFormat.mostRecentCall.args[0](); + expect(mockFormat.format).toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/platform/features/conductor-v2/conductor/src/ui/NumberFormat.js b/platform/features/conductor-v2/conductor/src/ui/NumberFormat.js new file mode 100644 index 00000000000..ac3f40cb7bd --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/NumberFormat.js @@ -0,0 +1,53 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([], function () { + + /** + * Formatter for basic numbers. Provides basic support for non-UTC + * numbering systems + * + * @implements {Format} + * @constructor + * @memberof platform/commonUI/formats + */ + function NumberFormat() { + } + + NumberFormat.prototype.format = function (value) { + if (isNaN(value)) { + return ''; + } else { + return '' + value; + } + }; + + NumberFormat.prototype.parse = function (text) { + return parseFloat(text); + }; + + NumberFormat.prototype.validate = function (text) { + return !isNaN(text); + }; + + return NumberFormat; +}); diff --git a/platform/features/conductor-v2/conductor/src/ui/NumberFormatSpec.js b/platform/features/conductor-v2/conductor/src/ui/NumberFormatSpec.js new file mode 100644 index 00000000000..f56c6e67d38 --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/NumberFormatSpec.js @@ -0,0 +1,49 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(['./NumberFormat'], function (NumberFormat) { + describe("The NumberFormat class", function () { + var format; + beforeEach(function () { + format = new NumberFormat(); + }); + + it("The format function takes a string and produces a number", function () { + var text = format.format(1); + expect(text).toBe("1"); + expect(typeof text).toBe("string"); + }); + + it("The parse function takes a string and produces a number", function () { + var number = format.parse("1"); + expect(number).toBe(1); + expect(typeof number).toBe("number"); + }); + + it("validates that the input is a number", function () { + expect(format.validate("1")).toBe(true); + expect(format.validate(1)).toBe(true); + expect(format.validate("1.1")).toBe(true); + expect(format.validate("abc")).toBe(false); + }); + }); +}); diff --git a/platform/features/conductor-v2/conductor/src/ui/TimeConductorController.js b/platform/features/conductor-v2/conductor/src/ui/TimeConductorController.js new file mode 100644 index 00000000000..83bc0558c61 --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/TimeConductorController.js @@ -0,0 +1,243 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define( + [ + './TimeConductorValidation' + ], + function (TimeConductorValidation) { + + function TimeConductorController($scope, $window, timeConductor, conductorViewService, timeSystems) { + + var self = this; + + //Bind all class functions to 'this' + Object.keys(TimeConductorController.prototype).filter(function (key) { + return typeof TimeConductorController.prototype[key] === 'function'; + }).forEach(function (key) { + self[key] = self[key].bind(self); + }); + + this.$scope = $scope; + this.$window = $window; + this.conductorViewService = conductorViewService; + this.conductor = timeConductor; + this.modes = conductorViewService.availableModes(); + this.validation = new TimeConductorValidation(this.conductor); + + // Construct the provided time system definitions + this.timeSystems = timeSystems.map(function (timeSystemConstructor) { + return timeSystemConstructor(); + }); + + //Set the initial state of the view based on current time conductor + this.initializeScope(); + + this.conductor.on('bounds', this.setFormFromBounds); + this.conductor.on('timeSystem', this.changeTimeSystem); + + // If no mode selected, select fixed as the default + if (!this.conductorViewService.mode()) { + this.setMode('fixed'); + } + } + + /** + * @private + */ + TimeConductorController.prototype.initializeScope = function () { + //Set time Conductor bounds in the form + this.$scope.boundsModel = this.conductor.bounds(); + + //If conductor has a time system selected already, populate the + //form from it + this.$scope.timeSystemModel = {}; + if (this.conductor.timeSystem()) { + this.setFormFromTimeSystem(this.conductor.timeSystem()); + } + + //Represents the various modes, and the currently selected mode + //in the view + this.$scope.modeModel = { + options: this.conductorViewService.availableModes() + }; + + var mode = this.conductorViewService.mode(); + if (mode) { + //If view already defines a mode (eg. controller is being + // initialized after navigation), then pre-populate form. + this.setFormFromMode(mode); + var deltas = this.conductorViewService.deltas(); + if (deltas) { + this.setFormFromDeltas(deltas); + } + + } + + this.setFormFromBounds(this.conductor.bounds()); + + // Watch scope for selection of mode or time system by user + this.$scope.$watch('modeModel.selectedKey', this.setMode); + + this.$scope.$on('$destroy', this.destroy); + }; + + TimeConductorController.prototype.destroy = function () { + this.conductor.off('bounds', this.setFormFromBounds); + this.conductor.off('timeSystem', this.changeTimeSystem); + }; + + /** + * Called when the bounds change in the time conductor. Synchronizes + * the bounds values in the time conductor with those in the form + * + * @private + */ + TimeConductorController.prototype.setFormFromBounds = function (bounds) { + this.$scope.boundsModel.start = bounds.start; + this.$scope.boundsModel.end = bounds.end; + if (!this.pendingUpdate) { + this.pendingUpdate = true; + this.$window.requestAnimationFrame(function () { + this.pendingUpdate = false; + this.$scope.$digest(); + }.bind(this)); + } + }; + + /** + * @private + */ + TimeConductorController.prototype.setFormFromMode = function (mode) { + this.$scope.modeModel.selectedKey = mode; + //Synchronize scope with time system on mode + this.$scope.timeSystemModel.options = + this.conductorViewService.availableTimeSystems() + .map(function (t) { + return t.metadata; + }); + }; + + /** + * @private + */ + TimeConductorController.prototype.setFormFromDeltas = function (deltas) { + this.$scope.boundsModel.startDelta = deltas.start; + this.$scope.boundsModel.endDelta = deltas.end; + }; + + /** + * @private + */ + TimeConductorController.prototype.setFormFromTimeSystem = function (timeSystem) { + this.$scope.timeSystemModel.selected = timeSystem; + this.$scope.timeSystemModel.format = timeSystem.formats()[0]; + this.$scope.timeSystemModel.deltaFormat = timeSystem.deltaFormat(); + }; + + + /** + * Called when form values are changed. Synchronizes the form with + * the time conductor + * @param formModel + */ + TimeConductorController.prototype.updateBoundsFromForm = function (boundsModel) { + this.conductor.bounds({ + start: boundsModel.start, + end: boundsModel.end + }); + }; + + /** + * Called when the delta values in the form change. Validates and + * sets the new deltas on the Mode. + * @param boundsModel + * @see TimeConductorMode + */ + TimeConductorController.prototype.updateDeltasFromForm = function (boundsFormModel) { + var deltas = { + start: boundsFormModel.startDelta, + end: boundsFormModel.endDelta + }; + if (this.validation.validateStartDelta(deltas.start) && this.validation.validateEndDelta(deltas.end)) { + //Sychronize deltas between form and mode + this.conductorViewService.deltas(deltas); + } + }; + + /** + * Change the selected Time Conductor mode. This will call destroy + * and initialization functions on the relevant modes, setting + * default values for bound and deltas in the form. + * + * @private + * @param newModeKey + * @param oldModeKey + */ + TimeConductorController.prototype.setMode = function (newModeKey, oldModeKey) { + if (newModeKey !== oldModeKey) { + this.conductorViewService.mode(newModeKey); + this.setFormFromMode(newModeKey); + } + }; + + /** + * Respond to time system selection from UI + * + * Allows time system to be changed by key. This supports selection + * from the menu. Resolves a TimeSystem object and then invokes + * TimeConductorController#setTimeSystem + * @param key + * @see TimeConductorController#setTimeSystem + */ + TimeConductorController.prototype.selectTimeSystemByKey = function (key) { + var selected = this.timeSystems.filter(function (timeSystem) { + return timeSystem.metadata.key === key; + })[0]; + this.conductor.timeSystem(selected, selected.defaults().bounds); + }; + + /** + * Handles time system change from time conductor + * + * Sets the selected time system. Will populate form with the default + * bounds and deltas defined in the selected time system. + * + * @private + * @param newTimeSystem + */ + TimeConductorController.prototype.changeTimeSystem = function (newTimeSystem) { + if (newTimeSystem && (newTimeSystem !== this.$scope.timeSystemModel.selected)) { + if (newTimeSystem.defaults()) { + var deltas = newTimeSystem.defaults().deltas || {start: 0, end: 0}; + var bounds = newTimeSystem.defaults().bounds || {start: 0, end: 0}; + + this.setFormFromDeltas(deltas); + this.setFormFromBounds(bounds); + } + this.setFormFromTimeSystem(newTimeSystem); + } + }; + + return TimeConductorController; + } +); diff --git a/platform/features/conductor-v2/conductor/src/ui/TimeConductorControllerSpec.js b/platform/features/conductor-v2/conductor/src/ui/TimeConductorControllerSpec.js new file mode 100644 index 00000000000..39759c60f55 --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/TimeConductorControllerSpec.js @@ -0,0 +1,335 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(['./TimeConductorController'], function (TimeConductorController) { + describe("The time conductor controller", function () { + var mockScope; + var mockWindow; + var mockTimeConductor; + var mockConductorViewService; + var mockTimeSystems; + var controller; + + beforeEach(function () { + mockScope = jasmine.createSpyObj("$scope", [ + "$watch", + "$on" + ]); + mockWindow = jasmine.createSpyObj("$window", ["requestAnimationFrame"]); + mockTimeConductor = jasmine.createSpyObj( + "TimeConductor", + [ + "bounds", + "timeSystem", + "on", + "off" + ] + ); + mockTimeConductor.bounds.andReturn({start: undefined, end: undefined}); + + mockConductorViewService = jasmine.createSpyObj( + "ConductorViewService", + [ + "availableModes", + "mode", + "availableTimeSystems", + "deltas" + ] + ); + mockConductorViewService.availableModes.andReturn([]); + mockConductorViewService.availableTimeSystems.andReturn([]); + + mockTimeSystems = []; + }); + + function getListener(name) { + return mockTimeConductor.on.calls.filter(function (call) { + return call.args[0] === name; + })[0].args[1]; + } + + describe("", function () { + beforeEach(function () { + controller = new TimeConductorController( + mockScope, + mockWindow, + mockTimeConductor, + mockConductorViewService, + mockTimeSystems + ); + }); + + }); + + describe("when time conductor state changes", function () { + var mockFormat; + var mockDeltaFormat; + var defaultBounds; + var defaultDeltas; + var mockDefaults; + var timeSystem; + var tsListener; + + beforeEach(function () { + mockFormat = {}; + mockDeltaFormat = {}; + defaultBounds = { + start: 2, + end: 3 + }; + defaultDeltas = { + start: 10, + end: 20 + }; + mockDefaults = { + deltas: defaultDeltas, + bounds: defaultBounds + }; + timeSystem = { + formats: function () { + return [mockFormat]; + }, + deltaFormat: function () { + return mockDeltaFormat; + }, + defaults: function () { + return mockDefaults; + } + }; + + controller = new TimeConductorController( + mockScope, + mockWindow, + mockTimeConductor, + mockConductorViewService, + mockTimeSystems + ); + + tsListener = getListener("timeSystem"); + }); + + it("listens for changes to conductor state", function () { + expect(mockTimeConductor.on).toHaveBeenCalledWith("timeSystem", controller.changeTimeSystem); + expect(mockTimeConductor.on).toHaveBeenCalledWith("bounds", controller.setFormFromBounds); + }); + + it("deregisters conductor listens when scope is destroyed", function () { + expect(mockScope.$on).toHaveBeenCalledWith("$destroy", controller.destroy); + + controller.destroy(); + expect(mockTimeConductor.off).toHaveBeenCalledWith("timeSystem", controller.changeTimeSystem); + expect(mockTimeConductor.off).toHaveBeenCalledWith("bounds", controller.setFormFromBounds); + }); + + it("when time system changes, sets time system on scope", function () { + expect(tsListener).toBeDefined(); + tsListener(timeSystem); + + expect(mockScope.timeSystemModel).toBeDefined(); + expect(mockScope.timeSystemModel.selected).toBe(timeSystem); + expect(mockScope.timeSystemModel.format).toBe(mockFormat); + expect(mockScope.timeSystemModel.deltaFormat).toBe(mockDeltaFormat); + }); + + it("when time system changes, sets defaults on scope", function () { + expect(tsListener).toBeDefined(); + tsListener(timeSystem); + + expect(mockScope.boundsModel.start).toEqual(defaultBounds.start); + expect(mockScope.boundsModel.end).toEqual(defaultBounds.end); + + expect(mockScope.boundsModel.startDelta).toEqual(defaultDeltas.start); + expect(mockScope.boundsModel.endDelta).toEqual(defaultDeltas.end); + }); + + it("when bounds change, sets them on scope", function () { + var bounds = { + start: 1, + end: 2 + }; + + var boundsListener = getListener("bounds"); + expect(boundsListener).toBeDefined(); + boundsListener(bounds); + + expect(mockScope.boundsModel).toBeDefined(); + expect(mockScope.boundsModel.start).toEqual(bounds.start); + expect(mockScope.boundsModel.end).toEqual(bounds.end); + }); + }); + + describe("when user makes changes from UI", function () { + var mode = "realtime"; + var ts1Metadata; + var ts2Metadata; + var ts3Metadata; + var mockTimeSystemConstructors; + + beforeEach(function () { + mode = "realtime"; + ts1Metadata = { + 'key': 'ts1', + 'name': 'Time System One', + 'cssClass': 'cssClassOne' + }; + ts2Metadata = { + 'key': 'ts2', + 'name': 'Time System Two', + 'cssClass': 'cssClassTwo' + }; + ts3Metadata = { + 'key': 'ts3', + 'name': 'Time System Three', + 'cssClass': 'cssClassThree' + }; + mockTimeSystems = [ + { + metadata: ts1Metadata + }, + { + metadata: ts2Metadata + }, + { + metadata: ts3Metadata + } + ]; + + //Wrap in mock constructors + mockTimeSystemConstructors = mockTimeSystems.map(function (mockTimeSystem) { + return function () { + return mockTimeSystem; + }; + }); + }); + + it("sets the mode on scope", function () { + controller = new TimeConductorController( + mockScope, + mockWindow, + mockTimeConductor, + mockConductorViewService, + mockTimeSystemConstructors + ); + + mockConductorViewService.availableTimeSystems.andReturn(mockTimeSystems); + controller.setMode(mode); + + expect(mockScope.modeModel.selectedKey).toEqual(mode); + }); + + it("sets available time systems on scope when mode changes", function () { + controller = new TimeConductorController( + mockScope, + mockWindow, + mockTimeConductor, + mockConductorViewService, + mockTimeSystemConstructors + ); + + mockConductorViewService.availableTimeSystems.andReturn(mockTimeSystems); + controller.setMode(mode); + + expect(mockScope.timeSystemModel.options.length).toEqual(3); + expect(mockScope.timeSystemModel.options[0]).toEqual(ts1Metadata); + expect(mockScope.timeSystemModel.options[1]).toEqual(ts2Metadata); + expect(mockScope.timeSystemModel.options[2]).toEqual(ts3Metadata); + }); + + it("sets bounds on the time conductor", function () { + var formModel = { + start: 1, + end: 10 + }; + + + controller = new TimeConductorController( + mockScope, + mockWindow, + mockTimeConductor, + mockConductorViewService, + mockTimeSystemConstructors + ); + + controller.updateBoundsFromForm(formModel); + expect(mockTimeConductor.bounds).toHaveBeenCalledWith(formModel); + }); + + it("applies deltas when they change in form", function () { + var deltas = { + start: 1000, + end: 2000 + }; + var formModel = { + startDelta: deltas.start, + endDelta: deltas.end + }; + + controller = new TimeConductorController( + mockScope, + mockWindow, + mockTimeConductor, + mockConductorViewService, + mockTimeSystemConstructors + ); + + controller.updateDeltasFromForm(formModel); + expect(mockConductorViewService.deltas).toHaveBeenCalledWith(deltas); + }); + + it("sets the time system on the time conductor", function () { + var defaultBounds = { + start: 5, + end: 6 + }; + var timeSystem = { + metadata: { + key: 'testTimeSystem' + }, + defaults: function () { + return { + bounds: defaultBounds + }; + } + }; + + mockTimeSystems = [ + // Wrap as constructor function + function () { + return timeSystem; + } + ]; + + controller = new TimeConductorController( + mockScope, + mockWindow, + mockTimeConductor, + mockConductorViewService, + mockTimeSystems + ); + + controller.selectTimeSystemByKey('testTimeSystem'); + expect(mockTimeConductor.timeSystem).toHaveBeenCalledWith(timeSystem, defaultBounds); + }); + }); + + }); +}); diff --git a/platform/features/conductor-v2/conductor/src/ui/TimeConductorMode.js b/platform/features/conductor-v2/conductor/src/ui/TimeConductorMode.js new file mode 100644 index 00000000000..1f9d3656af4 --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/TimeConductorMode.js @@ -0,0 +1,201 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define( + [], + function () { + + /** + * Supports mode-specific time conductor behavior. + * + * @constructor + * @param {TimeConductorMetadata} metadata + */ + function TimeConductorMode(metadata, conductor, timeSystems) { + this.conductor = conductor; + + this.mdata = metadata; + this.dlts = undefined; + this.source = undefined; + this.sourceUnlisten = undefined; + this.systems = timeSystems; + this.availableSources = undefined; + this.changeTimeSystem = this.changeTimeSystem.bind(this); + this.tick = this.tick.bind(this); + + //Set the time system initially + if (conductor.timeSystem()) { + this.changeTimeSystem(conductor.timeSystem()); + } + + //Listen for subsequent changes to time system + conductor.on('timeSystem', this.changeTimeSystem); + + if (metadata.key === 'fixed') { + //Fixed automatically supports all time systems + this.availableSystems = timeSystems; + } else { + this.availableSystems = timeSystems.filter(function (timeSystem) { + //Only include time systems that have tick sources that + // support the current mode + return timeSystem.tickSources().some(function (tickSource) { + return metadata.key === tickSource.metadata.mode; + }); + }); + } + } + + /** + * Get or set the currently selected time system + * @param timeSystem + * @returns {TimeSystem} the currently selected time system + */ + TimeConductorMode.prototype.changeTimeSystem = function (timeSystem) { + // On time system change, apply default deltas + var defaults = timeSystem.defaults() || { + bounds: { + start: 0, + end: 0 + }, + deltas: { + start: 0, + end: 0 + } + }; + + this.conductor.bounds(defaults.bounds); + this.deltas(defaults.deltas); + + // Tick sources are mode-specific, so restrict tick sources to only those supported by the current mode. + var key = this.mdata.key; + var tickSources = timeSystem.tickSources(); + if (tickSources) { + this.availableSources = tickSources.filter(function (source) { + return source.metadata.mode === key; + }); + } + + // Set an appropriate tick source from the new time system + this.tickSource(this.availableTickSources(timeSystem)[0]); + }; + + /** + * @returns {ModeMetadata} + */ + TimeConductorMode.prototype.metadata = function () { + return this.mdata; + }; + + TimeConductorMode.prototype.availableTimeSystems = function () { + return this.availableSystems; + }; + + /** + * Tick sources are mode-specific. This returns a filtered list of the tick sources available in the currently selected mode + * @param timeSystem + * @returns {Array.} + */ + TimeConductorMode.prototype.availableTickSources = function (timeSystem) { + return this.availableSources; + }; + + /** + * Get or set tick source. Setting tick source will also start + * listening to it and unlisten from any existing tick source + * @param tickSource + * @returns {TickSource} + */ + TimeConductorMode.prototype.tickSource = function (tickSource) { + if (arguments.length > 0) { + if (this.sourceUnlisten) { + this.sourceUnlisten(); + } + this.source = tickSource; + if (tickSource) { + this.sourceUnlisten = tickSource.listen(this.tick); + //Now following a tick source + this.conductor.follow(true); + } else { + this.conductor.follow(false); + } + } + return this.source; + }; + + TimeConductorMode.prototype.destroy = function () { + this.conductor.off('timeSystem', this.changeTimeSystem); + + if (this.sourceUnlisten) { + this.sourceUnlisten(); + } + }; + + /** + * @private + * @param {number} time some value that is valid in the current TimeSystem + */ + TimeConductorMode.prototype.tick = function (time) { + var deltas = this.deltas(); + var startTime = time; + var endTime = time; + + if (deltas) { + startTime = time - deltas.start; + endTime = time + deltas.end; + } + this.conductor.bounds({ + start: startTime, + end: endTime + }); + }; + + /** + * Get or set the current value for the deltas used by this time system. + * On change, the new deltas will be used to calculate and set the + * bounds on the time conductor. + * @param deltas + * @returns {TimeSystemDeltas} + */ + TimeConductorMode.prototype.deltas = function (deltas) { + if (arguments.length !== 0) { + var oldEnd = this.conductor.bounds().end; + + if (this.dlts && this.dlts.end !== undefined) { + //Calculate the previous raw end value (without delta) + oldEnd = oldEnd - this.dlts.end; + } + + this.dlts = deltas; + + var newBounds = { + start: oldEnd - this.dlts.start, + end: oldEnd + this.dlts.end + }; + + this.conductor.bounds(newBounds); + } + return this.dlts; + }; + + return TimeConductorMode; + } +); diff --git a/platform/features/conductor-v2/conductor/src/ui/TimeConductorModeSpec.js b/platform/features/conductor-v2/conductor/src/ui/TimeConductorModeSpec.js new file mode 100644 index 00000000000..6ea5ce79c2f --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/TimeConductorModeSpec.js @@ -0,0 +1,210 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(['./TimeConductorMode'], function (TimeConductorMode) { + describe("The Time Conductor Mode", function () { + var mockTimeConductor, + fixedModeMetaData, + mockTimeSystems, + fixedTimeSystem, + + realtimeModeMetaData, + realtimeTimeSystem, + mockTickSource, + + mockBounds, + mode; + + beforeEach(function () { + fixedModeMetaData = { + key: "fixed" + }; + realtimeModeMetaData = { + key: "realtime" + }; + mockBounds = { + start: 0, + end: 1 + }; + + fixedTimeSystem = jasmine.createSpyObj("timeSystem", [ + "defaults", + "tickSources" + ]); + fixedTimeSystem.tickSources.andReturn([]); + + mockTickSource = jasmine.createSpyObj("tickSource", [ + "listen" + ]); + mockTickSource.metadata = { + mode: "realtime" + }; + realtimeTimeSystem = jasmine.createSpyObj("realtimeTimeSystem", [ + "defaults", + "tickSources" + ]); + realtimeTimeSystem.tickSources.andReturn([mockTickSource]); + + //Do not return any time systems initially for a default + // construction configuration that works without any additional work + mockTimeSystems = []; + + mockTimeConductor = jasmine.createSpyObj("timeConductor", [ + "bounds", + "timeSystem", + "on", + "off", + "follow" + ]); + mockTimeConductor.bounds.andReturn(mockBounds); + }); + + it("Reacts to changes in conductor time system", function () { + mode = new TimeConductorMode(fixedModeMetaData, mockTimeConductor, mockTimeSystems); + expect(mockTimeConductor.on).toHaveBeenCalledWith("timeSystem", mode.changeTimeSystem); + }); + + it("Stops listening to time system changes on destroy", function () { + mode = new TimeConductorMode(fixedModeMetaData, mockTimeConductor, mockTimeSystems); + mode.destroy(); + expect(mockTimeConductor.off).toHaveBeenCalledWith("timeSystem", mode.changeTimeSystem); + }); + + it("Filters available time systems to those with tick sources that" + + " support this mode", function () { + mockTimeSystems = [fixedTimeSystem, realtimeTimeSystem]; + mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems); + + var availableTimeSystems = mode.availableTimeSystems(); + expect(availableTimeSystems.length).toBe(1); + expect(availableTimeSystems.indexOf(fixedTimeSystem)).toBe(-1); + expect(availableTimeSystems.indexOf(realtimeTimeSystem)).toBe(0); + }); + + describe("Changing the time system", function () { + var defaults; + + beforeEach(function () { + defaults = { + bounds: { + start: 1, + end: 2 + }, + deltas: { + start: 3, + end: 4 + } + }; + + fixedTimeSystem.defaults.andReturn(defaults); + + }); + it ("sets defaults from new time system", function () { + mode = new TimeConductorMode(fixedModeMetaData, mockTimeConductor, mockTimeSystems); + spyOn(mode, "deltas"); + mode.deltas.andCallThrough(); + + mode.changeTimeSystem(fixedTimeSystem); + expect(mockTimeConductor.bounds).toHaveBeenCalledWith(defaults.bounds); + expect(mode.deltas).toHaveBeenCalledWith(defaults.deltas); + }); + it ("If a tick source is available, sets the tick source", function () { + mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems); + mode.changeTimeSystem(realtimeTimeSystem); + + var currentTickSource = mode.tickSource(); + expect(currentTickSource).toBe(mockTickSource); + }); + }); + + describe("Setting a tick source", function () { + var mockUnlistener; + + beforeEach(function () { + mockUnlistener = jasmine.createSpy("unlistener"); + mockTickSource.listen.andReturn(mockUnlistener); + + mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems); + mode.tickSource(mockTickSource); + }); + + it ("Unlistens from old tick source", function () { + mode.tickSource(mockTickSource); + expect(mockUnlistener).toHaveBeenCalled(); + }); + + it ("Listens to new tick source", function () { + expect(mockTickSource.listen).toHaveBeenCalledWith(mode.tick); + }); + + it ("Sets 'follow' state on time conductor", function () { + expect(mockTimeConductor.follow).toHaveBeenCalledWith(true); + }); + + it ("on destroy, unlistens from tick source", function () { + mode.destroy(); + expect(mockUnlistener).toHaveBeenCalled(); + }); + }); + + describe("setting deltas", function () { + beforeEach(function () { + mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems); + }); + it ("sets the bounds on the time conductor based on new delta" + + " values", function () { + var deltas = { + start: 20, + end: 10 + }; + + mode.deltas(deltas); + + expect(mockTimeConductor.bounds).toHaveBeenCalledWith({ + start: mockBounds.end - deltas.start, + end: mockBounds.end + deltas.end + }); + }); + }); + + describe("ticking", function () { + beforeEach(function () { + mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems); + }); + it ("sets bounds based on current delta values", function () { + var deltas = { + start: 20, + end: 10 + }; + var time = 100; + + mode.deltas(deltas); + mode.tick(time); + + expect(mockTimeConductor.bounds).toHaveBeenCalledWith({ + start: time - deltas.start, + end: time + deltas.end + }); + }); + }); + }); +}); diff --git a/platform/features/conductor-v2/conductor/src/ui/TimeConductorValidation.js b/platform/features/conductor-v2/conductor/src/ui/TimeConductorValidation.js new file mode 100644 index 00000000000..3778c26a21f --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/TimeConductorValidation.js @@ -0,0 +1,69 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define( + [], + function () { + + /** + * Form validation for the TimeConductorController. + * @param conductor + * @constructor + */ + function TimeConductorValidation(conductor) { + var self = this; + this.conductor = conductor; + + /* + * Bind all class functions to 'this' + */ + Object.keys(TimeConductorValidation.prototype).filter(function (key) { + return typeof TimeConductorValidation.prototype[key] === 'function'; + }).forEach(function (key) { + self[key] = self[key].bind(self); + }); + } + + /** + * Validation methods below are invoked directly from controls in the TimeConductor form + */ + TimeConductorValidation.prototype.validateStart = function (start) { + var bounds = this.conductor.bounds(); + return this.conductor.validateBounds({start: start, end: bounds.end}) === true; + }; + + TimeConductorValidation.prototype.validateEnd = function (end) { + var bounds = this.conductor.bounds(); + return this.conductor.validateBounds({start: bounds.start, end: end}) === true; + }; + + TimeConductorValidation.prototype.validateStartDelta = function (startDelta) { + return !isNaN(startDelta) && startDelta > 0; + }; + + TimeConductorValidation.prototype.validateEndDelta = function (endDelta) { + return !isNaN(endDelta) && endDelta >= 0; + }; + + return TimeConductorValidation; + } +); diff --git a/platform/features/conductor-v2/conductor/src/ui/TimeConductorValidationSpec.js b/platform/features/conductor-v2/conductor/src/ui/TimeConductorValidationSpec.js new file mode 100644 index 00000000000..833ba309936 --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/TimeConductorValidationSpec.js @@ -0,0 +1,73 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(['./TimeConductorValidation'], function (TimeConductorValidation) { + describe("The Time Conductor Validation class", function () { + var timeConductorValidation, + mockTimeConductor; + + beforeEach(function () { + mockTimeConductor = jasmine.createSpyObj("timeConductor", [ + "validateBounds", + "bounds" + ]); + timeConductorValidation = new TimeConductorValidation(mockTimeConductor); + }); + + describe("Validates start and end values using Time Conductor", function () { + beforeEach(function () { + var mockBounds = { + start: 10, + end: 20 + }; + + mockTimeConductor.bounds.andReturn(mockBounds); + + }); + it("Validates start values using Time Conductor", function () { + var startValue = 30; + timeConductorValidation.validateStart(startValue); + expect(mockTimeConductor.validateBounds).toHaveBeenCalled(); + }); + it("Validates end values using Time Conductor", function () { + var endValue = 40; + timeConductorValidation.validateEnd(endValue); + expect(mockTimeConductor.validateBounds).toHaveBeenCalled(); + }); + }); + + it("Validates that start delta is valid number > 0", function () { + expect(timeConductorValidation.validateStartDelta(-1)).toBe(false); + expect(timeConductorValidation.validateStartDelta("abc")).toBe(false); + expect(timeConductorValidation.validateStartDelta("1")).toBe(true); + expect(timeConductorValidation.validateStartDelta(1)).toBe(true); + }); + + it("Validates that end delta is valid number >= 0", function () { + expect(timeConductorValidation.validateEndDelta(-1)).toBe(false); + expect(timeConductorValidation.validateEndDelta("abc")).toBe(false); + expect(timeConductorValidation.validateEndDelta("1")).toBe(true); + expect(timeConductorValidation.validateEndDelta(0)).toBe(true); + expect(timeConductorValidation.validateEndDelta(1)).toBe(true); + }); + }); +}); diff --git a/platform/features/conductor-v2/conductor/src/ui/TimeConductorViewService.js b/platform/features/conductor-v2/conductor/src/ui/TimeConductorViewService.js new file mode 100644 index 00000000000..8cbc3495204 --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/TimeConductorViewService.js @@ -0,0 +1,202 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define( + [ + './TimeConductorMode' + ], + function (TimeConductorMode) { + + /** + * A class representing the state of the time conductor view. This + * exposes details of the UI that are not represented on the + * TimeConductor API itself such as modes and deltas. + * + * @param conductor + * @param timeSystems + * @constructor + */ + function TimeConductorViewService(conductor, timeSystems) { + this.systems = timeSystems.map(function (timeSystemConstructor) { + return timeSystemConstructor(); + }); + + this.conductor = conductor; + this.currentMode = undefined; + + /** + * @typedef {object} ModeMetadata + * @property {string} key A unique identifying key for this mode + * @property {string} cssClass The css class for the glyph + * representing this mode + * @property {string} label A short label for this mode + * @property {string} name A longer name for the mode + * @property {string} description A description of the mode + */ + this.availModes = { + 'fixed': { + key: 'fixed', + cssclass: 'icon-calendar', + label: 'Fixed', + name: 'Fixed Timespan Mode', + description: 'Query and explore data that falls between two fixed datetimes.' + } + }; + + function hasTickSource(sourceType, timeSystem) { + return timeSystem.tickSources().some(function (tickSource) { + return tickSource.metadata.mode === sourceType; + }); + } + + var timeSystemsForMode = function (sourceType) { + return this.systems.filter(hasTickSource.bind(this, sourceType)); + }.bind(this); + + //Only show 'real-time mode' if appropriate time systems available + if (timeSystemsForMode('realtime').length > 0) { + var realtimeMode = { + key: 'realtime', + cssclass: 'icon-clock', + label: 'Real-time', + name: 'Real-time Mode', + description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.' + }; + this.availModes[realtimeMode.key] = realtimeMode; + } + + //Only show 'LAD mode' if appropriate time systems available + if (timeSystemsForMode('lad').length > 0) { + var ladMode = { + key: 'lad', + cssclass: 'icon-database', + label: 'LAD', + name: 'LAD Mode', + description: 'Latest Available Data mode monitors real-time streaming data as it comes in. The Time Conductor and displays will only advance when data becomes available.' + }; + this.availModes[ladMode.key] = ladMode; + } + } + + /** + * Getter/Setter for the Time Conductor Mode. Modes determine the + * behavior of the time conductor, especially with regards to the + * bounds and how they change with time. + * + * In fixed mode, the bounds do not change with time, but can be + * modified by the used + * + * In realtime mode, the bounds change with time. Bounds are not + * directly modifiable by the user, however deltas can be. + * + * In Latest Available Data (LAD) mode, the bounds are updated when + * data is received. As with realtime mode the + * + * @param {string} newModeKey One of 'fixed', 'realtime', or 'LAD' + * @returns {string} the current mode, one of 'fixed', 'realtime', + * or 'LAD'. + * + */ + TimeConductorViewService.prototype.mode = function (newModeKey) { + function contains(timeSystems, ts) { + return timeSystems.filter(function (t) { + return t.metadata.key === ts.metadata.key; + }).length > 0; + } + + if (arguments.length === 1) { + var timeSystem = this.conductor.timeSystem(); + var modes = this.availableModes(); + var modeMetaData = modes[newModeKey]; + + if (this.currentMode) { + this.currentMode.destroy(); + } + this.currentMode = new TimeConductorMode(modeMetaData, this.conductor, this.systems); + + // If no time system set on time conductor, or the currently selected time system is not available in + // the new mode, default to first available time system + if (!timeSystem || !contains(this.currentMode.availableTimeSystems(), timeSystem)) { + timeSystem = this.currentMode.availableTimeSystems()[0]; + this.conductor.timeSystem(timeSystem, timeSystem.defaults().bounds); + } + } + return this.currentMode ? this.currentMode.metadata().key : undefined; + }; + + /** + * @typedef {object} Delta + * @property {number} start Used to set the start bound of the + * TimeConductor on tick. A positive value that will be subtracted + * from the value provided by a tick source to determine the start + * bound. + * @property {number} end Used to set the end bound of the + * TimeConductor on tick. A positive value that will be added + * from the value provided by a tick source to determine the start + * bound. + */ + /** + * Deltas define the offset from the latest time value provided by + * the current tick source. Deltas are only valid in realtime or LAD + * modes. + * + * Realtime mode: + * - start: A time in ms before now which will be used to + * determine the 'start' bound on tick + * - end: A time in ms after now which will be used to determine + * the 'end' bound on tick + * + * LAD mode: + * - start: A time in ms before the timestamp of the last data + * received which will be used to determine the 'start' bound on + * tick + * - end: A time in ms after the timestamp of the last data received + * which will be used to determine the 'end' bound on tick + * @returns {Delta} current value of the deltas + */ + TimeConductorViewService.prototype.deltas = function () { + //Deltas stored on mode. Use .apply to preserve arguments + return this.currentMode.deltas.apply(this.currentMode, arguments); + }; + + /** + * Availability of modes depends on the time systems and tick + * sources available. For example, Latest Available Data mode will + * not be available if there are no time systems and tick sources + * that support LAD mode. + * @returns {ModeMetadata[]} + */ + TimeConductorViewService.prototype.availableModes = function () { + return this.availModes; + }; + + /** + * Availability of time systems depends on the currently selected + * mode. Time systems and tick sources are mode dependent + */ + TimeConductorViewService.prototype.availableTimeSystems = function () { + return this.currentMode.availableTimeSystems(); + }; + + return TimeConductorViewService; + } +); diff --git a/platform/features/conductor-v2/conductor/src/ui/TimeConductorViewServiceSpec.js b/platform/features/conductor-v2/conductor/src/ui/TimeConductorViewServiceSpec.js new file mode 100644 index 00000000000..39e7810320f --- /dev/null +++ b/platform/features/conductor-v2/conductor/src/ui/TimeConductorViewServiceSpec.js @@ -0,0 +1,185 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(['./TimeConductorViewService'], function (TimeConductorViewService) { + describe("The Time Conductor view service", function () { + var mockTimeConductor; + var basicTimeSystem; + var tickingTimeSystem; + var viewService; + var tickingTimeSystemDefaults; + + function mockConstructor(object) { + return function () { + return object; + }; + } + + beforeEach(function () { + mockTimeConductor = jasmine.createSpyObj("timeConductor", [ + "timeSystem", + "bounds", + "follow", + "on", + "off" + ]); + + basicTimeSystem = jasmine.createSpyObj("basicTimeSystem", [ + "tickSources", + "defaults" + ]); + basicTimeSystem.metadata = { + key: "basic" + }; + basicTimeSystem.tickSources.andReturn([]); + basicTimeSystem.defaults.andReturn({ + bounds: { + start: 0, + end: 1 + }, + deltas: { + start: 0, + end: 0 + } + }); + //Initialize conductor + mockTimeConductor.timeSystem.andReturn(basicTimeSystem); + mockTimeConductor.bounds.andReturn({start: 0, end: 1}); + + tickingTimeSystem = jasmine.createSpyObj("tickingTimeSystem", [ + "tickSources", + "defaults" + ]); + tickingTimeSystem.metadata = { + key: "ticking" + }; + tickingTimeSystemDefaults = { + bounds: { + start: 100, + end: 200 + }, + deltas: { + start: 1000, + end: 500 + } + }; + tickingTimeSystem.defaults.andReturn(tickingTimeSystemDefaults); + }); + + it("At a minimum supports fixed mode", function () { + var mockTimeSystems = [mockConstructor(basicTimeSystem)]; + viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems); + + var availableModes = viewService.availableModes(); + expect(availableModes.fixed).toBeDefined(); + }); + + it("Supports realtime mode if appropriate tick source(s) availables", function () { + var mockTimeSystems = [mockConstructor(tickingTimeSystem)]; + var mockRealtimeTickSource = { + metadata: { + mode: 'realtime' + } + }; + tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]); + + viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems); + + var availableModes = viewService.availableModes(); + expect(availableModes.realtime).toBeDefined(); + }); + + it("Supports LAD mode if appropriate tick source(s) available", function () { + var mockTimeSystems = [mockConstructor(tickingTimeSystem)]; + var mockLADTickSource = { + metadata: { + mode: 'lad' + } + }; + tickingTimeSystem.tickSources.andReturn([mockLADTickSource]); + + viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems); + + var availableModes = viewService.availableModes(); + expect(availableModes.lad).toBeDefined(); + }); + + describe("when mode is changed", function () { + + it("destroys previous mode", function () { + var mockTimeSystems = [mockConstructor(basicTimeSystem)]; + + var oldMode = jasmine.createSpyObj("conductorMode", [ + "destroy" + ]); + + viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems); + viewService.currentMode = oldMode; + viewService.mode('fixed'); + expect(oldMode.destroy).toHaveBeenCalled(); + }); + + describe("the time system", function () { + it("is retained if available in new mode", function () { + var mockTimeSystems = [mockConstructor(basicTimeSystem), mockConstructor(tickingTimeSystem)]; + var mockRealtimeTickSource = { + metadata: { + mode: 'realtime' + }, + listen: function () {} + }; + tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]); + + viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems); + + //Set time system to one known to support realtime mode + mockTimeConductor.timeSystem.andReturn(tickingTimeSystem); + + //Select realtime mode + mockTimeConductor.timeSystem.reset(); + viewService.mode('realtime'); + expect(mockTimeConductor.timeSystem).not.toHaveBeenCalledWith(tickingTimeSystem, tickingTimeSystemDefaults.bounds); + }); + it("is defaulted if selected time system not available in new mode", function () { + var mockTimeSystems = [mockConstructor(basicTimeSystem), mockConstructor(tickingTimeSystem)]; + var mockRealtimeTickSource = { + metadata: { + mode: 'realtime' + }, + listen: function () {} + }; + tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]); + + viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems); + + //Set time system to one known to not support realtime mode + mockTimeConductor.timeSystem.andReturn(basicTimeSystem); + + //Select realtime mode + mockTimeConductor.timeSystem.reset(); + viewService.mode('realtime'); + expect(mockTimeConductor.timeSystem).toHaveBeenCalledWith(tickingTimeSystem, tickingTimeSystemDefaults.bounds); + }); + }); + }); + }); +}); diff --git a/platform/features/conductor-v2/utcTimeSystem/bundle.js b/platform/features/conductor-v2/utcTimeSystem/bundle.js new file mode 100644 index 00000000000..5b13e605d41 --- /dev/null +++ b/platform/features/conductor-v2/utcTimeSystem/bundle.js @@ -0,0 +1,40 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define([ + "./src/UTCTimeSystem", + 'legacyRegistry' +], function ( + UTCTimeSystem, + legacyRegistry +) { + legacyRegistry.register("platform/features/conductor-v2/utcTimeSystem", { + "extensions": { + "timeSystems": [ + { + "implementation": UTCTimeSystem, + "depends": ["$timeout"] + } + ] + } + }); +}); diff --git a/platform/features/conductor-v2/utcTimeSystem/src/UTCTimeSystem.js b/platform/features/conductor-v2/utcTimeSystem/src/UTCTimeSystem.js new file mode 100644 index 00000000000..b6e969c3ebd --- /dev/null +++ b/platform/features/conductor-v2/utcTimeSystem/src/UTCTimeSystem.js @@ -0,0 +1,78 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define([ + '../../conductor/src/timeSystems/TimeSystem', + '../../conductor/src/timeSystems/LocalClock' +], function (TimeSystem, LocalClock) { + var FIFTEEN_MINUTES = 15 * 60 * 1000, + DEFAULT_PERIOD = 1000; + + /** + * This time system supports UTC dates and provides a ticking clock source. + * @implements TimeSystem + * @constructor + */ + function UTCTimeSystem($timeout) { + TimeSystem.call(this); + + /** + * Some metadata, which will be used to identify the time system in + * the UI + * @type {{key: string, name: string, cssclass: string}} + */ + this.metadata = { + 'key': 'utc', + 'name': 'UTC', + 'cssclass': 'icon-clock' + }; + + this.fmts = ['utc']; + this.sources = [new LocalClock($timeout, DEFAULT_PERIOD)]; + } + + UTCTimeSystem.prototype = Object.create(TimeSystem.prototype); + + UTCTimeSystem.prototype.formats = function () { + return this.fmts; + }; + + UTCTimeSystem.prototype.deltaFormat = function () { + return 'duration'; + }; + + UTCTimeSystem.prototype.tickSources = function () { + return this.sources; + }; + + UTCTimeSystem.prototype.defaults = function (key) { + var now = Math.ceil(Date.now() / 1000) * 1000; + return { + key: 'utc-default', + name: 'UTC time system defaults', + deltas: {start: FIFTEEN_MINUTES, end: 0}, + bounds: {start: now - FIFTEEN_MINUTES, end: now} + }; + }; + + return UTCTimeSystem; +}); diff --git a/platform/features/conductor-v2/utcTimeSystem/src/UTCTimeSystemSpec.js b/platform/features/conductor-v2/utcTimeSystem/src/UTCTimeSystemSpec.js new file mode 100644 index 00000000000..808eade72e5 --- /dev/null +++ b/platform/features/conductor-v2/utcTimeSystem/src/UTCTimeSystemSpec.js @@ -0,0 +1,46 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ + +define(['./UTCTimeSystem'], function (UTCTimeSystem) { + describe("The UTCTimeSystem class", function () { + var timeSystem, + mockTimeout; + + beforeEach(function () { + mockTimeout = jasmine.createSpy("timeout"); + timeSystem = new UTCTimeSystem(mockTimeout); + }); + + it("defines at least one format", function () { + expect(timeSystem.formats().length).toBeGreaterThan(0); + }); + + it("defines a tick source", function () { + var tickSources = timeSystem.tickSources(); + expect(tickSources.length).toBeGreaterThan(0); + }); + + it("defines some defaults", function () { + expect(timeSystem.defaults()).toBeDefined(); + }); + }); +}); diff --git a/platform/features/conductor/bundle.js b/platform/features/conductor/bundle.js index 5def15a6b91..c1349b4e583 100644 --- a/platform/features/conductor/bundle.js +++ b/platform/features/conductor/bundle.js @@ -47,6 +47,11 @@ define([ ] } ], + "stylesheets": [ + { + "stylesheetUrl": "css/time-conductor.css" + } + ], "components": [ { "type": "decorator", diff --git a/platform/features/conductor/res/sass/time-conductor.scss b/platform/features/conductor/res/sass/time-conductor.scss new file mode 100644 index 00000000000..bc50b42da51 --- /dev/null +++ b/platform/features/conductor/res/sass/time-conductor.scss @@ -0,0 +1,300 @@ +/***************************************************************************** + * 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. + *****************************************************************************/ +@import "bourbon"; + +@import "../../../../commonUI/general/res/sass/constants"; +@import "../../../../commonUI/general/res/sass/mixins"; +@import "../../../../commonUI/general/res/sass/mobile/constants"; +@import "../../../../commonUI/general/res/sass/mobile/mixins"; +@import "../../../../commonUI/themes/espresso/res/sass/constants"; +@import "../../../../commonUI/themes/espresso/res/sass/mixins"; + +$ueTimeConductorH: (33px, 18px, 20px); + +@mixin toiLineHovEffects() { + &:before, + &:after { + background-color: $timeControllerToiLineColorHov; + } +} + +.l-time-controller { + $minW: 500px; + $knobHOffset: 0px; + $knobM: ($sliderKnobW + $knobHOffset) * -1; + $rangeValPad: $interiorMargin; + $rangeValOffset: $sliderKnobW + $interiorMargin; + $timeRangeSliderLROffset: 150px + ($sliderKnobW * 2); + $r1H: nth($ueTimeConductorH,1); + $r2H: nth($ueTimeConductorH,2); + $r3H: nth($ueTimeConductorH,3); + + min-width: $minW; + font-size: 0.8rem; + + .l-time-range-inputs-holder, + .l-time-range-slider-holder, + .l-time-range-ticks-holder + { + box-sizing: border-box; + position: relative; + &:not(:first-child) { + margin-top: $interiorMargin; + } + } + .l-time-range-slider, + .l-time-range-ticks { + @include absPosDefault(0, visible); + left: $timeRangeSliderLROffset; right: $timeRangeSliderLROffset; + } + + .l-time-range-inputs-holder { + border-top: 1px solid $colorInteriorBorder; + padding-top: $interiorMargin; + &.l-flex-row, + .l-flex-row { + @include align-items(center); + .flex-elem { + height: auto; + line-height: normal; + } + } + .type-icon { + font-size: 120%; + vertical-align: middle; + } + .l-time-range-input-w, + .l-time-range-inputs-elem { + margin-right: $interiorMargin; + .lbl { + color: $colorPlotLabelFg; + } + .ui-symbol.icon { + font-size: 11px; + } + } + .l-time-range-input-w { + // Wraps a datetime text input field + position: relative; + input[type="text"] { + width: 200px; + &.picker-icon { + padding-right: 20px; + } + } + .icon-calendar { + position: absolute; + right: 5px; + top: 5px; + } + } + } + + .l-time-range-slider-holder { + height: $r2H; + .range-holder { + box-shadow: none; + background: none; + border: none; + .range { + .toi-line { + $myC: $timeControllerToiLineColor; + $myW: 8px; + @include transform(translateX(50%)); + position: absolute; + top: 0; right: 0; bottom: 0px; left: auto; + width: $myW; + height: auto; + z-index: 2; + &:before { + // Vert line + background-color: $myC; + position: absolute; + content: ""; + top: 0; right: auto; bottom: -10px; left: floor($myW/2) - 1; + width: 1px; + } + } + &:hover .toi-line { + @include toiLineHovEffects; + } + } + } + &:not(:active) { + .knob, + .range { + @include transition-property(left, right); + @include transition-duration(500ms); + @include transition-timing-function(ease-in-out); + } + } + } + + .l-time-range-ticks-holder { + height: $r3H; + .l-time-range-ticks { + border-top: 1px solid $colorTick; + .tick { + background-color: $colorTick; + border:none; + height: 5px; + width: 1px; + margin-left: -1px; + position: absolute; + &:first-child { + margin-left: 0; + } + .l-time-range-tick-label { + @include webkitProp(transform, translateX(-50%)); + color: $colorPlotLabelFg; + display: inline-block; + font-size: 0.7rem; + position: absolute; + top: 5px; + white-space: nowrap; + z-index: 2; + } + } + } + } + + .knob { + z-index: 2; + &:before { + $mTB: 2px; + $grippyW: 3px; + $mLR: ($sliderKnobW - $grippyW)/2; + @include bgStripes($c: pullForward($sliderColorKnob, 20%), $a: 1, $bgsize: 4px, $angle: 0deg); + content: ''; + display: block; + position: absolute; + top: $mTB; right: $mLR; bottom: $mTB; left: $mLR; + } + .range-value { + @include trans-prop-nice-fade(.25s); + font-size: 0.7rem; + position: absolute; + height: $r2H; + line-height: $r2H; + white-space: nowrap; + z-index: 1; + } + &:hover { + .range-value { + color: $sliderColorKnobHov; + } + } + &.knob-l { + margin-left: $knobM; + .range-value { + text-align: right; + right: $rangeValOffset; + } + } + &.knob-r { + margin-right: $knobM; + .range-value { + left: $rangeValOffset; + } + &:hover + .range-holder .range .toi-line { + @include toiLineHovEffects; + } + } + } + + .l-time-domain-selector { + position: absolute; + right: 0px; + top: $interiorMargin; + } + +} + +.s-time-range-val { + border-radius: $controlCr; + background-color: $colorInputBg; + padding: 1px 1px 0 $interiorMargin; +} + +/******************************************************************** MOBILE */ + +@include phoneandtablet { + .l-time-controller { + min-width: 0; + .l-time-range-slider-holder, + .l-time-range-ticks-holder { + display: none; + } + } +} + +@include phone { + .l-time-controller { + .l-time-range-inputs-holder { + &.l-flex-row, + .l-flex-row { + @include align-items(flex-start); + } + .l-time-range-inputs-elem { + &.type-icon { + margin-top: 3px; + } + } + .t-inputs-w, + .l-time-range-inputs-elem { + @include flex-direction(column); + .l-time-range-input-w:not(:first-child) { + &:not(:first-child) { + margin-top: $interiorMargin; + } + margin-right: 0; + } + .l-time-range-inputs-elem { + &.lbl { display: none; } + } + } + } + } +} + +@include phonePortrait { + .l-time-controller { + .l-time-range-inputs-holder { + .t-inputs-w, + .l-time-range-inputs-elem { + @include flex(1 1 auto); + padding-top: 25px; // Make room for the ever lovin' Time Domain Selector + .flex-elem { + @include flex(1 1 auto); + width: 100%; + } + input[type="text"] { + width: 100%; + } + } + } + } + .l-time-domain-selector { + right: auto; + left: 20px; + } +} \ No newline at end of file diff --git a/platform/features/layout/res/templates/layout.html b/platform/features/layout/res/templates/layout.html index ed1f9a50c8d..b2ffa16808e 100644 --- a/platform/features/layout/res/templates/layout.html +++ b/platform/features/layout/res/templates/layout.html @@ -26,11 +26,10 @@ ng-repeat="childObject in composition" ng-style="controller.getFrameStyle(childObject.getId())"> -
- - -
+ + diff --git a/platform/forms/src/MCTControl.js b/platform/forms/src/MCTControl.js index 680fd9dbe9d..a15a6b208f5 100644 --- a/platform/forms/src/MCTControl.js +++ b/platform/forms/src/MCTControl.js @@ -71,6 +71,9 @@ define( // Allow controls to trigger blur-like events ngBlur: "&", + // Allow controls to trigger blur-like events + ngMouseup: "&", + // The state of the form value itself ngModel: "=", diff --git a/test-main.js b/test-main.js index 74d554701bd..b300560588f 100644 --- a/test-main.js +++ b/test-main.js @@ -63,7 +63,8 @@ requirejs.config({ "text": "bower_components/text/text", "uuid": "bower_components/node-uuid/uuid", "zepto": "bower_components/zepto/zepto.min", - "lodash": "bower_components/lodash/lodash" + "lodash": "bower_components/lodash/lodash", + "d3": "bower_components/d3/d3.min" }, "shim": { @@ -87,6 +88,9 @@ requirejs.config({ }, "lodash": { "exports": "lodash" + }, + "d3": { + "exports": "d3" } }, From 887631500b3f4720589efb8fa1fcef017f7f0472 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Wed, 12 Oct 2016 13:53:22 -0700 Subject: [PATCH 65/78] [Style] Remove duplicate key Remove duplicate entry, fix JSHint issues. --- openmct.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/openmct.js b/openmct.js index f1bb593860d..68151008cda 100644 --- a/openmct.js +++ b/openmct.js @@ -53,9 +53,6 @@ requirejs.config({ "html2canvas": { "exports": "html2canvas" }, - "EventEmitter": { - "exports": "EventEmitter" - }, "moment-duration-format": { "deps": ["moment"] }, From ac4d21b25234334d1312c82bbcefa227753e4c13 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Wed, 12 Oct 2016 13:57:11 -0700 Subject: [PATCH 66/78] [Build] Load v2 conductor, don't enable Loads the v2 conductor so that it is included in the build openmct file. It is not enabled by default. --- src/defaultRegistry.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/defaultRegistry.js b/src/defaultRegistry.js index e75b513a3ca..efabce4d978 100644 --- a/src/defaultRegistry.js +++ b/src/defaultRegistry.js @@ -45,6 +45,7 @@ define([ '../example/scratchpad/bundle', '../example/taxonomy/bundle', '../example/worker/bundle', + '../example/localTimeSystem/bundle', '../platform/commonUI/about/bundle', '../platform/commonUI/browse/bundle', @@ -65,6 +66,9 @@ define([ '../platform/exporters/bundle', '../platform/features/clock/bundle', '../platform/features/conductor/bundle', + '../platform/features/conductor-v2/conductor/bundle', + '../platform/features/conductor-v2/compatibility/bundle', + '../platform/features/conductor-v2/utcTimeSystem/bundle', '../platform/features/imagery/bundle', '../platform/features/layout/bundle', '../platform/features/pages/bundle', From 137434af1b598915ff914e1d07ab54db658179cf Mon Sep 17 00:00:00 2001 From: Henry Date: Wed, 12 Oct 2016 14:06:39 -0700 Subject: [PATCH 67/78] [API] Enable fixed position view --- src/defaultRegistry.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/defaultRegistry.js b/src/defaultRegistry.js index e75b513a3ca..6176d72fea2 100644 --- a/src/defaultRegistry.js +++ b/src/defaultRegistry.js @@ -65,6 +65,7 @@ define([ '../platform/exporters/bundle', '../platform/features/clock/bundle', '../platform/features/conductor/bundle', + '../platform/features/fixed/bundle', '../platform/features/imagery/bundle', '../platform/features/layout/bundle', '../platform/features/pages/bundle', @@ -109,6 +110,7 @@ define([ 'platform/exporters', 'platform/telemetry', 'platform/features/clock', + 'platform/features/fixed', 'platform/features/imagery', 'platform/features/layout', 'platform/features/pages', From 2e49c5932a602838616e958b5720c7a61dbd8be1 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Wed, 12 Oct 2016 15:21:57 -0700 Subject: [PATCH 68/78] [Build] require correct module Require the openmct module so that distributed files work properly. Related to #1212 --- src/end.frag | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/end.frag b/src/end.frag index 5ef11daae2a..9746cce6b89 100644 --- a/src/end.frag +++ b/src/end.frag @@ -1,3 +1,2 @@ - - return require('main'); -})); \ No newline at end of file + return require('openmct'); +})); From a2711c2c083cd688775282367e0ce499cb67f842 Mon Sep 17 00:00:00 2001 From: Henry Date: Thu, 13 Oct 2016 15:36:59 -0700 Subject: [PATCH 69/78] [Documentation] Changed build script to produce new API docs for website. Fixes #1249t --- build-docs.sh | 2 +- docs/src/index.md | 3 +-- package.json | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/build-docs.sh b/build-docs.sh index 29841f921e4..22e87949bff 100755 --- a/build-docs.sh +++ b/build-docs.sh @@ -24,7 +24,7 @@ # Script to build and deploy docs. -OUTPUT_DIRECTORY="target/docs" +OUTPUT_DIRECTORY="dist/docs" # Docs, once built, are pushed to the private website repo REPOSITORY_URL="git@github.com:nasa/openmct-website.git" WEBSITE_DIRECTORY="website" diff --git a/docs/src/index.md b/docs/src/index.md index 9c1a24aa023..1d523657f40 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -34,5 +34,4 @@ As we transition to a new API, the following documentation for the old API platform and the functionality that it provides. * The [Tutorials](tutorials/) give examples of extending the platform to add - functionality, - and integrate with data sources. + functionality, and integrate with data sources. diff --git a/package.json b/package.json index 6e9a7c9801f..9ac04c51894 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "jshint": "jshint platform example", "watch": "karma start", "jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api", - "otherdoc": "node docs/gendocs.js --in docs/src --out target/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'", + "otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'", "docs": "npm run jsdoc ; npm run otherdoc", "prepublish": "node ./node_modules/bower/bin/bower install && node ./node_modules/gulp/bin/gulp.js install" }, From 1947802a35e1294d48c1587e776818dc79627ccd Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Thu, 13 Oct 2016 17:59:31 -0700 Subject: [PATCH 70/78] [ObjectAPI] support async root loading Overload addRoot method to support async root loading. By supplying a function you can defer loading to later, and by allowing those functions to return a promise, execution can be asynchronous. Remove "removeRoot" as the assumption will be that roots are opt in, and instead of removing a Root, an application developer should never configure it in the first place. Fixes https://github.com/nasa/openmct/issues/1251 --- src/api/objects/ObjectAPI.js | 44 ++++++++++-------------- src/api/objects/RootRegistry.js | 59 +++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 27 deletions(-) create mode 100644 src/api/objects/RootRegistry.js diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index eb67c7adbdb..697716b3bee 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -23,11 +23,13 @@ define([ 'lodash', './object-utils', - './MutableObject' + './MutableObject', + './RootRegistry' ], function ( _, utils, - MutableObject + MutableObject, + RootRegistry ) { @@ -40,14 +42,17 @@ define([ function ObjectAPI() { this.providers = {}; - this.rootRegistry = []; + this.rootRegistry = new RootRegistry(); this.rootProvider = { 'get': function () { - return Promise.resolve({ - name: 'The root object', - type: 'root', - composition: this.rootRegistry - }); + return this.rootRegistry.getRoots() + .then(function (roots) { + return { + name: 'The root object', + type: 'root', + composition: roots + }; + }); }.bind(this) }; } @@ -143,29 +148,14 @@ define([ /** * Add a root-level object. - * @param {module:openmct.DomainObject} domainObject the root-level object - * to add. + * @param {module:openmct.ObjectAPI~Identifier|function} an array of + * identifiers for root level objects, or a function that returns a + * promise for an identifier or an array of root level objects. * @method addRoot * @memberof module:openmct.ObjectAPI# */ ObjectAPI.prototype.addRoot = function (key) { - this.rootRegistry.unshift(key); - }; - - /** - * Remove a root-level object. - * @param {module:openmct.ObjectAPI~Identifier} id the identifier of the - * root-level object to remove. - * @method removeRoot - * @memberof module:openmct.ObjectAPI# - */ - ObjectAPI.prototype.removeRoot = function (key) { - this.rootRegistry = this.rootRegistry.filter(function (k) { - return ( - k.identifier !== key.identifier || - k.namespace !== key.namespace - ); - }); + this.rootRegistry.addRoot(key); }; /** diff --git a/src/api/objects/RootRegistry.js b/src/api/objects/RootRegistry.js new file mode 100644 index 00000000000..b2971be1762 --- /dev/null +++ b/src/api/objects/RootRegistry.js @@ -0,0 +1,59 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + 'lodash' +], function ( + _ +) { + + function RootRegistry() { + this.providers = []; + } + + RootRegistry.prototype.getRoots = function () { + var promises = this.providers.map(function (provider) { + return provider(); + }); + return Promise.all(promises) + .then(_.flatten); + }; + + + RootRegistry.prototype.addRoot = function (key) { + if (_.isObject(key) && _.has(key, 'identifier') && _.has(key, 'namespace')) { + this.providers.push(function () { + return Promise.resolve(key); + }); + } else if (_.isFunction(key)) { + this.providers.push(key); + } + }; + + // Assume no one ever removes a root. everything is opt in. + RootRegistry.prototype.removeRoot = function (key) { + throw new Error('you should never remove a root'); + }; + + return RootRegistry; + +}); From 891412bdb91297afff15a924c9c8f6081be1c42e Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Thu, 13 Oct 2016 18:04:37 -0700 Subject: [PATCH 71/78] [Roots] Move my-items to separate bundle My Items root is now opt-in, and does not need to be enabled for all deployments. My Items is enabled by default in the development edition. https://github.com/nasa/openmct/issues/1251 --- index.html | 3 +- platform/core/bundle.js | 10 ------- platform/features/my-items/bundle.js | 45 ++++++++++++++++++++++++++++ src/defaultRegistry.js | 1 + 4 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 platform/features/my-items/bundle.js diff --git a/index.html b/index.html index aa5d79cfdc7..b6b1715116d 100644 --- a/index.html +++ b/index.html @@ -32,7 +32,8 @@ [ 'example/imagery', 'example/eventGenerator', - 'example/generator' + 'example/generator', + 'platform/features/my-items' ].forEach( openmct.legacyRegistry.enable.bind(openmct.legacyRegistry) ); diff --git a/platform/core/bundle.js b/platform/core/bundle.js index 26a49e16d91..93b64f5739f 100644 --- a/platform/core/bundle.js +++ b/platform/core/bundle.js @@ -409,16 +409,6 @@ define([ ] } ], - "roots": [ - { - "id": "mine", - "model": { - "name": "My Items", - "type": "folder", - "composition": [] - } - } - ], "runs": [ { "implementation": TransactingMutationListener, diff --git a/platform/features/my-items/bundle.js b/platform/features/my-items/bundle.js new file mode 100644 index 00000000000..42b66ad3b4f --- /dev/null +++ b/platform/features/my-items/bundle.js @@ -0,0 +1,45 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ + 'legacyRegistry' +], function ( + legacyRegistry +) { + + legacyRegistry.register("platform/features/my-items", { + "name": "My Items", + "description": "Defines a root named My Items", + "extensions": { + "roots": [ + { + "id": "mine", + "model": { + "name": "My Items", + "type": "folder", + "composition": [] + } + } + ] + } + }); +}); diff --git a/src/defaultRegistry.js b/src/defaultRegistry.js index efabce4d978..910dc8b94ae 100644 --- a/src/defaultRegistry.js +++ b/src/defaultRegistry.js @@ -71,6 +71,7 @@ define([ '../platform/features/conductor-v2/utcTimeSystem/bundle', '../platform/features/imagery/bundle', '../platform/features/layout/bundle', + '../platform/features/my-items/bundle', '../platform/features/pages/bundle', '../platform/features/plot/bundle', '../platform/features/static-markup/bundle', From 6ec858b237a9422d1cd50e7fa1603add17de07d8 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 14 Oct 2016 12:37:03 -0700 Subject: [PATCH 72/78] [Docs] Update root registration in README. https://github.com/nasa/openmct/issues/1251 --- API.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/API.md b/API.md index 24083e1cf69..fff93ef2d74 100644 --- a/API.md +++ b/API.md @@ -118,18 +118,11 @@ To do so, use the [`addRoot`]{@link module:openmct.ObjectAPI#addRoot} method of the [object API]{@link module:openmct.ObjectAPI}: ``` -openmct.objects.addRoot({ - identifier: { key: "my-key", namespace: "my-namespace" } - name: "My Root-level Object", - type: "my-type" -}); +openmct.objects.addRoot({ key: "my-key", namespace: "my-namespace" }); ``` -You can also remove this root-level object via its identifier: - -``` -openmct.objects.removeRoot({ key: "my-key", namespace: "my-namespace" }); -``` +Root objects are loaded just like any other objects, i.e. via an object +provider. ### Adding Composition Providers From b0940eb33ee479ea036a18fa6e23f20bf363bb98 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 14 Oct 2016 12:47:09 -0700 Subject: [PATCH 73/78] [Objects] refactor out RootObjectProvider Refactor RootObjectProvider to separate file for simplicty. https://github.com/nasa/openmct/issues/1251 --- src/api/objects/ObjectAPI.js | 19 ++++-------- src/api/objects/RootObjectProvider.js | 43 +++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 src/api/objects/RootObjectProvider.js diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index 697716b3bee..6b68e075d0a 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -24,12 +24,14 @@ define([ 'lodash', './object-utils', './MutableObject', - './RootRegistry' + './RootRegistry', + './RootObjectProvider' ], function ( _, utils, MutableObject, - RootRegistry + RootRegistry, + RootObjectProvider ) { @@ -43,18 +45,7 @@ define([ function ObjectAPI() { this.providers = {}; this.rootRegistry = new RootRegistry(); - this.rootProvider = { - 'get': function () { - return this.rootRegistry.getRoots() - .then(function (roots) { - return { - name: 'The root object', - type: 'root', - composition: roots - }; - }); - }.bind(this) - }; + this.rootProvider = new RootObjectProvider(this.rootRegistry); } ObjectAPI.prototype.supersecretSetFallbackProvider = function (p) { diff --git a/src/api/objects/RootObjectProvider.js b/src/api/objects/RootObjectProvider.js new file mode 100644 index 00000000000..fce0d26704e --- /dev/null +++ b/src/api/objects/RootObjectProvider.js @@ -0,0 +1,43 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ + +define([ +], function ( +) { + + function RootObjectProvider(rootRegistry) { + this.rootRegistry = rootRegistry; + } + + RootObjectProvider.prototype.get = function () { + return this.rootRegistry.getRoots() + .then(function (roots) { + return { + name: 'The root object', + type: 'root', + composition: roots + }; + }); + } + + return RootObjectProvider; +}); From 08c0aeb2d5e320f781ef125217d580df602e3c09 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 14 Oct 2016 13:12:45 -0700 Subject: [PATCH 74/78] [Test] Add tests for root registration Add unit tests for RootRegistry and RootObjectProvider. https://github.com/nasa/openmct/issues/1251 --- src/api/objects/RootObjectProvider.js | 2 +- src/api/objects/RootRegistry.js | 7 +- .../objects/test/RootObjectProviderSpec.js | 59 ++++++++++ src/api/objects/test/RootRegistrySpec.js | 102 ++++++++++++++++++ 4 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 src/api/objects/test/RootObjectProviderSpec.js create mode 100644 src/api/objects/test/RootRegistrySpec.js diff --git a/src/api/objects/RootObjectProvider.js b/src/api/objects/RootObjectProvider.js index fce0d26704e..4b44d66f9cc 100644 --- a/src/api/objects/RootObjectProvider.js +++ b/src/api/objects/RootObjectProvider.js @@ -37,7 +37,7 @@ define([ composition: roots }; }); - } + }; return RootObjectProvider; }); diff --git a/src/api/objects/RootRegistry.js b/src/api/objects/RootRegistry.js index b2971be1762..cb0ff5d167e 100644 --- a/src/api/objects/RootRegistry.js +++ b/src/api/objects/RootRegistry.js @@ -38,11 +38,14 @@ define([ .then(_.flatten); }; + function isKey(key) { + return _.isObject(key) && _.has(key, 'key') && _.has(key, 'namespace'); + } RootRegistry.prototype.addRoot = function (key) { - if (_.isObject(key) && _.has(key, 'identifier') && _.has(key, 'namespace')) { + if (isKey(key) || (_.isArray(key) && _.every(key, isKey))) { this.providers.push(function () { - return Promise.resolve(key); + return key; }); } else if (_.isFunction(key)) { this.providers.push(key); diff --git a/src/api/objects/test/RootObjectProviderSpec.js b/src/api/objects/test/RootObjectProviderSpec.js new file mode 100644 index 00000000000..2734c2e55fa --- /dev/null +++ b/src/api/objects/test/RootObjectProviderSpec.js @@ -0,0 +1,59 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ +define([ + '../RootObjectProvider' +], function ( + RootObjectProvider +) { + describe('RootObjectProvider', function () { + var rootRegistry, + rootObjectProvider; + + function done() { + var isDone = false; + waitsFor(function () { + return isDone; + }); + return function () { + isDone = true; + }; + } + + beforeEach(function () { + rootRegistry = jasmine.createSpyObj('rootRegistry', ['getRoots']); + rootRegistry.getRoots.andReturn(Promise.resolve(['some root'])); + rootObjectProvider = new RootObjectProvider(rootRegistry); + }); + + it('supports fetching root', function () { + rootObjectProvider.get() + .then(function (root) { + expect(root).toEqual({ + name: 'The root object', + type: 'root', + composition: ['some root'] + }); + }) + .then(done()); + }); + }); +}); diff --git a/src/api/objects/test/RootRegistrySpec.js b/src/api/objects/test/RootRegistrySpec.js new file mode 100644 index 00000000000..4f918b9eb0a --- /dev/null +++ b/src/api/objects/test/RootRegistrySpec.js @@ -0,0 +1,102 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2014-2016, United States Government + * as represented by the Administrator of the National Aeronautics and Space + * Administration. All rights reserved. + * + * Open MCT 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 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. + *****************************************************************************/ +define([ + '../RootRegistry' +], function ( + RootRegistry +) { + describe('RootRegistry', function () { + var idA, + idB, + idC, + registry; + + function done() { + var isDone = false; + waitsFor(function () { + return isDone; + }); + return function () { + isDone = true; + }; + } + + beforeEach(function () { + idA = {key: 'keyA', namespace: 'something'}; + idB = {key: 'keyB', namespace: 'something'}; + idC = {key: 'keyC', namespace: 'something'}; + registry = new RootRegistry(); + }); + + it('can register a root by key', function () { + registry.addRoot(idA); + registry.getRoots() + .then(function (roots) { + expect(roots).toEqual([idA]); + }) + .then(done()); + }); + + it('can register multiple roots by key', function () { + registry.addRoot([idA, idB]); + registry.getRoots() + .then(function (roots) { + expect(roots).toEqual([idA, idB]); + }) + .then(done()); + }); + + it('can register an asynchronous root ', function () { + registry.addRoot(function () { + return Promise.resolve(idA); + }); + registry.getRoots() + .then(function (roots) { + expect(roots).toEqual([idA]); + }) + .then(done()); + }); + + it('can register multiple asynchronous roots', function () { + registry.addRoot(function () { + return Promise.resolve([idA, idB]); + }); + registry.getRoots() + .then(function (roots) { + expect(roots).toEqual([idA, idB]); + }) + .then(done()); + }); + + it('can combine different types of registration', function () { + registry.addRoot([idA, idB]); + registry.addRoot(function () { + return Promise.resolve([idC]); + }); + registry.getRoots() + .then(function (roots) { + expect(roots).toEqual([idA, idB, idC]); + }) + .then(done()); + }); + }); +}); From d643efa9bbc150a33e9a5d471c6921c4f3589852 Mon Sep 17 00:00:00 2001 From: Pete Richards Date: Fri, 14 Oct 2016 13:16:00 -0700 Subject: [PATCH 75/78] [Style] Remove unused function https://github.com/nasa/openmct/issues/1251 --- src/api/objects/RootRegistry.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/api/objects/RootRegistry.js b/src/api/objects/RootRegistry.js index cb0ff5d167e..2e04d834107 100644 --- a/src/api/objects/RootRegistry.js +++ b/src/api/objects/RootRegistry.js @@ -52,11 +52,6 @@ define([ } }; - // Assume no one ever removes a root. everything is opt in. - RootRegistry.prototype.removeRoot = function (key) { - throw new Error('you should never remove a root'); - }; - return RootRegistry; }); From b8cb41b1daedaf8d553a3eb40dd44cd3afbc45c7 Mon Sep 17 00:00:00 2001 From: Charles Hacskaylo Date: Fri, 14 Oct 2016 16:53:00 -0700 Subject: [PATCH 76/78] [Frontend] Fixes to export buttons Fixes #1235 Sass and markup mods; changes to .l-btn-set and related classes --- .../general/res/sass/controls/_buttons.scss | 30 +++++++++++-------- .../features/plot/res/templates/plot.html | 4 +-- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/platform/commonUI/general/res/sass/controls/_buttons.scss b/platform/commonUI/general/res/sass/controls/_buttons.scss index 17e03941dfc..fe2c6d660c0 100644 --- a/platform/commonUI/general/res/sass/controls/_buttons.scss +++ b/platform/commonUI/general/res/sass/controls/_buttons.scss @@ -104,9 +104,17 @@ body.desktop .mini-tab-icon { } } +@mixin btnSetButtonFirst() { + @include border-left-radius($controlCr); + margin-left: 0; +} + +@mixin btnSetButtonLast() { + @include border-right-radius($controlCr); +} + .l-btn-set { // Buttons that have a very tight conceptual grouping - no internal space between them. - // Structure: .btn-set > mct-representation class=first|last > .s-button font-size: 0; // Remove space between s-button elements due to white space in markup .s-button { @@ -114,20 +122,16 @@ body.desktop .mini-tab-icon { margin-left: 1px; } - .first { - .s-button, - &.s-button { - @include border-left-radius($controlCr); - margin-left: 0; - } + > .s-button { + // Styles for .s-button as immediate descendants in .l-btn-set + &:first-child { @include btnSetButtonFirst(); } + &:last-child { @include btnSetButtonLast(); } } - .last { - .s-button, - &.s-button { - @include border-right-radius($controlCr); - } - } + // Must use following due to DOM structure of action buttons, + // which have structure like .l-btn-set > mct-representation class=first|last > .s-button + .first > .s-button { @include btnSetButtonFirst(); } + .last > .s-button { @include btnSetButtonLast(); } } .paused { diff --git a/platform/features/plot/res/templates/plot.html b/platform/features/plot/res/templates/plot.html index 461389e2c17..53b5d348b0e 100644 --- a/platform/features/plot/res/templates/plot.html +++ b/platform/features/plot/res/templates/plot.html @@ -23,12 +23,12 @@ class="abs holder holder-plot has-control-bar">
- PNG - JPG From 0759ba6722d966e5c2144ee4e6c1a1f46021ce78 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 17 Oct 2016 10:43:12 -0700 Subject: [PATCH 77/78] [Composition] Fix adapter composition policy ...as this had fallen out-of-date with existing APIs. Fixes #1257. --- .../policies/AdapterCompositionPolicy.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/adapter/policies/AdapterCompositionPolicy.js b/src/adapter/policies/AdapterCompositionPolicy.js index 50e9312d543..865b08cb59b 100644 --- a/src/adapter/policies/AdapterCompositionPolicy.js +++ b/src/adapter/policies/AdapterCompositionPolicy.js @@ -21,8 +21,8 @@ *****************************************************************************/ define([], function () { - function AdapterCompositionPolicy(mct) { - this.mct = mct; + function AdapterCompositionPolicy(openmct) { + this.openmct = openmct; } AdapterCompositionPolicy.prototype.allow = function ( @@ -32,16 +32,10 @@ define([], function () { var containerObject = containerType.getInitialModel(); var childObject = childType.getInitialModel(); - containerObject.type = containerType.getKey(); - childObject.type = childType.getKey(); - - var composition = this.mct.Composition(containerObject); - - if (composition) { - return composition.canContain(childObject); - } - - return true; + return this.openmct.composition.checkPolicy( + containerObject, + childObject + ); }; return AdapterCompositionPolicy; From 4eca80a770d9bcd18432bba71e5db4a1526a7f81 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 18 Oct 2016 10:48:26 -0700 Subject: [PATCH 78/78] Revert "Async root registration" --- API.md | 13 ++- index.html | 3 +- platform/core/bundle.js | 10 ++ platform/features/my-items/bundle.js | 45 -------- src/api/objects/ObjectAPI.js | 43 +++++--- src/api/objects/RootObjectProvider.js | 43 -------- src/api/objects/RootRegistry.js | 57 ---------- .../objects/test/RootObjectProviderSpec.js | 59 ---------- src/api/objects/test/RootRegistrySpec.js | 102 ------------------ src/defaultRegistry.js | 1 - 10 files changed, 52 insertions(+), 324 deletions(-) delete mode 100644 platform/features/my-items/bundle.js delete mode 100644 src/api/objects/RootObjectProvider.js delete mode 100644 src/api/objects/RootRegistry.js delete mode 100644 src/api/objects/test/RootObjectProviderSpec.js delete mode 100644 src/api/objects/test/RootRegistrySpec.js diff --git a/API.md b/API.md index fff93ef2d74..24083e1cf69 100644 --- a/API.md +++ b/API.md @@ -118,11 +118,18 @@ To do so, use the [`addRoot`]{@link module:openmct.ObjectAPI#addRoot} method of the [object API]{@link module:openmct.ObjectAPI}: ``` -openmct.objects.addRoot({ key: "my-key", namespace: "my-namespace" }); +openmct.objects.addRoot({ + identifier: { key: "my-key", namespace: "my-namespace" } + name: "My Root-level Object", + type: "my-type" +}); ``` -Root objects are loaded just like any other objects, i.e. via an object -provider. +You can also remove this root-level object via its identifier: + +``` +openmct.objects.removeRoot({ key: "my-key", namespace: "my-namespace" }); +``` ### Adding Composition Providers diff --git a/index.html b/index.html index b6b1715116d..aa5d79cfdc7 100644 --- a/index.html +++ b/index.html @@ -32,8 +32,7 @@ [ 'example/imagery', 'example/eventGenerator', - 'example/generator', - 'platform/features/my-items' + 'example/generator' ].forEach( openmct.legacyRegistry.enable.bind(openmct.legacyRegistry) ); diff --git a/platform/core/bundle.js b/platform/core/bundle.js index 93b64f5739f..26a49e16d91 100644 --- a/platform/core/bundle.js +++ b/platform/core/bundle.js @@ -409,6 +409,16 @@ define([ ] } ], + "roots": [ + { + "id": "mine", + "model": { + "name": "My Items", + "type": "folder", + "composition": [] + } + } + ], "runs": [ { "implementation": TransactingMutationListener, diff --git a/platform/features/my-items/bundle.js b/platform/features/my-items/bundle.js deleted file mode 100644 index 42b66ad3b4f..00000000000 --- a/platform/features/my-items/bundle.js +++ /dev/null @@ -1,45 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2016, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT 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 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. - *****************************************************************************/ - -define([ - 'legacyRegistry' -], function ( - legacyRegistry -) { - - legacyRegistry.register("platform/features/my-items", { - "name": "My Items", - "description": "Defines a root named My Items", - "extensions": { - "roots": [ - { - "id": "mine", - "model": { - "name": "My Items", - "type": "folder", - "composition": [] - } - } - ] - } - }); -}); diff --git a/src/api/objects/ObjectAPI.js b/src/api/objects/ObjectAPI.js index 6b68e075d0a..eb67c7adbdb 100644 --- a/src/api/objects/ObjectAPI.js +++ b/src/api/objects/ObjectAPI.js @@ -23,15 +23,11 @@ define([ 'lodash', './object-utils', - './MutableObject', - './RootRegistry', - './RootObjectProvider' + './MutableObject' ], function ( _, utils, - MutableObject, - RootRegistry, - RootObjectProvider + MutableObject ) { @@ -44,8 +40,16 @@ define([ function ObjectAPI() { this.providers = {}; - this.rootRegistry = new RootRegistry(); - this.rootProvider = new RootObjectProvider(this.rootRegistry); + this.rootRegistry = []; + this.rootProvider = { + 'get': function () { + return Promise.resolve({ + name: 'The root object', + type: 'root', + composition: this.rootRegistry + }); + }.bind(this) + }; } ObjectAPI.prototype.supersecretSetFallbackProvider = function (p) { @@ -139,14 +143,29 @@ define([ /** * Add a root-level object. - * @param {module:openmct.ObjectAPI~Identifier|function} an array of - * identifiers for root level objects, or a function that returns a - * promise for an identifier or an array of root level objects. + * @param {module:openmct.DomainObject} domainObject the root-level object + * to add. * @method addRoot * @memberof module:openmct.ObjectAPI# */ ObjectAPI.prototype.addRoot = function (key) { - this.rootRegistry.addRoot(key); + this.rootRegistry.unshift(key); + }; + + /** + * Remove a root-level object. + * @param {module:openmct.ObjectAPI~Identifier} id the identifier of the + * root-level object to remove. + * @method removeRoot + * @memberof module:openmct.ObjectAPI# + */ + ObjectAPI.prototype.removeRoot = function (key) { + this.rootRegistry = this.rootRegistry.filter(function (k) { + return ( + k.identifier !== key.identifier || + k.namespace !== key.namespace + ); + }); }; /** diff --git a/src/api/objects/RootObjectProvider.js b/src/api/objects/RootObjectProvider.js deleted file mode 100644 index 4b44d66f9cc..00000000000 --- a/src/api/objects/RootObjectProvider.js +++ /dev/null @@ -1,43 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2016, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT 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 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. - *****************************************************************************/ - -define([ -], function ( -) { - - function RootObjectProvider(rootRegistry) { - this.rootRegistry = rootRegistry; - } - - RootObjectProvider.prototype.get = function () { - return this.rootRegistry.getRoots() - .then(function (roots) { - return { - name: 'The root object', - type: 'root', - composition: roots - }; - }); - }; - - return RootObjectProvider; -}); diff --git a/src/api/objects/RootRegistry.js b/src/api/objects/RootRegistry.js deleted file mode 100644 index 2e04d834107..00000000000 --- a/src/api/objects/RootRegistry.js +++ /dev/null @@ -1,57 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2016, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT 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 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. - *****************************************************************************/ - -define([ - 'lodash' -], function ( - _ -) { - - function RootRegistry() { - this.providers = []; - } - - RootRegistry.prototype.getRoots = function () { - var promises = this.providers.map(function (provider) { - return provider(); - }); - return Promise.all(promises) - .then(_.flatten); - }; - - function isKey(key) { - return _.isObject(key) && _.has(key, 'key') && _.has(key, 'namespace'); - } - - RootRegistry.prototype.addRoot = function (key) { - if (isKey(key) || (_.isArray(key) && _.every(key, isKey))) { - this.providers.push(function () { - return key; - }); - } else if (_.isFunction(key)) { - this.providers.push(key); - } - }; - - return RootRegistry; - -}); diff --git a/src/api/objects/test/RootObjectProviderSpec.js b/src/api/objects/test/RootObjectProviderSpec.js deleted file mode 100644 index 2734c2e55fa..00000000000 --- a/src/api/objects/test/RootObjectProviderSpec.js +++ /dev/null @@ -1,59 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2016, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT 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 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. - *****************************************************************************/ -define([ - '../RootObjectProvider' -], function ( - RootObjectProvider -) { - describe('RootObjectProvider', function () { - var rootRegistry, - rootObjectProvider; - - function done() { - var isDone = false; - waitsFor(function () { - return isDone; - }); - return function () { - isDone = true; - }; - } - - beforeEach(function () { - rootRegistry = jasmine.createSpyObj('rootRegistry', ['getRoots']); - rootRegistry.getRoots.andReturn(Promise.resolve(['some root'])); - rootObjectProvider = new RootObjectProvider(rootRegistry); - }); - - it('supports fetching root', function () { - rootObjectProvider.get() - .then(function (root) { - expect(root).toEqual({ - name: 'The root object', - type: 'root', - composition: ['some root'] - }); - }) - .then(done()); - }); - }); -}); diff --git a/src/api/objects/test/RootRegistrySpec.js b/src/api/objects/test/RootRegistrySpec.js deleted file mode 100644 index 4f918b9eb0a..00000000000 --- a/src/api/objects/test/RootRegistrySpec.js +++ /dev/null @@ -1,102 +0,0 @@ -/***************************************************************************** - * Open MCT, Copyright (c) 2014-2016, United States Government - * as represented by the Administrator of the National Aeronautics and Space - * Administration. All rights reserved. - * - * Open MCT 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 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. - *****************************************************************************/ -define([ - '../RootRegistry' -], function ( - RootRegistry -) { - describe('RootRegistry', function () { - var idA, - idB, - idC, - registry; - - function done() { - var isDone = false; - waitsFor(function () { - return isDone; - }); - return function () { - isDone = true; - }; - } - - beforeEach(function () { - idA = {key: 'keyA', namespace: 'something'}; - idB = {key: 'keyB', namespace: 'something'}; - idC = {key: 'keyC', namespace: 'something'}; - registry = new RootRegistry(); - }); - - it('can register a root by key', function () { - registry.addRoot(idA); - registry.getRoots() - .then(function (roots) { - expect(roots).toEqual([idA]); - }) - .then(done()); - }); - - it('can register multiple roots by key', function () { - registry.addRoot([idA, idB]); - registry.getRoots() - .then(function (roots) { - expect(roots).toEqual([idA, idB]); - }) - .then(done()); - }); - - it('can register an asynchronous root ', function () { - registry.addRoot(function () { - return Promise.resolve(idA); - }); - registry.getRoots() - .then(function (roots) { - expect(roots).toEqual([idA]); - }) - .then(done()); - }); - - it('can register multiple asynchronous roots', function () { - registry.addRoot(function () { - return Promise.resolve([idA, idB]); - }); - registry.getRoots() - .then(function (roots) { - expect(roots).toEqual([idA, idB]); - }) - .then(done()); - }); - - it('can combine different types of registration', function () { - registry.addRoot([idA, idB]); - registry.addRoot(function () { - return Promise.resolve([idC]); - }); - registry.getRoots() - .then(function (roots) { - expect(roots).toEqual([idA, idB, idC]); - }) - .then(done()); - }); - }); -}); diff --git a/src/defaultRegistry.js b/src/defaultRegistry.js index 485caacca9e..a4070313dcf 100644 --- a/src/defaultRegistry.js +++ b/src/defaultRegistry.js @@ -72,7 +72,6 @@ define([ '../platform/features/conductor-v2/utcTimeSystem/bundle', '../platform/features/imagery/bundle', '../platform/features/layout/bundle', - '../platform/features/my-items/bundle', '../platform/features/pages/bundle', '../platform/features/plot/bundle', '../platform/features/static-markup/bundle',