Refactoring based on feedback

Refactoring controller

Migrating functions off controller onto service class

Simplified modes

Adding comments

Removed unnecessary validation

Fixing testing issues
This commit is contained in:
Henry 2016-08-10 18:22:30 -07:00 committed by Andrew Henry
parent 4ae6da0334
commit 4cf6126d35
20 changed files with 576 additions and 661 deletions

View File

@ -30,6 +30,7 @@ define(['../../../platform/features/conductor-v2/conductor/src/timeSystems/Local
this.metadata = {
key: 'test-lad',
mode: 'LAD',
cssclass: 'icon-clock',
label: 'Latest Available Data',
name: 'Latest available data',
@ -38,9 +39,5 @@ define(['../../../platform/features/conductor-v2/conductor/src/timeSystems/Local
}
LADTickSource.prototype = Object.create(LocalClock.prototype);
LADTickSource.prototype.type = function () {
return 'data';
};
return LADTickSource;
});

View File

@ -39,7 +39,7 @@ define([
"key": "conductorService",
"implementation": ConductorService,
"depends": [
"timeConductorService"
"timeConductor"
]
}
],
@ -47,7 +47,7 @@ define([
{
"implementation": ConductorRepresenter,
"depends": [
"timeConductorService"
"timeConductor"
]
}
],
@ -57,7 +57,7 @@ define([
"provides": "telemetryService",
"implementation": ConductorTelemetryDecorator,
"depends": [
"timeConductorService"
"timeConductor"
]
}
]

View File

@ -37,11 +37,11 @@ define(
* @constructor
*/
function ConductorRepresenter(
conductorService,
timeConductor,
scope,
element
) {
this.conductor = conductorService.conductor();
this.conductor = timeConductor;
this.scope = scope;
this.element = element;

View File

@ -36,8 +36,8 @@ define(
* the service which exposes the global time conductor
* @param {TelemetryService} telemetryService the decorated service
*/
function ConductorTelemetryDecorator(conductorService, telemetryService) {
this.conductor = conductorService.conductor();
function ConductorTelemetryDecorator(timeConductor, telemetryService) {
this.conductor = timeConductor;
this.telemetryService = telemetryService;
this.amendRequests = ConductorTelemetryDecorator.prototype.amendRequests.bind(this);

View File

@ -21,17 +21,19 @@
*****************************************************************************/
define([
"./src/ui/TimeConductorService",
"./src/ui/TimeConductorViewService",
"./src/ui/TimeConductorController",
"./src/ui/MCTConductorAxis",
"./src/TimeConductor",
"./src/ui/MctConductorAxis",
"./src/ui/NumberFormat",
"text!./res/templates/time-conductor.html",
"text!./res/templates/mode-selector/mode-selector.html",
"text!./res/templates/mode-selector/mode-menu.html",
'legacyRegistry'
], function (
TimeConductorService,
TimeConductorViewService,
TimeConductorController,
TimeConductor,
MCTConductorAxis,
NumberFormat,
timeConductorTemplate,
@ -45,16 +47,15 @@ define([
"services": [
{
"key": "timeConductor",
"implementation": function (timeConductorService) {
return timeConductorService.conductor();
},
"depends": [
"timeConductorService"
]
"implementation": TimeConductor
},
{
"key": "timeConductorService",
"implementation": TimeConductorService
"key": "timeConductorViewService",
"implementation": TimeConductorViewService,
"depends": [
"timeConductor",
"timeSystems[]"
]
}
],
"controllers": [
@ -63,7 +64,8 @@ define([
"implementation": TimeConductorController,
"depends": [
"$scope",
"timeConductorService",
"timeConductor",
"timeConductorViewService",
"timeSystems[]"
]
}
@ -73,7 +75,7 @@ define([
"key": "mctConductorAxis",
"implementation": MCTConductorAxis,
"depends": [
"timeConductorService",
"timeConductor",
"formatService"
]
}

View File

@ -11,7 +11,7 @@
<!-- Holds inputs and ticks -->
<div class="l-time-conductor-inputs-and-ticks l-row-elem flex-elem no-margin">
<form class="l-time-conductor-inputs-holder"
ng-submit="tcController.updateBoundsFromForm(formModel)">
ng-submit="tcController.updateBoundsFromForm(boundsModel)">
<span class="l-time-range-w start-w">
<span class="l-time-range-input-w start-date">
<span class="title"></span>
@ -20,8 +20,8 @@
format: timeSystemModel.format,
validate: tcController.validation.validateStart
}"
ng-model="formModel"
ng-blur="tcController.updateBoundsFromForm(formModel)"
ng-model="boundsModel"
ng-blur="tcController.updateBoundsFromForm(boundsModel)"
field="'start'"
class="time-range-input">
</mct-control>
@ -34,8 +34,8 @@
format: timeSystemModel.deltaFormat,
validate: tcController.validation.validateStartDelta
}"
ng-model="formModel"
ng-blur="tcController.updateDeltasFromForm(formModel)"
ng-model="boundsModel"
ng-blur="tcController.updateDeltasFromForm(boundsModel)"
field="'startDelta'"
class="hrs-min-input">
</mct-control>
@ -50,8 +50,8 @@
format: timeSystemModel.format,
validate: tcController.validation.validateEnd
}"
ng-model="formModel"
ng-blur="tcController.updateBoundsFromForm(formModel)"
ng-model="boundsModel"
ng-blur="tcController.updateBoundsFromForm(boundsModel)"
ng-disabled="modeModel.selectedKey !== 'fixed'"
field="'end'"
class="time-range-input">
@ -65,8 +65,8 @@
format: timeSystemModel.deltaFormat,
validate: tcController.validation.validateEndDelta
}"
ng-model="formModel"
ng-blur="tcController.updateDeltasFromForm(formModel)"
ng-model="boundsModel"
ng-blur="tcController.updateDeltasFromForm(boundsModel)"
field="'endDelta'"
class="hrs-min-input">
</mct-control>
@ -100,7 +100,7 @@
</mct-control>
<!-- Zoom control -->
<div class="l-time-conductor-zoom-w grows flex-elem l-flex-row">
<span class="time-conductor-zoom-current-range flex-elem flex-fixed holder">Mars Minutes</span>
<span class="time-conductor-zoom-current-range flex-elem flex-fixed holder"></span>
<input class="time-conductor-zoom flex-elem" type="range" />
</div>
</div>

View File

@ -148,9 +148,6 @@ define(['EventEmitter'], function (EventEmitter) {
* Time System
* */
this.emit('timeSystem', this.system);
// Do something with bounds here. Try and convert between
// time systems? Or just set defaults when time system changes?
// eg.
this.bounds(bounds);
} else if (arguments.length === 1) {
throw new Error('Must set bounds when changing time system');

View File

@ -29,7 +29,8 @@ define(['./TickSource'], function (TickSource) {
TickSource.call(this);
this.metadata = {
key: 'real-time',
key: 'local',
mode: 'realtime',
cssclass: 'icon-clock',
label: 'Real-time',
name: 'Real-time Mode',
@ -84,9 +85,5 @@ define(['./TickSource'], function (TickSource) {
}.bind(this);
};
LocalClock.prototype.type = function () {
return 'clock';
};
return LocalClock;
});

View File

@ -43,14 +43,5 @@ define([], function () {
throw new Error('Not implemented');
};
/**
* 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

@ -32,9 +32,7 @@ define(
* labelled 'ticks'. It requires 'start' and 'end' integer values to
* be specified as attributes.
*/
function MCTConductorAxis(conductorService, formatService) {
var conductor = conductorService.conductor();
function MCTConductorAxis(conductor, formatService) {
function link(scope, element, attrs, ngModelController) {
var target = element[0].firstChild,
height = target.offsetHeight,
@ -45,25 +43,24 @@ define(
.attr('width', '100%')
.attr('height', height);
var xAxis = d3.axisTop();
var xScale = d3.scaleUtc();
// draw x axis with labels and move to the bottom of the chart area
var axisElement = vis.append("g")
.attr("transform", "translate(0," + (height - padding) + ")");
function setScale() {
var xScale = undefined;
var width = target.offsetWidth;
var timeSystem = conductor.timeSystem();
var bounds = conductor.bounds();
if (timeSystem.isUTCBased()) {
xScale = d3.scaleUtc();
xScale.domain([new Date(bounds.start), new Date(bounds.end)]);
} else {
xScale = d3.scaleLinear();
xScale.domain([bounds.start, bounds.end]);
}
xScale.range([padding, width - padding * 2]);
xAxis.scale(xScale);
axisElement.call(xAxis);
}
@ -73,6 +70,13 @@ define(
var format = formatService.getFormat(key);
var b = conductor.bounds();
if (timeSystem.isUTCBased()) {
xScale = d3.scaleUtc();
} else {
xScale = d3.scaleLinear();
}
xAxis.scale(xScale);
//Define a custom format function
xAxis.tickFormat(function (tickValue) {
// Normalize date representations to numbers
@ -97,23 +101,16 @@ define(
setScale();
});
setScale();
if (conductor.timeSystem() !== undefined) {
changeTimeSystem(conductor.timeSystem());
setScale();
}
}
return {
// Only show at the element level
restrict: "E",
template: "<div class=\"l-axis-holder\" mct-resize=\"resize()\"></div>",
// ngOptions is terminal, so we need to be higher priority
priority: 1000,
// Link function
link: link
};
}

View File

@ -20,11 +20,7 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'moment'
], function (
moment
) {
define([], function () {
/**
* Formatter for basic numbers. Provides basic support for non-UTC

View File

@ -22,13 +22,11 @@
define(
[
'./modes/FixedMode',
'./modes/FollowMode',
'./TimeConductorValidation'
],
function (FixedMode, FollowMode, TimeConductorValidation) {
function (TimeConductorValidation) {
function TimeConductorController($scope, conductorService, timeSystems) {
function TimeConductorController($scope, timeConductor, conductorViewService, timeSystems) {
var self = this;
@ -39,112 +37,80 @@ define(
self[key] = self[key].bind(self);
});
this.conductorService = conductorService;
this.conductor = conductorService.conductor();
this.conductor.on('bounds', this.setBounds);
this.conductor.on('follow', function (follow){
$scope.followMode = follow;
});
this.$scope = $scope;
this.conductorViewService = conductorViewService;
this.conductor = timeConductor;
this.modes = conductorViewService.availableModes();
this.validation = new TimeConductorValidation(this.conductor);
// Construct the provided time system definitions
this._timeSystems = timeSystems.map(function (timeSystemConstructor){
this.timeSystems = timeSystems.map(function (timeSystemConstructor){
return timeSystemConstructor();
});
this.modes = {
'fixed': {
cssclass: 'icon-calendar',
label: 'Fixed',
name: 'Fixed Timespan Mode',
description: 'Query and explore data that falls between two fixed datetimes.'
}
};
//Set the initial state of the view based on current time conductor
this.initializeScope();
//Only show 'real-time mode' if a clock source is available
if (this.timeSystemsForSourceType('clock').length > 0 ) {
this.modes['realtime'] = {
cssclass: 'icon-clock',
label: 'Real-time',
name: 'Real-time Mode',
tickSourceType: 'clock',
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.conductor.on('bounds', this.setFormFromBounds);
this.conductor.on('follow', function (follow){
$scope.followMode = follow;
});
this.conductor.on('timeSystem', this.changeTimeSystem);
//Only show 'real-time mode' if a clock source is available
if (this.timeSystemsForSourceType('data').length > 0) {
this.modes['latest'] = {
cssclass: 'icon-database',
label: 'LAD',
name: 'LAD Mode',
tickSourceType: 'data',
description: 'Latest Available Data mode monitors real-time streaming data as it comes in. The Time Conductor and displays will only advance when data becomes available.'
};
}
this.validation = new TimeConductorValidation(this.conductor);
this.$scope = $scope;
/*
Set time Conductor bounds in the form
*/
$scope.formModel = this.conductor.bounds();
/*
Represents the various time system options, and the currently
selected time system in the view. Additionally holds the
default format from the selected time system for convenience
of access from the template.
*/
$scope.timeSystemModel = {};
if (this.conductor.timeSystem()) {
$scope.timeSystemModel.selected = this.conductor.timeSystem();
$scope.timeSystemModel.format = this.conductor.timeSystem().formats()[0];
$scope.timeSystemModel.deltaFormat = this.conductor.timeSystem().deltaFormat();
}
/*
Represents the various modes, and the currently
selected mode in the view
*/
$scope.modeModel = {
options: this.modes
};
var mode = conductorService.mode();
if (mode) {
$scope.modeModel.selectedKey = mode.key();
var deltas = mode.deltas && mode.deltas();
if (deltas) {
$scope.formModel.startDelta = deltas.start;
$scope.formModel.endDelta = deltas.end;
}
// Show filtered list of time systems available for the
// current mode
var tickSourceType = this.modes[mode.key()].tickSourceType;
$scope.timeSystemModel.options = this.timeSystemsForSourceType(tickSourceType).map(function (t) {
return t.metadata;
});
} else {
// Default to fixed mode
// If no mode selected, select fixed as the default
if (!this.conductorViewService.mode()) {
this.setMode('fixed');
}
$scope.$watch('modeModel.selectedKey', this.setMode);
$scope.$watch('timeSystem', this.setTimeSystem);
}
/**
* @private
*/
TimeConductorController.prototype.initializeScope = function() {
//Set time Conductor bounds in the form
this.$scope.boundsModel = this.conductor.bounds();
//If conductor has a time system selected already, populate the
//form from it
this.$scope.timeSystemModel = {};
if (this.conductor.timeSystem()) {
this.setFormFromTimeSystem(this.conductor.timeSystem());
}
//Represents the various modes, and the currently selected mode
//in the view
this.$scope.modeModel = {
options: this.conductorViewService.availableModes()
};
var mode = this.conductorViewService.mode();
if (mode) {
//If view already defines a mode (eg. controller is being
// initialized after navigation), then pre-populate form.
this.setFormFromMode(mode);
var deltas = this.conductorViewService.deltas();
if (deltas) {
this.setFormFromDeltas(deltas);
}
}
this.setFormFromBounds(this.conductor.bounds());
// Watch scope for selection of mode or time system by user
this.$scope.$watch('modeModel.selectedKey', this.setMode);
this.$scope.$watch('timeSystem', this.changeTimeSystem);
};
/**
* Called when the bounds change in the time conductor. Synchronizes
* the bounds values in the time conductor with those in the form
* @param bounds
*
* @private
*/
TimeConductorController.prototype.setBounds = function (bounds) {
this.$scope.formModel.start = bounds.start;
this.$scope.formModel.end = bounds.end;
TimeConductorController.prototype.setFormFromBounds = function (bounds) {
this.$scope.boundsModel.start = bounds.start;
this.$scope.boundsModel.end = bounds.end;
if (!this.pendingUpdate) {
this.pendingUpdate = true;
requestAnimationFrame(function () {
@ -154,148 +120,86 @@ define(
}
};
/**
* @private
*/
TimeConductorController.prototype.setFormFromMode = function (mode) {
this.$scope.modeModel.selectedKey = mode;
//Synchronize scope with time system on mode
this.$scope.timeSystemModel.options = this.conductorViewService.availableTimeSystems()
.map(function (t) {
return t.metadata;
});
};
/**
* @private
*/
TimeConductorController.prototype.setFormFromDeltas = function (deltas) {
/*
* If the selected mode defines deltas, set them in the form
*/
if (deltas !== undefined) {
this.$scope.boundsModel.startDelta = deltas.start;
this.$scope.boundsModel.endDelta = deltas.end;
} else {
this.$scope.boundsModel.startDelta = 0;
this.$scope.boundsModel.endDelta = 0;
}
};
/**
* @private
*/
TimeConductorController.prototype.setFormFromTimeSystem = function (timeSystem) {
this.$scope.timeSystemModel.selected = timeSystem;
this.$scope.timeSystemModel.format = timeSystem.formats()[0];
this.$scope.timeSystemModel.deltaFormat = timeSystem.deltaFormat();
};
/**
* Called when form values are changed. Synchronizes the form with
* the time conductor
* @param formModel
*/
TimeConductorController.prototype.updateBoundsFromForm = function (formModel) {
var newBounds = {
start: formModel.start,
end: formModel.end
};
if (this.conductor.validateBounds(newBounds) === true) {
this.conductor.bounds(newBounds);
}
TimeConductorController.prototype.updateBoundsFromForm = function (boundsModel) {
this.conductor.bounds({
start: boundsModel.start,
end: boundsModel.end
});
};
/**
* Called when the delta values in the form change. Validates and
* sets the new deltas on the Mode.
* @param formModel
* @param boundsModel
* @see TimeConductorMode
*/
TimeConductorController.prototype.updateDeltasFromForm = function (formModel) {
var mode = this.conductorService.mode(),
deltas = mode.deltas();
if (deltas !== undefined && this.validation.validateDeltas(formModel.startDelta, formModel.endDelta)) {
TimeConductorController.prototype.updateDeltasFromForm = function (boundsFormModel) {
var deltas = {
start: boundsFormModel.startDelta,
end: boundsFormModel.endDelta
}
if (this.validation.validateStartDelta(deltas.start) && this.validation.validateEndDelta(deltas.end)) {
//Sychronize deltas between form and mode
mode.deltas({start: parseFloat(formModel.startDelta), end: parseFloat(formModel.endDelta)});
this.conductorViewService.deltas(deltas);
}
};
/**
* @private
*/
TimeConductorController.prototype.timeSystemsForSourceType = function(type){
if (!type) {
return this._timeSystems;
} else {
return this._timeSystems.filter(function (timeSystem){
return timeSystem.tickSources().some(function (tickSource){
return tickSource.type() === type;
});
});
}
};
TimeConductorController.prototype.selectTickSource = function (timeSystem, sourceType) {
return timeSystem.tickSources().filter(function (source){
return source.type() === sourceType;
})[0];
}
/**
* Change the selected Time Conductor mode. This will call destroy
* and initialization functions on the relevant modes, setting
* default values for bound and deltas in the form.
*
* @private
* @param newModeKey
* @param oldModeKey
*/
TimeConductorController.prototype.setMode = function (newModeKey, oldModeKey) {
if (newModeKey !== oldModeKey) {
var newMode = undefined;
var timeSystems = [];
var timeSystem = undefined;
var $scope = this.$scope;
var tickSourceType = this.modes[newModeKey].tickSourceType;
this.$scope.modeModel.selectedKey = newModeKey;
if (this.conductorService.mode()) {
this.conductorService.mode().destroy();
}
switch (newModeKey) {
case 'fixed':
timeSystems = this._timeSystems;
timeSystem = timeSystems[0];
newMode = new FixedMode(this.conductor, timeSystem, newModeKey);
break;
case 'realtime':
// Filter time systems to only those with clock tick
// sources
timeSystems = this.timeSystemsForSourceType(tickSourceType);
//Retain currently selected time system if available
timeSystem = timeSystems.filter(function (t) {
return t.metadata.key === $scope.timeSystemModel.selected.metadata.key;
})[0];
//Default to first available time system
timeSystem = timeSystem || timeSystems[0];
newMode = new FollowMode(this.conductor, timeSystem, newModeKey);
newMode.tickSource(this.selectTickSource(timeSystem, tickSourceType));
break;
case 'latest':
// Filter time systems to only those with data tick
// sources
timeSystems = this.timeSystemsForSourceType(tickSourceType);
//Retain currently selected time system if available
timeSystem = timeSystems.filter(function (t) {
return t.metadata.key === $scope.timeSystemModel.selected.metadata.key;
})[0];
//Default to first available time system
timeSystem = timeSystem || timeSystems[0];
newMode = new FollowMode(this.conductor, timeSystem, newModeKey);
newMode.tickSource(this.selectTickSource(timeSystem, tickSourceType));
break;
}
newMode.initialize();
this.setDeltasFromDefaults(newMode.defaults());
this.conductorService.mode(newMode);
//Synchronize scope with time system on mode
this.$scope.timeSystemModel.options = timeSystems.map(function (t) {
return t.metadata;
});
this.setTimeSystem(timeSystem);
}
};
/**
* @private
*/
TimeConductorController.prototype.setDeltasFromDefaults = function (defaults) {
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;
this.conductorViewService.mode(newModeKey);
this.setFormFromMode(newModeKey);
}
};
@ -307,36 +211,23 @@ define(
* @see TimeConductorController#setTimeSystem
*/
TimeConductorController.prototype.selectTimeSystemByKey = function(key){
var selected = this._timeSystems.find(function (timeSystem){
var selected = this.timeSystems.find(function (timeSystem){
return timeSystem.metadata.key === key;
});
this.setTimeSystem(selected);
this.conductor.timeSystem(selected, selected.defaults().bounds);
};
/**
* Sets the selected time system. Will populate form with the default
* bounds and deltas defined in the selected time system.
*
* @private
* @param newTimeSystem
*/
TimeConductorController.prototype.setTimeSystem = function (newTimeSystem) {
if (newTimeSystem && newTimeSystem !== this.$scope.timeSystemModel.selected) {
var mode = this.conductorService.mode();
mode.timeSystem(newTimeSystem);
var defaults = mode.defaults();
this.$scope.timeSystemModel.selected = newTimeSystem;
this.$scope.timeSystemModel.format = newTimeSystem.formats()[0];
this.$scope.timeSystemModel.deltaFormat = newTimeSystem.deltaFormat();
this.setDeltasFromDefaults(defaults);
// If current mode supports ticking, set an appropriate tick
// source from the new time system
if (mode.tickSource) {
var tickSourceType = this.modes[mode.key()].tickSourceType;
mode.tickSource(this.selectTickSource(newTimeSystem, tickSourceType));
}
TimeConductorController.prototype.changeTimeSystem = function (newTimeSystem) {
if (newTimeSystem && (newTimeSystem !== this.$scope.timeSystemModel.selected)) {
this.setFormFromDeltas((newTimeSystem.defaults() || {}).deltas);
this.setFormFromTimeSystem(newTimeSystem);
}
};

View File

@ -0,0 +1,200 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Supports mode-specific time conductor behavior.
*
* @constructor
* @param {TimeConductorMetadata} metadata
*/
function TimeConductorMode(metadata, conductor, timeSystems) {
this.conductor = conductor;
this._metadata = metadata;
this._deltas = undefined;
this._tickSource = undefined;
this._tickSourceUnlisten = undefined;
this._timeSystems = timeSystems;
this._availableTickSources = undefined;
this.changeTimeSystem = this.changeTimeSystem.bind(this);
//Set the time system initially
if (conductor.timeSystem()) {
this.changeTimeSystem(conductor.timeSystem());
}
//Listen for subsequent changes to time system
conductor.on('timeSystem', this.changeTimeSystem);
if (metadata.key === 'fixed') {
//Fixed automatically supports all time systems
this._availableTimeSystems = timeSystems;
} else {
this._availableTimeSystems = timeSystems.filter(function (timeSystem) {
//Only include time systems that have tick sources that
// support the current mode
return timeSystem.tickSources().some(function (tickSource) {
return metadata.key === tickSource.metadata.mode;
});
});
}
}
/**
* Get or set the currently selected time system
* @param timeSystem
* @returns {TimeSystem} the currently selected time system
*/
TimeConductorMode.prototype.changeTimeSystem = function (timeSystem) {
// On time system change, apply default deltas
var defaults = timeSystem.defaults() || {
bounds: {
start: 0,
end: 0
},
deltas: {
start: 0,
end: 0
}
};
this.conductor.bounds(defaults.bounds);
this.deltas(defaults.deltas);
// Tick sources are mode-specific, so restrict tick sources to only those supported by the current mode.
var key = this._metadata.key;
var tickSources = timeSystem.tickSources();
if (tickSources) {
this._availableTickSources = tickSources.filter(function (source){
return source.metadata.mode === key;
});
}
// Set an appropriate tick source from the new time system
this.tickSource(this.availableTickSources(timeSystem)[0]);
};
/**
* @returns {ModeMetadata}
*/
TimeConductorMode.prototype.metadata = function () {
return this._metadata;
};
TimeConductorMode.prototype.availableTimeSystems = function () {
return this._availableTimeSystems;
};
/**
* Tick sources are mode-specific. This returns a filtered list of the tick sources available in the currently selected mode
* @param timeSystem
* @returns {Array.<T>}
*/
TimeConductorMode.prototype.availableTickSources = function (timeSystem) {
return this._availableTickSources;
};
/**
* Get or set tick source. Setting tick source will also start
* listening to it and unlisten from any existing tick source
* @param tickSource
* @returns {TickSource}
*/
TimeConductorMode.prototype.tickSource = function (tickSource) {
if (arguments.length > 0) {
if (this._tickSourceUnlisten) {
this._tickSourceUnlisten();
}
this._tickSource = tickSource;
if (tickSource) {
this._tickSourceUnlisten = tickSource.listen(this.tick.bind(this));
//Now following a tick source
this.conductor.follow(true);
} else {
this.conductor.follow(false);
}
}
return this._tickSource;
};
TimeConductorMode.prototype.destroy = function () {
this.conductor.off('timeSystem', this.changeTimeSystem);
if (this._tickSourceUnlisten) {
this._tickSourceUnlisten();
}
};
/**
* @private
* @param {number} time some value that is valid in the current TimeSystem
*/
TimeConductorMode.prototype.tick = function (time) {
var deltas = this.deltas();
var startTime = time;
var endTime = time;
if (deltas) {
startTime = time - deltas.start;
endTime = time + deltas.end;
}
this.conductor.bounds({
start: startTime,
end: endTime
});
};
/**
* Get or set the current value for the deltas used by this time system.
* On change, the new deltas will be used to calculate and set the
* bounds on the time conductor.
* @param deltas
* @returns {TimeSystemDeltas}
*/
TimeConductorMode.prototype.deltas = function (deltas) {
if (arguments.length !== 0) {
var oldEnd = this.conductor.bounds().end;
if (this._deltas && this._deltas.end !== undefined){
//Calculate the previous raw 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;
};
return TimeConductorMode;
}
);

View File

@ -1,45 +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(
['../TimeConductor'],
function (TimeConductor) {
function TimeConductorService() {
this._conductor = new TimeConductor();
this._mode = undefined;
}
TimeConductorService.prototype.mode = function (mode) {
if (arguments.length === 1) {
this._mode = mode;
}
return this._mode;
};
TimeConductorService.prototype.conductor = function () {
return this._conductor;
};
return TimeConductorService;
}
);

View File

@ -43,6 +43,9 @@ define(
});
}
/**
* Validation methods below are invoked directly from controls in the TimeConductor form
*/
TimeConductorValidation.prototype.validateStart = function (start) {
var bounds = this.conductor.bounds();
return this.conductor.validateBounds({start: start, end: bounds.end}) === true;
@ -61,19 +64,6 @@ define(
return !isNaN(endDelta) && 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 (startDelta, endDelta) {
// 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(startDelta) && this.validateEndDelta(endDelta);
};
return TimeConductorValidation;
}
);

View File

@ -0,0 +1,196 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[
'./TimeConductorMode'
],
function (TimeConductorMode) {
/**
* A class representing the state of the time conductor view. This
* exposes details of the UI that are not represented on the
* TimeConductor API itself such as modes and deltas.
*
* @param conductor
* @param timeSystems
* @constructor
*/
function TimeConductorViewService(conductor, timeSystems) {
this._timeSystems = timeSystems = timeSystems.map(
function (timeSystemConstructor) {
return timeSystemConstructor();
});
this._conductor = conductor;
this._mode = undefined;
/**
* @typedef {object} ModeMetadata
* @property {string} key A unique identifying key for this mode
* @property {string} cssClass The css class for the glyph
* representing this mode
* @property {string} label A short label for this mode
* @property {string} name A longer name for the mode
* @property {string} description A description of the mode
*/
this._availableModes = {
'fixed': {
key: 'fixed',
cssclass: 'icon-calendar',
label: 'Fixed',
name: 'Fixed Timespan Mode',
description: 'Query and explore data that falls between two fixed datetimes.'
}
};
function timeSystemsForMode(sourceType) {
return timeSystems.filter(function (timeSystem){
return timeSystem.tickSources().some(function (tickSource){
return tickSource.metadata.mode === sourceType;
});
});
}
//Only show 'real-time mode' if appropriate time systems available
if (timeSystemsForMode('realtime').length > 0 ) {
this._availableModes['realtime'] = {
key: 'realtime',
cssclass: 'icon-clock',
label: 'Real-time',
name: 'Real-time Mode',
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
};
}
//Only show 'LAD mode' if appropriate time systems available
if (timeSystemsForMode('LAD').length > 0) {
this._availableModes['latest'] = {
key: 'LAD',
cssclass: 'icon-database',
label: 'LAD',
name: 'LAD Mode',
description: 'Latest Available Data mode monitors real-time streaming data as it comes in. The Time Conductor and displays will only advance when data becomes available.'
};
}
}
/**
* Getter/Setter for the Time Conductor Mode. Modes determine the
* behavior of the time conductor, especially with regards to the
* bounds and how they change with time.
*
* In fixed mode, the bounds do not change with time, but can be
* modified by the used
*
* In realtime mode, the bounds change with time. Bounds are not
* directly modifiable by the user, however deltas can be.
*
* In Latest Available Data (LAD) mode, the bounds are updated when
* data is received. As with realtime mode the
*
* @param {string} newModeKey One of 'fixed', 'realtime', or 'LAD'
* @returns {string} the current mode, one of 'fixed', 'realtime',
* or 'LAD'.
*
*/
TimeConductorViewService.prototype.mode = function (newModeKey) {
if (arguments.length === 1) {
var timeSystem = this._conductor.timeSystem();
var modes = this.availableModes();
var modeMetaData = modes[newModeKey];
if (this._mode) {
this._mode.destroy();
}
function contains(timeSystems, timeSystem) {
return timeSystems.find(function (t) {
return t.metadata.key === timeSystem.metadata.key;
}) !== undefined;
}
this._mode = new TimeConductorMode(modeMetaData, this._conductor, this._timeSystems);
if (!timeSystem || !contains(this._mode.availableTimeSystems(), timeSystem)) {
timeSystem = this._mode.availableTimeSystems()[0];
this._conductor.timeSystem(timeSystem, timeSystem.defaults().bounds);
}
}
return this._mode ? this._mode.metadata().key : undefined;
};
/**
* @typedef {object} Delta
* @property {number} start Used to set the start bound of the
* TimeConductor on tick. A positive value that will be subtracted
* from the value provided by a tick source to determine the start
* bound.
* @property {number} end Used to set the end bound of the
* TimeConductor on tick. A positive value that will be added
* from the value provided by a tick source to determine the start
* bound.
*/
/**
* Deltas define the offset from the latest time value provided by
* the current tick source. Deltas are only valid in realtime or LAD
* modes.
*
* Realtime mode:
* - start: A time in ms before now which will be used to
* determine the 'start' bound on tick
* - end: A time in ms after now which will be used to determine
* the 'end' bound on tick
*
* LAD mode:
* - start: A time in ms before the timestamp of the last data
* received which will be used to determine the 'start' bound on
* tick
* - end: A time in ms after the timestamp of the last data received
* which will be used to determine the 'end' bound on tick
* @returns {Delta} current value of the deltas
*/
TimeConductorViewService.prototype.deltas = function () {
//Deltas stored on mode. Use .apply to preserve arguments
return this._mode.deltas.apply(this._mode, arguments)
};
/**
* Availability of modes depends on the time systems and tick
* sources available. For example, Latest Available Data mode will
* not be available if there are no time systems and tick sources
* that support LAD mode.
* @returns {ModeMetadata[]}
*/
TimeConductorViewService.prototype.availableModes = function () {
return this._availableModes;
};
/**
* Availability of time systems depends on the currently selected
* mode. Time systems and tick sources are mode dependent
*/
TimeConductorViewService.prototype.availableTimeSystems = function () {
return this._mode.availableTimeSystems();
};
return TimeConductorViewService;
}
);

View File

@ -1,70 +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(
['./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, timeSystem, key) {
TimeConductorMode.call(this, conductor, timeSystem, key);
}
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.timeSystem = function (timeSystem){
TimeConductorMode.prototype.timeSystem.apply(this, arguments);
if (timeSystem) {
var defaults = this.defaults();
var bounds = {
start: defaults.bounds.start,
end: defaults.bounds.end
};
this.conductor.timeSystem(timeSystem, bounds);
}
return this._timeSystem;
};
return FixedMode;
}
);

View File

@ -1,149 +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(
['./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(conductor, timeSystem, key) {
TimeConductorMode.call(this, conductor, timeSystem, key);
this._deltas = undefined;
this._tickSource = undefined;
this._tickSourceUnlisten = 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
});
};
/**
* Get or set tick source. Setting tick source will also start
* listening to it and unlisten from any existing tick source
* @param tickSource
* @returns {TickSource}
*/
FollowMode.prototype.tickSource = function (tickSource) {
if (tickSource) {
if (this._tickSourceUnlisten) {
this._tickSourceUnlisten();
}
this._tickSource = tickSource;
this._tickSourceUnlisten = tickSource.listen(this.tick.bind(this));
}
return this._tickSource;
};
/**
* 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.timeSystem = function (timeSystem) {
TimeConductorMode.prototype.timeSystem.apply(this, arguments);
if (timeSystem) {
var defaults = this.defaults();
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);
}
}
return this._timeSystem;
};
/**
* 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

@ -1,79 +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 () {
/**
* 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(conductor, timeSystem, key) {
this.conductor = conductor;
this._timeSystem = timeSystem;
this._key = key;
}
/**
* Function is called when mode becomes active (ie. is selected)
*/
TimeConductorMode.prototype.initialize = function () {
this.timeSystem(this._timeSystem);
};
/**
* Get or set the currently selected time system
* @param timeSystem
* @returns {TimeSystem} the currently selected time system
*/
TimeConductorMode.prototype.timeSystem = function (timeSystem) {
if (arguments.length > 0) {
this._timeSystem = timeSystem;
}
return this._timeSystem;
};
TimeConductorMode.prototype.key = function () {
return this._key;
};
TimeConductorMode.prototype.defaults = function () {
var timeSystem = this.timeSystem(),
key = this.key();
if (timeSystem) {
return timeSystem.defaults(key);
}
};
TimeConductorMode.prototype.destroy = function () {
};
return TimeConductorMode;
}
);

View File

@ -62,6 +62,7 @@ requirejs.config({
"text": "bower_components/text/text",
"uuid": "bower_components/node-uuid/uuid",
"zepto": "bower_components/zepto/zepto.min",
"d3": "bower_components/d3/d3.min"
},
"shim": {
@ -82,6 +83,9 @@ requirejs.config({
},
"zepto": {
"exports": "Zepto"
},
"d3": {
"exports": "d3"
}
},