Compare commits
	
		
			11 Commits
		
	
	
		
			notebook-v
			...
			timeline-f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 88cb206058 | ||
|   | 14592d1c3e | ||
|   | 7f9fd5c705 | ||
|   | 370b910d36 | ||
|   | 883d1feb32 | ||
|   | cff85fbbde | ||
|   | 563a86b69f | ||
|   | fc49e5d023 | ||
|   | 8ec3c42291 | ||
|   | 1e1a2443d5 | ||
|   | d65e1e604e | 
| @@ -50,7 +50,6 @@ | ||||
|         content:''; | ||||
|         font-family: symbolsfont; | ||||
|         font-size: 0.8em; | ||||
|         display: inline; | ||||
|         margin-right: $interiorMarginSm; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -120,7 +120,11 @@ | ||||
| 	} | ||||
|  | ||||
| 	.status-indicator { | ||||
|         background: none !important; | ||||
| 		margin-right: $interiorMarginSm; | ||||
|         &[class*='s-status']:before { | ||||
|             font-size: 1em; | ||||
|         } | ||||
| 	} | ||||
|  | ||||
| 	.count { | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
							
								
								
									
										54
									
								
								platform/features/clock/src/actions/FollowTimerAction.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								platform/features/clock/src/actions/FollowTimerAction.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										57
									
								
								platform/features/clock/src/indicators/FollowIndicator.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								platform/features/clock/src/indicators/FollowIndicator.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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 (this.timerService.getTimer()) ? "icon-timer s-status-ok" : "icon-timer"; | ||||
|         }; | ||||
|  | ||||
|         FollowIndicator.prototype.getText = function () { | ||||
|             var timer = this.timerService.getTimer(); | ||||
|             return (timer) ? 'Following timer ' + timer.getModel().name : NO_TIMER; | ||||
|         }; | ||||
|  | ||||
|         FollowIndicator.prototype.getDescription = function () { | ||||
|             return ""; | ||||
|         }; | ||||
|  | ||||
|         return FollowIndicator; | ||||
|     } | ||||
| ); | ||||
							
								
								
									
										102
									
								
								platform/features/clock/src/services/TimerService.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								platform/features/clock/src/services/TimerService.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||
| }); | ||||
| @@ -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); | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| @@ -0,0 +1,61 @@ | ||||
| /***************************************************************************** | ||||
|  * 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("displays the timer's name", function () { | ||||
|                 expect(indicator.getText().indexOf(testModel.name)) | ||||
|                     .not.toEqual(-1); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
							
								
								
									
										63
									
								
								platform/features/clock/test/services/TimerServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								platform/features/clock/test/services/TimerServiceSpec.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| /***************************************************************************** | ||||
|  * 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 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); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
| @@ -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, | ||||
|   | ||||
| @@ -29,6 +29,44 @@ | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Follow Line | ||||
|     .l-follow-line { | ||||
|         // TODO: move before and after into l-timeline-gantt so those only render in that pane | ||||
|         pointer-events: none; | ||||
|         position: absolute; | ||||
|         top: 0; bottom: 0; | ||||
|         width: 1px; | ||||
|         z-index: 9; // Just below .l-hover-btns-holder | ||||
|     } | ||||
| } | ||||
|  | ||||
| .l-timeline-gantt { | ||||
|     .l-follow-line { | ||||
|         $d: 0.8rem; | ||||
|         top: $interiorMargin; | ||||
|         &:before, | ||||
|         &:after { | ||||
|             content: ''; | ||||
|             display: block; | ||||
|             height: $d; | ||||
|             width: $d; | ||||
|             position: absolute; | ||||
|             top: 0; | ||||
|             @include transform(translateX(-50%)); | ||||
|         } | ||||
|         &:before { | ||||
|             // Icon blocker | ||||
|             width: 2 * $d; | ||||
|         } | ||||
|         &:after { | ||||
|             // Icon | ||||
|             font-size: $d; | ||||
|             line-height: $d; | ||||
|             text-align: center; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| .s-timeline-gantt { | ||||
| @@ -108,10 +146,9 @@ | ||||
|     } | ||||
|     .s-hover-btns-holder { | ||||
|         $bg: $timelineHeaderColorBg; | ||||
|         $bga: 1; | ||||
|         $l: 5%; | ||||
|         @include user-select(none); | ||||
|         @include background-image(linear-gradient(-90deg, rgba($bg, $bga), rgba($bg, $bga) 70%, rgba($bg, 0) 100%)); | ||||
|         @include background-image(linear-gradient(-90deg, rgba($bg, 1), rgba($bg, 1) 70%, rgba($bg, 0) 100%)); | ||||
|         .s-button { | ||||
|             height: 16px; | ||||
|             line-height: 16px; | ||||
| @@ -129,4 +166,27 @@ | ||||
|             color: $timelineResourceGraphFg; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .s-follow-line { | ||||
|         background: rgba($timeControllerToiLineColor, 0.5); | ||||
|     } | ||||
|  | ||||
|     .s-timeline-gantt { | ||||
|         .s-follow-line { | ||||
|             &:after { | ||||
|                 // Icon | ||||
|                 color: $timeControllerToiLineColor; | ||||
|                 content: $glyph-icon-timer; | ||||
|                 font-family: symbolsfont; | ||||
|                 text-shadow: $shdwItemText; | ||||
|             } | ||||
|             &:before { | ||||
|                 // Blocker | ||||
|                 $bg: $timelineHeaderColorBg; | ||||
|                 $l: 30%; | ||||
|                 @include background-image(linear-gradient(90deg, rgba($bg, 0), rgba($bg, 1) $l, rgba($bg, 1) 100% - $l, rgba($bg, 0))); | ||||
|  | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -75,6 +75,10 @@ | ||||
|                 } | ||||
|             } | ||||
|             &.l-timeline-gantt { | ||||
|                 .abs.l-timeline-gantt-header-w { | ||||
|                     overflow: hidden; | ||||
|                     height: $timelineTopPaneHeaderH; | ||||
|                 } | ||||
|                 .l-swimlanes-holder { | ||||
|                     @include scrollV(scroll); | ||||
|                     bottom: $scrollbarTrackSize; | ||||
|   | ||||
| @@ -24,6 +24,7 @@ $output-bourbon-deprecation-warnings: false; | ||||
|  | ||||
| @import "../../../../commonUI/general/res/sass/constants"; | ||||
| @import "../../../../commonUI/general/res/sass/mixins"; | ||||
| @import "../../../../commonUI/general/res/sass/glyphs"; | ||||
| @import "../../../../commonUI/themes/espresso/res/sass/constants"; | ||||
| @import "../../../../commonUI/themes/espresso/res/sass/mixins"; | ||||
| @import "constants"; | ||||
|   | ||||
| @@ -24,6 +24,7 @@ $output-bourbon-deprecation-warnings: false; | ||||
|  | ||||
| @import "../../../../commonUI/general/res/sass/constants"; | ||||
| @import "../../../../commonUI/general/res/sass/mixins"; | ||||
| @import "../../../../commonUI/general/res/sass/glyphs"; | ||||
| @import "../../../../commonUI/themes/snow/res/sass/constants"; | ||||
| @import "../../../../commonUI/themes/snow/res/sass/mixins"; | ||||
| @import "constants"; | ||||
|   | ||||
| @@ -24,6 +24,7 @@ $output-bourbon-deprecation-warnings: false; | ||||
|  | ||||
| @import "../../../../commonUI/general/res/sass/constants"; | ||||
| @import "../../../../commonUI/general/res/sass/mixins"; | ||||
| @import "../../../../commonUI/general/res/sass/glyphs"; | ||||
| @import "../../../../commonUI/themes/espresso/res/sass/constants"; | ||||
| @import "../../../../commonUI/themes/espresso/res/sass/mixins"; | ||||
| @import "constants"; | ||||
|   | ||||
| @@ -96,109 +96,124 @@ | ||||
|  | ||||
|     <!-- RIGHT PANE: GANTT AND RESOURCE PLOTS --> | ||||
|     <span ng-controller="TimelineZoomController as zoomController" class="abs"> | ||||
|     <mct-split-pane anchor="bottom" | ||||
|  | ||||
|         <span class="toi-control-holder temp" ng-controller="TimelineTOIController as toiController"> | ||||
|             <mct-split-pane anchor="bottom" | ||||
|                     position="pane.y" | ||||
|                     class="abs split-pane-component l-timeline-pane l-pane-r t-pane-v"> | ||||
|  | ||||
|         <!-- TOP PANE GANTT BARS --> | ||||
|         <div class="split-pane-component l-timeline-pane t-pane-h l-pane-top t-timeline-gantt l-timeline-gantt s-timeline-gantt"> | ||||
|             <div class="l-hover-btns-holder s-hover-btns-holder"> | ||||
|                 <a class="s-button icon-arrows-out" | ||||
|                    ng-click="zoomController.fit()" | ||||
|                    ng-show="true" | ||||
|                    title="Zoom to fit"> | ||||
|                 </a> | ||||
|                 <!-- TOP PANE GANTT BARS --> | ||||
|                 <div class="split-pane-component l-timeline-pane t-pane-h l-pane-top t-timeline-gantt l-timeline-gantt s-timeline-gantt"> | ||||
|                     <div class="l-hover-btns-holder s-hover-btns-holder"> | ||||
|                         <a class="s-button icon-timer" | ||||
|                            ng-click="scroll.follow = true" | ||||
|                            ng-show="!toiController.isFollowing() && toiController.isActive()" | ||||
|                            title="Follow time bounds"> | ||||
|                         </a> | ||||
|  | ||||
|                 <a class="s-button icon-magnify-in" | ||||
|                    ng-click="zoomController.zoom(-1)" | ||||
|                    ng-show="true" | ||||
|                    title="Zoom in"> | ||||
|                 </a> | ||||
|                         <a class="s-button icon-arrows-out" | ||||
|                            ng-click="scroll.follow = false; zoomController.fit()" | ||||
|                            ng-show="true" | ||||
|                            title="Zoom to fit"> | ||||
|                         </a> | ||||
|  | ||||
|                 <a class="s-button icon-magnify-out" | ||||
|                    ng-click="zoomController.zoom(1)" | ||||
|                    ng-show="true" | ||||
|                    title="Zoom out"> | ||||
|                 </a> | ||||
|             </div> | ||||
|                         <a class="s-button icon-magnify-in" | ||||
|                            ng-click="scroll.follow = false; zoomController.zoom(-1)" | ||||
|                            ng-show="true" | ||||
|                            title="Zoom in"> | ||||
|                         </a> | ||||
|  | ||||
|             <div style="overflow: hidden; position: absolute; left: 0; top: 0; right: 0; height: 30px;" mct-scroll-x="scroll.x"> | ||||
|                 <mct-include key="'timeline-ticks'" | ||||
|                              parameters="{ | ||||
|                                          fullWidth: zoomController.width(timelineController.end()), | ||||
|                                          start: scroll.x, | ||||
|                                          width: scroll.width, | ||||
|                                          step: zoomController.toPixels(zoomController.zoom()), | ||||
|                                          toMillis: zoomController.toMillis | ||||
|                                          }"> | ||||
|                 </mct-include> | ||||
|             </div> | ||||
|                         <a class="s-button icon-magnify-out" | ||||
|                            ng-click="scroll.follow = false; zoomController.zoom(1)" | ||||
|                            ng-show="true" | ||||
|                            title="Zoom out"> | ||||
|                         </a> | ||||
|                     </div> | ||||
|  | ||||
|             <div class="t-swimlanes-holder l-swimlanes-holder" | ||||
|                  mct-scroll-x="scroll.x" | ||||
|                  mct-scroll-y="scroll.y"> | ||||
|                 <div class="l-width-control" | ||||
|                      ng-style="{ width: zoomController.width(timelineController.end()) + 'px' }"> | ||||
|                     <div class="t-swimlane s-swimlane l-swimlane" | ||||
|                          ng-repeat="swimlane in timelineController.swimlanes()" | ||||
|                          ng-class="{ | ||||
|                                    exceeded: swimlane.exceeded(), | ||||
|                                    selected: selection.selected(swimlane), | ||||
|                                    'drop-into': swimlane.highlight(), | ||||
|                                    'drop-after': swimlane.highlightBottom() | ||||
|                                    }" | ||||
|                          ng-click="selection.select(swimlane)" | ||||
|                          mct-swimlane-drop="swimlane"> | ||||
|                     <div style="overflow: hidden; position: absolute; left: 0; top: 0; right: 0; height: 30px;" mct-scroll-x="scroll.x"> | ||||
|                         <mct-include key="'timeline-ticks'" | ||||
|                                      parameters="{ | ||||
|                                                  fullWidth: zoomController.width(timelineController.end()), | ||||
|                                                  start: scroll.x, | ||||
|                                                  width: scroll.width, | ||||
|                                                  step: zoomController.toPixels(zoomController.zoom()), | ||||
|                                                  toMillis: zoomController.toMillis | ||||
|                                                  }"> | ||||
|                         </mct-include> | ||||
|                     </div> | ||||
|                     <div ng-if="toiController.isActive()" class="l-follow-line s-follow-line" | ||||
|                          ng-style="{ left: toiController.x() - scroll.x + 'px' }"></div> | ||||
|  | ||||
|                         <mct-representation key="'gantt'" | ||||
|                                             mct-object="swimlane.domainObject" | ||||
|                                             parameters="{ | ||||
|                                                         scroll: scroll, | ||||
|                                                         toPixels: zoomController.toPixels | ||||
|                                                         }"> | ||||
|                         </mct-representation> | ||||
|                     <div class="t-swimlanes-holder l-swimlanes-holder" | ||||
|                          mct-scroll-x="scroll.x" | ||||
|                          mct-scroll-y="scroll.y"> | ||||
|                         <div class="l-width-control" | ||||
|                              ng-style="{ width: zoomController.width(timelineController.end()) + 'px' }"> | ||||
|                             <div class="t-swimlane s-swimlane l-swimlane" | ||||
|                                  ng-repeat="swimlane in timelineController.swimlanes()" | ||||
|                                  ng-class="{ | ||||
|                                            exceeded: swimlane.exceeded(), | ||||
|                                            selected: selection.selected(swimlane), | ||||
|                                            'drop-into': swimlane.highlight(), | ||||
|                                            'drop-after': swimlane.highlightBottom() | ||||
|                                            }" | ||||
|                                  ng-click="selection.select(swimlane)" | ||||
|                                  mct-swimlane-drop="swimlane"> | ||||
|  | ||||
|                         <span ng-if="selection.selected(swimlane)"> | ||||
|                             <span ng-repeat="handle in timelineController.handles()" | ||||
|                                   ng-style="handle.style(zoomController)" | ||||
|                                   style="position: absolute; top: 0px; bottom: 0px;" | ||||
|                                   class="handle" | ||||
|                                   ng-class="{ start: $index === 0, mid: $index === 1, end: $index > 1 }" | ||||
|                                   mct-drag-down="handle.begin()" | ||||
|                                   mct-drag="handle.drag(delta[0], zoomController); timelineController.refresh()" | ||||
|                                   mct-drag-up="handle.finish()"> | ||||
|                             </span> | ||||
|                         </span> | ||||
|                                 <mct-representation key="'gantt'" | ||||
|                                                     mct-object="swimlane.domainObject" | ||||
|                                                     parameters="{ | ||||
|                                                                 scroll: scroll, | ||||
|                                                                 toPixels: zoomController.toPixels | ||||
|                                                                 }"> | ||||
|                                 </mct-representation> | ||||
|  | ||||
|                                 <span ng-if="selection.selected(swimlane)"> | ||||
|                                     <span ng-repeat="handle in timelineController.handles()" | ||||
|                                           ng-style="handle.style(zoomController)" | ||||
|                                           style="position: absolute; top: 0px; bottom: 0px;" | ||||
|                                           class="handle" | ||||
|                                           ng-class="{ start: $index === 0, mid: $index === 1, end: $index > 1 }" | ||||
|                                           mct-drag-down="handle.begin()" | ||||
|                                           mct-drag="handle.drag(delta[0], zoomController); timelineController.refresh()" | ||||
|                                           mct-drag-up="handle.finish()"> | ||||
|                                     </span> | ||||
|                                 </span> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|         <!-- HORZ SPLITTER --> | ||||
|         <mct-splitter></mct-splitter> | ||||
|                 <!-- HORZ SPLITTER --> | ||||
|                 <mct-splitter></mct-splitter> | ||||
|  | ||||
|         <!-- BOTTOM PANE RESOURCE GRAPHS AND RIGHT PANE HORIZONTAL SCROLL CONTROL --> | ||||
|         <div class="split-pane-component l-timeline-resource-graph l-timeline-pane t-pane-h l-pane-btm"> | ||||
|             <div class="l-graphs-holder" | ||||
|                  mct-resize="scroll.width = bounds.width"> | ||||
|                 <div class="t-graphs l-graphs"> | ||||
|                     <mct-include key="'timeline-resource-graphs'" | ||||
|                                  parameters="{ | ||||
|                                              origin: zoomController.toMillis(scroll.x), | ||||
|                                              duration: zoomController.toMillis(scroll.width), | ||||
|                                              graphs: timelineController.graphs() | ||||
|                                              }"> | ||||
|                     </mct-include> | ||||
|                 <!-- BOTTOM PANE RESOURCE GRAPHS AND RIGHT PANE HORIZONTAL SCROLL CONTROL --> | ||||
|                 <div class="split-pane-component l-timeline-resource-graph l-timeline-pane t-pane-h l-pane-btm"> | ||||
|                     <div class="l-graphs-holder" | ||||
|                          mct-resize="scroll.width = bounds.width"> | ||||
|                         <div class="t-graphs l-graphs"> | ||||
|                             <mct-include key="'timeline-resource-graphs'" | ||||
|                                          parameters="{ | ||||
|                                                      origin: zoomController.toMillis(scroll.x), | ||||
|                                                      duration: zoomController.toMillis(scroll.width), | ||||
|                                                      graphs: timelineController.graphs() | ||||
|                                                      }"> | ||||
|                             </mct-include> | ||||
|                         </div> | ||||
|                         <div ng-if="toiController.isActive()" class="l-follow-line s-follow-line" | ||||
|                              ng-style="{ left: toiController.x() - scroll.x + 'px' }"></div> | ||||
|                     </div> | ||||
|                     <div mct-scroll-x="scroll.x" | ||||
|                          class="t-pane-r-scroll-h-control l-scroll-control s-scroll-control"> | ||||
|                         <div class="l-width-control" | ||||
|                              ng-style="{ width: zoomController.width(timelineController.end()) + 'px' }"> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div mct-scroll-x="scroll.x" | ||||
|                  class="t-pane-r-scroll-h-control l-scroll-control s-scroll-control"> | ||||
|                 <div class="l-width-control" | ||||
|                      ng-style="{ width: zoomController.width(timelineController.end()) + 'px' }"> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </mct-split-pane> | ||||
|             </mct-split-pane> | ||||
|         </span> | ||||
|  | ||||
|  | ||||
|     </span> | ||||
| </mct-split-pane> | ||||
| </div> | ||||
|   | ||||
| @@ -0,0 +1,111 @@ | ||||
| /***************************************************************************** | ||||
|  * 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 () { | ||||
|  | ||||
|     /** | ||||
|      * Tracks time-of-interest in timelines, updating both scroll state | ||||
|      * (when appropriate) and positioning of the displayed line. | ||||
|      */ | ||||
|     function TimelineTOIController(openmct, timerService, $scope) { | ||||
|         this.openmct = openmct; | ||||
|         this.timerService = timerService; | ||||
|         this.$scope = $scope; | ||||
|  | ||||
|         this.change = this.change.bind(this); | ||||
|         this.bounds = this.bounds.bind(this); | ||||
|         this.destroy = this.destroy.bind(this); | ||||
|  | ||||
|         this.timerService.on('change', this.change); | ||||
|         this.openmct.time.on('bounds', this.bounds); | ||||
|  | ||||
|         this.$scope.$on('$destroy', this.destroy); | ||||
|  | ||||
|         this.$scope.scroll.follow = this.timerService.hasTimer(); | ||||
|         if (this.$scope.zoomController) { | ||||
|             this.bounds(this.openmct.time.bounds()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Handle a `change` event from the timer service; track the | ||||
|      * new timer. | ||||
|      */ | ||||
|     TimelineTOIController.prototype.change = function () { | ||||
|         this.$scope.scroll.follow = | ||||
|             this.$scope.scroll.follow || this.timerService.hasTimer(); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Handle a `bounds` event from the time API; scroll the timeline | ||||
|      * to match the current bounds, if currently in follow mode. | ||||
|      */ | ||||
|     TimelineTOIController.prototype.bounds = function (bounds) { | ||||
|         if (this.isFollowing()) { | ||||
|             var start = this.timerService.convert(bounds.start); | ||||
|             var end = this.timerService.convert(bounds.end); | ||||
|             this.duration = bounds.end - bounds.start; | ||||
|             this.$scope.zoomController.bounds(start, end); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Handle a `$destroy` event from scope; detach all observers. | ||||
|      */ | ||||
|     TimelineTOIController.prototype.destroy = function () { | ||||
|         this.timerService.off('change', this.change); | ||||
|         this.openmct.time.off('bounds', this.bounds); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Get the x position of the time-of-interest line, | ||||
|      * in pixels from the left edge of the timeline area. | ||||
|      */ | ||||
|     TimelineTOIController.prototype.x = function () { | ||||
|         var now = this.timerService.now(); | ||||
|  | ||||
|         if (now === undefined) { | ||||
|             return undefined; | ||||
|         } | ||||
|  | ||||
|         return this.$scope.zoomController.toPixels(this.timerService.now()); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Check if there is an active time-of-interest to be shown. | ||||
|      * @return {boolean} true when active | ||||
|      */ | ||||
|     TimelineTOIController.prototype.isActive = function () { | ||||
|         return this.x() !== undefined; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Check if the timeline should be following time conductor bounds. | ||||
|      * @return {boolean} true when following | ||||
|      */ | ||||
|     TimelineTOIController.prototype.isFollowing = function () { | ||||
|         return !!this.$scope.scroll.follow && this.timerService.now() !== undefined; | ||||
|     }; | ||||
|  | ||||
|     return TimelineTOIController; | ||||
| }); | ||||
| @@ -32,7 +32,8 @@ define( | ||||
|             // Prefer to start with the middle index | ||||
|             var zoomLevels = ZOOM_CONFIGURATION.levels || [1000], | ||||
|                 zoomIndex = Math.floor(zoomLevels.length / 2), | ||||
|                 tickWidth = ZOOM_CONFIGURATION.width || 200; | ||||
|                 tickWidth = ZOOM_CONFIGURATION.width || 200, | ||||
|                 lastWidth = Number.MAX_VALUE; // Don't constrain prematurely | ||||
|  | ||||
|             function toMillis(pixels) { | ||||
|                 return (pixels / tickWidth) * zoomLevels[zoomIndex]; | ||||
| @@ -55,19 +56,29 @@ define( | ||||
|  | ||||
|             function setScroll(x) { | ||||
|                 $window.requestAnimationFrame(function () { | ||||
|                     $scope.scroll.x = x; | ||||
|                     $scope.scroll.x = Math.min( | ||||
|                         Math.max(x, 0), | ||||
|                         lastWidth - $scope.scroll.width | ||||
|                     ); | ||||
|                     $scope.$apply(); | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             function initializeZoomFromTimespan(timespan) { | ||||
|                 var timelineDuration = timespan.getDuration(); | ||||
|             function initializeZoomFromStartEnd(start, end) { | ||||
|                 var duration = end - start; | ||||
|                 zoomIndex = 0; | ||||
|                 while (toMillis($scope.scroll.width) < timelineDuration && | ||||
|                 while (toMillis($scope.scroll.width) < duration && | ||||
|                         zoomIndex < zoomLevels.length - 1) { | ||||
|                     zoomIndex += 1; | ||||
|                 } | ||||
|                 setScroll(toPixels(timespan.getStart())); | ||||
|                 setScroll(toPixels(start)); | ||||
|             } | ||||
|  | ||||
|             function initializeZoomFromTimespan(timespan) { | ||||
|                 return initializeZoomFromStartEnd( | ||||
|                     timespan.getStart(), | ||||
|                     timespan.getEnd() | ||||
|                 ); | ||||
|             } | ||||
|  | ||||
|             function initializeZoom() { | ||||
| @@ -101,6 +112,13 @@ define( | ||||
|                     } | ||||
|                     return zoomLevels[zoomIndex]; | ||||
|                 }, | ||||
|                 /** | ||||
|                  * Adjust the current zoom bounds to fit both the | ||||
|                  * start and the end time provided. | ||||
|                  * @param {number} start the starting timestamp | ||||
|                  * @param {number} end the ending timestamp | ||||
|                  */ | ||||
|                 bounds: initializeZoomFromStartEnd, | ||||
|                 /** | ||||
|                  * Set the zoom level to fit the bounds of the timeline | ||||
|                  * being viewed. | ||||
| @@ -119,14 +137,14 @@ define( | ||||
|                  */ | ||||
|                 toMillis: toMillis, | ||||
|                 /** | ||||
|                  * Get the pixel width necessary to fit the specified | ||||
|                  * timestamp, expressed as an offset in milliseconds from | ||||
|                  * the start of the timeline. | ||||
|                  * Set the maximum timestamp value to be displayed, and get | ||||
|                  * the pixel width necessary to display this value. | ||||
|                  * @param {number} timestamp the time to display | ||||
|                  */ | ||||
|                 width: function (timestamp) { | ||||
|                     var pixels = Math.ceil(toPixels(timestamp * (1 + PADDING))); | ||||
|                     return Math.max($scope.scroll.width, pixels); | ||||
|                     lastWidth = Math.max($scope.scroll.width, pixels); | ||||
|                     return lastWidth; | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|   | ||||
| @@ -0,0 +1,138 @@ | ||||
| /***************************************************************************** | ||||
|  * 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/controllers/TimelineTOIController", | ||||
|     "EventEmitter" | ||||
| ], function (TimelineTOIController, EventEmitter) { | ||||
|     describe("The timeline TOI controller", function () { | ||||
|         var mockmct; | ||||
|         var mockTimerService; | ||||
|         var mockScope; | ||||
|         var controller; | ||||
|  | ||||
|         beforeEach(function () { | ||||
|             mockmct = { time: new EventEmitter() }; | ||||
|             mockmct.time.bounds = jasmine.createSpy('bounds'); | ||||
|             mockTimerService = new EventEmitter(); | ||||
|             mockTimerService.getTimer = jasmine.createSpy('getTimer'); | ||||
|             mockTimerService.hasTimer = jasmine.createSpy('hasTimer'); | ||||
|             mockTimerService.now = jasmine.createSpy('now'); | ||||
|             mockTimerService.convert = jasmine.createSpy('convert'); | ||||
|             mockScope = new EventEmitter(); | ||||
|             mockScope.$on = mockScope.on.bind(mockScope); | ||||
|             mockScope.zoomController = jasmine.createSpyObj('zoom', [ | ||||
|                 'bounds', | ||||
|                 'toPixels' | ||||
|             ]); | ||||
|             mockScope.scroll = { x: 10, width: 1000 }; | ||||
|  | ||||
|             spyOn(mockmct.time, "on").andCallThrough(); | ||||
|             spyOn(mockmct.time, "off").andCallThrough(); | ||||
|             spyOn(mockTimerService, "on").andCallThrough(); | ||||
|             spyOn(mockTimerService, "off").andCallThrough(); | ||||
|  | ||||
|             controller = new TimelineTOIController( | ||||
|                 mockmct, | ||||
|                 mockTimerService, | ||||
|                 mockScope | ||||
|             ); | ||||
|         }); | ||||
|  | ||||
|         it("reports an undefined x position initially", function () { | ||||
|             expect(controller.x()).toBeUndefined(); | ||||
|         }); | ||||
|  | ||||
|         it("listens for bounds changes", function () { | ||||
|             expect(mockmct.time.on) | ||||
|                 .toHaveBeenCalledWith('bounds', controller.bounds); | ||||
|         }); | ||||
|  | ||||
|         it("listens for timer changes", function () { | ||||
|             expect(mockTimerService.on) | ||||
|                 .toHaveBeenCalledWith('change', controller.change); | ||||
|         }); | ||||
|  | ||||
|         it("is not active", function () { | ||||
|             expect(controller.isActive()).toBe(false); | ||||
|         }); | ||||
|  | ||||
|         describe("on $destroy from scope", function () { | ||||
|             beforeEach(function () { | ||||
|                 mockScope.emit("$destroy"); | ||||
|             }); | ||||
|  | ||||
|             it("unregisters listeners", function () { | ||||
|                 expect(mockmct.time.off) | ||||
|                     .toHaveBeenCalledWith('bounds', controller.bounds); | ||||
|                 expect(mockTimerService.off) | ||||
|                     .toHaveBeenCalledWith('change', controller.change); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         describe("when a timer and timestamp present", function () { | ||||
|             var mockTimer; | ||||
|             var testNow; | ||||
|  | ||||
|             beforeEach(function () { | ||||
|                 testNow = 333221; | ||||
|                 mockScope.zoomController.toPixels | ||||
|                     .andCallFake(function (millis) { | ||||
|                         return millis * 2; | ||||
|                     }); | ||||
|                 mockTimerService.emit('change', mockTimer); | ||||
|                 mockTimerService.now.andReturn(testNow); | ||||
|             }); | ||||
|  | ||||
|             it("reports an x value from the zoomController", function () { | ||||
|                 var now = mockTimerService.now(); | ||||
|                 var expected = mockScope.zoomController.toPixels(now); | ||||
|                 expect(controller.x()).toEqual(expected); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         describe("when follow mode is disabled", function () { | ||||
|             beforeEach(function () { | ||||
|                 mockScope.scroll.follow = false; | ||||
|             }); | ||||
|  | ||||
|             it("ignores bounds events", function () { | ||||
|                 mockmct.time.emit('bounds', { start: 0, end: 1000 }); | ||||
|                 expect(mockScope.zoomController.bounds) | ||||
|                     .not.toHaveBeenCalled(); | ||||
|             }); | ||||
|         }); | ||||
|  | ||||
|         describe("when follow mode is enabled", function () { | ||||
|             beforeEach(function () { | ||||
|                 mockScope.scroll.follow = true; | ||||
|                 mockTimerService.now.andReturn(500); | ||||
|             }); | ||||
|  | ||||
|             it("zooms on bounds events", function () { | ||||
|                 mockmct.time.emit('bounds', { start: 0, end: 1000 }); | ||||
|                 expect(mockScope.zoomController.bounds) | ||||
|                     .toHaveBeenCalled(); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
		Reference in New Issue
	
	Block a user