Compare commits

...

121 Commits

Author SHA1 Message Date
8b2047ca32 Fixed issue with setting deltas 2016-10-11 13:25:24 -07:00
02c543fddc Fixed zoom in real-time mode 2016-10-11 12:45:18 -07:00
b384e84872 Merge branch 'open933' into open1182 2016-10-07 15:02:41 -07:00
07140b179f Merge branch 'master' into open933 2016-10-07 14:54:18 -07:00
3e9c0eb7a5 Merged from master to resolve build issues 2016-10-05 12:25:04 -07:00
f1d2072bb9 Added license information 2016-10-05 11:17:22 -07:00
5babf7274d [Time Conductor] Tweaked the break points for zoom level indicator 2016-09-29 11:21:11 -07:00
2db4aa6235 [Time Conductor] Added zoom level label 2016-09-23 13:06:22 -07:00
3c95c095f1 [Time Conductor] Refactored out use of angular event bus in favor of making TimeConductorViewService an event emitter. 2016-09-22 17:22:25 -07:00
49ee5cb74b [Time Conductor] Destroy listeners in ConductorAxisController 2016-09-22 15:18:48 -07:00
98122cc730 [Time Conductor] Added Zoom 2016-09-22 15:08:19 -07:00
7fcafb6b58 [Time Conductor] Added pan to Time Conductor 2016-09-22 15:08:19 -07:00
d77922d66c Revert "[proxyUrl] pass URL parameters to proxied URL" 2016-09-22 15:07:29 -07:00
2e81550c86 Revert "[Build] Check dependencies for vulnerabilities" 2016-09-22 15:07:29 -07:00
8eb7585653 [README] Warn about root installation issues
Mitigates #1151.
2016-09-22 15:07:28 -07:00
904d56a089 [Time Conductor] #933 Clean up time conductor listeners on scope destruction 2016-09-22 13:32:43 -07:00
11e06039ec Merge branch 'master' into open933 2016-09-13 13:25:37 -07:00
a1331b7bb3 Merge branch 'master' into open933 2016-09-12 16:54:45 -07:00
d1960b2f46 [Time Conductor] Resolved merge conflicts 2016-09-12 14:54:16 -07:00
9a06325533 Merge branch 'master' into open933 2016-09-12 14:39:24 -07:00
e639e056ba [Time Conductor] Fixing bugs found in smoke testing. Fixes #933 2016-09-09 18:51:47 -07:00
fbab890081 [Time Conductor] Switched conductor to mct-include rather than mct-representation to avoid warnings 2016-09-07 14:29:21 -07:00
d37dd52ee1 Merge branch 'master' into open933 2016-09-06 16:43:17 -07:00
7af5875dd5 [Time Conductor] #933 Fixed code style errors 2016-09-06 10:05:10 -07:00
c6eaa3d528 [Time Conductor] Adding tests and fixing failing ones. #933 2016-09-05 19:32:19 -07:00
4cf6126d35 Refactoring based on feedback
Refactoring controller

Migrating functions off controller onto service class

Simplified modes

Adding comments

Removed unnecessary validation

Fixing testing issues
2016-08-23 18:10:05 +01:00
4ae6da0334 [Frontend] Data viz in Time Conductor smaller
Fixes #933
Reduced height of data viz bar in Time Conductor
v2;
2016-08-09 17:47:31 -07:00
ae39343b76 [Frontend] Fix for bad fix
Fixes #1112
Put overflow: hidden back at
outer wrapper level (now on .t-object.primary-pane
) which doens't clip the Inspector expand/collapse;
did better unit testing;
2016-08-08 12:14:20 -07:00
62ee7e569b [Frontend] Fix for collapse Inspector button
Fixes #1112
Moved min-width and overflow: hidden
to TC-specific elements; removed
overflow: hidden from .primary-pane
2016-08-08 11:21:01 -07:00
7557a86208 Merge branch 'master' into open933 2016-08-05 16:37:36 -07:00
46e644e6dc Use key to retrieve default 2016-08-05 14:44:18 -07:00
af7954c5a1 Trigger digests when bounds are set 2016-08-05 11:24:51 -07:00
0e0ad64830 Fixed issue with wrong deltas being applied 2016-08-04 11:00:56 -07:00
f3fd386e3b Retain time system on mode change 2016-08-03 21:03:09 -07:00
25b9f371e2 Fixed loss of time system options on navigation 2016-08-03 20:16:03 -07:00
6b482d487b Merged mode-specific defaults, with some refactoring 2016-08-03 19:57:03 -07:00
f96f78ff79 Select appropriate tick source based on mode 2016-08-03 19:34:31 -07:00
579233ade9 Fixed delta format issue on navigation 2016-08-03 18:30:01 -07:00
9a72c96ea4 [TCv2] different defaults by mode 2016-08-03 18:06:39 -07:00
f4e1879a2d stop listening to tick source on time system change 2016-08-03 17:43:07 -07:00
f844495cc1 Support deltaFormat on timeSystems 2016-08-03 17:40:37 -07:00
900752208f [TCv2] get conductor without service 2016-08-03 13:11:22 -07:00
6501e2eb5f Added isUTCBased to TimeSystem interface 2016-08-03 13:09:54 -07:00
b9c41107c1 Time Conductor state retained on navigation 2016-08-02 22:18:44 -07:00
34c62ba405 Time Conductor in edit mode 2016-08-02 15:16:35 -07:00
1eea5ce480 merged from master 2016-08-01 20:29:50 -07:00
4cd579d274 Pass numerical value to format functions 2016-08-01 20:16:46 -07:00
11738286df [Frontend] Styling for unsynced elements
Fixes #933
2016-08-01 18:56:33 -07:00
ca5206d4a0 [Frontend] Fixing issues with theme coloring
Fixes #933
2016-08-01 18:55:49 -07:00
573f1f9f99 [Frontend] Hide zoom slider control
Fixes #933
Temporarily hiding per request from Andrew
today;
2016-08-01 18:04:17 -07:00
c5c45f0a0e [Frontend] Update TC2 markup and sass
Fixes #933
Update markup and sass in TC2 to be in line with
updates from master from #1047 glyphs
to cssclass approach;
2016-08-01 18:01:28 -07:00
121ab413ff Apply formatting, filter modes by tick source availability 2016-08-01 17:51:15 -07:00
753bd97c8a Merge remote-tracking branch 'origin/master' into open933-c
# Conflicts:
#	platform/commonUI/edit/res/templates/create/create-menu.html
#	platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot
#	platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg
#	platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf
#	platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff
#	platform/commonUI/general/res/sass/_archetypes.scss
#	platform/commonUI/general/res/sass/_constants.scss
#	platform/commonUI/general/res/sass/_icons.scss
#	platform/commonUI/general/res/sass/_main.scss
#	platform/commonUI/general/res/sass/_mixins.scss
#	platform/commonUI/general/res/sass/controls/_buttons.scss
#	platform/commonUI/general/res/templates/controls/time-controller.html
#	platform/commonUI/themes/snow/res/sass/_constants.scss
2016-08-01 17:12:44 -07:00
fcd7ab93e5 Merge branch 'open933' of https://github.com/nasa/openmctweb into open933 2016-08-01 17:12:00 -07:00
c699cb8b4b Merge remote-tracking branch 'origin/master' into open933-c
# Conflicts:
#	platform/commonUI/edit/res/templates/create/create-menu.html
#	platform/commonUI/general/res/fonts/symbols/wtdsymbols.eot
#	platform/commonUI/general/res/fonts/symbols/wtdsymbols.svg
#	platform/commonUI/general/res/fonts/symbols/wtdsymbols.ttf
#	platform/commonUI/general/res/fonts/symbols/wtdsymbols.woff
#	platform/commonUI/general/res/sass/_archetypes.scss
#	platform/commonUI/general/res/sass/_constants.scss
#	platform/commonUI/general/res/sass/_icons.scss
#	platform/commonUI/general/res/sass/_main.scss
#	platform/commonUI/general/res/sass/_mixins.scss
#	platform/commonUI/general/res/sass/controls/_buttons.scss
#	platform/commonUI/general/res/templates/controls/time-controller.html
#	platform/commonUI/themes/snow/res/sass/_constants.scss
2016-08-01 17:11:37 -07:00
a75ea67b8c Format updates when time system selected 2016-08-01 17:11:01 -07:00
9b58aa0052 [TCv2] Add conductorService for compatibility
Add conductor service for compatibility with old plugins that depend
on the conductor service.
2016-08-01 17:03:00 -07:00
579c6b6d24 [Frontend] Styling TC unsynced elements
Fixes #933
WIP: Styling for unsynced elements
2016-08-01 16:30:51 -07:00
762f43fa61 [Frontend] Styling TC unsynced elements
Fixes #933
WIP: Styling for unsynced elements
2016-08-01 16:26:47 -07:00
ce5d0ef5bd Merged stylesheet changes 2016-08-01 16:16:38 -07:00
142ee2f336 Added LocalTimeSystem and merged latest styles 2016-08-01 15:44:49 -07:00
482fcbf6ee Refactored bundle 2016-08-01 15:38:12 -07:00
523d6743fb [Frontend] Added support for thematic styling of Time Conductor v2
Fixes #933
Added theme sass files
2016-08-01 12:00:53 -07:00
7af22126d4 Prevent tabbing into end bounds when not in fixed mode 2016-07-27 12:05:03 -04:00
8e59072537 Removed LAD and Realtime modes 2016-07-27 12:04:02 -04:00
c1bbc4f01d Code cleanup 2016-07-27 10:38:04 -04:00
aa4a5e56f9 Improved support from plot 2016-07-26 16:55:00 -04:00
5b2eb72b16 [Time Conductor] Addressed documentation issues 2016-07-26 08:33:30 -04:00
1b7fc57d21 Added status support to plots 2016-07-25 16:55:27 -04:00
a4f6f6f50b Added license 2016-07-25 12:09:23 -04:00
19fd63b850 Merge branch 'open933-frontend-b' into open933 2016-07-21 20:06:50 -07:00
c2c8e16453 Added scale sensitive formatting to UTCTimeFormat 2016-07-21 20:05:28 -07:00
a10ded25b4 Merge remote-tracking branch 'origin/open933' into open933-frontend-b
# Conflicts:
#	platform/features/conductor-v2/res/sass/time-conductor.scss
#	platform/features/conductor/res/sass/time-conductor.scss
2016-07-20 18:33:58 -07:00
da7c636724 [Frontend] Time Conductor v2 styling
Fixes #933
Redo TC icon to use font symbol, added
new symbol for brackets to font files; font
anti-aliasing mod for .ui-symbol class;
layout tweaks; mobile tweaks.
2016-07-20 18:22:20 -07:00
b392633bc6 [Frontend] Time Conductor v2 styling
Fixes #933
WIP: Significant mobile and desktop style tweaks;
moved constants into their own include file;
2016-07-20 15:48:22 -07:00
ff1678435e [Frontend] Time Conductor v2 styling
Fixes #933
Changed desktop and mobile RT UI to display
end datetime and hide start;
WIP: mobile styling for main UI of TC;
2016-07-20 11:43:40 -07:00
2124fe01e1 [Frontend] Renew support for Time Conductor v1
Fixes #933
Minor fixes to TCv1 for mobile
2016-07-20 10:18:42 -07:00
e6d8944547 Modified main.js 2016-07-19 20:17:06 -07:00
ea1defac28 [Frontend] Renew support for Time Conductor v1
Fixes #933
Time Conductors v1 and v2 now build and load their
own isolated CSS files. All previous styling for TCv1
should be re-enabled. Note that Conductor v2 mobile
is not complete yet.
2016-07-19 20:00:32 -07:00
2a19394334 Added compatibility layer to support existing plots and historical tables 2016-07-19 19:54:52 -07:00
f641edbce7 [Frontend] Renew support for Time Conductor v1
Fixes #933
Time Conductors v1 and v2 now build and load their
own isolated CSS files. All previous styling for TCv1
should be re-enabled. Note that Conductor v2 mobile
is not complete yet.
2016-07-19 18:33:24 -07:00
15a608a861 Populate format in input fields 2016-07-18 18:44:29 -07:00
334ca64551 Merged open933-frontend 2016-07-18 14:25:02 -07:00
0af49efe06 Refactored out modes, time systems, etc. 2016-07-18 12:49:44 -07:00
4087b9cdde [Frontend] Styling for Time Conductor v2
Fixes #933
WIP: Fixed look for Firefox
2016-07-15 14:39:29 -07:00
43a804eef4 [Frontend] Styling for Time Conductor v2
Fixes #933
WIP: Added zoom current range indicator;
tweaks to style
2016-07-15 07:54:32 -07:00
b3a4f52fe2 [Frontend] Styling for Time Conductor v2
Fixes #933
WIP: Adding zoom control with HTML5
input range type; Refactored sass slightly
to move display: inline-block out of mixin
containerBase and into .s-btn.
2016-07-14 18:30:49 -07:00
671e3016d4 [Frontend] Styling for Time Conductor v2
Fixes #933
New _animations scss include, moved
scss around.
2016-07-14 16:40:05 -07:00
379828315f [Frontend] Styling for Time Conductor v2
Fixes #933
New _animations scss include, moved
scss around.
2016-07-14 16:39:27 -07:00
8c5538ec4d [Frontend] Styling for Time Conductor v2
Fixes #933
"Sticky" clock anim for LAD mode
2016-07-14 14:58:18 -07:00
2f9fbfef7f More refactoring 2016-07-13 20:33:47 -07:00
2baca659ca Refactoring 2016-07-13 19:50:58 -07:00
8b694ef337 [Frontend] Styling for Time Conductor v2
Fixes #933
In-progress: color/size tweaks, fixes for espresso
theme
2016-07-13 19:42:51 -07:00
e193e3dfba Merge open933 latest, resolve conflicts
Fixes #933
Fair amount of manual fixing in time-conductor.html
2016-07-13 19:16:27 -07:00
8214c8e895 Merge open933 latest, resolve conflicts
Fixes #933
Fair amount of manual fixing in time-conductor.html
2016-07-13 19:16:00 -07:00
33b2225d10 [Frontend] Styling for Time Conductor v2
Fixes #933
In-progress: restructured markup to move
modeModel farther out; animated icon
2016-07-13 18:48:17 -07:00
14463d39a8 Added end delta 2016-07-13 18:17:27 -07:00
fcfda50e73 [Frontend] Styling for Time Conductor v2
Fixes #933
In-progress: color tweaks, bar sizing,
field widths
2016-07-13 15:00:16 -07:00
06af84c161 [Frontend] Styling for Time Conductor v2
Fixes #933
In-progress: fixed SVG text color, field
styling for fixed vs. real-time, markup cleanup
2016-07-13 13:14:32 -07:00
5238aa2731 [Frontend] Styling for Time Conductor v2
Fixes #933
In-progress; fixed SVG text color
2016-07-13 08:07:59 -07:00
fd29473664 Support resize 2016-07-12 15:02:39 -07:00
97f3fd516b Changed default duration to fifteen minutes 2016-07-12 10:14:26 -07:00
088416905d Added duration 2016-07-12 10:08:08 -07:00
2056d87453 Merge branch 'open933-frontend' into open933 2016-07-11 14:27:30 -07:00
64ce8a2b2a [Frontend] Styling for Time Conductor v2
Fixes #933
Color adjusts, mini super-menu size
and font tweaks, glyphs added to selector,
SVG style fixes in progress
2016-07-11 14:03:41 -07:00
585da38a16 Fixed some merge issues 2016-07-11 13:46:46 -07:00
bf0e85a94c [Frontend] Styling for Time Conductor v2
Fixes #933
Renamed main class; removed unused <style>
defs
2016-07-11 11:35:26 -07:00
84b7a9dc2f Merge remote-tracking branch 'origin/open933' into open933-frontend
Fixes #933
Conflicts:
	platform/features/conductor-v2/src/TimeConductorController.js
2016-07-11 11:29:35 -07:00
11caa8396a Updated modes 2016-07-11 11:18:23 -07:00
0017b77439 Merged markup changes 2016-07-11 11:06:22 -07:00
7b7b21d748 [Frontend] Styling of Time Conductor v2
Fixes #933
Tweaks to language; tweak to class name in markup
2016-07-11 11:05:47 -07:00
788483ec13 [Frontend] Styling of Time Conductor v2
Fixes #933
Tweaks to language
2016-07-11 10:37:08 -07:00
7b19f91ce6 [Frontend] Merge latest from open933
Fixes #933
Resolve conflicts in
mode-menu.html, mode-selector.html,
time-conductor.html; apply tweaks, language, etc.
2016-07-11 10:31:14 -07:00
5cc81ba12a [Time Conductor] Added mode class to time conductor 2016-07-11 10:26:34 -07:00
0a0bc55f5f [Frontend] Styling for Time Conductor v2
Fixes #993
In-progress; mode menu names and
descriptors modified, markup cleaned up
2016-07-08 17:11:43 -07:00
4e7b69c4df Enabled fixed and real-time modes 2016-07-08 16:57:50 -07:00
cf83040c4b [Frontend] Styling for Time Conductor v2
Fixes #993
In-progress; Create menu refactoring and new
mini Create menu
2016-07-08 16:54:49 -07:00
32f7bc86af [Frontend] Styling for Time Conductor v2
Fixes #993
In-progress; class renaming continued,
cleanups in markup file, in-page styles
ported to scss
2016-07-08 16:54:13 -07:00
e230b92946 Fixed bug with date selector having to be clicked twice 2016-07-08 15:15:12 -07:00
58ed500ecf Time sync via conductor 2016-07-07 16:57:03 -07:00
bca5eb0fdb [Time Conductor] #933 Initial markup 2016-07-07 09:47:46 -07:00
77 changed files with 5269 additions and 358 deletions

View File

@ -19,6 +19,8 @@
"comma-separated-values": "^3.6.4",
"FileSaver.js": "^0.0.2",
"zepto": "^1.1.6",
"eventemitter3": "^1.2.0",
"d3": "~4.1.0",
"html2canvas": "^0.4.1"
}
}

View File

@ -0,0 +1,48 @@
/*****************************************************************************
* 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([
"./src/LocalTimeSystem",
"./src/LocalTimeFormat",
'legacyRegistry'
], function (
LocalTimeSystem,
LocalTimeFormat,
legacyRegistry
) {
legacyRegistry.register("example/localTimeSystem", {
"extensions": {
"formats": [
{
"key": "local-format",
"implementation": LocalTimeFormat
}
],
"timeSystems": [
{
"implementation": LocalTimeSystem,
"depends": ["$timeout"]
}
]
}
});
});

View File

@ -0,0 +1,43 @@
/*****************************************************************************
* 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(['../../../platform/features/conductor-v2/conductor/src/timeSystems/LocalClock'], function (LocalClock) {
/**
* @implements TickSource
* @constructor
*/
function LADTickSource ($timeout, period) {
LocalClock.call(this, $timeout, period);
this.metadata = {
key: 'test-lad',
mode: 'lad',
cssclass: 'icon-clock',
label: 'Latest Available Data',
name: 'Latest available data',
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
};
}
LADTickSource.prototype = Object.create(LocalClock.prototype);
return LADTickSource;
});

View File

@ -0,0 +1,112 @@
/*****************************************************************************
* 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([
'moment'
], function (
moment
) {
var DATE_FORMAT = "YYYY-MM-DD h:mm:ss.SSS a",
DATE_FORMATS = [
DATE_FORMAT,
"YYYY-MM-DD h:mm:ss a",
"YYYY-MM-DD h:mm a",
"YYYY-MM-DD"
];
/**
* @typedef Scale
* @property {number} min the minimum scale value, in ms
* @property {number} max the maximum scale value, in ms
*/
/**
* Formatter for UTC timestamps. Interprets numeric values as
* milliseconds since the start of 1970.
*
* @implements {Format}
* @constructor
* @memberof platform/commonUI/formats
*/
function LocalTimeFormat() {
}
/**
* Returns an appropriate time format based on the provided value and
* the threshold required.
* @private
*/
function getScaledFormat (d) {
var m = moment.utc(d);
/**
* Uses logic from d3 Time-Scales, v3 of the API. See
* https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Scales.md
*
* Licensed
*/
return [
[".SSS", function(m) { return m.milliseconds(); }],
[":ss", function(m) { return m.seconds(); }],
["hh:mma", function(m) { return m.minutes(); }],
["hha", function(m) { return m.hours(); }],
["ddd DD", function(m) {
return m.days() &&
m.date() != 1;
}],
["MMM DD", function(m) { return m.date() != 1; }],
["MMMM", function(m) {
return m.month();
}],
["YYYY", function() { return true; }]
].filter(function (row){
return row[1](m);
})[0][0];
};
/**
*
* @param value
* @param {Scale} [scale] Optionally provides context to the
* format request, allowing for scale-appropriate formatting.
* @returns {string} the formatted date
*/
LocalTimeFormat.prototype.format = function (value, scale) {
if (scale !== undefined){
var scaledFormat = getScaledFormat(value, scale);
if (scaledFormat) {
return moment.utc(value).format(scaledFormat);
}
}
return moment(value).format(DATE_FORMAT);
};
LocalTimeFormat.prototype.parse = function (text) {
return moment(text, DATE_FORMATS).valueOf();
};
LocalTimeFormat.prototype.validate = function (text) {
return moment(text, DATE_FORMATS).isValid();
};
return LocalTimeFormat;
});

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([
'../../../platform/features/conductor-v2/conductor/src/timeSystems/TimeSystem',
'../../../platform/features/conductor-v2/conductor/src/timeSystems/LocalClock',
'./LADTickSource'
], function (TimeSystem, LocalClock, LADTickSource) {
var THIRTY_MINUTES = 30 * 60 * 1000,
DEFAULT_PERIOD = 1000;
/**
* This time system supports UTC dates and provides a ticking clock source.
* @implements TimeSystem
* @constructor
*/
function LocalTimeSystem ($timeout) {
TimeSystem.call(this);
/**
* Some metadata, which will be used to identify the time system in
* the UI
* @type {{key: string, name: string, glyph: string}}
*/
this.metadata = {
'key': 'local',
'name': 'Local',
'glyph': '\u0043'
};
this.fmts = ['local-format'];
this.sources = [new LocalClock($timeout, DEFAULT_PERIOD), new LADTickSource($timeout, DEFAULT_PERIOD)];
}
LocalTimeSystem.prototype = Object.create(TimeSystem.prototype);
LocalTimeSystem.prototype.formats = function () {
return this.fmts;
};
LocalTimeSystem.prototype.deltaFormat = function () {
return 'duration';
};
LocalTimeSystem.prototype.tickSources = function () {
return this.sources;
};
LocalTimeSystem.prototype.defaults = function (key) {
var now = Math.ceil(Date.now() / 1000) * 1000;
return {
key: 'local-default',
name: 'Local 12 hour time system defaults',
deltas: {start: THIRTY_MINUTES, end: 0},
bounds: {start: now - THIRTY_MINUTES, end: now}
};
};
return LocalTimeSystem;
});

10
main.js
View File

@ -27,6 +27,7 @@ requirejs.config({
"angular": "bower_components/angular/angular.min",
"angular-route": "bower_components/angular-route/angular-route.min",
"csv": "bower_components/comma-separated-values/csv.min",
"EventEmitter": "bower_components/eventemitter3/index",
"es6-promise": "bower_components/es6-promise/es6-promise.min",
"html2canvas": "bower_components/html2canvas/build/html2canvas.min",
"moment": "bower_components/moment/moment",
@ -35,7 +36,8 @@ requirejs.config({
"screenfull": "bower_components/screenfull/dist/screenfull.min",
"text": "bower_components/text/text",
"uuid": "bower_components/node-uuid/uuid",
"zepto": "bower_components/zepto/zepto.min"
"zepto": "bower_components/zepto/zepto.min",
"d3": "bower_components/d3/d3.min"
},
"shim": {
"angular": {
@ -47,6 +49,9 @@ requirejs.config({
"html2canvas": {
"exports": "html2canvas"
},
"EventEmitter": {
"exports": "EventEmitter"
},
"moment-duration-format": {
"deps": ["moment"]
},
@ -55,6 +60,9 @@ requirejs.config({
},
"zepto": {
"exports": "Zepto"
},
"d3": {
"exports": "d3"
}
}
});

View File

@ -43,7 +43,7 @@
</mct-representation>
</div>
</div>
<div class="holder l-flex-col flex-elem grows l-object-wrapper">
<div class="holder l-flex-col flex-elem grows l-object-wrapper l-controls-visible l-time-controller-visible">
<div class="holder l-flex-col flex-elem grows l-object-wrapper-inner">
<!-- Toolbar and Save/Cancel buttons -->
<div class="l-edit-controls flex-elem l-flex-row flex-align-end">
@ -59,4 +59,5 @@
</mct-representation>
</div>
</div>
<mct-include key="'conductor'" class="abs holder flex-elem flex-fixed l-flex-row l-time-conductor-holder"></mct-include>
</div>

View File

@ -22,7 +22,8 @@
<span class='type-icon flex-elem {{type.getCssClass()}}'></span>
<span class="l-elem-wrapper l-flex-row flex-elem grows">
<span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
<span class='title-label flex-elem flex-can-shrink'>{{model.name}}</span>
<span class='title-label flex-elem holder flex-can-shrink'>{{model.name}}</span>
<span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span>
<mct-representation
key="'menu-arrow'"
mct-object='domainObject'

View File

@ -66,4 +66,9 @@
</mct-representation>
</div><!--/ l-object-wrapper-inner -->
</div>
<mct-representation mct-object="domainObject"
key="'time-conductor'"
class="abs holder flex-elem flex-fixed l-flex-row l-time-conductor-holder">
</mct-representation>
</div>

View File

@ -23,10 +23,12 @@
define([
"./src/FormatProvider",
"./src/UTCTimeFormat",
"./src/DurationFormat",
'legacyRegistry'
], function (
FormatProvider,
UTCTimeFormat,
DurationFormat,
legacyRegistry
) {
@ -48,6 +50,10 @@ define([
{
"key": "utc",
"implementation": UTCTimeFormat
},
{
"key": "duration",
"implementation": DurationFormat
}
],
"constants": [
@ -55,6 +61,17 @@ define([
"key": "DEFAULT_TIME_FORMAT",
"value": "utc"
}
],
"licenses": [
{
"name": "d3",
"version": "3.0.0",
"description": "Incorporates modified code from d3 Time Scales",
"author": "Mike Bostock",
"copyright": "Copyright 2010-2016 Mike Bostock. " +
"All rights reserved.",
"link": "https://github.com/d3/d3/blob/master/LICENSE"
}
]
}
});

View File

@ -0,0 +1,62 @@
/*****************************************************************************
* 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([
'moment'
], function (
moment
) {
var DATE_FORMAT = "HH:mm:ss",
DATE_FORMATS = [
DATE_FORMAT
];
/**
* Formatter for duration. Uses moment to produce a date from a given
* value, but output is formatted to display only time. Can be used for
* specifying a time duration. For specifying duration, it's best to
* specify a date of January 1, 1970, as the ms offset will equal the
* duration represented by the time.
*
* @implements {Format}
* @constructor
* @memberof platform/commonUI/formats
*/
function DurationFormat() {
}
DurationFormat.prototype.format = function (value) {
return moment.utc(value).format(DATE_FORMAT);
};
DurationFormat.prototype.parse = function (text) {
return moment.duration(text).asMilliseconds();
};
DurationFormat.prototype.validate = function (text) {
return moment.utc(text, DATE_FORMATS).isValid();
};
return DurationFormat;
});

View File

@ -58,6 +58,10 @@ define([
* @method format
* @memberof Format#
* @param {number} value the numeric value to format
* @param {number} [threshold] Optionally provides context to the
* format request, allowing for scale-appropriate formatting. This value
* should be the minimum unit to be represented by this format, in ms. For
* example, to display seconds, a threshold of 1 * 1000 should be provided.
* @returns {string} the text representation of the value
*/

View File

@ -34,6 +34,11 @@ define([
"YYYY-MM-DD"
];
/**
* @typedef Scale
* @property {number} min the minimum scale value, in ms
* @property {number} max the maximum scale value, in ms
*/
/**
* Formatter for UTC timestamps. Interprets numeric values as
@ -46,7 +51,96 @@ define([
function UTCTimeFormat() {
}
UTCTimeFormat.prototype.format = function (value) {
/**
* Returns an appropriate time format based on the provided value and
* the threshold required.
* @private
*/
function getScaledFormat(d) {
var momentified = moment.utc(d);
/**
* Uses logic from d3 Time-Scales, v3 of the API. See
* https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Scales.md
*
* Licensed
*/
return [
[".SSS", function (m) {
return m.milliseconds();
}],
[":ss", function (m) {
return m.seconds();
}],
["HH:mm", function (m) {
return m.minutes();
}],
["HH", function (m) {
return m.hours();
}],
["ddd DD", function (m) {
return m.days() &&
m.date() !== 1;
}],
["MMM DD", function (m) {
return m.date() !== 1;
}],
["MMMM", function (m) {
return m.month();
}],
["YYYY", function () {
return true;
}]
].filter(function (row) {
return row[1](momentified);
})[0][0];
}
UTCTimeFormat.prototype.timeUnits = function (timeRange) {
var momentified = moment.duration(timeRange);
return [
["Decades", function (r) {
return r.years() > 15;
}],
["Years", function (r) {
return r.years() > 1;
}],
["Months", function (r) {
return r.years() === 1 || r.months() > 1;
}],
["Days", function (r) {
return r.months() === 1 || r.days() > 1;
}],
["Hours", function (r) {
return r.days() === 1 || r.hours() > 1;
}],
["Minutes", function (r) {
return r.hours() === 1 || r.minutes() > 1;
}],
["Seconds", function (r) {
return r.minutes() === 1 || r.seconds() > 1;
}],
["Milliseconds", function (r) {
return true;
}]
].filter(function (row){
return row[1](momentified);
})[0][0];
};
/**
*
* @param value
* @param {Scale} [scale] Optionally provides context to the
* format request, allowing for scale-appropriate formatting.
* @returns {string} the formatted date
*/
UTCTimeFormat.prototype.format = function (value, scale) {
if (scale !== undefined) {
var scaledFormat = getScaledFormat(value, scale);
if (scaledFormat) {
return moment.utc(value).format(scaledFormat);
}
}
return moment.utc(value).format(DATE_FORMAT) + "Z";
};

View File

@ -0,0 +1,62 @@
/*****************************************************************************
* 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([
"./UTCTimeFormat",
"moment"
], function (
UTCTimeFormat,
moment
) {
describe("The UTCTimeFormat class", function () {
var format;
var scale;
beforeEach(function () {
format = new UTCTimeFormat();
scale = {min: 0, max: 0};
});
it("Provides an appropriately scaled time format based on the input" +
" time", function () {
var TWO_HUNDRED_MS = 200;
var THREE_SECONDS = 3000;
var FIVE_MINUTES = 5 * 60 * 1000;
var ONE_HOUR_TWENTY_MINS = (1 * 60 * 60 * 1000) + (20 * 60 * 1000);
var TEN_HOURS = (10 * 60 * 60 * 1000);
var JUNE_THIRD = moment.utc("2016-06-03", "YYYY-MM-DD");
var APRIL = moment.utc("2016-04", "YYYY-MM");
var TWENTY_SIXTEEN = moment.utc("2016", "YYYY");
expect(format.format(TWO_HUNDRED_MS, scale)).toBe(".200");
expect(format.format(THREE_SECONDS, scale)).toBe(":03");
expect(format.format(FIVE_MINUTES, scale)).toBe("00:05");
expect(format.format(ONE_HOUR_TWENTY_MINS, scale)).toBe("01:20");
expect(format.format(TEN_HOURS, scale)).toBe("10");
expect(format.format(JUNE_THIRD, scale)).toBe("Fri 03");
expect(format.format(APRIL, scale)).toBe("April");
expect(format.format(TWENTY_SIXTEEN, scale)).toBe("2016");
});
});
});

View File

@ -0,0 +1,91 @@
@include keyframes(rotation) {
100% { @include transform(rotate(360deg)); }
}
@include keyframes(rotation-centered) {
0% { @include transform(translate(-50%, -50%) rotate(0deg)); }
100% { @include transform(translate(-50%, -50%) rotate(360deg)); }
}
@include keyframes(clock-hands) {
0% { @include transform(translate(-50%, -50%) rotate(0deg)); }
100% { @include transform(translate(-50%, -50%) rotate(360deg)); }
}
@include keyframes(clock-hands-sticky) {
0% {
@include transform(translate(-50%, -50%) rotate(0deg));
}
7% {
@include transform(translate(-50%, -50%) rotate(0deg));
}
8% {
@include transform(translate(-50%, -50%) rotate(30deg));
}
15% {
@include transform(translate(-50%, -50%) rotate(30deg));
}
16% {
@include transform(translate(-50%, -50%) rotate(60deg));
}
24% {
@include transform(translate(-50%, -50%) rotate(60deg));
}
25% {
@include transform(translate(-50%, -50%) rotate(90deg));
}
32% {
@include transform(translate(-50%, -50%) rotate(90deg));
}
33% {
@include transform(translate(-50%, -50%) rotate(120deg));
}
40% {
@include transform(translate(-50%, -50%) rotate(120deg));
}
41% {
@include transform(translate(-50%, -50%) rotate(150deg));
}
49% {
@include transform(translate(-50%, -50%) rotate(150deg));
}
50% {
@include transform(translate(-50%, -50%) rotate(180deg));
}
57% {
@include transform(translate(-50%, -50%) rotate(180deg));
}
58% {
@include transform(translate(-50%, -50%) rotate(210deg));
}
65% {
@include transform(translate(-50%, -50%) rotate(210deg));
}
66% {
@include transform(translate(-50%, -50%) rotate(240deg));
}
74% {
@include transform(translate(-50%, -50%) rotate(240deg));
}
75% {
@include transform(translate(-50%, -50%) rotate(270deg));
}
82% {
@include transform(translate(-50%, -50%) rotate(270deg));
}
83% {
@include transform(translate(-50%, -50%) rotate(300deg));
}
90% {
@include transform(translate(-50%, -50%) rotate(300deg));
}
91% {
@include transform(translate(-50%, -50%) rotate(330deg));
}
99% {
@include transform(translate(-50%, -50%) rotate(330deg));
}
100% {
@include transform(translate(-50%, -50%) rotate(360deg));
}
}

View File

@ -108,6 +108,9 @@
&.grows {
@include flex(1 1 auto);
}
&.contents-align-right {
text-align: right;
}
}
.flex-container {
// Apply to wrapping elements, mct-includes, etc.

View File

@ -49,7 +49,6 @@ $uePaneMiniTabFontSize: 8px;
$uePaneMiniTabCollapsedW: 18px;
$ueEditLeftPaneW: 75%;
$treeSearchInputBarH: 25px;
$ueTimeControlH: (33px, 18px, 20px);
/*************** Panes */
$ueBrowseLeftPaneTreeMinW: 150px;
$ueBrowseLeftPaneTreeMaxW: 35%;
@ -112,6 +111,7 @@ $bubbleMaxW: 300px;
$reqSymbolW: 15px;
$reqSymbolM: $interiorMargin * 2;
$reqSymbolFontSize: 0.75em;
$inputTextP: 3px 5px;
/*************** Wait Spinner Defaults */
$waitSpinnerD: 32px;
$waitSpinnerTreeD: 20px;

View File

@ -4,4 +4,3 @@
@include s-stale();
}
}

View File

@ -39,20 +39,20 @@
@include pulse($animName: pulse-subtle, $dur: 500ms, $opacity0: 0.7);
}
@mixin animTo($animName, $propName, $propValStart, $propValEnd, $dur: 500ms, $delay: 0) {
@mixin animTo($animName, $propName, $propValStart, $propValEnd, $dur: 500ms, $delay: 0, $dir: normal, $count: 1) {
@include keyframes($animName) {
from { #{propName}: $propValStart; }
to { #{$propName}: $propValEnd; }
}
@include animToParams($animName, $dur: 500ms, $delay: 0)
@include animToParams($animName, $dur: $dur, $delay: $delay, $dir: $dir, $count: $count)
}
@mixin animToParams($animName, $dur: 500ms, $delay: 0) {
@mixin animToParams($animName, $dur: 500ms, $delay: 0, $dir: normal, $count: 1) {
@include animation-name($animName);
@include animation-duration($dur);
@include animation-delay($delay);
@include animation-fill-mode(both);
@include animation-direction(normal);
@include animation-iteration-count(1);
@include animation-direction($dir);
@include animation-iteration-count($count);
@include animation-timing-function(ease-in-out);
}

View File

@ -82,7 +82,7 @@ input, textarea {
input[type="text"],
input[type="search"] {
vertical-align: baseline;
padding: 3px 5px;
padding: $inputTextP;
}
h1, h2, h3 {

View File

@ -30,6 +30,7 @@
.ui-symbol {
font-family: 'symbolsfont';
-webkit-font-smoothing: antialiased;
}
.ui-symbol.icon {
@ -70,7 +71,6 @@
line-height: inherit;
position: relative;
&.l-icon-link {
.t-item-icon-glyph {
&:after {
color: $colorIconLink;
content: $glyph-icon-link;
@ -81,6 +81,18 @@
@include transform(scale(0.3));
z-index: 2;
}
}
/* .t-item-icon-glyph {
&:after {
color: $colorIconLink;
content: '\e921'; //$glyph-icon-link;
height: auto; width: auto;
position: absolute;
left: 0; top: 0; right: 0; bottom: 20%;
@include transform-origin(bottom left);
@include transform(scale(0.3));
z-index: 2;
}
}*/
}
}

View File

@ -22,6 +22,7 @@
@import "effects";
@import "global";
@import "glyphs";
@import "animations";
@import "archetypes";
@import "about";
@import "text";
@ -41,7 +42,6 @@
@import "controls/lists";
@import "controls/menus";
@import "controls/messages";
@import "controls/time-controller";
@import "mobile/controls/menus";
/********************************* FORMS */

View File

@ -185,21 +185,15 @@
}
@mixin sliderTrack($bg: $scrollbarTrackColorBg) {
//$b: 1px solid lighten($bg, 30%);
border-radius: 2px;
box-sizing: border-box;
@include boxIncised(0.7);
background-color: $bg;
//border-bottom: $b;
//border-right: $b;
}
@mixin controlGrippy($b, $direction: horizontal, $w: 1px, $style: dotted) {
//&:before {
//@include trans-prop-nice("border-color", 25ms);
content: '';
display: block;
//height: auto;
pointer-events: none;
position: absolute;
z-index: 2;
@ -274,16 +268,6 @@
text-shadow: rgba(black, $sVal) 0 3px 7px;
}
@function pullForward($c, $p: 20%) {
// For dark interfaces, lighter things come forward
@return lighten($c, $p);
}
@function pushBack($c, $p: 20%) {
// For dark interfaces, darker things move back
@return darken($c, $p);
}
@function percentToDecimal($p) {
@return $p / 100%;
}
@ -304,7 +288,6 @@
border-radius: $controlCr;
box-sizing: border-box;
color: $fg;
//display: inline-block;
}
@mixin btnBase($bg: $colorBtnBg, $bgHov: $colorBtnBgHov, $fg: $colorBtnFg, $fgHov: $colorBtnFgHov, $ic: $colorBtnIcon, $icHov: $colorBtnIconHov) {

View File

@ -296,8 +296,6 @@ input[type="search"] {
.title-label {
color: $colorObjHdrTxt;
@include ellipsize();
@include webkitProp(flex, '0 1 auto');
padding-right: 0.35em; // For context arrow. Done with em's so pad is relative to the scale of the text.
}
.context-available-w {
@ -308,6 +306,10 @@ input[type="search"] {
font-size: 0.7em;
@include flex(0 0 1);
}
.t-object-alert {
display: none;
}
}
/******************************************************** PROGRESS BAR */
@ -441,6 +443,63 @@ input[type="search"] {
}
}
@mixin sliderKnob() {
$h: 16px;
cursor: pointer;
width: floor($h/1.75);
height: $h;
margin-top: 1 + floor($h/2) * -1;
@include btnSubtle(pullForward($colorBtnBg, 10%));
//border-radius: 50% !important;
}
@mixin sliderKnobRound() {
$h: 12px;
cursor: pointer;
width: $h;
height: $h;
margin-top: 1 + floor($h/2) * -1;
@include btnSubtle(pullForward($colorBtnBg, 10%));
border-radius: 50% !important;
}
input[type="range"] {
// HTML5 range inputs
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
background: transparent; /* Otherwise white in Chrome */
&:focus {
outline: none; /* Removes the blue border. */
}
// Thumb
&::-webkit-slider-thumb {
-webkit-appearance: none;
@include sliderKnobRound();
}
&::-moz-range-thumb {
border: none;
@include sliderKnobRound();
}
&::-ms-thumb {
border: none;
@include sliderKnobRound();
}
// Track
&::-webkit-slider-runnable-track {
width: 100%;
height: 3px;
@include sliderTrack();
}
&::-moz-range-track {
width: 100%;
height: 3px;
@include sliderTrack();
}
}
/******************************************************** DATETIME PICKER */
.l-datetime-picker {
$r1H: 15px;

View File

@ -178,7 +178,7 @@
}
.pane {
box-sizing: border-box;
&.left {
&.menu-items {
border-right: 1px solid pullForward($colorMenuBg, 10%);
left: 0;
padding-right: $interiorMargin;
@ -194,39 +194,54 @@
}
}
}
&.right {
&.menu-item-description {
left: auto;
right: 0;
padding: $interiorMargin * 5;
width: $prw;
}
}
.menu-item-description {
.desc-area {
&.icon {
$h: 150px;
color: $colorCreateMenuLgIcon;
position: relative;
font-size: 8em;
left: 0;
height: $h;
line-height: $h;
margin-bottom: $interiorMargin * 5;
margin-bottom: $interiorMargin * 3;
position: relative;
text-align: center;
}
&.title {
color: $colorCreateMenuText;
font-size: 1.2em;
margin-bottom: 0.5em;
margin-bottom: $interiorMargin * 2;
}
&.description {
color: $colorCreateMenuText;
color: pushBack($colorCreateMenuText, 20%);
font-size: 0.8em;
line-height: 1.5em;
}
}
}
}
&.mini {
width: 400px;
height: 300px;
.pane {
&.menu-items {
font-size: 0.8em;
}
&.menu-item-description {
padding: $interiorMargin * 3;
.desc-area {
&.icon {
font-size: 4em;
}
&.title {
font-size: 1em;
}
}
}
}
}
}
.context-menu {
font-size: 0.80rem;
}
@ -262,3 +277,7 @@
right: 0;
width: auto;
}
.menus-up .menu {
bottom: $btnStdH; top: auto;
}

View File

@ -345,3 +345,29 @@ body.desktop .t-message-single {
body.desktop .t-message-list {
.message-contents .l-message { margin-right: $interiorMarginLg; }
}
// Alert elements in views
.s-unsynced {
$c: $colorPausedBg;
border: 1px solid $c;
@include animTo($animName: pulsePaused, $propName: border-color, $propValStart: rgba($c, 0.8), $propValEnd: rgba($c, 0.5), $dur: $animPausedPulseDur, $dir: alternate, $count: infinite);
}
.s-status-timeconductor-unsynced {
// Plot areas
.gl-plot .gl-plot-display-area {
@extend .s-unsynced;
}
// Object headers
.object-header {
.t-object-alert {
display: inline;
&.t-alert-unsynced {
@extend .icon-alert-triangle;
color: $colorPausedBg;
}
}
}
}

View File

@ -1,266 +0,0 @@
@mixin toiLineHovEffects() {
&:before,
&:after {
background-color: $timeControllerToiLineColorHov;
}
}
.l-time-controller {
$minW: 500px;
$knobHOffset: 0px;
$knobM: ($sliderKnobW + $knobHOffset) * -1;
$rangeValPad: $interiorMargin;
$rangeValOffset: $sliderKnobW + $interiorMargin;
$timeRangeSliderLROffset: 150px + ($sliderKnobW * 2);
$r1H: nth($ueTimeControlH,1); // Not currently used
$r2H: nth($ueTimeControlH,2);
$r3H: nth($ueTimeControlH,3);
min-width: $minW;
font-size: 0.8rem;
.l-time-range-inputs-holder,
.l-time-range-slider-holder,
.l-time-range-ticks-holder
{
box-sizing: border-box;
position: relative;
&:not(:first-child) {
margin-top: $interiorMargin;
}
}
.l-time-range-slider,
.l-time-range-ticks {
@include absPosDefault(0, visible);
left: $timeRangeSliderLROffset; right: $timeRangeSliderLROffset;
}
.l-time-range-inputs-holder {
border-top: 1px solid $colorInteriorBorder;
padding-top: $interiorMargin;
&.l-flex-row,
.l-flex-row {
@include align-items(center);
.flex-elem {
height: auto;
line-height: normal;
}
}
.type-icon {
font-size: 120%;
vertical-align: middle;
}
.l-time-range-input-w,
.l-time-range-inputs-elem {
margin-right: $interiorMargin;
.lbl {
color: $colorPlotLabelFg;
}
.ui-symbol.icon {
font-size: 11px;
}
}
.l-time-range-input-w {
// Wraps a datetime text input field
position: relative;
input[type="text"] {
width: 200px;
&.picker-icon {
padding-right: 20px;
}
}
.icon-calendar {
position: absolute;
right: 5px;
top: 5px;
}
}
}
.l-time-range-slider-holder {
height: $r2H;
.range-holder {
box-shadow: none;
background: none;
border: none;
.range {
.toi-line {
$myC: $timeControllerToiLineColor;
$myW: 8px;
@include transform(translateX(50%));
position: absolute;
top: 0; right: 0; bottom: 0px; left: auto;
width: $myW;
height: auto;
z-index: 2;
&:before {
// Vert line
background-color: $myC;
position: absolute;
content: "";
top: 0; right: auto; bottom: -10px; left: floor($myW/2) - 1;
width: 1px;
}
}
&:hover .toi-line {
@include toiLineHovEffects;
}
}
}
&:not(:active) {
.knob,
.range {
@include transition-property(left, right);
@include transition-duration(500ms);
@include transition-timing-function(ease-in-out);
}
}
}
.l-time-range-ticks-holder {
height: $r3H;
.l-time-range-ticks {
border-top: 1px solid $colorTick;
.tick {
background-color: $colorTick;
border:none;
height: 5px;
width: 1px;
margin-left: -1px;
position: absolute;
&:first-child {
margin-left: 0;
}
.l-time-range-tick-label {
@include webkitProp(transform, translateX(-50%));
color: $colorPlotLabelFg;
display: inline-block;
font-size: 0.7rem;
position: absolute;
top: 5px;
white-space: nowrap;
z-index: 2;
}
}
}
}
.knob {
z-index: 2;
&:before {
$mTB: 2px;
$grippyW: 3px;
$mLR: ($sliderKnobW - $grippyW)/2;
@include bgStripes($c: pullForward($sliderColorKnob, 20%), $a: 1, $bgsize: 4px, $angle: 0deg);
content: '';
display: block;
position: absolute;
top: $mTB; right: $mLR; bottom: $mTB; left: $mLR;
}
.range-value {
@include trans-prop-nice-fade(.25s);
font-size: 0.7rem;
position: absolute;
height: $r2H;
line-height: $r2H;
white-space: nowrap;
z-index: 1;
}
&:hover {
.range-value {
color: $sliderColorKnobHov;
}
}
&.knob-l {
margin-left: $knobM;
.range-value {
text-align: right;
right: $rangeValOffset;
}
}
&.knob-r {
margin-right: $knobM;
.range-value {
left: $rangeValOffset;
}
&:hover + .range-holder .range .toi-line {
@include toiLineHovEffects;
}
}
}
.l-time-domain-selector {
position: absolute;
right: 0px;
top: $interiorMargin;
}
}
.s-time-range-val {
border-radius: $controlCr;
background-color: $colorInputBg;
padding: 1px 1px 0 $interiorMargin;
}
/******************************************************************** MOBILE */
@include phoneandtablet {
.l-time-controller {
min-width: 0;
.l-time-range-slider-holder,
.l-time-range-ticks-holder {
display: none;
}
}
}
@include phone {
.l-time-controller {
.l-time-range-inputs-holder {
&.l-flex-row,
.l-flex-row {
@include align-items(flex-start);
}
.l-time-range-inputs-elem {
&.type-icon {
margin-top: 3px;
}
}
.t-inputs-w {
@include flex-direction(column);
.l-time-range-input-w:not(:first-child) {
&:not(:first-child) {
margin-top: $interiorMargin;
}
margin-right: 0;
}
.l-time-range-inputs-elem {
&.lbl { display: none; }
}
}
}
}
}
@include phonePortrait {
.l-time-controller {
.l-time-range-inputs-holder {
.t-inputs-w {
@include flex(1 1 auto);
padding-top: 25px; // Make room for the ever lovin' Time Domain Selector
.flex-elem {
@include flex(1 1 auto);
width: 100%;
}
input[type="text"] {
width: 100%;
}
}
}
}
.l-time-domain-selector {
right: auto;
left: 20px;
}
}

View File

@ -74,7 +74,7 @@
.s-image-main {
border: 1px solid transparent;
&.paused {
border-color: $colorPausedBg;
@extend .s-unsynced;
}
}

View File

@ -19,15 +19,6 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
@include keyframes(rotation) {
100% { @include transform(rotate(360deg)); }
}
@include keyframes(rotation-centered) {
0% { @include transform(translate(-50%, -50%) rotate(0deg)); }
100% { @include transform(translate(-50%, -50%) rotate(360deg)); }
}
@mixin spinner($b: 5px, $c: $colorKey) {
@include transform-origin(center);
@include animation-name(rotation-centered);

View File

@ -128,7 +128,7 @@
line-height: $ueTopBarH;
}
.primary-pane {
.t-object.primary-pane {
// Need to lift up this pane to ensure that 'collapsed' panes don't block user interactions
z-index: 4;
}
@ -212,6 +212,8 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
.holder-object {
top: $bodyMargin;
bottom: $interiorMargin;
// Clip element that have min-widths
overflow: hidden;
}
.holder-inspector {
top: $bodyMargin;

View File

@ -23,6 +23,8 @@
<input type="text"
ng-model="textValue"
ng-blur="restoreTextValue(); ngBlur()"
ng-mouseup="ngMouseup()"
ng-disabled="ngDisabled"
ng-class="{
error: textInvalid ||
(structure.validate &&

View File

@ -116,6 +116,7 @@ $colorProgressBarAmt: $colorKey;
$progressBarHOverlay: 15px;
$progressBarStripeW: 20px;
$shdwStatusIc: rgba(black, 0.4) 0 1px 2px;
$animPausedPulseDur: 500ms;
// Selects
$colorSelectBg: $colorBtnBg;

View File

@ -116,6 +116,7 @@ $colorProgressBarAmt: #0a0;
$progressBarHOverlay: 15px;
$progressBarStripeW: 20px;
$shdwStatusIc: rgba(white, 0.8) 0 0px 5px;
$animPausedPulseDur: 1s;
// Selects
$colorSelectBg: $colorBtnBg;

View File

@ -0,0 +1,66 @@
/*****************************************************************************
* 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([
"./src/ConductorTelemetryDecorator",
"./src/ConductorRepresenter",
"./src/ConductorService",
'legacyRegistry'
], function (
ConductorTelemetryDecorator,
ConductorRepresenter,
ConductorService,
legacyRegistry
) {
legacyRegistry.register("platform/features/conductor-v2/compatibility", {
"extensions": {
"services": [
{
"key": "conductorService",
"implementation": ConductorService,
"depends": [
"timeConductor"
]
}
],
"representers": [
{
"implementation": ConductorRepresenter,
"depends": [
"timeConductor"
]
}
],
"components": [
{
"type": "decorator",
"provides": "telemetryService",
"implementation": ConductorTelemetryDecorator,
"depends": [
"timeConductor"
]
}
]
}
});
});

View File

@ -0,0 +1,94 @@
/*****************************************************************************
* 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 () {
/**
* Representer that provides a compatibility layer between the new
* time conductor and existing representations / views. Listens to
* the v2 time conductor API and generates v1 style events using the
* Angular event bus. This is transitional code code and will be
* removed.
*
* Deprecated immediately as this is temporary code
*
* @deprecated
* @constructor
*/
function ConductorRepresenter(
timeConductor,
scope,
element
) {
this.conductor = timeConductor;
this.scope = scope;
this.element = element;
this.boundsListener = this.boundsListener.bind(this);
this.timeSystemListener = this.timeSystemListener.bind(this);
this.followListener = this.followListener.bind(this);
}
ConductorRepresenter.prototype.boundsListener = function (bounds) {
this.scope.$broadcast('telemetry:display:bounds', {
start: bounds.start,
end: bounds.end,
domain: this.conductor.timeSystem().metadata.key
}, this.conductor.follow());
};
ConductorRepresenter.prototype.timeSystemListener = function (timeSystem) {
var bounds = this.conductor.bounds();
this.scope.$broadcast('telemetry:display:bounds', {
start: bounds.start,
end: bounds.end,
domain: timeSystem.metadata.key
}, this.conductor.follow());
};
ConductorRepresenter.prototype.followListener = function () {
this.boundsListener(this.conductor.bounds());
};
// Handle a specific representation of a specific domain object
ConductorRepresenter.prototype.represent = function represent(representation) {
if (representation.key === 'browse-object') {
this.destroy();
this.conductor.on("bounds", this.boundsListener);
this.conductor.on("timeSystem", this.timeSystemListener);
this.conductor.on("follow", this.followListener);
}
};
ConductorRepresenter.prototype.destroy = function destroy() {
this.conductor.off("bounds", this.boundsListener);
this.conductor.off("timeSystem", this.timeSystemListener);
this.conductor.off("follow", this.followListener);
};
return ConductorRepresenter;
}
);

View File

@ -0,0 +1,72 @@
/*****************************************************************************
* 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 (
) {
function Conductor(timeConductorService) {
this.timeConductor = timeConductorService.conductor();
}
Conductor.prototype.displayStart = function () {
return this.timeConductor.bounds().start;
};
Conductor.prototype.displayEnd = function () {
return this.timeConductor.bounds().end;
};
Conductor.prototype.domainOptions = function () {
throw new Error([
'domainOptions not implemented in compatibility layer,',
' you must be using some crazy unknown code'
].join(''));
};
Conductor.prototype.domain = function () {
var system = this.timeConductor.timeSystem();
return {
key: system.metadata.key,
name: system.metadata.name,
format: system.formats()[0]
};
};
/**
* Small compatibility layer that implements old conductor service by
* wrapping new time conductor. This allows views that previously depended
* directly on the conductor service to continue to do so without
* modification.
*/
function ConductorService(timeConductor) {
this.tc = new Conductor(timeConductor);
}
ConductorService.prototype.getConductor = function () {
return this.tc;
};
return ConductorService;
});

View File

@ -0,0 +1,87 @@
/*****************************************************************************
* 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 () {
/**
* Decorates the `telemetryService` such that requests are
* mediated by the time conductor. This is a modified version of the
* decorator used in the old TimeConductor that integrates with the
* new TimeConductor API.
*
* @constructor
* @memberof platform/features/conductor
* @implements {TelemetryService}
* @param {platform/features/conductor.TimeConductor} conductor
* the service which exposes the global time conductor
* @param {TelemetryService} telemetryService the decorated service
*/
function ConductorTelemetryDecorator(timeConductor, telemetryService) {
this.conductor = timeConductor;
this.telemetryService = telemetryService;
this.amendRequests = ConductorTelemetryDecorator.prototype.amendRequests.bind(this);
}
function amendRequest(request, bounds, timeSystem) {
request = request || {};
request.start = bounds.start;
request.end = bounds.end;
request.domain = timeSystem.metadata.key;
return request;
}
ConductorTelemetryDecorator.prototype.amendRequests = function (requests) {
var bounds = this.conductor.bounds(),
timeSystem = this.conductor.timeSystem();
return (requests || []).map(function (request) {
return amendRequest(request, bounds, timeSystem);
});
};
ConductorTelemetryDecorator.prototype.requestTelemetry = function (requests) {
return this.telemetryService
.requestTelemetry(this.amendRequests(requests));
};
ConductorTelemetryDecorator.prototype.subscribe = function (callback, requests) {
var unsubscribeFunc = this.telemetryService.subscribe(callback, this.amendRequests(requests)),
conductor = this.conductor,
self = this;
function amendRequests() {
return self.amendRequests(requests);
}
conductor.on('bounds', amendRequests);
return function () {
unsubscribeFunc();
conductor.off('bounds', amendRequests);
};
};
return ConductorTelemetryDecorator;
}
);

View File

@ -0,0 +1,146 @@
/*****************************************************************************
* 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([
"./src/ui/TimeConductorViewService",
"./src/ui/TimeConductorController",
"./src/TimeConductor",
"./src/ui/ConductorAxisController",
"./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 (
TimeConductorViewService,
TimeConductorController,
TimeConductor,
ConductorAxisController,
MCTConductorAxis,
NumberFormat,
timeConductorTemplate,
modeSelectorTemplate,
modeMenuTemplate,
legacyRegistry
) {
legacyRegistry.register("platform/features/conductor-v2/conductor", {
"extensions": {
"services": [
{
"key": "timeConductor",
"implementation": TimeConductor
},
{
"key": "timeConductorViewService",
"implementation": TimeConductorViewService,
"depends": [
"timeConductor",
"timeSystems[]"
]
}
],
"controllers": [
{
"key": "TimeConductorController",
"implementation": TimeConductorController,
"depends": [
"$scope",
"$window",
"timeConductor",
"timeConductorViewService",
"timeSystems[]",
"formatService"
]
},
{
"key": "ConductorAxisController",
"implementation": ConductorAxisController,
"depends": [
"timeConductor",
"formatService",
"timeConductorViewService"
]
}
],
"directives": [
{
"key": "mctConductorAxis",
"implementation": MCTConductorAxis,
"depends": [
"timeConductor",
"formatService"
]
}
],
"stylesheets": [
{
"stylesheetUrl": "css/time-conductor-espresso.css",
"theme": "espresso"
},
{
"stylesheetUrl": "css/time-conductor-snow.css",
"theme": "snow"
}
],
"templates": [
{
"key": "conductor",
"template": timeConductorTemplate
},
{
"key": "mode-menu",
"template": modeMenuTemplate
},
{
"key": "mode-selector",
"template": modeSelectorTemplate
}
],
"representations": [
{
"key": "time-conductor",
"template": timeConductorTemplate
}
],
"licenses": [
{
"name": "D3: Data-Driven Documents",
"version": "4.1.0",
"author": "Mike Bostock",
"description": "D3 (or D3.js) is a JavaScript library for visualizing data using web standards. D3 helps you bring data to life using SVG, Canvas and HTML. D3 combines powerful visualization and interaction techniques with a data-driven approach to DOM manipulation, giving you the full capabilities of modern browsers and the freedom to design the right visual interface for your data.",
"website": "https://d3js.org/",
"copyright": "Copyright 2010-2016 Mike Bostock",
"license": "BSD-3-Clause",
"link": "https://github.com/d3/d3/blob/master/LICENSE"
}
],
"formats": [
{
"key": "number",
"implementation": NumberFormat
}
]
}
});
});

View File

@ -0,0 +1,3 @@
$ueTimeConductorH: (25px, 3px, 20px);
$timeCondInputTimeSysDefW: 165px; // Default width for datetime value inputs
$timeCondInputDeltaDefW: 60px; // Default width for delta value inputs, typically 00:00:00

View File

@ -0,0 +1,435 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2015, 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.
*****************************************************************************/
@mixin toiLineHovEffects() {
&:before,
&:after {
background-color: $timeControllerToiLineColorHov;
}
}
.l-time-conductor-holder {
border-top: 1px solid $colorInteriorBorder;
min-width: 500px;
padding-top: $interiorMargin;
}
.time-conductor-icon {
$c: $colorObjHdrIc;
$d: 18px;
height: $d !important;
width: $d;
position: relative;
&:before {
@extend .ui-symbol;
color: $c;
content: $glyph-icon-brackets;
font-size: $d;
line-height: normal;
display: block;
width: 100%;
height: 100%;
z-index: 1;
}
// Clock hands
div[class*="hand"] {
$handW: 2px;
$handH: $d * 0.4; //8px;
@include transform(translate(-50%, -50%));
@include animation-iteration-count(infinite);
@include animation-timing-function(linear);
position: absolute;
height: $handW;
width: $handW;
left: 50%;
top: 50%;
z-index: 2;
&:before {
background: $colorObjHdrIc;
content: '';
display: block;
position: absolute;
width: 100%;
bottom: -1px;
}
&.hand-little {
z-index: 2;
@include animation-duration(12s);
&:before {
height: ceil($handH * 0.7);
}
}
&.hand-big {
z-index: 1;
@include animation-duration(1s);
&:before {
height: $handH;
}
}
}
}
.l-time-conductor {
$knobHOffset: 0px;
$rangeValPad: $interiorMargin;
$rangeValOffset: $sliderKnobW + $interiorMargin;
$r1H: nth($ueTimeConductorH, 1);
$r2H: nth($ueTimeConductorH, 2);
$r3H: nth($ueTimeConductorH, 3);
position: relative;
> .l-row-elem {
// First order row elements
box-sizing: border-box;
width: 100%;
position: relative;
}
.mode-selector .s-menu-button,
.time-delta {
&:before {
@extend .ui-symbol;
}
}
.time-delta {
&:before {
color: $colorTimeCondKeyBg;
}
}
.l-time-conductor-inputs-holder,
.l-time-conductor-inputs-and-ticks,
.l-time-conductor-zoom-w {
font-size: 0.8rem;
}
.l-time-conductor-inputs-holder {
$ticksBlockerFadeW: 50px;
$iconCalendarW: 16px;
$wBgColor: $colorBodyBg;
height: $r1H;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
pointer-events: none;
.l-time-range-w {
// Wraps a datetime text input field
height: 100%;
position: absolute;
.title {
display: inline-block;
margin-right: $interiorMarginSm;
}
&.start-w {
@include background-image(linear-gradient(270deg, transparent, $wBgColor $ticksBlockerFadeW));
padding-right: $ticksBlockerFadeW;
.title:before {
content: 'Start';
}
}
&.end-w {
@include background-image(linear-gradient(90deg, transparent, $wBgColor $ticksBlockerFadeW));
padding-left: $ticksBlockerFadeW;
right: 0;
.title:before {
content: 'End';
}
}
.l-time-conductor-inputs {
pointer-events: auto;
}
input[type="text"] {
@include trans-prop-nice(padding, 250ms);
}
.time-range-input input[type="text"] {
width: $timeCondInputTimeSysDefW;
}
.hrs-min-input input[type="text"] {
width: $timeCondInputDeltaDefW;
}
.icon-calendar {
margin-top: 4px;
}
}
}
.l-time-conductor-inputs-and-ticks {
$c: $colorTimeCondTicks; //$colorTick;
height: $r1H;
mct-conductor-axis {
display: block;
position: relative;
width: 100%;
}
.l-axis-holder {
height: $r1H;
position: relative;
width: 100%;
svg {
text-rendering: geometricPrecision;
width: 100%;
height: 100%;
> g {
font-size: 0.9em;
}
path {
// Line beneath ticks
display: none;
}
line {
// Tick marks
stroke: $c;
}
text {
// Tick labels
fill: $c;
}
}
}
}
.l-data-visualization {
background: $colorTimeCondDataVisBg;
height: $r2H;
}
.l-time-conductor-controls {
align-items: center;
margin-top: $interiorMargin;
.l-time-conductor-zoom-w {
@include justify-content(flex-end);
.time-conductor-zoom {
//display: none; // TEMP per request from Andrew 8/1/16
height: $r3H;
min-width: 100px;
width: 20%;
}
.time-conductor-zoom-current-range {
//display: none; // TEMP per request from Andrew 8/1/16
color: $colorTick;
}
}
}
// Real-time, latest modes
&.realtime-mode,
&.lad-mode {
.time-conductor-icon {
&:before { color: $colorTimeCondKeyBg; }
div[class*="hand"] {
@include animation-name(clock-hands);
&:before {
background: $colorTimeCondKeyBg;
}
}
}
.l-time-conductor-inputs-holder {
.l-time-range-input-w {
input[type="text"]:not(.error) {
background: transparent;
box-shadow: none;
border-radius: 0;
padding-left: 0;
padding-right: 0;
&:hover,
&:focus {
@include nice-input();
padding: $inputTextP;
}
}
.icon-calendar {
display: none;
}
&.start-date {
display: none;
}
&.end-date {
pointer-events: none;
input[type="text"] {
color: pullForward($colorTimeCondKeyBg, 5%);
margin-right: $interiorMargin;
tab-index: -1;
}
}
}
}
.l-data-visualization {
background: $colorTimeCondDataVisRtBg !important
}
.mode-selector .s-menu-button {
$fg: $colorTimeCondKeyFg;
@include btnSubtle($bg: $colorTimeCondKeyBg, $bgHov: pullForward($colorTimeCondKeyBg, $ltGamma), $fg: $colorTimeCondKeyFg);
&:before { color: $fg !important; };
color: $fg !important;
}
}
// Fixed mode
&.fixed-mode {
$i: $glyph-icon-calendar;
.time-conductor-icon div[class*="hand"] {
&.hand-little {
@include transform(rotate(120deg));
}
}
.mode-selector .s-menu-button:before {
content: $i;
}
}
// Realtime mode
&.realtime-mode {
$i: $glyph-icon-clock;
.time-conductor-icon div[class*="hand"] {
@include animation-name(clock-hands);
}
.time-delta:before {
content: $i;
}
.l-time-conductor-inputs-holder .l-time-range-w.end-w .title:before {
content: 'Now';
}
.mode-selector .s-menu-button:before {
content: $i;
}
}
// LAD mode
&.lad-mode {
$i: $glyph-icon-database;
.time-conductor-icon div[class*="hand"] {
@include animation-name(clock-hands-sticky);
&.hand-big {
@include animation-duration(5s);
}
&.hand-little {
@include animation-duration(60s);
}
}
.time-delta:before {
content: $i;
}
.l-time-conductor-inputs-holder .l-time-range-w.end-w .title:before {
content: 'LAD';
}
.mode-selector .s-menu-button:before {
content: $i;
}
}
}
/******************************************************************** MOBILE */
@include phoneandtablet {
.l-time-conductor-holder { min-width: 0 !important; }
.super-menu.mini {
width: 200px;
height: 100px;
.pane.menu-item-description {
display: none;
}
}
}
@include phone {
.l-time-conductor {
min-width: 0;
.l-time-conductor-inputs-and-ticks {
.l-time-conductor-inputs-holder {
.l-time-range-w {
background-image: none !important;
}
}
mct-conductor-axis {
display: none;
}
}
}
}
@include phonePortrait {
.l-time-conductor {
.l-data-visualization,
.l-time-conductor-zoom-w,
.time-delta {
display: none;
}
.l-time-conductor-inputs-and-ticks {
height: auto !important;
.l-time-conductor-inputs-holder {
position: relative;
height: auto !important;
.l-time-range-w {
background-image: none !important;
display: block;
height: auto !important;
padding: 0 !important;
position: relative;
text-align: left;
&:not(:first-child) {
margin-top: $interiorMargin;
}
}
}
}
// Fixed mode
&.fixed-mode {
.l-time-conductor-inputs-and-ticks {
.l-time-range-w {
.title {
width: 30px;
}
}
}
}
// Real-time, latest modes
&.realtime-mode,
&.lad-mode {
.l-time-conductor-inputs-and-ticks {
.l-time-range-w {
&.start-w {
display: none;
}
&.end-w {
margin-top: 0;
.end-date input[type="text"] {
margin: 0;
text-align: left;
}
}
}
}
}
}
}

View File

@ -0,0 +1,39 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2015, 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.
*****************************************************************************/
@import "bourbon";
@import "../../../../../commonUI/general/res/sass/constants";
@import "../../../../../commonUI/general/res/sass/mixins";
@import "../../../../../commonUI/general/res/sass/mobile/constants";
@import "../../../../../commonUI/general/res/sass/mobile/mixins";
@import "../../../../../commonUI/themes/espresso/res/sass/constants";
@import "../../../../../commonUI/themes/espresso/res/sass/mixins";
@import "../../../../../commonUI/general/res/sass/glyphs";
@import "../../../../../commonUI/general/res/sass/icons";
@import "constants";
// Thematic constants
$colorTimeCondTicks: pullForward($colorBodyBg, 30%);
$colorTimeCondKeyBg: #4e70dc;
$colorTimeCondKeyFg: #fff;
$colorTimeCondDataVisBg: pullForward($colorBodyBg, 10%);
$colorTimeCondDataVisRtBg: pushBack($colorTimeCondKeyBg, 10%);
@import "time-conductor-base";

View File

@ -0,0 +1,39 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2015, 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.
*****************************************************************************/
@import "bourbon";
@import "../../../../../commonUI/general/res/sass/constants";
@import "../../../../../commonUI/general/res/sass/mixins";
@import "../../../../../commonUI/general/res/sass/mobile/constants";
@import "../../../../../commonUI/general/res/sass/mobile/mixins";
@import "../../../../../commonUI/themes/snow/res/sass/constants";
@import "../../../../../commonUI/themes/snow/res/sass/mixins";
@import "../../../../../commonUI/general/res/sass/glyphs";
@import "../../../../../commonUI/general/res/sass/icons";
@import "constants";
// Thematic constants
$colorTimeCondTicks: pullForward($colorBodyBg, 30%);
$colorTimeCondKeyBg: #6178dc;
$colorTimeCondKeyFg: #fff;
$colorTimeCondDataVisBg: pullForward($colorBodyBg, 10%);
$colorTimeCondDataVisRtBg: pushBack($colorTimeCondKeyBg, 30%);
@import "time-conductor-base";

View File

@ -0,0 +1,45 @@
<!--
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.
-->
<div class="contents">
<div class="pane left menu-items">
<ul>
<li ng-repeat="(key, metadata) in ngModel.options"
ng-click="ngModel.selectedKey=key">
<a ng-mouseover="ngModel.activeMetadata = metadata"
ng-mouseleave="ngModel.activeMetadata = undefined"
class="menu-item-a {{metadata.cssclass}}">
{{metadata.name}}
</a>
</li>
</ul>
</div>
<div class="pane right menu-item-description">
<div
class="desc-area ui-symbol icon type-icon {{ngModel.activeMetadata.cssclass}}"></div>
<div class="desc-area title">
{{ngModel.activeMetadata.name}}
</div>
<div class="desc-area description">
{{ngModel.activeMetadata.description}}
</div>
</div>
</div>

View File

@ -0,0 +1,34 @@
<!--
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.
-->
<span ng-controller="ClickAwayController as modeController">
<div class="s-menu-button"
ng-click="modeController.toggle()">
<span class="title-label">{{ngModel.options[ngModel.selectedKey]
.label}}</span>
</div>
<div class="menu super-menu mini mode-selector-menu"
ng-show="modeController.isActive()">
<mct-include key="'mode-menu'"
ng-model="ngModel">
</mct-include>
</div>
</span>

View File

@ -0,0 +1,131 @@
<!-- Parent holder for time conductor. follow-mode | fixed-mode -->
<style>
.fixed-mode .l-axis-holder {
cursor: grab;
cursor: -webkit-grab;
}
.fixed-mode .l-axis-holder:active {
cursor: grabbing;
cursor: -webkit-grabbing;
}
</style>
<div ng-controller="TimeConductorController as tcController"
class="holder grows flex-elem l-flex-row l-time-conductor {{modeModel.selectedKey}}-mode {{timeSystemModel.selected.metadata.key}}-time-system"
ng-class="{'status-panning': panning}">
<div class="flex-elem holder time-conductor-icon">
<div class="hand-little"></div>
<div class="hand-big"></div>
</div>
<div class="flex-elem holder grows l-flex-col l-time-conductor-inner">
<!-- 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(boundsModel)">
<span class="l-time-range-w start-w">
<span class="l-time-conductor-inputs">
<span class="l-time-range-input-w start-date">
<span class="title"></span>
<mct-control key="'datetime-field'"
structure="{
format: timeSystemModel.format,
validate: tcController.validation.validateStart
}"
ng-model="boundsModel"
ng-blur="tcController.updateBoundsFromForm(boundsModel)"
field="'start'"
class="time-range-input">
</mct-control>
</span>
<span class="l-time-range-input-w time-delta start-delta"
ng-class="{'hide':(modeModel.selectedKey === 'fixed')}">
-
<mct-control key="'datetime-field'"
structure="{
format: timeSystemModel.deltaFormat,
validate: tcController.validation.validateStartDelta
}"
ng-model="boundsModel"
ng-blur="tcController.updateDeltasFromForm(boundsModel)"
field="'startDelta'"
class="hrs-min-input">
</mct-control>
</span>
</span>
</span>
<span class="l-time-range-w end-w">
<span class="l-time-conductor-inputs">
<span class="l-time-range-input-w end-date"
ng-controller="ToggleController as t2">
<span class="title"></span>
<mct-control key="'datetime-field'"
structure="{
format: timeSystemModel.format,
validate: tcController.validation.validateEnd
}"
ng-model="boundsModel"
ng-blur="tcController.updateBoundsFromForm(boundsModel)"
ng-disabled="modeModel.selectedKey !== 'fixed'"
field="'end'"
class="time-range-input">
</mct-control>
</span>
<span class="l-time-range-input-w time-delta end-delta"
ng-class="{'hide':(modeModel.selectedKey === 'fixed')}">
+
<mct-control key="'datetime-field'"
structure="{
format: timeSystemModel.deltaFormat,
validate: tcController.validation.validateEndDelta
}"
ng-model="boundsModel"
ng-blur="tcController.updateDeltasFromForm(boundsModel)"
field="'endDelta'"
class="hrs-min-input">
</mct-control>
</span>
</span>
</span>
<input type="submit" class="hidden">
</form>
<mct-conductor-axis></mct-conductor-axis>
</div>
<!-- Holds data availability, time of interest -->
<div class="l-data-visualization l-row-elem flex-elem"></div>
<!-- Holds time system and session selectors, and zoom control -->
<div class="l-time-conductor-controls l-row-elem l-flex-row flex-elem">
<mct-include
key="'mode-selector'"
ng-model="modeModel"
class="holder flex-elem menus-up mode-selector">
</mct-include>
<mct-control
key="'menu-button'"
class="holder flex-elem menus-up time-system"
structure="{
text: timeSystemModel.selected.metadata.name,
click: tcController.selectTimeSystemByKey,
options: timeSystemModel.options
}">
</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">{{timeUnits}}</span>
<input class="time-conductor-zoom flex-elem" type="range"
ng-model="currentZoom"
ng-mouseUp="tcController.zoomStop(currentZoom)"
ng-change="tcController.zoomDrag(currentZoom)"
min="0.01"
step="0.01"
max="0.99" />
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,179 @@
/*****************************************************************************
* 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(['EventEmitter'], function (EventEmitter) {
/**
* The public API for setting and querying time conductor state. The
* time conductor is the means by which the temporal bounds of a view
* are controlled. Time-sensitive views will typically respond to
* changes to bounds or other properties of the time conductor and
* update the data displayed based on the time conductor state.
*
* The TimeConductor extends the EventEmitter class. A number of events are
* fired when properties of the time conductor change, which are
* documented below.
* @constructor
*/
function TimeConductor() {
EventEmitter.call(this);
//The Time System
this.system = undefined;
//The Time Of Interest
this.toi = undefined;
this.boundsVal = {
start: undefined,
end: undefined
};
//Default to fixed mode
this.followMode = false;
}
TimeConductor.prototype = Object.create(EventEmitter.prototype);
/**
* Validate the given bounds. This can be used for pre-validation of
* bounds, for example by views validating user inputs.
* @param bounds The start and end time of the conductor.
* @returns {string | true} A validation error, or true if valid
*/
TimeConductor.prototype.validateBounds = function (bounds) {
if ((bounds.start === undefined) ||
(bounds.end === undefined) ||
isNaN(bounds.start) ||
isNaN(bounds.end)
) {
return "Start and end must be specified as integer values";
} else if (bounds.start > bounds.end) {
return "Specified start date exceeds end bound";
}
return true;
};
/**
* Get or set the follow mode of the time conductor. In follow mode the
* time conductor ticks, regularly updating the bounds from a timing
* source appropriate to the selected time system and mode of the time
* conductor.
* @fires TimeConductor#follow
* @param {boolean} followMode
* @returns {boolean}
*/
TimeConductor.prototype.follow = function (followMode) {
if (arguments.length > 0) {
this.followMode = followMode;
/**
* @event TimeConductor#follow The TimeConductor has toggled
* into or out of follow mode.
* @property {boolean} followMode true if follow mode is
* enabled, otherwise false.
*/
this.emit('follow', this.followMode);
}
return this.followMode;
};
/**
* @typedef {Object} TimeConductorBounds
* @property {number} start The start time displayed by the time conductor in ms since epoch. Epoch determined by current time system
* @property {number} end The end time displayed by the time conductor in ms since epoch.
*/
/**
* Get or set the start and end time of the time conductor. Basic validation
* of bounds is performed.
*
* @param {TimeConductorBounds} newBounds
* @throws {Error} Validation error
* @fires TimeConductor#bounds
* @returns {TimeConductorBounds}
*/
TimeConductor.prototype.bounds = function (newBounds) {
if (arguments.length > 0) {
var validationResult = this.validateBounds(newBounds);
if (validationResult !== true) {
throw new Error(validationResult);
}
//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
* @property {TimeConductorBounds} bounds
*/
this.emit('bounds', this.boundsVal);
}
//Return a copy to prevent direct mutation of time conductor bounds.
return JSON.parse(JSON.stringify(this.boundsVal));
};
/**
* Get or set the time system of the TimeConductor. Time systems determine
* units, epoch, and other aspects of time representation. When changing
* the time system in use, new valid bounds must also be provided.
* @param {TimeSystem} newTimeSystem
* @param {TimeConductorBounds} bounds
* @fires TimeConductor#timeSystem
* @returns {TimeSystem} The currently applied time system
*/
TimeConductor.prototype.timeSystem = function (newTimeSystem, bounds) {
if (arguments.length >= 2) {
this.system = newTimeSystem;
/**
* @event TimeConductor#timeSystem The time system used by the time
* conductor has changed. A change in Time System will always be
* followed by a bounds event specifying new query bounds
* @property {TimeSystem} The value of the currently applied
* Time System
* */
this.emit('timeSystem', this.system);
this.bounds(bounds);
} else if (arguments.length === 1) {
throw new Error('Must set bounds when changing time system');
}
return this.system;
};
/**
* Get or set the Time of Interest. The Time of Interest is the temporal
* focus of the current view. It can be manipulated by the user from the
* time conductor or from other views.
* @fires TimeConductor#timeOfInterest
* @param newTOI
* @returns {number} the current time of interest
*/
TimeConductor.prototype.timeOfInterest = function (newTOI) {
if (arguments.length > 0) {
this.toi = newTOI;
/**
* @event TimeConductor#timeOfInterest The Time of Interest has moved.
* @property {number} Current time of interest
*/
this.emit('timeOfInterest', this.toi);
}
return this.toi;
};
return TimeConductor;
});

View File

@ -0,0 +1,110 @@
/*****************************************************************************
* 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) {
describe("The Time Conductor", function () {
var tc,
timeSystem,
bounds,
eventListener,
toi,
follow;
beforeEach(function () {
tc = new TimeConductor();
timeSystem = {};
bounds = {start: 0, end: 0};
eventListener = jasmine.createSpy("eventListener");
toi = 111;
follow = true;
});
it("Supports setting and querying of time of interest and and follow mode", function () {
expect(tc.timeOfInterest()).not.toBe(toi);
tc.timeOfInterest(toi);
expect(tc.timeOfInterest()).toBe(toi);
expect(tc.follow()).not.toBe(follow);
tc.follow(follow);
expect(tc.follow()).toBe(follow);
});
it("Allows setting of valid bounds", function () {
bounds = {start: 0, end: 1};
expect(tc.bounds()).not.toEqual(bounds);
expect(tc.bounds.bind(tc, bounds)).not.toThrow();
expect(tc.bounds()).toEqual(bounds);
});
it("Disallows setting of invalid bounds", function () {
bounds = {start: 1, end: 0};
expect(tc.bounds()).not.toEqual(bounds);
expect(tc.bounds.bind(tc, bounds)).toThrow();
expect(tc.bounds()).not.toEqual(bounds);
bounds = {start: 1};
expect(tc.bounds()).not.toEqual(bounds);
expect(tc.bounds.bind(tc, bounds)).toThrow();
expect(tc.bounds()).not.toEqual(bounds);
});
it("Allows setting of time system with bounds", function () {
expect(tc.timeSystem()).not.toBe(timeSystem);
expect(tc.timeSystem.bind(tc, timeSystem, bounds)).not.toThrow();
expect(tc.timeSystem()).toBe(timeSystem);
});
it("Disallows setting of time system without bounds", function () {
expect(tc.timeSystem()).not.toBe(timeSystem);
expect(tc.timeSystem.bind(tc, timeSystem)).toThrow();
expect(tc.timeSystem()).not.toBe(timeSystem);
});
it("Emits an event when time system changes", function () {
expect(eventListener).not.toHaveBeenCalled();
tc.on("timeSystem", eventListener);
tc.timeSystem(timeSystem, bounds);
expect(eventListener).toHaveBeenCalledWith(timeSystem);
});
it("Emits an event when time of interest changes", function () {
expect(eventListener).not.toHaveBeenCalled();
tc.on("timeOfInterest", eventListener);
tc.timeOfInterest(toi);
expect(eventListener).toHaveBeenCalledWith(toi);
});
it("Emits an event when bounds change", function () {
expect(eventListener).not.toHaveBeenCalled();
tc.on("bounds", eventListener);
tc.bounds(bounds);
expect(eventListener).toHaveBeenCalledWith(bounds);
});
it("Emits an event when follow mode changes", function () {
expect(eventListener).not.toHaveBeenCalled();
tc.on("follow", eventListener);
tc.follow(follow);
expect(eventListener).toHaveBeenCalledWith(follow);
});
});
});

View File

@ -0,0 +1,89 @@
/*****************************************************************************
* 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(['./TickSource'], function (TickSource) {
/**
* @implements TickSource
* @constructor
*/
function LocalClock($timeout, period) {
TickSource.call(this);
this.metadata = {
key: 'local',
mode: '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.'
};
this.period = period;
this.$timeout = $timeout;
this.timeoutHandle = undefined;
}
LocalClock.prototype = Object.create(TickSource.prototype);
LocalClock.prototype.start = function () {
this.timeoutHandle = this.$timeout(this.tick.bind(this), this.period);
};
LocalClock.prototype.stop = function () {
if (this.timeoutHandle) {
this.$timeout.cancel(this.timeoutHandle);
}
};
LocalClock.prototype.tick = function () {
var now = Date.now();
this.listeners.forEach(function (listener) {
listener(now);
});
this.timeoutHandle = this.$timeout(this.tick.bind(this), this.period);
};
/**
* Register a listener for the local clock. When it ticks, the local
* clock will provide the current local system time
*
* @param listener
* @returns {function} a function for deregistering the provided listener
*/
LocalClock.prototype.listen = function (listener) {
var listeners = this.listeners;
listeners.push(listener);
if (listeners.length === 1) {
this.start();
}
return function () {
listeners.splice(listeners.indexOf(listener));
if (listeners.length === 0) {
this.stop();
}
}.bind(this);
};
return LocalClock;
});

View File

@ -0,0 +1,50 @@
/*****************************************************************************
* 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(["./LocalClock"], function (LocalClock) {
describe("The LocalClock class", function () {
var clock,
mockTimeout,
timeoutHandle = {};
beforeEach(function () {
mockTimeout = jasmine.createSpy("timeout");
mockTimeout.andReturn(timeoutHandle);
mockTimeout.cancel = jasmine.createSpy("cancel");
clock = new LocalClock(mockTimeout, 0);
clock.start();
});
it("calls listeners on tick with current time", function () {
var mockListener = jasmine.createSpy("listener");
clock.listen(mockListener);
clock.tick();
expect(mockListener).toHaveBeenCalledWith(jasmine.any(Number));
});
it("stops ticking when stop is called", function () {
clock.stop();
expect(mockTimeout.cancel).toHaveBeenCalledWith(timeoutHandle);
});
});
});

View File

@ -0,0 +1,47 @@
/*****************************************************************************
* 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 () {
/**
* A tick source is an event generator such as a timing signal, or
* indicator of data availability, which can be used to advance the Time
* Conductor. Usage is simple, a listener registers a callback which is
* invoked when this source 'ticks'.
*
* @interface
* @constructor
*/
function TickSource() {
this.listeners = [];
}
/**
* @param callback Function to be called when this tick source ticks.
* @returns an 'unlisten' function that will remove the callback from
* the registered listeners
*/
TickSource.prototype.listen = function (callback) {
throw new Error('Not implemented');
};
return TickSource;
});

View File

@ -0,0 +1,105 @@
/*****************************************************************************
* 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 () {
/**
* @interface
* @constructor
*/
function TimeSystem() {
/**
* @typedef TimeSystemMetadata
* @property {string} key
* @property {string} name
* @property {string} description
*
* @type {TimeSystemMetadata}
*/
this.metadata = undefined;
}
/**
* Time formats are defined as extensions. Time systems that implement
* this interface should provide an array of format keys supported by them.
*
* @returns {string[]} An array of time format keys
*/
TimeSystem.prototype.formats = function () {
throw new Error('Not implemented');
};
/**
* @typedef DeltaFormat
* @property {string} type the type of MctControl used to represent this
* field. Typically 'datetime-field' for UTC based dates, or 'textfield'
* otherwise
* @property {string} [format] An optional field specifying the
* Format to use for delta fields in this time system.
*/
/**
* Specifies a format for deltas in this time system.
*
* @returns {DeltaFormat} a delta format specifier
*/
TimeSystem.prototype.deltaFormat = function () {
throw new Error('Not implemented');
};
/**
* Returns the tick sources supported by this time system. Tick sources
* are event generators that can be used to advance the time conductor
* @returns {TickSource[]} The tick sources supported by this time system.
*/
TimeSystem.prototype.tickSources = function () {
throw new Error('Not implemented');
};
/***
*
* @typedef {object} TimeConductorZoom
* @property {number} min The largest time span that the time
* conductor can display in this time system
* @property {number} max The smallest time span that the time
* conductor can display in this time system
*
* @typedef {object} TimeSystemDefault
* @property {TimeConductorDeltas} deltas The deltas to apply by default
* when this time system is active. Applies to real-time modes only
* @property {TimeConductorBounds} bounds The bounds to apply by default
* when this time system is active
* @property {TimeConductorZoom} zoom Default min and max zoom levels
* @returns {TimeSystemDefault[]} At least one set of default values for
* this time system.
*/
TimeSystem.prototype.defaults = function () {
throw new Error('Not implemented');
};
/**
* @return {boolean}
*/
TimeSystem.prototype.isUTCBased = function () {
return true;
};
return TimeSystem;
});

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(
[
"d3"
],
function (d3) {
var PADDING = 1;
/**
* 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 ConductorAxisController(conductor, formatService, conductorViewService) {
// Dependencies
this.d3 = d3;
this.formatService = formatService;
this.conductor = conductor;
this.conductorViewService = conductorViewService;
// Runtime properties (set by 'link' function)
this.target = undefined;
this.xScale = undefined;
this.xAxis = undefined;
this.axisElement = undefined;
this.initialized = false;
this.msPerPixel = undefined;
this.bounds = conductor.bounds();
this.timeSystem = conductor.timeSystem();
//Bind all class functions to 'this'
Object.keys(ConductorAxisController.prototype).filter(function (key) {
return typeof ConductorAxisController.prototype[key] === 'function';
}).forEach(function (key) {
this[key] = ConductorAxisController.prototype[key].bind(this);
}.bind(this));
}
ConductorAxisController.prototype.destroy = function () {
this.conductor.off('timeSystem', this.changeTimeSystem);
this.conductor.off('bounds', this.setScale);
this.conductorViewService.off("zoom", this.onZoom);
this.conductorViewService.off("zoom-stop", this.onZoomStop)
};
ConductorAxisController.prototype.changeBounds = function (bounds) {
this.bounds = bounds;
if (this.initialized && !this.zooming) {
this.setScale();
}
};
ConductorAxisController.prototype.setScale = function () {
var width = this.target.offsetWidth;
var timeSystem = this.conductor.timeSystem();
var bounds = this.bounds;
if (timeSystem.isUTCBased()) {
this.xScale = this.xScale || this.d3.scaleUtc();
this.xScale.domain([new Date(bounds.start), new Date(bounds.end)]);
} else {
this.xScale = this.xScale || this.d3.scaleLinear();
this.xScale.domain([bounds.start, bounds.end]);
}
this.xScale.range([PADDING, width - PADDING * 2]);
this.axisElement.call(this.xAxis);
this.msPerPixel = (bounds.end - bounds.start) / width;
};
ConductorAxisController.prototype.changeTimeSystem = function (timeSystem) {
this.timeSystem = timeSystem;
var key = timeSystem.formats()[0];
if (this.initialized && key !== undefined) {
var format = this.formatService.getFormat(key);
var bounds = this.conductor.bounds();
if (timeSystem.isUTCBased()) {
this.xScale = this.d3.scaleUtc();
} else {
this.xScale = this.d3.scaleLinear();
}
this.xAxis.scale(this.xScale);
//Define a custom format function
this.xAxis.tickFormat(function (tickValue) {
// Normalize date representations to numbers
if (tickValue instanceof Date) {
tickValue = tickValue.getTime();
}
return format.format(tickValue, {
min: bounds.start,
max: bounds.end
});
});
this.axisElement.call(this.xAxis);
}
};
ConductorAxisController.prototype.link = function (scope, element) {
this.target = element[0].firstChild;
this.scope = scope;
var height = this.target.offsetHeight;
var vis = this.d3.select(this.target)
.append("svg:svg")
.attr("width", "100%")
.attr("height", height);
this.xAxis = this.d3.axisTop();
// draw x axis with labels and move to the bottom of the chart area
this.axisElement = vis.append("g")
.attr("transform", "translate(0," + (height - PADDING) + ")");
this.initialized = true;
if (this.timeSystem !== undefined) {
this.changeTimeSystem(this.timeSystem);
this.setScale();
}
//Respond to changes in conductor
this.conductor.on("timeSystem", this.changeTimeSystem);
this.conductor.on("bounds", this.changeBounds);
this.scope.$on("$destroy", this.destroy);
this.conductorViewService.on("zoom", this.onZoom);
this.conductorViewService.on("zoom-stop", this.onZoomStop);
};
ConductorAxisController.prototype.panStop = function () {
//resync view bounds with time conductor bounds
this.conductorViewService.emit("pan-stop");
this.conductor.bounds(this.bounds);
};
ConductorAxisController.prototype.onZoom = function (zoom) {
this.zooming = true;
this.bounds = zoom.bounds;
this.setScale();
};
ConductorAxisController.prototype.onZoomStop = function (zoom) {
this.zooming = false;
};
ConductorAxisController.prototype.pan = function (delta) {
if (!this.conductor.follow()) {
var deltaInMs = delta[0] * this.msPerPixel;
var bounds = this.conductor.bounds();
var start = Math.floor((bounds.start - deltaInMs) / 1000) * 1000;
var end = Math.floor((bounds.end - deltaInMs) / 1000) * 1000;
this.bounds = {
start: start,
end: end
};
this.setScale();
this.conductorViewService.emit("pan", this.bounds);
}
};
ConductorAxisController.prototype.resize = function () {
if (this.initialized) {
this.setScale();
}
};
return ConductorAxisController;
}
);

View File

@ -0,0 +1,52 @@
/*****************************************************************************
* 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 () {
function MctConductorAxis() {
/**
* The mct-conductor-axis renders a horizontal axis with regular
* labelled 'ticks'. It requires 'start' and 'end' integer values to
* be specified as attributes.
*/
return {
controller: 'ConductorAxisController',
controllerAs: 'axis',
link: function(scope, element, attrs, controller){
controller.link(scope, element);
},
restrict: 'E',
priority: 1000,
template: '<div class="l-axis-holder" ' +
' mct-drag-down="axis.panStart()"' +
' mct-drag-up="axis.panStop(delta)"' +
' mct-drag="axis.pan(delta)"' +
' mct-resize="axis.resize()"></div>'
}
}
return MctConductorAxis;
}
);

View File

@ -0,0 +1,146 @@
/*****************************************************************************
* 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(['./MctConductorAxis'], function (MctConductorAxis) {
describe("The MctConductorAxis directive", function () {
var directive,
mockConductor,
mockFormatService,
mockScope,
mockElement,
mockTarget,
mockBounds,
d3;
beforeEach(function () {
mockScope = jasmine.createSpyObj("scope", [
"$on"
]);
//Add some HTML elements
mockTarget = {
offsetWidth: 0,
offsetHeight: 0
};
mockElement = {
firstChild: mockTarget
};
mockBounds = {
start: 100,
end: 200
};
mockConductor = jasmine.createSpyObj("conductor", [
"timeSystem",
"bounds",
"on",
"off"
]);
mockConductor.bounds.andReturn(mockBounds);
mockFormatService = jasmine.createSpyObj("formatService", [
"getFormat"
]);
var d3Functions = [
"scale",
"scaleUtc",
"scaleLinear",
"select",
"append",
"attr",
"axisTop",
"call",
"tickFormat",
"domain",
"range"
];
d3 = jasmine.createSpyObj("d3", d3Functions);
d3Functions.forEach(function (func) {
d3[func].andReturn(d3);
});
directive = new MctConductorAxis(mockConductor, mockFormatService);
directive.d3 = d3;
directive.link(mockScope, [mockElement]);
});
it("listens for changes to time system and bounds", function () {
expect(mockConductor.on).toHaveBeenCalledWith("timeSystem", directive.changeTimeSystem);
expect(mockConductor.on).toHaveBeenCalledWith("bounds", directive.setScale);
});
it("on scope destruction, deregisters listeners", function () {
expect(mockScope.$on).toHaveBeenCalledWith("$destroy", directive.destroy);
directive.destroy();
expect(mockConductor.off).toHaveBeenCalledWith("timeSystem", directive.changeTimeSystem);
expect(mockConductor.off).toHaveBeenCalledWith("bounds", directive.setScale);
});
describe("when the time system changes", function () {
var mockTimeSystem;
var mockFormat;
beforeEach(function () {
mockTimeSystem = jasmine.createSpyObj("timeSystem", [
"formats",
"isUTCBased"
]);
mockFormat = jasmine.createSpyObj("format", [
"format"
]);
mockTimeSystem.formats.andReturn(["mockFormat"]);
mockFormatService.getFormat.andReturn(mockFormat);
});
it("uses a UTC scale for UTC time systems", function () {
mockTimeSystem.isUTCBased.andReturn(true);
directive.changeTimeSystem(mockTimeSystem);
expect(d3.scaleUtc).toHaveBeenCalled();
expect(d3.scaleLinear).not.toHaveBeenCalled();
});
it("uses a linear scale for non-UTC time systems", function () {
mockTimeSystem.isUTCBased.andReturn(false);
directive.changeTimeSystem(mockTimeSystem);
expect(d3.scaleLinear).toHaveBeenCalled();
expect(d3.scaleUtc).not.toHaveBeenCalled();
});
it("sets axis domain to time conductor bounds", function () {
mockTimeSystem.isUTCBased.andReturn(false);
mockConductor.timeSystem.andReturn(mockTimeSystem);
directive.setScale();
expect(d3.domain).toHaveBeenCalledWith([mockBounds.start, mockBounds.end]);
});
it("uses the format specified by the time system to format tick" +
" labels", function () {
directive.changeTimeSystem(mockTimeSystem);
expect(d3.tickFormat).toHaveBeenCalled();
d3.tickFormat.mostRecentCall.args[0]();
expect(mockFormat.format).toHaveBeenCalled();
});
});
});
});

View File

@ -0,0 +1,53 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-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 () {
/**
* Formatter for basic numbers. Provides basic support for non-UTC
* numbering systems
*
* @implements {Format}
* @constructor
* @memberof platform/commonUI/formats
*/
function NumberFormat() {
}
NumberFormat.prototype.format = function (value) {
if (isNaN(value)) {
return '';
} else {
return '' + value;
}
};
NumberFormat.prototype.parse = function (text) {
return parseFloat(text);
};
NumberFormat.prototype.validate = function (text) {
return !isNaN(text);
};
return NumberFormat;
});

View File

@ -0,0 +1,49 @@
/*****************************************************************************
* 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(['./NumberFormat'], function (NumberFormat) {
describe("The NumberFormat class", function () {
var format;
beforeEach(function () {
format = new NumberFormat();
});
it("The format function takes a string and produces a number", function () {
var text = format.format(1);
expect(text).toBe("1");
expect(typeof text).toBe("string");
});
it("The parse function takes a string and produces a number", function () {
var number = format.parse("1");
expect(number).toBe(1);
expect(typeof number).toBe("number");
});
it("validates that the input is a number", function () {
expect(format.validate("1")).toBe(true);
expect(format.validate(1)).toBe(true);
expect(format.validate("1.1")).toBe(true);
expect(format.validate("abc")).toBe(false);
});
});
});

View File

@ -0,0 +1,317 @@
/*****************************************************************************
* 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(
[
'./TimeConductorValidation'
],
function (TimeConductorValidation) {
function TimeConductorController($scope, $window, timeConductor, conductorViewService, timeSystems, formatService) {
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.$scope = $scope;
this.$window = $window;
this.conductorViewService = conductorViewService;
this.conductor = timeConductor;
this.modes = conductorViewService.availableModes();
this.validation = new TimeConductorValidation(this.conductor);
this.formatService = formatService;
// Construct the provided time system definitions
this.timeSystems = timeSystems.map(function (timeSystemConstructor) {
return timeSystemConstructor();
});
//Set the initial state of the view based on current time conductor
this.initializeScope();
this.conductor.on('bounds', this.changeBounds);
this.conductor.on('timeSystem', this.changeTimeSystem);
// If no mode selected, select fixed as the default
if (!this.conductorViewService.mode()) {
this.setMode('fixed');
}
}
/**
* @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.conductorViewService.on('pan', this.onPan);
this.conductorViewService.on('pan-stop', this.onPanStop);
this.$scope.$on('$destroy', this.destroy);
};
TimeConductorController.prototype.destroy = function () {
this.conductor.off('bounds', this.setFormFromBounds);
this.conductor.off('timeSystem', this.changeTimeSystem);
this.conductorViewService.off('pan', this.onPan);
this.conductorViewService.off('pan-stop', this.onPanStop);
};
TimeConductorController.prototype.onPan = function (bounds) {
this.$scope.panning = true;
this.$scope.boundsModel.start = bounds.start;
this.$scope.boundsModel.end = bounds.end;
};
TimeConductorController.prototype.onPanStop = function () {
this.$scope.panning = false;
};
TimeConductorController.prototype.changeBounds = function (bounds) {
if (!this.$scope.zooming && !this.$scope.panning) {
this.setFormFromBounds(bounds);
}
};
/**
* Called when the bounds change in the time conductor. Synchronizes
* the bounds values in the time conductor with those in the form
*
* @private
*/
TimeConductorController.prototype.setFormFromBounds = function (bounds) {
if (!this.$scope.zooming && ! this.$scope.panning) {
this.$scope.boundsModel.start = bounds.start;
this.$scope.boundsModel.end = bounds.end;
this.$scope.currentZoom = this.toSliderValue(bounds.end - bounds.start);
this.toTimeUnits(bounds.end - bounds.start);
if (!this.pendingUpdate) {
this.pendingUpdate = true;
this.$window.requestAnimationFrame(function () {
this.pendingUpdate = false;
this.$scope.$digest();
}.bind(this));
}
}
};
/**
* @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) {
this.$scope.boundsModel.startDelta = deltas.start;
this.$scope.boundsModel.endDelta = deltas.end;
};
/**
* @private
*/
TimeConductorController.prototype.setFormFromTimeSystem = function (timeSystem) {
var timeSystemModel = this.$scope.timeSystemModel;
timeSystemModel.selected = timeSystem;
timeSystemModel.format = timeSystem.formats()[0];
timeSystemModel.deltaFormat = timeSystem.deltaFormat();
timeSystemModel.minZoom = timeSystem.defaults().zoom.min;
timeSystemModel.maxZoom = timeSystem.defaults().zoom.max;
};
/**
* Called when form values are changed. Synchronizes the form with
* the time conductor
* @param formModel
*/
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 boundsModel
* @see TimeConductorMode
*/
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
this.conductorViewService.deltas(deltas);
}
};
/**
* 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) {
this.conductorViewService.mode(newModeKey);
this.setFormFromMode(newModeKey);
}
};
/**
* Respond to time system selection from UI
*
* 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.selectTimeSystemByKey = function (key) {
var selected = this.timeSystems.filter(function (timeSystem) {
return timeSystem.metadata.key === key;
})[0];
this.conductor.timeSystem(selected, selected.defaults().bounds);
};
/**
* Handles time system change from time conductor
*
* 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.changeTimeSystem = function (newTimeSystem) {
if (newTimeSystem && (newTimeSystem !== this.$scope.timeSystemModel.selected)) {
if (newTimeSystem.defaults()) {
var deltas = newTimeSystem.defaults().deltas || {start: 0, end: 0};
var bounds = newTimeSystem.defaults().bounds || {start: 0, end: 0};
this.setFormFromDeltas(deltas);
this.setFormFromBounds(bounds);
}
this.setFormFromTimeSystem(newTimeSystem);
}
};
TimeConductorController.prototype.toSliderValue = function (timeSpan) {
var timeSystem = this.conductor.timeSystem();
if (timeSystem) {
var zoomDefaults = this.conductor.timeSystem().defaults().zoom;
var perc = timeSpan / (zoomDefaults.min - zoomDefaults.max);
return 1 - Math.pow(perc, 1 / 4);
}
};
TimeConductorController.prototype.toTimeUnits = function (timeSpan) {
if (this.conductor.timeSystem()) {
var timeFormat = this.formatService.getFormat(this.conductor.timeSystem().formats()[0]);
this.$scope.timeUnits = timeFormat.timeUnits && timeFormat.timeUnits(timeSpan);
}
};
TimeConductorController.prototype.zoomDrag = function(sliderValue) {
var zoomDefaults = this.conductor.timeSystem().defaults().zoom;
var timeSpan = Math.pow((1 - sliderValue), 4) * (zoomDefaults.min - zoomDefaults.max);
var zoom = this.conductorViewService.zoom(timeSpan);
this.$scope.boundsModel.start = zoom.bounds.start;
this.$scope.boundsModel.end = zoom.bounds.end;
this.toTimeUnits(zoom.bounds.end - zoom.bounds.start);
if (zoom.deltas) {
this.setFormFromDeltas(zoom.deltas);
}
this.$scope.zooming = true;
};
TimeConductorController.prototype.zoomStop = function () {
this.updateBoundsFromForm(this.$scope.boundsModel);
this.updateDeltasFromForm(this.$scope.boundsModel);
this.$scope.zooming = false;
this.conductorViewService.emit('zoom-stop');
};
return TimeConductorController;
}
);

View File

@ -0,0 +1,335 @@
/*****************************************************************************
* 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(['./TimeConductorController'], function (TimeConductorController) {
describe("The time conductor controller", function () {
var mockScope;
var mockWindow;
var mockTimeConductor;
var mockConductorViewService;
var mockTimeSystems;
var controller;
beforeEach(function () {
mockScope = jasmine.createSpyObj("$scope", [
"$watch",
"$on"
]);
mockWindow = jasmine.createSpyObj("$window", ["requestAnimationFrame"]);
mockTimeConductor = jasmine.createSpyObj(
"TimeConductor",
[
"bounds",
"timeSystem",
"on",
"off"
]
);
mockTimeConductor.bounds.andReturn({start: undefined, end: undefined});
mockConductorViewService = jasmine.createSpyObj(
"ConductorViewService",
[
"availableModes",
"mode",
"availableTimeSystems",
"deltas"
]
);
mockConductorViewService.availableModes.andReturn([]);
mockConductorViewService.availableTimeSystems.andReturn([]);
mockTimeSystems = [];
});
function getListener(name) {
return mockTimeConductor.on.calls.filter(function (call) {
return call.args[0] === name;
})[0].args[1];
}
describe("", function () {
beforeEach(function () {
controller = new TimeConductorController(
mockScope,
mockWindow,
mockTimeConductor,
mockConductorViewService,
mockTimeSystems
);
});
});
describe("when time conductor state changes", function () {
var mockFormat;
var mockDeltaFormat;
var defaultBounds;
var defaultDeltas;
var mockDefaults;
var timeSystem;
var tsListener;
beforeEach(function () {
mockFormat = {};
mockDeltaFormat = {};
defaultBounds = {
start: 2,
end: 3
};
defaultDeltas = {
start: 10,
end: 20
};
mockDefaults = {
deltas: defaultDeltas,
bounds: defaultBounds
};
timeSystem = {
formats: function () {
return [mockFormat];
},
deltaFormat: function () {
return mockDeltaFormat;
},
defaults: function () {
return mockDefaults;
}
};
controller = new TimeConductorController(
mockScope,
mockWindow,
mockTimeConductor,
mockConductorViewService,
mockTimeSystems
);
tsListener = getListener("timeSystem");
});
it("listens for changes to conductor state", function () {
expect(mockTimeConductor.on).toHaveBeenCalledWith("timeSystem", controller.changeTimeSystem);
expect(mockTimeConductor.on).toHaveBeenCalledWith("bounds", controller.setFormFromBounds);
});
it("deregisters conductor listens when scope is destroyed", function () {
expect(mockScope.$on).toHaveBeenCalledWith("$destroy", controller.destroy);
controller.destroy();
expect(mockTimeConductor.off).toHaveBeenCalledWith("timeSystem", controller.changeTimeSystem);
expect(mockTimeConductor.off).toHaveBeenCalledWith("bounds", controller.setFormFromBounds);
});
it("when time system changes, sets time system on scope", function () {
expect(tsListener).toBeDefined();
tsListener(timeSystem);
expect(mockScope.timeSystemModel).toBeDefined();
expect(mockScope.timeSystemModel.selected).toBe(timeSystem);
expect(mockScope.timeSystemModel.format).toBe(mockFormat);
expect(mockScope.timeSystemModel.deltaFormat).toBe(mockDeltaFormat);
});
it("when time system changes, sets defaults on scope", function () {
expect(tsListener).toBeDefined();
tsListener(timeSystem);
expect(mockScope.boundsModel.start).toEqual(defaultBounds.start);
expect(mockScope.boundsModel.end).toEqual(defaultBounds.end);
expect(mockScope.boundsModel.startDelta).toEqual(defaultDeltas.start);
expect(mockScope.boundsModel.endDelta).toEqual(defaultDeltas.end);
});
it("when bounds change, sets them on scope", function () {
var bounds = {
start: 1,
end: 2
};
var boundsListener = getListener("bounds");
expect(boundsListener).toBeDefined();
boundsListener(bounds);
expect(mockScope.boundsModel).toBeDefined();
expect(mockScope.boundsModel.start).toEqual(bounds.start);
expect(mockScope.boundsModel.end).toEqual(bounds.end);
});
});
describe("when user makes changes from UI", function () {
var mode = "realtime";
var ts1Metadata;
var ts2Metadata;
var ts3Metadata;
var mockTimeSystemConstructors;
beforeEach(function () {
mode = "realtime";
ts1Metadata = {
'key': 'ts1',
'name': 'Time System One',
'cssClass': 'cssClassOne'
};
ts2Metadata = {
'key': 'ts2',
'name': 'Time System Two',
'cssClass': 'cssClassTwo'
};
ts3Metadata = {
'key': 'ts3',
'name': 'Time System Three',
'cssClass': 'cssClassThree'
};
mockTimeSystems = [
{
metadata: ts1Metadata
},
{
metadata: ts2Metadata
},
{
metadata: ts3Metadata
}
];
//Wrap in mock constructors
mockTimeSystemConstructors = mockTimeSystems.map(function (mockTimeSystem) {
return function () {
return mockTimeSystem;
};
});
});
it("sets the mode on scope", function () {
controller = new TimeConductorController(
mockScope,
mockWindow,
mockTimeConductor,
mockConductorViewService,
mockTimeSystemConstructors
);
mockConductorViewService.availableTimeSystems.andReturn(mockTimeSystems);
controller.setMode(mode);
expect(mockScope.modeModel.selectedKey).toEqual(mode);
});
it("sets available time systems on scope when mode changes", function () {
controller = new TimeConductorController(
mockScope,
mockWindow,
mockTimeConductor,
mockConductorViewService,
mockTimeSystemConstructors
);
mockConductorViewService.availableTimeSystems.andReturn(mockTimeSystems);
controller.setMode(mode);
expect(mockScope.timeSystemModel.options.length).toEqual(3);
expect(mockScope.timeSystemModel.options[0]).toEqual(ts1Metadata);
expect(mockScope.timeSystemModel.options[1]).toEqual(ts2Metadata);
expect(mockScope.timeSystemModel.options[2]).toEqual(ts3Metadata);
});
it("sets bounds on the time conductor", function () {
var formModel = {
start: 1,
end: 10
};
controller = new TimeConductorController(
mockScope,
mockWindow,
mockTimeConductor,
mockConductorViewService,
mockTimeSystemConstructors
);
controller.updateBoundsFromForm(formModel);
expect(mockTimeConductor.bounds).toHaveBeenCalledWith(formModel);
});
it("applies deltas when they change in form", function () {
var deltas = {
start: 1000,
end: 2000
};
var formModel = {
startDelta: deltas.start,
endDelta: deltas.end
};
controller = new TimeConductorController(
mockScope,
mockWindow,
mockTimeConductor,
mockConductorViewService,
mockTimeSystemConstructors
);
controller.updateDeltasFromForm(formModel);
expect(mockConductorViewService.deltas).toHaveBeenCalledWith(deltas);
});
it("sets the time system on the time conductor", function () {
var defaultBounds = {
start: 5,
end: 6
};
var timeSystem = {
metadata: {
key: 'testTimeSystem'
},
defaults: function () {
return {
bounds: defaultBounds
};
}
};
mockTimeSystems = [
// Wrap as constructor function
function () {
return timeSystem;
}
];
controller = new TimeConductorController(
mockScope,
mockWindow,
mockTimeConductor,
mockConductorViewService,
mockTimeSystems
);
controller.selectTimeSystemByKey('testTimeSystem');
expect(mockTimeConductor.timeSystem).toHaveBeenCalledWith(timeSystem, defaultBounds);
});
});
});
});

View File

@ -0,0 +1,234 @@
/*****************************************************************************
* 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.mdata = metadata;
this.dlts = undefined;
this.source = undefined;
this.sourceUnlisten = undefined;
this.systems = timeSystems;
this.availableSources = undefined;
this.changeTimeSystem = this.changeTimeSystem.bind(this);
this.tick = this.tick.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.availableSystems = timeSystems;
} else {
this.availableSystems = 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.mdata.key;
var tickSources = timeSystem.tickSources();
if (tickSources) {
this.availableSources = 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.mdata;
};
TimeConductorMode.prototype.availableTimeSystems = function () {
return this.availableSystems;
};
/**
* 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.availableSources;
};
/**
* 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.sourceUnlisten) {
this.sourceUnlisten();
}
this.source = tickSource;
if (tickSource) {
this.sourceUnlisten = tickSource.listen(this.tick);
//Now following a tick source
this.conductor.follow(true);
} else {
this.conductor.follow(false);
}
}
return this.source;
};
TimeConductorMode.prototype.destroy = function () {
this.conductor.off('timeSystem', this.changeTimeSystem);
if (this.sourceUnlisten) {
this.sourceUnlisten();
}
};
/**
* @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 bounds = this.calculateBoundsFromDeltas(deltas);
this.dlts = deltas;
if (this.metadata().key!=='fixed') {
this.conductor.bounds(bounds);
}
}
return this.dlts;
};
TimeConductorMode.prototype.calculateBoundsFromDeltas = function (deltas) {
var oldEnd = this.conductor.bounds().end;
if (this.dlts && this.dlts.end !== undefined) {
//Calculate the previous raw end value (without delta)
oldEnd = oldEnd - this.dlts.end;
}
var bounds = {
start: oldEnd - deltas.start,
end: oldEnd + deltas.end
};
return bounds;
};
/**
* Performs zoom calculation. Will calculate new bounds and deltas
* based on desired timeSpan
* @param timeSpan
*/
TimeConductorMode.prototype.calculateZoom = function (timeSpan) {
var zoom = {};
// If a tick source is defined, then the concept of 'now' is
// important. Calculate zoom based on 'now'.
if (this.tickSource()){
zoom.deltas = {
start: timeSpan,
end: this.dlts.end
};
zoom.bounds = this.calculateBoundsFromDeltas(zoom.deltas);
// Calculate bounds based on deltas;
} else {
var bounds = this.conductor.bounds();
var center = bounds.start + ((bounds.end - bounds.start)) / 2;
bounds.start = center - timeSpan / 2;
bounds.end = center + timeSpan / 2;
zoom.bounds = bounds;
}
return zoom;
};
return TimeConductorMode;
}
);

View File

@ -0,0 +1,210 @@
/*****************************************************************************
* 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) {
describe("The Time Conductor Mode", function () {
var mockTimeConductor,
fixedModeMetaData,
mockTimeSystems,
fixedTimeSystem,
realtimeModeMetaData,
realtimeTimeSystem,
mockTickSource,
mockBounds,
mode;
beforeEach(function () {
fixedModeMetaData = {
key: "fixed"
};
realtimeModeMetaData = {
key: "realtime"
};
mockBounds = {
start: 0,
end: 1
};
fixedTimeSystem = jasmine.createSpyObj("timeSystem", [
"defaults",
"tickSources"
]);
fixedTimeSystem.tickSources.andReturn([]);
mockTickSource = jasmine.createSpyObj("tickSource", [
"listen"
]);
mockTickSource.metadata = {
mode: "realtime"
};
realtimeTimeSystem = jasmine.createSpyObj("realtimeTimeSystem", [
"defaults",
"tickSources"
]);
realtimeTimeSystem.tickSources.andReturn([mockTickSource]);
//Do not return any time systems initially for a default
// construction configuration that works without any additional work
mockTimeSystems = [];
mockTimeConductor = jasmine.createSpyObj("timeConductor", [
"bounds",
"timeSystem",
"on",
"off",
"follow"
]);
mockTimeConductor.bounds.andReturn(mockBounds);
});
it("Reacts to changes in conductor time system", function () {
mode = new TimeConductorMode(fixedModeMetaData, mockTimeConductor, mockTimeSystems);
expect(mockTimeConductor.on).toHaveBeenCalledWith("timeSystem", mode.changeTimeSystem);
});
it("Stops listening to time system changes on destroy", function () {
mode = new TimeConductorMode(fixedModeMetaData, mockTimeConductor, mockTimeSystems);
mode.destroy();
expect(mockTimeConductor.off).toHaveBeenCalledWith("timeSystem", mode.changeTimeSystem);
});
it("Filters available time systems to those with tick sources that" +
" support this mode", function () {
mockTimeSystems = [fixedTimeSystem, realtimeTimeSystem];
mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems);
var availableTimeSystems = mode.availableTimeSystems();
expect(availableTimeSystems.length).toBe(1);
expect(availableTimeSystems.indexOf(fixedTimeSystem)).toBe(-1);
expect(availableTimeSystems.indexOf(realtimeTimeSystem)).toBe(0);
});
describe("Changing the time system", function () {
var defaults;
beforeEach(function () {
defaults = {
bounds: {
start: 1,
end: 2
},
deltas: {
start: 3,
end: 4
}
};
fixedTimeSystem.defaults.andReturn(defaults);
});
it ("sets defaults from new time system", function () {
mode = new TimeConductorMode(fixedModeMetaData, mockTimeConductor, mockTimeSystems);
spyOn(mode, "deltas");
mode.deltas.andCallThrough();
mode.changeTimeSystem(fixedTimeSystem);
expect(mockTimeConductor.bounds).toHaveBeenCalledWith(defaults.bounds);
expect(mode.deltas).toHaveBeenCalledWith(defaults.deltas);
});
it ("If a tick source is available, sets the tick source", function () {
mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems);
mode.changeTimeSystem(realtimeTimeSystem);
var currentTickSource = mode.tickSource();
expect(currentTickSource).toBe(mockTickSource);
});
});
describe("Setting a tick source", function () {
var mockUnlistener;
beforeEach(function () {
mockUnlistener = jasmine.createSpy("unlistener");
mockTickSource.listen.andReturn(mockUnlistener);
mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems);
mode.tickSource(mockTickSource);
});
it ("Unlistens from old tick source", function () {
mode.tickSource(mockTickSource);
expect(mockUnlistener).toHaveBeenCalled();
});
it ("Listens to new tick source", function () {
expect(mockTickSource.listen).toHaveBeenCalledWith(mode.tick);
});
it ("Sets 'follow' state on time conductor", function () {
expect(mockTimeConductor.follow).toHaveBeenCalledWith(true);
});
it ("on destroy, unlistens from tick source", function () {
mode.destroy();
expect(mockUnlistener).toHaveBeenCalled();
});
});
describe("setting deltas", function () {
beforeEach(function () {
mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems);
});
it ("sets the bounds on the time conductor based on new delta" +
" values", function () {
var deltas = {
start: 20,
end: 10
};
mode.deltas(deltas);
expect(mockTimeConductor.bounds).toHaveBeenCalledWith({
start: mockBounds.end - deltas.start,
end: mockBounds.end + deltas.end
});
});
});
describe("ticking", function () {
beforeEach(function () {
mode = new TimeConductorMode(realtimeModeMetaData, mockTimeConductor, mockTimeSystems);
});
it ("sets bounds based on current delta values", function () {
var deltas = {
start: 20,
end: 10
};
var time = 100;
mode.deltas(deltas);
mode.tick(time);
expect(mockTimeConductor.bounds).toHaveBeenCalledWith({
start: time - deltas.start,
end: time + deltas.end
});
});
});
});
});

View File

@ -0,0 +1,69 @@
/*****************************************************************************
* 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);
});
}
/**
* 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;
};
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 !isNaN(startDelta) && startDelta > 0;
};
TimeConductorValidation.prototype.validateEndDelta = function (endDelta) {
return !isNaN(endDelta) && endDelta >= 0;
};
return TimeConductorValidation;
}
);

View File

@ -0,0 +1,73 @@
/*****************************************************************************
* 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(['./TimeConductorValidation'], function (TimeConductorValidation) {
describe("The Time Conductor Validation class", function () {
var timeConductorValidation,
mockTimeConductor;
beforeEach(function () {
mockTimeConductor = jasmine.createSpyObj("timeConductor", [
"validateBounds",
"bounds"
]);
timeConductorValidation = new TimeConductorValidation(mockTimeConductor);
});
describe("Validates start and end values using Time Conductor", function () {
beforeEach(function () {
var mockBounds = {
start: 10,
end: 20
};
mockTimeConductor.bounds.andReturn(mockBounds);
});
it("Validates start values using Time Conductor", function () {
var startValue = 30;
timeConductorValidation.validateStart(startValue);
expect(mockTimeConductor.validateBounds).toHaveBeenCalled();
});
it("Validates end values using Time Conductor", function () {
var endValue = 40;
timeConductorValidation.validateEnd(endValue);
expect(mockTimeConductor.validateBounds).toHaveBeenCalled();
});
});
it("Validates that start delta is valid number > 0", function () {
expect(timeConductorValidation.validateStartDelta(-1)).toBe(false);
expect(timeConductorValidation.validateStartDelta("abc")).toBe(false);
expect(timeConductorValidation.validateStartDelta("1")).toBe(true);
expect(timeConductorValidation.validateStartDelta(1)).toBe(true);
});
it("Validates that end delta is valid number >= 0", function () {
expect(timeConductorValidation.validateEndDelta(-1)).toBe(false);
expect(timeConductorValidation.validateEndDelta("abc")).toBe(false);
expect(timeConductorValidation.validateEndDelta("1")).toBe(true);
expect(timeConductorValidation.validateEndDelta(0)).toBe(true);
expect(timeConductorValidation.validateEndDelta(1)).toBe(true);
});
});
});

View File

@ -0,0 +1,214 @@
/*****************************************************************************
* 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(
[
'EventEmitter',
'./TimeConductorMode'
],
function (EventEmitter, 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) {
EventEmitter.call(this);
this.systems = timeSystems.map(function (timeSystemConstructor) {
return timeSystemConstructor();
});
this.conductor = conductor;
this.currentMode = 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.availModes = {
'fixed': {
key: 'fixed',
cssclass: 'icon-calendar',
label: 'Fixed',
name: 'Fixed Timespan Mode',
description: 'Query and explore data that falls between two fixed datetimes.'
}
};
function hasTickSource(sourceType, timeSystem) {
return timeSystem.tickSources().some(function (tickSource) {
return tickSource.metadata.mode === sourceType;
});
}
var timeSystemsForMode = function (sourceType) {
return this.systems.filter(hasTickSource.bind(this, sourceType));
}.bind(this);
//Only show 'real-time mode' if appropriate time systems available
if (timeSystemsForMode('realtime').length > 0) {
var realtimeMode = {
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.'
};
this.availModes[realtimeMode.key] = realtimeMode;
}
//Only show 'LAD mode' if appropriate time systems available
if (timeSystemsForMode('lad').length > 0) {
var ladMode = {
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.'
};
this.availModes[ladMode.key] = ladMode;
}
}
TimeConductorViewService.prototype = Object.create(EventEmitter.prototype);
/**
* 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) {
function contains(timeSystems, ts) {
return timeSystems.filter(function (t) {
return t.metadata.key === ts.metadata.key;
}).length > 0;
}
if (arguments.length === 1) {
var timeSystem = this.conductor.timeSystem();
var modes = this.availableModes();
var modeMetaData = modes[newModeKey];
if (this.currentMode) {
this.currentMode.destroy();
}
this.currentMode = new TimeConductorMode(modeMetaData, this.conductor, this.systems);
// If no time system set on time conductor, or the currently selected time system is not available in
// the new mode, default to first available time system
if (!timeSystem || !contains(this.currentMode.availableTimeSystems(), timeSystem)) {
timeSystem = this.currentMode.availableTimeSystems()[0];
this.conductor.timeSystem(timeSystem, timeSystem.defaults().bounds);
}
}
return this.currentMode ? this.currentMode.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.currentMode.deltas.apply(this.currentMode, 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.availModes;
};
/**
* Availability of time systems depends on the currently selected
* mode. Time systems and tick sources are mode dependent
*/
TimeConductorViewService.prototype.availableTimeSystems = function () {
return this.currentMode.availableTimeSystems();
};
TimeConductorViewService.prototype.zoom = function (timeSpan) {
var zoom = this.currentMode.calculateZoom(timeSpan);
this.emit("zoom", zoom);
return zoom;
};
return TimeConductorViewService;
}
);

View File

@ -0,0 +1,185 @@
/*****************************************************************************
* 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(['./TimeConductorViewService'], function (TimeConductorViewService) {
describe("The Time Conductor view service", function () {
var mockTimeConductor;
var basicTimeSystem;
var tickingTimeSystem;
var viewService;
var tickingTimeSystemDefaults;
function mockConstructor(object) {
return function () {
return object;
};
}
beforeEach(function () {
mockTimeConductor = jasmine.createSpyObj("timeConductor", [
"timeSystem",
"bounds",
"follow",
"on",
"off"
]);
basicTimeSystem = jasmine.createSpyObj("basicTimeSystem", [
"tickSources",
"defaults"
]);
basicTimeSystem.metadata = {
key: "basic"
};
basicTimeSystem.tickSources.andReturn([]);
basicTimeSystem.defaults.andReturn({
bounds: {
start: 0,
end: 1
},
deltas: {
start: 0,
end: 0
}
});
//Initialize conductor
mockTimeConductor.timeSystem.andReturn(basicTimeSystem);
mockTimeConductor.bounds.andReturn({start: 0, end: 1});
tickingTimeSystem = jasmine.createSpyObj("tickingTimeSystem", [
"tickSources",
"defaults"
]);
tickingTimeSystem.metadata = {
key: "ticking"
};
tickingTimeSystemDefaults = {
bounds: {
start: 100,
end: 200
},
deltas: {
start: 1000,
end: 500
}
};
tickingTimeSystem.defaults.andReturn(tickingTimeSystemDefaults);
});
it("At a minimum supports fixed mode", function () {
var mockTimeSystems = [mockConstructor(basicTimeSystem)];
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
var availableModes = viewService.availableModes();
expect(availableModes.fixed).toBeDefined();
});
it("Supports realtime mode if appropriate tick source(s) availables", function () {
var mockTimeSystems = [mockConstructor(tickingTimeSystem)];
var mockRealtimeTickSource = {
metadata: {
mode: 'realtime'
}
};
tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]);
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
var availableModes = viewService.availableModes();
expect(availableModes.realtime).toBeDefined();
});
it("Supports LAD mode if appropriate tick source(s) available", function () {
var mockTimeSystems = [mockConstructor(tickingTimeSystem)];
var mockLADTickSource = {
metadata: {
mode: 'lad'
}
};
tickingTimeSystem.tickSources.andReturn([mockLADTickSource]);
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
var availableModes = viewService.availableModes();
expect(availableModes.lad).toBeDefined();
});
describe("when mode is changed", function () {
it("destroys previous mode", function () {
var mockTimeSystems = [mockConstructor(basicTimeSystem)];
var oldMode = jasmine.createSpyObj("conductorMode", [
"destroy"
]);
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
viewService.currentMode = oldMode;
viewService.mode('fixed');
expect(oldMode.destroy).toHaveBeenCalled();
});
describe("the time system", function () {
it("is retained if available in new mode", function () {
var mockTimeSystems = [mockConstructor(basicTimeSystem), mockConstructor(tickingTimeSystem)];
var mockRealtimeTickSource = {
metadata: {
mode: 'realtime'
},
listen: function () {}
};
tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]);
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
//Set time system to one known to support realtime mode
mockTimeConductor.timeSystem.andReturn(tickingTimeSystem);
//Select realtime mode
mockTimeConductor.timeSystem.reset();
viewService.mode('realtime');
expect(mockTimeConductor.timeSystem).not.toHaveBeenCalledWith(tickingTimeSystem, tickingTimeSystemDefaults.bounds);
});
it("is defaulted if selected time system not available in new mode", function () {
var mockTimeSystems = [mockConstructor(basicTimeSystem), mockConstructor(tickingTimeSystem)];
var mockRealtimeTickSource = {
metadata: {
mode: 'realtime'
},
listen: function () {}
};
tickingTimeSystem.tickSources.andReturn([mockRealtimeTickSource]);
viewService = new TimeConductorViewService(mockTimeConductor, mockTimeSystems);
//Set time system to one known to not support realtime mode
mockTimeConductor.timeSystem.andReturn(basicTimeSystem);
//Select realtime mode
mockTimeConductor.timeSystem.reset();
viewService.mode('realtime');
expect(mockTimeConductor.timeSystem).toHaveBeenCalledWith(tickingTimeSystem, tickingTimeSystemDefaults.bounds);
});
});
});
});
});

View File

@ -0,0 +1,40 @@
/*****************************************************************************
* 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([
"./src/UTCTimeSystem",
'legacyRegistry'
], function (
UTCTimeSystem,
legacyRegistry
) {
legacyRegistry.register("platform/features/conductor-v2/utcTimeSystem", {
"extensions": {
"timeSystems": [
{
"implementation": UTCTimeSystem,
"depends": ["$timeout"]
}
]
}
});
});

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([
'../../conductor/src/timeSystems/TimeSystem',
'../../conductor/src/timeSystems/LocalClock'
], function (TimeSystem, LocalClock) {
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);
/**
* Some metadata, which will be used to identify the time system in
* the UI
* @type {{key: string, name: string, cssclass: string}}
*/
this.metadata = {
'key': 'utc',
'name': 'UTC',
'cssclass': 'icon-clock'
};
this.fmts = ['utc'];
this.sources = [new LocalClock($timeout, DEFAULT_PERIOD)];
}
UTCTimeSystem.prototype = Object.create(TimeSystem.prototype);
UTCTimeSystem.prototype.formats = function () {
return this.fmts;
};
UTCTimeSystem.prototype.deltaFormat = function () {
return 'duration';
};
UTCTimeSystem.prototype.tickSources = function () {
return this.sources;
};
UTCTimeSystem.prototype.defaults = function () {
var now = Math.ceil(Date.now() / 1000) * 1000;
var ONE_MINUTE = 60 * 1 * 1000;
var FIFTY_YEARS = 50 * 365 * 24 * 60 * 60 * 1000;
return {
key: 'utc-default',
name: 'UTC time system defaults',
deltas: {start: FIFTEEN_MINUTES, end: 0},
bounds: {start: now - FIFTEEN_MINUTES, end: now},
zoom: {min: FIFTY_YEARS, max: ONE_MINUTE}
};
};
return UTCTimeSystem;
});

View File

@ -0,0 +1,46 @@
/*****************************************************************************
* 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(['./UTCTimeSystem'], function (UTCTimeSystem) {
describe("The UTCTimeSystem class", function () {
var timeSystem,
mockTimeout;
beforeEach(function () {
mockTimeout = jasmine.createSpy("timeout");
timeSystem = new UTCTimeSystem(mockTimeout);
});
it("defines at least one format", function () {
expect(timeSystem.formats().length).toBeGreaterThan(0);
});
it("defines a tick source", function () {
var tickSources = timeSystem.tickSources();
expect(tickSources.length).toBeGreaterThan(0);
});
it("defines some defaults", function () {
expect(timeSystem.defaults()).toBeDefined();
});
});
});

View File

@ -47,6 +47,11 @@ define([
]
}
],
"stylesheets": [
{
"stylesheetUrl": "css/time-conductor.css"
}
],
"components": [
{
"type": "decorator",

View File

@ -0,0 +1,300 @@
/*****************************************************************************
* 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.
*****************************************************************************/
@import "bourbon";
@import "../../../../commonUI/general/res/sass/constants";
@import "../../../../commonUI/general/res/sass/mixins";
@import "../../../../commonUI/general/res/sass/mobile/constants";
@import "../../../../commonUI/general/res/sass/mobile/mixins";
@import "../../../../commonUI/themes/espresso/res/sass/constants";
@import "../../../../commonUI/themes/espresso/res/sass/mixins";
$ueTimeConductorH: (33px, 18px, 20px);
@mixin toiLineHovEffects() {
&:before,
&:after {
background-color: $timeControllerToiLineColorHov;
}
}
.l-time-controller {
$minW: 500px;
$knobHOffset: 0px;
$knobM: ($sliderKnobW + $knobHOffset) * -1;
$rangeValPad: $interiorMargin;
$rangeValOffset: $sliderKnobW + $interiorMargin;
$timeRangeSliderLROffset: 150px + ($sliderKnobW * 2);
$r1H: nth($ueTimeConductorH,1);
$r2H: nth($ueTimeConductorH,2);
$r3H: nth($ueTimeConductorH,3);
min-width: $minW;
font-size: 0.8rem;
.l-time-range-inputs-holder,
.l-time-range-slider-holder,
.l-time-range-ticks-holder
{
box-sizing: border-box;
position: relative;
&:not(:first-child) {
margin-top: $interiorMargin;
}
}
.l-time-range-slider,
.l-time-range-ticks {
@include absPosDefault(0, visible);
left: $timeRangeSliderLROffset; right: $timeRangeSliderLROffset;
}
.l-time-range-inputs-holder {
border-top: 1px solid $colorInteriorBorder;
padding-top: $interiorMargin;
&.l-flex-row,
.l-flex-row {
@include align-items(center);
.flex-elem {
height: auto;
line-height: normal;
}
}
.type-icon {
font-size: 120%;
vertical-align: middle;
}
.l-time-range-input-w,
.l-time-range-inputs-elem {
margin-right: $interiorMargin;
.lbl {
color: $colorPlotLabelFg;
}
.ui-symbol.icon {
font-size: 11px;
}
}
.l-time-range-input-w {
// Wraps a datetime text input field
position: relative;
input[type="text"] {
width: 200px;
&.picker-icon {
padding-right: 20px;
}
}
.icon-calendar {
position: absolute;
right: 5px;
top: 5px;
}
}
}
.l-time-range-slider-holder {
height: $r2H;
.range-holder {
box-shadow: none;
background: none;
border: none;
.range {
.toi-line {
$myC: $timeControllerToiLineColor;
$myW: 8px;
@include transform(translateX(50%));
position: absolute;
top: 0; right: 0; bottom: 0px; left: auto;
width: $myW;
height: auto;
z-index: 2;
&:before {
// Vert line
background-color: $myC;
position: absolute;
content: "";
top: 0; right: auto; bottom: -10px; left: floor($myW/2) - 1;
width: 1px;
}
}
&:hover .toi-line {
@include toiLineHovEffects;
}
}
}
&:not(:active) {
.knob,
.range {
@include transition-property(left, right);
@include transition-duration(500ms);
@include transition-timing-function(ease-in-out);
}
}
}
.l-time-range-ticks-holder {
height: $r3H;
.l-time-range-ticks {
border-top: 1px solid $colorTick;
.tick {
background-color: $colorTick;
border:none;
height: 5px;
width: 1px;
margin-left: -1px;
position: absolute;
&:first-child {
margin-left: 0;
}
.l-time-range-tick-label {
@include webkitProp(transform, translateX(-50%));
color: $colorPlotLabelFg;
display: inline-block;
font-size: 0.7rem;
position: absolute;
top: 5px;
white-space: nowrap;
z-index: 2;
}
}
}
}
.knob {
z-index: 2;
&:before {
$mTB: 2px;
$grippyW: 3px;
$mLR: ($sliderKnobW - $grippyW)/2;
@include bgStripes($c: pullForward($sliderColorKnob, 20%), $a: 1, $bgsize: 4px, $angle: 0deg);
content: '';
display: block;
position: absolute;
top: $mTB; right: $mLR; bottom: $mTB; left: $mLR;
}
.range-value {
@include trans-prop-nice-fade(.25s);
font-size: 0.7rem;
position: absolute;
height: $r2H;
line-height: $r2H;
white-space: nowrap;
z-index: 1;
}
&:hover {
.range-value {
color: $sliderColorKnobHov;
}
}
&.knob-l {
margin-left: $knobM;
.range-value {
text-align: right;
right: $rangeValOffset;
}
}
&.knob-r {
margin-right: $knobM;
.range-value {
left: $rangeValOffset;
}
&:hover + .range-holder .range .toi-line {
@include toiLineHovEffects;
}
}
}
.l-time-domain-selector {
position: absolute;
right: 0px;
top: $interiorMargin;
}
}
.s-time-range-val {
border-radius: $controlCr;
background-color: $colorInputBg;
padding: 1px 1px 0 $interiorMargin;
}
/******************************************************************** MOBILE */
@include phoneandtablet {
.l-time-controller {
min-width: 0;
.l-time-range-slider-holder,
.l-time-range-ticks-holder {
display: none;
}
}
}
@include phone {
.l-time-controller {
.l-time-range-inputs-holder {
&.l-flex-row,
.l-flex-row {
@include align-items(flex-start);
}
.l-time-range-inputs-elem {
&.type-icon {
margin-top: 3px;
}
}
.t-inputs-w,
.l-time-range-inputs-elem {
@include flex-direction(column);
.l-time-range-input-w:not(:first-child) {
&:not(:first-child) {
margin-top: $interiorMargin;
}
margin-right: 0;
}
.l-time-range-inputs-elem {
&.lbl { display: none; }
}
}
}
}
}
@include phonePortrait {
.l-time-controller {
.l-time-range-inputs-holder {
.t-inputs-w,
.l-time-range-inputs-elem {
@include flex(1 1 auto);
padding-top: 25px; // Make room for the ever lovin' Time Domain Selector
.flex-elem {
@include flex(1 1 auto);
width: 100%;
}
input[type="text"] {
width: 100%;
}
}
}
}
.l-time-domain-selector {
right: auto;
left: 20px;
}
}

View File

@ -26,11 +26,10 @@
ng-repeat="childObject in composition"
ng-style="controller.getFrameStyle(childObject.getId())">
<div class="frame child-frame holder contents abs">
<mct-representation key="'frame'"
class="frame child-frame holder contents abs"
mct-object="childObject">
</mct-representation>
</div>
<!-- Drag handles -->
<span ng-show="domainObject.hasCapability('editor')">
<span class="edit-handle edit-move"

View File

@ -229,7 +229,11 @@ define(
}
// Respond to a display bounds change (requery for data)
function changeDisplayBounds(event, bounds) {
function changeDisplayBounds(event, bounds, follow) {
//'hack' for follow mode
if (follow === true) {
setBasePanZoom(bounds);
} else {
var domainAxis = $scope.axes[0];
domainAxis.chooseOption(bounds.domain);
@ -237,6 +241,8 @@ define(
setBasePanZoom(bounds);
requery();
}
self.setUnsynchedStatus($scope.domainObject, follow && self.isZoomed());
}
this.modeOptions = new PlotModeOptions([], subPlotFactory);
this.updateValues = updateValues;
@ -368,6 +374,12 @@ define(
return this.pending;
};
PlotController.prototype.setUnsynchedStatus = function (domainObject, status) {
if (domainObject.hasCapability('status')) {
domainObject.getCapability('status').set('timeconductor-unsynced', status);
}
};
/**
* Export the plot to PNG
*/

View File

@ -39,6 +39,7 @@ define(
mockHandle,
mockDomainObject,
mockSeries,
mockStatusCapability,
controller;
function bind(method, thisObj) {
@ -80,7 +81,7 @@ define(
);
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getId", "getModel", "getCapability"]
["getId", "getModel", "getCapability", "hasCapability"]
);
mockHandler = jasmine.createSpyObj(
"telemetrySubscriber",
@ -104,6 +105,11 @@ define(
['getPointCount', 'getDomainValue', 'getRangeValue']
);
mockStatusCapability = jasmine.createSpyObj(
"statusCapability",
["set"]
);
mockHandler.handle.andReturn(mockHandle);
mockThrottle.andCallFake(function (fn) {
return fn;
@ -241,6 +247,34 @@ define(
expect(bind(controller.unzoom, controller)).not.toThrow();
});
it("sets status when plot becomes detached from time conductor", function () {
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
function boundsEvent() {
fireEvent("telemetry:display:bounds", [
{},
{ start: 10, end: 100 },
true
]);
}
mockDomainObject.hasCapability.andCallFake(function (name) {
return name === "status";
});
mockDomainObject.getCapability.andReturn(mockStatusCapability);
spyOn(controller, "isZoomed");
//Mock zoomed in state
controller.isZoomed.andReturn(true);
boundsEvent();
expect(mockStatusCapability.set).toHaveBeenCalledWith("timeconductor-unsynced", true);
//"Reset" zoom
controller.isZoomed.andReturn(false);
boundsEvent();
expect(mockStatusCapability.set).toHaveBeenCalledWith("timeconductor-unsynced", false);
});
it("indicates if a request is pending", function () {
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
expect(controller.isRequestPending()).toBeTruthy();
@ -297,7 +331,6 @@ define(
expect(mockHandle.request.calls.length).toEqual(2);
});
it("maintains externally-provided domain axis bounds after data is received", function () {
mockSeries.getPointCount.andReturn(3);
mockSeries.getRangeValue.andReturn(42);

View File

@ -63,6 +63,28 @@ define(
this.$scope.loading = false;
};
/**
* @private
*/
HistoricalTableController.prototype.registerChangeListeners = function () {
TableController.prototype.registerChangeListeners.call(this);
//Change of bounds in time conductor
this.changeListeners.push(this.$scope.$on('telemetry:display:bounds',
this.boundsChange.bind(this))
);
};
/**
* @private
*/
HistoricalTableController.prototype.boundsChange = function (event, bounds, follow) {
// If in follow mode, don't bother re-subscribing, data will be
// received from existing subscription.
if (follow !== true) {
this.subscribe();
}
};
/**
* Processes an array of objects, formatting the telemetry available
* for them and setting it on scope when done

View File

@ -96,11 +96,6 @@ define(
}
})
);
//Change of bounds in time conductor
this.changeListeners.push(this.$scope.$on('telemetry:display:bounds',
this.subscribe.bind(this))
);
};
/**

View File

@ -24,6 +24,7 @@
<input type="text"
ng-required="ngRequired"
ng-model="ngModel[field]"
ng-blur="ngBlur()"
ng-pattern="ngPattern"
size="{{structure.size}}"
name="mctControl">

View File

@ -71,6 +71,9 @@ define(
// Allow controls to trigger blur-like events
ngBlur: "&",
// Allow controls to trigger blur-like events
ngMouseup: "&",
// The state of the form value itself
ngModel: "=",

View File

@ -53,6 +53,7 @@ requirejs.config({
"angular": "bower_components/angular/angular.min",
"angular-route": "bower_components/angular-route/angular-route.min",
"csv": "bower_components/comma-separated-values/csv.min",
"EventEmitter": "bower_components/eventemitter3/index",
"es6-promise": "bower_components/es6-promise/es6-promise.min",
"html2canvas": "bower_components/html2canvas/build/html2canvas.min",
"moment": "bower_components/moment/moment",
@ -61,7 +62,8 @@ requirejs.config({
"screenfull": "bower_components/screenfull/dist/screenfull.min",
"text": "bower_components/text/text",
"uuid": "bower_components/node-uuid/uuid",
"zepto": "bower_components/zepto/zepto.min"
"zepto": "bower_components/zepto/zepto.min",
"d3": "bower_components/d3/d3.min"
},
"shim": {
@ -71,6 +73,9 @@ requirejs.config({
"angular-route": {
"deps": [ "angular" ]
},
"EventEmitter": {
"exports": "EventEmitter"
},
"moment-duration-format": {
"deps": [ "moment" ]
},
@ -79,6 +84,9 @@ requirejs.config({
},
"zepto": {
"exports": "Zepto"
},
"d3": {
"exports": "d3"
}
},