diff --git a/platform/features/clock/bundle.js b/platform/features/clock/bundle.js index d3f98426bf..54908385c0 100644 --- a/platform/features/clock/bundle.js +++ b/platform/features/clock/bundle.js @@ -23,10 +23,13 @@ define([ "moment-timezone", "./src/indicators/ClockIndicator", + "./src/indicators/FollowIndicator", "./src/services/TickerService", + "./src/services/TimerService", "./src/controllers/ClockController", "./src/controllers/TimerController", "./src/controllers/RefreshingController", + "./src/actions/FollowTimerAction", "./src/actions/StartTimerAction", "./src/actions/RestartTimerAction", "./src/actions/StopTimerAction", @@ -37,10 +40,13 @@ define([ ], function ( MomentTimezone, ClockIndicator, + FollowIndicator, TickerService, + TimerService, ClockController, TimerController, RefreshingController, + FollowTimerAction, StartTimerAction, RestartTimerAction, StopTimerAction, @@ -80,6 +86,11 @@ define([ "CLOCK_INDICATOR_FORMAT" ], "priority": "preferred" + }, + { + "implementation": FollowIndicator, + "depends": ["timerService"], + "priority": "fallback" } ], "services": [ @@ -90,6 +101,11 @@ define([ "$timeout", "now" ] + }, + { + "key": "timerService", + "implementation": TimerService, + "depends": ["openmct"] } ], "controllers": [ @@ -134,6 +150,15 @@ define([ } ], "actions": [ + { + "key": "timer.follow", + "implementation": FollowTimerAction, + "depends": ["timerService"], + "category": "contextual", + "name": "Follow Timer", + "cssClass": "icon-clock", + "priority": "optional" + }, { "key": "timer.start", "implementation": StartTimerAction, diff --git a/platform/features/clock/src/actions/FollowTimerAction.js b/platform/features/clock/src/actions/FollowTimerAction.js new file mode 100644 index 0000000000..723fd05fcb --- /dev/null +++ b/platform/features/clock/src/actions/FollowTimerAction.js @@ -0,0 +1,54 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2009-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 () { + + /** + * Designates a specific timer for following. Timelines, for example, + * use the actively followed timer to display a time-of-interest line + * and interpret time conductor bounds in the Timeline's relative + * time frame. + * + * @implements {Action} + * @memberof platform/features/clock + * @constructor + * @param {ActionContext} context the context for this action + */ + function FollowTimerAction(timerService, context) { + var domainObject = context.domainObject; + this.perform = + timerService.setTimer.bind(timerService, domainObject); + } + + FollowTimerAction.appliesTo = function (context) { + var model = + (context.domainObject && context.domainObject.getModel()) || + {}; + + return model.type === 'timer'; + }; + + return FollowTimerAction; + } +); diff --git a/platform/features/clock/src/indicators/FollowIndicator.js b/platform/features/clock/src/indicators/FollowIndicator.js new file mode 100644 index 0000000000..7574cb4201 --- /dev/null +++ b/platform/features/clock/src/indicators/FollowIndicator.js @@ -0,0 +1,57 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2009-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( + ['moment'], + function (moment) { + var NO_TIMER = "No timer being followed."; + + /** + * Indicator that displays the active timer, as well as its + * current state. + * @implements {Indicator} + * @memberof platform/features/clock + */ + function FollowIndicator(timerService) { + this.timerService = timerService; + } + + FollowIndicator.prototype.getGlyphClass = function () { + return ""; + }; + + FollowIndicator.prototype.getCssClass = function () { + return "icon-clock"; + }; + + FollowIndicator.prototype.getText = function () { + var timer = this.timerService.getTimer(); + return timer ? timer.getModel().name : NO_TIMER; + }; + + FollowIndicator.prototype.getDescription = function () { + return ""; + }; + + return FollowIndicator; + } +); diff --git a/platform/features/clock/src/services/TimerService.js b/platform/features/clock/src/services/TimerService.js new file mode 100644 index 0000000000..81ea8eb281 --- /dev/null +++ b/platform/features/clock/src/services/TimerService.js @@ -0,0 +1,102 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2009-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) { + + /** + * Tracks the currently-followed Timer object. Used by + * timelines et al to synchronize to a particular timer. + * + * The TimerService emits `change` events when the active timer + * is changed. + */ + function TimerService(openmct) { + EventEmitter.apply(this); + this.time = openmct.time; + } + + TimerService.prototype = Object.create(EventEmitter.prototype); + + /** + * Set (or clear, if `timer` is undefined) the currently active timer. + * @param {DomainObject} timer the new active timer + * @emits change + */ + TimerService.prototype.setTimer = function (timer) { + this.timer = timer; + this.emit('change'); + }; + + /** + * Get the currently active timer. + * @return {DomainObject} the active timer + * @emits change + */ + TimerService.prototype.getTimer = function () { + return this.timer; + }; + + + /** + * Check if there is a currently active timer. + * @return {boolean} true if there is a timer + */ + TimerService.prototype.hasTimer = function () { + return !!this.timer; + }; + + /** + * Convert the provided timestamp to milliseconds relative to + * the active timer. + * @return {number} milliseconds since timer start + */ + TimerService.prototype.convert = function (timestamp) { + var clock = this.time.clock(); + var canConvert = this.hasTimer() && + !!clock && + this.timer.getModel().timerState !== 'stopped'; + + if (!canConvert) { + return undefined; + } + + var now = clock.currentValue(); + var model = this.timer.getModel(); + var delta = model.timerState === 'paused' ? now - model.pausedTime : 0; + var epoch = model.timestamp; + + return timestamp - epoch - delta; + }; + + /** + * Get the value of the active clock, adjusted to be relative to the active + * timer. If there is no clock or no active timer, this will return + * `undefined`. + * @return {number} milliseconds since the start of the active timer + */ + TimerService.prototype.now = function () { + var clock = this.time.clock(); + return clock && this.convert(clock.currentValue()); + }; + + return TimerService; +}); diff --git a/platform/features/clock/test/actions/FollowTimerActionSpec.js b/platform/features/clock/test/actions/FollowTimerActionSpec.js new file mode 100644 index 0000000000..18219ddc56 --- /dev/null +++ b/platform/features/clock/test/actions/FollowTimerActionSpec.js @@ -0,0 +1,80 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2009-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/actions/FollowTimerAction" +], function (FollowTimerAction) { + var TIMER_SERVICE_METHODS = + ['setTimer', 'getTimer', 'clearTimer', 'on', 'off']; + + describe("The Follow Timer action", function () { + var testContext; + var testModel; + + beforeEach(function () { + testModel = {}; + testContext = { domainObject: { getModel: function () { + return testModel; + } } }; + }); + + it("is applicable to timers", function () { + testModel.type = "timer"; + expect(FollowTimerAction.appliesTo(testContext)).toBe(true); + }); + + it("is inapplicable to non-timers", function () { + testModel.type = "folder"; + expect(FollowTimerAction.appliesTo(testContext)).toBe(false); + }); + + describe("when instantiated", function () { + var mockTimerService; + var action; + + beforeEach(function () { + mockTimerService = jasmine.createSpyObj( + 'timerService', + TIMER_SERVICE_METHODS + ); + action = new FollowTimerAction(mockTimerService, testContext); + }); + + it("does not interact with the timer service", function () { + TIMER_SERVICE_METHODS.forEach(function (method) { + expect(mockTimerService[method]).not.toHaveBeenCalled(); + }); + }); + + describe("and performed", function () { + beforeEach(function () { + action.perform(); + }); + + it("sets the active timer", function () { + expect(mockTimerService.setTimer) + .toHaveBeenCalledWith(testContext.domainObject); + }); + }); + }); + }); +}); diff --git a/platform/features/clock/test/indicators/FollowIndicatorSpec.js b/platform/features/clock/test/indicators/FollowIndicatorSpec.js new file mode 100644 index 0000000000..f158d07ec3 --- /dev/null +++ b/platform/features/clock/test/indicators/FollowIndicatorSpec.js @@ -0,0 +1,60 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2009-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/indicators/FollowIndicator"], function (FollowIndicator) { + var TIMER_SERVICE_METHODS = + ['setTimer', 'getTimer', 'clearTimer', 'on', 'off']; + + describe("The timer-following indicator", function () { + var mockTimerService; + var indicator; + + beforeEach(function () { + mockTimerService = + jasmine.createSpyObj('timerService', TIMER_SERVICE_METHODS); + indicator = new FollowIndicator(mockTimerService); + }); + + it("implements the Indicator interface", function () { + expect(indicator.getGlyphClass()).toEqual(jasmine.any(String)); + expect(indicator.getCssClass()).toEqual(jasmine.any(String)); + expect(indicator.getText()).toEqual(jasmine.any(String)); + expect(indicator.getDescription()).toEqual(jasmine.any(String)); + }); + + describe("when a timer is set", function () { + var testModel; + var mockDomainObject; + + beforeEach(function () { + testModel = { name: "some timer!" }; + mockDomainObject = jasmine.createSpyObj('timer', ['getModel']); + mockDomainObject.getModel.andReturn(testModel); + mockTimerService.getTimer.andReturn(mockDomainObject); + }); + + it("display's the timer's name", function () { + expect(indicator.getText()).toEqual(testModel.name); + }); + }); + }); +}); diff --git a/platform/features/clock/test/services/TimerServiceSpec.js b/platform/features/clock/test/services/TimerServiceSpec.js new file mode 100644 index 0000000000..fe137a4877 --- /dev/null +++ b/platform/features/clock/test/services/TimerServiceSpec.js @@ -0,0 +1,64 @@ +/***************************************************************************** + * Open MCT, Copyright (c) 2009-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/services/TimerService' +], function (TimerService) { + describe("TimerService", function () { + var callback; + var mockClock; + var mockmct; + var timerService; + + beforeEach(function () { + callback = jasmine.createSpy('callback'); + mockmct = { time: { clock: jasmine.createSpy('clock') } }; + timerService = new TimerService(mockmct); + timerService.on('change', callback); + }); + + it("initially emits no change events", function () { + expect(callback).not.toHaveBeenCalled(); + }); + + it("reports no current timer", function () { + expect(timerService.getTimer()).toBeUndefined(); + }); + + describe("setTimer", function () { + var testTimer; + + beforeEach(function () { + testTimer = { name: "I am some timer; you are nobody." }; + timerService.setTimer(testTimer); + }); + + it("emits a change event", function () { + expect(callback).toHaveBeenCalled(); + }); + + it("reports the current timer", function () { + expect(timerService.getTimer()).toBe(testTimer); + }); + }); + }); +}); diff --git a/platform/features/timeline/bundle.js b/platform/features/timeline/bundle.js index 82646b55e9..eec6113054 100644 --- a/platform/features/timeline/bundle.js +++ b/platform/features/timeline/bundle.js @@ -29,6 +29,7 @@ define([ "./src/controllers/TimelineTickController", "./src/controllers/TimelineTableController", "./src/controllers/TimelineGanttController", + "./src/controllers/TimelineTOIController", "./src/controllers/ActivityModeValuesController", "./src/capabilities/ActivityTimespanCapability", "./src/capabilities/TimelineTimespanCapability", @@ -59,6 +60,7 @@ define([ TimelineTickController, TimelineTableController, TimelineGanttController, + TimelineTOIController, ActivityModeValuesController, ActivityTimespanCapability, TimelineTimespanCapability, @@ -502,6 +504,15 @@ define([ "TIMELINE_MAXIMUM_OFFSCREEN" ] }, + { + "key": "TimelineTOIController", + "implementation": TimelineTOIController, + "depends": [ + "openmct", + "timerService", + "$scope" + ] + }, { "key": "ActivityModeValuesController", "implementation": ActivityModeValuesController, diff --git a/platform/features/timeline/res/templates/timeline.html b/platform/features/timeline/res/templates/timeline.html index d4c022f150..16b899494c 100644 --- a/platform/features/timeline/res/templates/timeline.html +++ b/platform/features/timeline/res/templates/timeline.html @@ -101,22 +101,29 @@ class="abs split-pane-component l-timeline-pane l-pane-r t-pane-v"> -
+
+ + + @@ -132,6 +139,10 @@ toMillis: zoomController.toMillis }"> +
+
+