Compare commits
11 Commits
master
...
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