Refactored out modes, time systems, etc.

This commit is contained in:
Henry 2016-07-18 08:23:27 -07:00
parent 2f9fbfef7f
commit 0af49efe06
18 changed files with 817 additions and 288 deletions

View File

@ -22,8 +22,9 @@
define([
"./src/TimeConductor",
"./src/TimeConductorController",
"./src/MCTConductorAxis",
"./src/ui/TimeConductorController",
"./src/ui/MCTConductorAxis",
"./src/timeSystems/UTCTimeSystem",
"text!./res/templates/time-conductor.html",
"text!./res/templates/mode-selector/mode-selector.html",
"text!./res/templates/mode-selector/mode-menu.html",
@ -32,6 +33,7 @@ define([
TimeConductor,
TimeConductorController,
MCTConductorAxis,
UTCTimeSystem,
timeConductorTemplate,
modeSelectorTemplate,
modeMenuTemplate,
@ -52,8 +54,8 @@ define([
"implementation": TimeConductorController,
"depends": [
"$scope",
"$timeout",
"timeConductor"
"timeConductor",
"timeSystems[]"
]
}
],
@ -79,6 +81,12 @@ define([
"key": "mode-menu",
"template": modeMenuTemplate
}
],
"timeSystems": [
{
"implementation": UTCTimeSystem,
"depends": ["$timeout"]
}
]
}
});

View File

@ -22,15 +22,15 @@
<div class="contents">
<div class="pane left menu-items">
<ul>
<li ng-repeat="(selected, option) in ngModel.options"
ng-click="ngModel.selected=selected">
<li ng-repeat="option in ngModel.options"
ng-click="ngModel.selected=option">
<a
ng-mouseover="representation.activeMetadata = option"
ng-mouseover="representation.activeMetadata = option.metadata"
ng-mouseleave="representation.activeMetadata = undefined">
<span class="ui-symbol icon type-icon">
{{option.glyph}}
{{option.metadata.glyph}}
</span>
{{option.name}}
{{option.metadata.name}}
</a>
</li>
</ul>

View File

@ -22,7 +22,7 @@
<span ng-controller="ClickAwayController as modeController">
<div class="s-menu-btn"
ng-click="modeController.toggle()">
<span class="title-label">{{ngModel.options[ngModel.selected].label}}</span>
<span class="title-label">{{ngModel.selected.metadata.label}}</span>
</div>
<div class="menu super-menu mini mode-selector-menu"
ng-show="modeController.isActive()">

View File

@ -8,7 +8,7 @@
}
</style>
<div ng-controller="TimeConductorController as tcController"
class="l-time-conductor l-flex-col {{modeModel.selected}}-mode">
class="l-time-conductor l-flex-col {{modeModel.selected.metadata.key}}-mode {{timeSystemModel.selected.metadata.key}}-time-system">
<!-- Holds inputs and ticks -->
<div class="l-time-conductor-ticks l-row-elem l-flex-row flex-elem no-margin">
<form class="abs l-time-conductor-inputs-holder"
@ -17,39 +17,37 @@
<mct-control key="'datetime-field'"
structure="{
format: 'utc',
validate: tcController.validateStart
validate: tcController.validation.validateStart
}"
ng-model="formModel"
ng-mouseup="tcController.changing['start'] = true"
ng-blur="tcController.changing['start'] = false; tcController.updateBoundsFromForm(formModel)"
ng-mouseup="changing['start'] = true"
ng-blur="changing['start'] = false; tcController.updateBoundsFromForm(formModel)"
field="'start'"
class="time-range-start">
</mct-control>
</span>
<span class="l-time-range-input-w start-delta"
ng-class="{'hide':(modeModel.selected === 'fixed')}">
ng-class="{'hide':(modeModel.selected.metadata.key === 'fixed')}">
<mct-control key="'datetime-field'"
structure="{
format: 'duration',
validate: tcController.validateStartDelta
validate: tcController.validation.validateStartDelta
}"
ng-model="formModel"
ng-mouseup="tcController.changing['startDelta'] = true"
ng-blur="tcController.changing['startDelta'] = false; tcController.updateDeltasFromForm(formModel)"
ng-blur="tcController.updateDeltasFromForm(formModel)"
field="'startDelta'"
class="time-delta-start">
</mct-control>
</span>
<span class="l-time-range-input-w end-delta"
ng-class="{'hide':(modeModel.selected === 'fixed')}">
ng-class="{'hide':(modeModel.selected.metadata.key === 'fixed')}">
<mct-control key="'datetime-field'"
structure="{
format: 'duration',
validate: tcController.validateEndDelta
validate: tcController.validation.validateEndDelta
}"
ng-model="formModel"
ng-mouseup="tcController.changing['endDelta'] = true"
ng-blur="tcController.changing['endDelta'] = false; tcController.updateDeltasFromForm(formModel)"
ng-blur="tcController.updateDeltasFromForm(formModel)"
field="'endDelta'"
class="time-delta-end">
</mct-control>
@ -59,11 +57,11 @@
<mct-control key="'datetime-field'"
structure="{
format: 'utc',
validate: tcController.validateEnd
validate: tcController.validation.validateEnd
}"
ng-model="formModel"
ng-mouseup="tcController.changing['end'] = true"
ng-blur="tcController.changing['end'] = false; tcController.updateBoundsFromForm(formModel)"
ng-mouseup="changing['end'] = true"
ng-blur="changing['end'] = false; tcController.updateBoundsFromForm(formModel)"
field="'end'"
class="time-range-end">
</mct-control>
@ -87,14 +85,11 @@
<mct-control
key="'menu-button'"
class="holder flex-elem menus-up time-system"
ng-model="conductorModel.timeSystem"
structure="{
text: 'UTC',
options: [
{name: 'UTC', key:'utc', glyph:'\u0043'},
{name: 'SCET', key:'scet', glyph:'\u0043'},
{name: 'SCLK', key:'sclk', glyph:'\u0043'}
]}">
text: timeSystemModel.selected.metadata.name,
click: tcController.selectTimeSystem,
options: timeSystemModel.options
}">
</mct-control>
</div>
</div>

View File

@ -118,7 +118,8 @@ define(['EventEmitter'], function (EventEmitter) {
TimeConductor.prototype.bounds = function (newBounds) {
if (arguments.length > 0) {
throwOnError(this.validateBounds(newBounds));
this.boundsVal = newBounds;
//Create a copy to avoid direct mutation of conductor bounds
this.boundsVal = JSON.parse(JSON.stringify(newBounds));
/**
* @event TimeConductor#bounds The start time, end time, or
* both have been updated
@ -126,7 +127,8 @@ define(['EventEmitter'], function (EventEmitter) {
*/
this.emit('bounds', this.boundsVal);
}
return this.boundsVal;
//Return a copy to prevent direct mutation of time conductor bounds.
return JSON.parse(JSON.stringify(this.boundsVal));
};
/**

View File

@ -1,224 +0,0 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
var FIFTEEN_MINUTES = 15 * 60 * 1000;
function TimeConductorController($scope, $timeout, conductor) {
var self = this;
this.$scope = $scope;
this.$timeout = $timeout;
this.conductor = conductor;
this.startDelta = FIFTEEN_MINUTES;
this.endDelta = 0;
this.changing = {
'start': false,
'end': false
};
$scope.formModel = {
startDelta: this.startDelta,
endDelta: this.endDelta
};
conductor.on('bounds', function (bounds) {
if (!self.changing['start']) {
$scope.formModel.start = bounds.start;
}
if (!self.changing['end']) {
$scope.formModel.end = bounds.end;
}
});
conductor.on('follow', function (follow){
$scope.followMode = follow;
});
Object.keys(TimeConductorController.prototype).filter(function (key) {
return typeof TimeConductorController.prototype[key] === 'function';
}).forEach(function (key) {
self[key] = self[key].bind(self);
});
$scope.$watch('modeModel.selected', this.switchMode);
$scope.$watch('timeSystem', function (newTimeSystem, oldTimeSystem) {
$scope.modeModel = {
selected: 'fixed',
options: {
'fixed': {
glyph: '\ue604',
label: 'Fixed',
name: 'Fixed Timespan Mode',
description: 'Query and explore data that falls between two fixed datetimes.'
},
}
}
newTimeSystem.tickSources().forEach(function (tickSource) {
var option = {};
$scope.modeModel.options.push({
});
});
});
$scope.modeModel = {
selected: 'fixed',
options: {
'fixed': {
glyph: '\ue604',
label: 'Fixed',
name: 'Fixed Timespan Mode',
description: 'Query and explore data that falls between two fixed datetimes.'
},
'realtime': {
glyph: '\u0043',
label: 'Real-time',
name: 'Real-time Mode',
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
},
'latest': {
glyph: '\u0044',
label: 'LAD',
name: 'LAD Mode',
description: 'Latest Available Data mode monitors real-time streaming data as it comes in. The Time Conductor and displays will only advance when data becomes available.'
}
}
}
$scope.$on('$destroy', function() {
if (self.mode) {
self.mode();
}
});
self.initialize();
}
TimeConductorController.prototype.initialize = function () {
var now = Math.ceil(Date.now() / 1000) * 1000;
//Set the time conductor to some default
this.conductor.bounds({start: now - FIFTEEN_MINUTES, end: now});
this.$scope.modeModel.selected = 'fixed';
this.conductor.follow(false);
};
TimeConductorController.prototype.validateStart = function (start) {
var bounds = this.conductor.bounds();
return this.conductor.validateBounds({start: start, end: bounds.end}) === true;
};
TimeConductorController.prototype.validateEnd = function (end) {
var bounds = this.conductor.bounds();
return this.conductor.validateBounds({start: bounds.start, end: end}) === true;
};
TimeConductorController.prototype.updateBoundsFromForm = function (formModel) {
var newBounds = {start: formModel.start, end: formModel.end};
if (this.conductor.validateBounds(newBounds) === true) {
this.conductor.bounds(newBounds);
}
};
TimeConductorController.prototype.validateStartDelta = function (startDelta) {
return startDelta > 0;
};
TimeConductorController.prototype.validateEndDelta = function (endDelta) {
return endDelta >= 0;
};
TimeConductorController.prototype.validateDeltas = function (formModel) {
// Validate that start Delta is some non-zero value, and that end
// delta is zero or positive (ie. 'now' or some time in the future).
return this.validateStartDelta(formModel.startDelta) && this.validateEndDelta(formModel.endDelta);
};
TimeConductorController.prototype.updateDeltasFromForm = function (formModel) {
if (this.validateDeltas(formModel)) {
//Calculate the previous 'true' end value (without delta)
var oldEnd = this.conductor.bounds().end - this.endDelta || 0;
this.startDelta = formModel.startDelta;
this.endDelta = formModel.endDelta;
var newBounds = {
start: oldEnd - this.startDelta,
end: oldEnd + this.endDelta
};
this.conductor.bounds(newBounds);
}
};
TimeConductorController.prototype.switchMode = function (newMode) {
if (this.mode) {
this.mode();
}
this.mode = TimeConductorController.modes[newMode].call(this);
};
TimeConductorController.modes = {
'fixed': function () {
this.conductor.follow(false);
},
'realtime': function () {
var tickInterval = 1000;
var conductor = this.conductor;
var $timeout = this.$timeout;
var self = this;
conductor.follow(true);
setBoundsToNow(self.startDelta, self.endDelta);
var timeoutPromise = $timeout(tick, tickInterval);
function setBoundsToNow(startDelta, endDelta) {
var now = Math.ceil(Date.now() / 1000) * 1000;
conductor.bounds({start: now - startDelta, end: now + endDelta});
}
function tick() {
setBoundsToNow(self.startDelta, self.endDelta);
timeoutPromise = $timeout(tick, tickInterval)
}
return function destroy() {
$timeout.cancel(timeoutPromise);
}
},
'latest': function () {
//Don't know what to do here yet...
this.conductor.follow(true);
}
};
return TimeConductorController;
}
);

View File

@ -25,7 +25,7 @@ define(['./TickSource'], function (TickSource) {
* @implements TickSource
* @constructor
*/
function LocalClock ($timeout) {
function LocalClock ($timeout, period) {
TickSource.call(this);
this.metadata = {
@ -36,7 +36,7 @@ define(['./TickSource'], function (TickSource) {
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
};
this.interval = interval;
this.period = period;
this.$timeout = $timeout;
this.timeoutHandle = undefined;
}
@ -44,7 +44,7 @@ define(['./TickSource'], function (TickSource) {
LocalClock.prototype = Object.create(TickSource.prototype);
LocalClock.prototype.start = function () {
this.timeoutHandle = this.$timeout(this.tick.bind(this), this.interval);
this.timeoutHandle = this.$timeout(this.tick.bind(this), this.period);
};
LocalClock.prototype.stop = function () {
@ -58,6 +58,7 @@ define(['./TickSource'], function (TickSource) {
this.listeners.forEach(function (listener){
listener(now);
});
this.timeoutHandle = this.$timeout(this.tick.bind(this), this.period);
};
/**
@ -68,19 +69,24 @@ define(['./TickSource'], function (TickSource) {
* @returns {function} a function for deregistering the provided listener
*/
LocalClock.prototype.listen = function (listener) {
this.listeners.push(listener);
var listeners = this.listeners;
listeners.push(listener);
if (this.listeners.length === 1){
if (listeners.length === 1){
this.start();
}
return function () {
this.listeners.splice(listeners.indexOf(listener));
if (this.listeners.length === 0) {
listeners.splice(listeners.indexOf(listener));
if (listeners.length === 0) {
this.stop();
}
}.bind(this);
};
return TimeSystem;
LocalClock.prototype.type = function () {
return 'clock';
};
return LocalClock;
});

View File

@ -41,5 +41,14 @@ define([], function () {
throw new Error('Not implemented');
};
return TimeSystem;
/**
* What does this source tick on? A clock, or data availability.
* Information is required to support time conductor modes.
* @returns {string} type One of 'clock' or 'data'
*/
TickSource.prototype.type = function () {
throw new Error('Not implemented');
}
return TickSource;
});

View File

@ -26,7 +26,16 @@ define([], function () {
* @constructor
*/
function TimeSystem () {
/**
* @typedef TimeSystemMetadata
* @property {string} key
* @property {string} name
* @property {string} description
*
* @type {TimeSystemMetadata}
*/
this.metadata = undefined;
this._tickSources = [];
}
TimeSystem.prototype.formats = function () {
@ -37,7 +46,12 @@ define([], function () {
throw new Error('Not implemented');
};
TimeSystem.prototype.defaultBounds = function () {
/**
*
* @returns {TimeSystemDefault[]} At least one set of default values for
* this time system.
*/
TimeSystem.prototype.defaults = function () {
throw new Error('Not implemented');
};

View File

@ -25,17 +25,25 @@ define([
'./LocalClock',
'../../../../commonUI/formats/src/UTCTimeFormat'
], function (TimeSystem, LocalClock, UTCTimeFormat) {
var FIFTEEN_MINUTES = 15 * 60 * 1000;
var FIFTEEN_MINUTES = 15 * 60 * 1000,
DEFAULT_PERIOD = 1000;
/**
* This time system supports UTC dates and provides a ticking clock source.
* @implements TimeSystem
* @constructor
*/
function UTCTimeSystem ($timeout) {
TimeSystem.call(this);
this.metadata = {
'key': 'utc',
'name': 'UTC',
'glyph': '\u0043'
};
this._formats = [new UTCTimeFormat()];
this._tickSources = [new LocalClock($timeout)];
this._tickSources = [new LocalClock($timeout, DEFAULT_PERIOD)];
}
UTCTimeSystem.prototype = Object.create(TimeSystem.prototype);
@ -48,9 +56,16 @@ define([
return this._tickSources;
};
UTCTimeSystem.prototype.defaultBounds = function () {
UTCTimeSystem.prototype.defaults = function () {
var now = Math.ceil(Date.now() / 1000) * 1000;
return {start: now - FIFTEEN_MINUTES, end: now};
return [
{
key: 'utc-default',
name: 'UTC time system defaults',
deltas: {start: FIFTEEN_MINUTES, end: 0},
bounds: {start: now - FIFTEEN_MINUTES, end: now}
}
];
};
return UTCTimeSystem;

View File

@ -28,14 +28,9 @@ define(
function ($, d3) {
/**
* The mct-control will dynamically include the control
* for a form element based on a symbolic key. Individual
* controls are defined under the extension category
* `controls`; this allows plug-ins to introduce new form
* control types while still making use of the form
* generator to ensure an overall consistent form style.
* @constructor
* @memberof platform/forms
* The mct-conductor-axis renders a horizontal axis with regular
* labelled 'ticks'. It requires 'start' and 'end' integer values to
* be specified as attributes.
*/
function MCTConductorAxis(conductor) {
@ -66,6 +61,7 @@ define(
setScale(conductor.bounds().start, conductor.bounds().end);
};
//On conductor bounds changes, redraw ticks
conductor.on('bounds', function (bounds) {
setScale(bounds.start, bounds.end);
});
@ -84,14 +80,7 @@ define(
priority: 1000,
// Link function
link: link,
// Pass through Angular's normal input field attributes
scope: {
// Used to choose which form control to use
start: "=",
end: "="
}
link: link
};
}

View File

@ -0,0 +1,218 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
'./modes/FixedMode',
'./modes/RealtimeMode',
'./modes/LADMode',
'./TimeConductorValidation'
],
function (FixedMode, RealtimeMode, LADMode, TimeConductorValidation) {
function TimeConductorController($scope, conductor, timeSystems) {
var self = this;
//Bind all class functions to 'this'
Object.keys(TimeConductorController.prototype).filter(function (key) {
return typeof TimeConductorController.prototype[key] === 'function';
}).forEach(function (key) {
self[key] = self[key].bind(self);
});
this.conductor = conductor;
// Construct the provided time system definitions
this.timeSystems = timeSystems.map(function (timeSystemConstructor){
return timeSystemConstructor();
});
// Populate a list of modes supported by the time conductor
this.modes = [
new FixedMode(this.conductor, this.timeSystems),
new RealtimeMode(this.conductor, this.timeSystems),
new LADMode(this.conductor, this.timeSystems)
];
this.validation = new TimeConductorValidation(conductor);
this.$scope = $scope;
this.initializeScope($scope);
conductor.on('bounds', this.setBounds);
conductor.on('follow', function (follow){
$scope.followMode = follow;
});
//Set the time conductor mode to the first one in the list,
// effectively initializing the time conductor
this.setMode(this.modes[0]);
}
/**
* @private
*/
TimeConductorController.prototype.initializeScope = function ($scope) {
$scope.timeSystemModel = {
selected: undefined,
options: []
};
$scope.modeModel = {
selected: undefined,
options: this.modes
};
$scope.formModel = {
start: 0,
end: 0
};
$scope.changing = {
'start': false,
'end': false
};
$scope.$watch('modeModel.selected', this.setMode);
$scope.$watch('timeSystem', this.setTimeSystem);
$scope.$on('$destroy', function() {
var mode = $scope.modeModel.selected;
if (mode && mode.destroy) {
mode.destroy();
}
});
};
/**
* Called when the bounds change in the time conductor. Synchronizes
* the bounds values in the time conductor with those in the form
* @param bounds
*/
TimeConductorController.prototype.setBounds = function (bounds) {
if (!this.$scope.changing['start']) {
this.$scope.formModel.start = bounds.start;
}
if (!this.$scope.changing['end']) {
this.$scope.formModel.end = bounds.end;
}
};
/**
* Called when form values are changed. Synchronizes the form with
* the time conductor
* @param formModel
*/
TimeConductorController.prototype.updateBoundsFromForm = function (formModel) {
var newBounds = {start: formModel.start, end: formModel.end};
if (this.conductor.validateBounds(newBounds) === true) {
this.conductor.bounds(newBounds);
}
};
/**
* Called when the delta values in the form change. Validates and
* sets the new deltas on the Mode.
* @param formModel
* @see TimeConductorMode
*/
TimeConductorController.prototype.updateDeltasFromForm = function (formModel) {
var mode = this.$scope.modeModel.selected,
deltas = mode.deltas();
if (deltas !== undefined && this.validation.validateDeltas(formModel)) {
//Sychronize deltas between form and mode
mode.deltas({start: formModel.startDelta, end: formModel.endDelta});
}
};
/**
* Change the selected Time Conductor mode. This will call destroy
* and initialization functions on the relevant modes, setting
* default values for bound and deltas in the form.
* @param newMode
* @param oldMode
*/
TimeConductorController.prototype.setMode = function (newMode, oldMode) {
if (newMode !== oldMode) {
if (oldMode && oldMode.destroy) {
oldMode.destroy();
}
newMode.initialize();
this.$scope.modeModel.selected = newMode;
//Synchronize scope with time system on mode
this.$scope.timeSystemModel.options = newMode.timeSystems().map(function (timeSystem) {
return timeSystem.metadata;
});
this.$scope.timeSystemModel.selected = newMode.selectedTimeSystem();
this.setDefaultsFromTimeSystem(newMode.selectedTimeSystem());
}
};
/**
* @private
*/
TimeConductorController.prototype.setDefaultsFromTimeSystem = function (timeSystem) {
var defaults = timeSystem.defaults()[0];
var deltas = defaults.deltas;
/*
* If the selected mode defines deltas, set them in the form
*/
if (deltas !== undefined) {
this.$scope.formModel.startDelta = deltas.start;
this.$scope.formModel.endDelta = deltas.end;
} else {
this.$scope.formModel.startDelta = 0;
this.$scope.formModel.endDelta = 0;
}
};
/**
* Allows time system to be changed by key. This supports selection
* from the menu. Resolves a TimeSystem object and then invokes
* TimeConductorController#setTimeSystem
* @param key
* @see TimeConductorController#setTimeSystem
*/
TimeConductorController.prototype.selectTimeSystem = function(key){
var selected = this.timeSystems.find(function (timeSystem){
return timeSystem.metadata.key === key;
});
this.setTimeSystem(selected);
};
/**
* Sets the selected time system. Will populate form with the default
* bounds and deltas defined in the selected time system.
* @param newTimeSystem
*/
TimeConductorController.prototype.setTimeSystem = function (newTimeSystem) {
if (newTimeSystem && newTimeSystem !== this.$scope.timeSystemModel.selected) {
this.$scope.timeSystemModel.selected = newTimeSystem;
var mode = this.$scope.modeModel.selected;
mode.selectedTimeSystem(newTimeSystem);
this.setDefaultsFromTimeSystem(newTimeSystem);
}
};
return TimeConductorController;
}
);

View File

@ -0,0 +1,79 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Form validation for the TimeConductorController.
* @param conductor
* @constructor
*/
function TimeConductorValidation(conductor) {
var self = this;
this.conductor = conductor
/*
* Bind all class functions to 'this'
*/
Object.keys(TimeConductorValidation.prototype).filter(function (key) {
return typeof TimeConductorValidation.prototype[key] === 'function';
}).forEach(function (key) {
self[key] = self[key].bind(self);
});
}
TimeConductorValidation.prototype.validateStart = function (start) {
var bounds = this.conductor.bounds();
return this.conductor.validateBounds({start: start, end: bounds.end}) === true;
};
TimeConductorValidation.prototype.validateEnd = function (end) {
var bounds = this.conductor.bounds();
return this.conductor.validateBounds({start: bounds.start, end: end}) === true;
};
TimeConductorValidation.prototype.validateStartDelta = function (startDelta) {
return startDelta > 0;
};
TimeConductorValidation.prototype.validateEndDelta = function (endDelta) {
return endDelta >= 0;
};
/**
* Validates the delta values in the form model. Deltas are offsets
* from a fixed point in time, and are used in follow modes as the
* primary determinant of conductor bounds.
* @param formModel
* @returns {*}
*/
TimeConductorValidation.prototype.validateDeltas = function (formModel) {
// Validate that start Delta is some non-zero value, and that end
// delta is zero or positive (ie. 'now' or some time in the future).
return this.validateStartDelta(formModel.startDelta) && this.validateEndDelta(formModel.endDelta);
};
return TimeConductorValidation;
}
);

View File

@ -0,0 +1,78 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./TimeConductorMode'],
function (TimeConductorMode) {
/**
* Handles time conductor behavior when it is in 'fixed' mode. In
* fixed mode, the time conductor is bound by two dates and does not
* progress.
* @param conductor
* @param timeSystems
* @constructor
*/
function FixedMode(conductor, timeSystems) {
var metadata = {
key: 'fixed',
glyph: '\ue604',
label: 'Fixed',
name: 'Fixed Timespan Mode',
description: 'Query and explore data that falls between two fixed datetimes.'
};
TimeConductorMode.call(this, metadata, conductor, timeSystems);
}
FixedMode.prototype = Object.create(TimeConductorMode.prototype);
FixedMode.prototype.initialize = function () {
TimeConductorMode.prototype.initialize.apply(this);
this.conductor.follow(false);
};
/**
* Defines behavior to occur when selected time system changes. In
* this case, sets default bounds on the time conductor.
* @param timeSystem
* @returns {*}
*/
FixedMode.prototype.selectedTimeSystem = function (timeSystem){
TimeConductorMode.prototype.selectedTimeSystem.apply(this, arguments);
if (timeSystem) {
var defaults = timeSystem.defaults()[0];
var bounds = {
start: defaults.bounds.start,
end: defaults.bounds.end
};
this.conductor.timeSystem(timeSystem, bounds);
}
return this._selectedTimeSystem;
};
return FixedMode;
}
);

View File

@ -0,0 +1,147 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./TimeConductorMode'],
function (TimeConductorMode) {
/**
* A parent class for Realtime and LAD modes, which both advance the
* time conductor bounds over time. The event that advances the time
* conductor is abstracted to a TickSource. Unlike FixedMode, the
* two 'follow' modes define 'deltas' which are offsets from a fixed
* end point. Thus, in follow mode, the end time of the conductor is
* the mode relevant, with both offsets defined relative to it.
* @constructor
*/
function FollowMode(metadata, conductor, timeSystems) {
TimeConductorMode.call(this, metadata, conductor, timeSystems);
this._deltas = undefined;
}
FollowMode.prototype = Object.create(TimeConductorMode.prototype);
FollowMode.prototype.initialize = function () {
TimeConductorMode.prototype.initialize.apply(this);
this.conductor.follow(true);
};
/**
* @private
* @param time
*/
FollowMode.prototype.tick = function (time) {
var deltas = this.deltas();
this.conductor.bounds({
start: time - deltas.start,
end: time + deltas.end
});
};
/**
* @private
* @param tickSource
*/
FollowMode.prototype.listenToTickSource = function () {
if (this._selectedTimeSystem &&
this._timeSystems[0]) {
var tickSource = this._timeSystems[0].tickSources()[0];
if (tickSource) {
this.tickSourceUnlisten = tickSource.listen(this.tick.bind(this));
}
}
};
/**
* On time system change, default the bounds values in the time
* conductor, using the deltas associated with this mode.
* @param timeSystem
* @returns {TimeSystem}
*/
FollowMode.prototype.selectedTimeSystem = function (timeSystem) {
TimeConductorMode.prototype.selectedTimeSystem.apply(this, arguments);
if (timeSystem) {
var defaults = timeSystem.defaults()[0];
if (arguments.length > 0) {
var bounds = {
start: defaults.bounds.start,
end: defaults.bounds.end
};
if (defaults.deltas) {
bounds.start = bounds.end - defaults.deltas.start;
bounds.end = bounds.end + defaults.deltas.end;
this._deltas = JSON.parse(JSON.stringify(defaults.deltas));
}
this.conductor.timeSystem(timeSystem, bounds);
this.listenToTickSource();
}
}
return this._selectedTimeSystem;
};
/**
* Get or set the current value for the deltas used by this time system.
* On change, the new deltas will be used to calculate and set the
* bounds on the time conductor.
* @param deltas
* @returns {TimeSystemDeltas}
*/
FollowMode.prototype.deltas = function (deltas) {
if (arguments.length !== 0) {
var oldEnd = this.conductor.bounds().end;
if (this._deltas && this._deltas.end){
//Calculate the previous 'true' end value (without delta)
oldEnd = oldEnd - this._deltas.end;
}
this._deltas = deltas;
var newBounds = {
start: oldEnd - this._deltas.start,
end: oldEnd + this._deltas.end
};
this.conductor.bounds(newBounds);
}
return this._deltas;
};
/**
* Stop listening to tick sources
*/
FollowMode.prototype.destroy = function () {
if (this.tickSourceUnlisten) {
this.tickSourceUnlisten();
}
};
return FollowMode;
}
);

View File

@ -0,0 +1,56 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./FollowMode'],
function (FollowMode) {
/**
* Supports the 'Latest Available Data' mode of the time conductor.
* This is a special case of FollowMode that advances on 'data' type
* tick sources.
* @param conductor
* @param timeSystems
* @constructor
*/
function LADMode(conductor, timeSystems) {
var metadata = {
key: 'latest',
glyph: '\u0044',
label: 'LAD',
name: 'LAD Mode',
description: 'Latest Available Data mode monitors real-time streaming data as it comes in. The Time Conductor and displays will only advance when data becomes available.'
};
var filteredTimeSystems = timeSystems.filter(function (timeSystem){
return timeSystem.tickSources().some(function (tickSource){
return tickSource.type() === 'data';
});
});
FollowMode.call(this, metadata, conductor, filteredTimeSystems);
}
LADMode.prototype = Object.create(FollowMode.prototype);
return LADMode;
}
);

View File

@ -0,0 +1,55 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./FollowMode'],
function (FollowMode) {
/**
* Class representing the 'realtime' mode of the time conductor.
* This is a special case of FollowMode that only supports 'clock'
* type tick sources.
* @param conductor
* @param timeSystems
* @constructor
*/
function RealtimeMode(conductor, timeSystems) {
var metadata = {
key: 'realtime',
glyph: '\u0043',
label: 'Real-time',
name: 'Real-time Mode',
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
};
var filteredTimeSystems = timeSystems.filter(function (timeSystem){
return timeSystem.tickSources().some(function (tickSource){
return tickSource.type() === 'clock';
});
});
FollowMode.call(this, metadata, conductor, filteredTimeSystems);
}
RealtimeMode.prototype = Object.create(FollowMode.prototype);
return RealtimeMode;
}
);

View File

@ -0,0 +1,82 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Supports mode-specific time conductor behavior. This class
* defines a parent with default behavior that specific modes are
* expected to override.
*
* @interface
* @constructor
* @param {TimeConductorMetadata} metadata
*/
function TimeConductorMode(metadata, conductor, timeSystems) {
this.metadata = metadata;
this.conductor = conductor;
this._timeSystems = timeSystems;
}
/**
* Function is called when mode becomes active (ie. is selected)
*/
TimeConductorMode.prototype.initialize = function () {
this.selectedTimeSystem(this._timeSystems[0]);
};
/**
* Return the time systems supported by this mode. Modes are
* expected to determine which time systems they will support by
* filtering a provided list of all time systems.
*
* @returns {TimeSystem[]} The time systems supported by this mode
*/
TimeConductorMode.prototype.timeSystems = function () {
return this._timeSystems;
};
/**
* Get or set the currently selected time system
* @param timeSystem
* @returns {TimeSystem} the currently selected time system
*/
TimeConductorMode.prototype.selectedTimeSystem = function (timeSystem) {
if (arguments.length > 0) {
if (this._timeSystems.indexOf(timeSystem) === -1){
throw new Error("Unsupported time system");
} else {
this._selectedTimeSystem = timeSystem;
}
}
return this._selectedTimeSystem;
};
TimeConductorMode.prototype.destroy = function () {
};
return TimeConductorMode;
}
);