mirror of
https://github.com/nasa/openmct.git
synced 2025-07-02 05:09:27 +00:00
Compare commits
168 Commits
api-toolba
...
open933_mo
Author | SHA1 | Date | |
---|---|---|---|
2a8a119b67 | |||
15a608a861 | |||
334ca64551 | |||
0af49efe06 | |||
4087b9cdde | |||
43a804eef4 | |||
b3a4f52fe2 | |||
671e3016d4 | |||
379828315f | |||
8c5538ec4d | |||
2f9fbfef7f | |||
2baca659ca | |||
8b694ef337 | |||
e193e3dfba | |||
8214c8e895 | |||
33b2225d10 | |||
14463d39a8 | |||
fcfda50e73 | |||
06af84c161 | |||
5238aa2731 | |||
fd29473664 | |||
97f3fd516b | |||
088416905d | |||
2056d87453 | |||
64ce8a2b2a | |||
585da38a16 | |||
bf0e85a94c | |||
84b7a9dc2f | |||
11caa8396a | |||
0017b77439 | |||
7b7b21d748 | |||
788483ec13 | |||
7b19f91ce6 | |||
5cc81ba12a | |||
0a0bc55f5f | |||
4e7b69c4df | |||
cf83040c4b | |||
32f7bc86af | |||
e230b92946 | |||
58ed500ecf | |||
bca5eb0fdb | |||
652a50c700 | |||
736cba76bb | |||
2339560363 | |||
24e870a126 | |||
8bdf1e3072 | |||
e8e9598721 | |||
ce87ad2564 | |||
2a2f6e8142 | |||
3e5057c285 | |||
5485950130 | |||
c8f4568bd0 | |||
cefb40856b | |||
ee7c450e11 | |||
d741e0f23c | |||
8080490e5c | |||
a3443d8077 | |||
53e8e7c688 | |||
dea94e4e68 | |||
d40c7f1821 | |||
c7e7e0c302 | |||
8ac6ac97f0 | |||
1afa4ab329 | |||
c2517c1670 | |||
717ceff02c | |||
6878c59f45 | |||
c00b053aa7 | |||
480e12c3ab | |||
50bd233b0a | |||
79406cf1ed | |||
2d5824c4ab | |||
3480809129 | |||
11bc48c7e0 | |||
d759401b69 | |||
5a2e5ac48f | |||
1144f818cf | |||
0aebecfbb0 | |||
531d40993a | |||
4c097faf88 | |||
5152e64895 | |||
0263237b2c | |||
acd0fae040 | |||
29dd51439d | |||
a1b2175801 | |||
b0f06a2195 | |||
f9c93ca022 | |||
8e0858bb24 | |||
ee0fa0451a | |||
e86e955682 | |||
9913fb48f5 | |||
71c362f016 | |||
fa6e8fd5f9 | |||
23b64951f3 | |||
99590d18f7 | |||
86b31bc040 | |||
d52bfed1df | |||
808ccd0376 | |||
44d6456de1 | |||
a394b95259 | |||
beeefe517a | |||
d02f4041b2 | |||
9fd75ff91e | |||
026ece3956 | |||
214a843dba | |||
1d6880c283 | |||
8ddad9bf4c | |||
f167022eea | |||
76edba1014 | |||
7904989a23 | |||
ea676b4368 | |||
cc2b102256 | |||
b1266abf01 | |||
cc7d0477e8 | |||
5a2d1a746d | |||
4f0e3fdf85 | |||
be9f56107c | |||
787f3815df | |||
35d7d9b380 | |||
8b9c51f303 | |||
661b3d5889 | |||
01c85cb58d | |||
0218f42e2b | |||
d8d0f22889 | |||
d8a097a95a | |||
dc577d4c24 | |||
8f9308de01 | |||
8f7a5e113b | |||
9820f9d9c5 | |||
56ff98cce7 | |||
dade6b2254 | |||
e9cac6eff3 | |||
7fc2fcfa07 | |||
5689279954 | |||
165e158f37 | |||
f301741852 | |||
eda1f23df9 | |||
d15d27af73 | |||
438511c5f7 | |||
3eb960cf5a | |||
699f6ba458 | |||
f21f22d95c | |||
b520d08818 | |||
f9fd97230f | |||
536e2290b8 | |||
73b922facf | |||
ba0d9a186b | |||
80f5cb756d | |||
d7f566088f | |||
a3bcaea7f9 | |||
23c71b7218 | |||
463f7ccf65 | |||
87fe407739 | |||
bb4f1ce7cd | |||
0cc2ba7595 | |||
8162429106 | |||
ed519d89d7 | |||
0e4f6185b8 | |||
1ced47fc2c | |||
677b65d124 | |||
31d31d7819 | |||
d32ef4bc3d | |||
b4faf8991d | |||
0564759481 | |||
24e391edf7 | |||
cf295105d4 | |||
f16a107105 | |||
f683ca44a2 | |||
546cde56a8 |
@ -1,4 +1,4 @@
|
|||||||
# Open MCT
|
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
||||||
Open MCT is a web-based platform for mission operations user interface
|
Open MCT is a web-based platform for mission operations user interface
|
||||||
software.
|
software.
|
||||||
@ -7,7 +7,7 @@ software.
|
|||||||
|
|
||||||
A bundle is a group of software components (including source code, declared
|
A bundle is a group of software components (including source code, declared
|
||||||
as AMD modules, as well as resources such as images and HTML templates)
|
as AMD modules, as well as resources such as images and HTML templates)
|
||||||
that are intended to be added or removed as a single unit. A plug-in for
|
that is intended to be added or removed as a single unit. A plug-in for
|
||||||
Open MCT will be expressed as a bundle; platform components are also
|
Open MCT will be expressed as a bundle; platform components are also
|
||||||
expressed as bundles.
|
expressed as bundles.
|
||||||
|
|
||||||
@ -133,6 +133,6 @@ documentation, may presume an understanding of these terms.
|
|||||||
it, and it is thereafter considered the _navigated_ object (until the
|
it, and it is thereafter considered the _navigated_ object (until the
|
||||||
user makes another such choice.)
|
user makes another such choice.)
|
||||||
* _space_: A name used to identify a persistence store. Interactions with
|
* _space_: A name used to identify a persistence store. Interactions with
|
||||||
persistence with generally involve a `space` parameter in some form, to
|
persistence will generally involve a `space` parameter in some form, to
|
||||||
distinguish multiple persistence stores from one another (for cases
|
distinguish multiple persistence stores from one another (for cases
|
||||||
where there are multiple valid persistence locations available.)
|
where there are multiple valid persistence locations available.)
|
||||||
|
8
app.js
8
app.js
@ -75,6 +75,8 @@
|
|||||||
// Expose everything else as static files
|
// Expose everything else as static files
|
||||||
app.use(express['static'](options.directory));
|
app.use(express['static'](options.directory));
|
||||||
|
|
||||||
// Finally, open the HTTP server
|
// Finally, open the HTTP server and log the instance to the console
|
||||||
app.listen(options.port);
|
app.listen(options.port, function() {
|
||||||
}());
|
console.log('Open MCT application running at localhost:' + options.port)
|
||||||
|
});
|
||||||
|
}());
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
"node-uuid": "^1.4.7",
|
"node-uuid": "^1.4.7",
|
||||||
"comma-separated-values": "^3.6.4",
|
"comma-separated-values": "^3.6.4",
|
||||||
"FileSaver.js": "^0.0.2",
|
"FileSaver.js": "^0.0.2",
|
||||||
"zepto": "^1.1.6"
|
"zepto": "^1.1.6",
|
||||||
|
"eventemitter3": "^1.2.0",
|
||||||
|
"d3": "~4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
main.js
14
main.js
@ -28,13 +28,15 @@ requirejs.config({
|
|||||||
"angular-route": "bower_components/angular-route/angular-route.min",
|
"angular-route": "bower_components/angular-route/angular-route.min",
|
||||||
"csv": "bower_components/comma-separated-values/csv.min",
|
"csv": "bower_components/comma-separated-values/csv.min",
|
||||||
"es6-promise": "bower_components/es6-promise/promise.min",
|
"es6-promise": "bower_components/es6-promise/promise.min",
|
||||||
|
"EventEmitter": "bower_components/eventemitter3/index",
|
||||||
"moment": "bower_components/moment/moment",
|
"moment": "bower_components/moment/moment",
|
||||||
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
||||||
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
|
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
|
||||||
"screenfull": "bower_components/screenfull/dist/screenfull.min",
|
"screenfull": "bower_components/screenfull/dist/screenfull.min",
|
||||||
"text": "bower_components/text/text",
|
"text": "bower_components/text/text",
|
||||||
"uuid": "bower_components/node-uuid/uuid",
|
"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": {
|
"shim": {
|
||||||
"angular": {
|
"angular": {
|
||||||
@ -43,6 +45,9 @@ requirejs.config({
|
|||||||
"angular-route": {
|
"angular-route": {
|
||||||
"deps": ["angular"]
|
"deps": ["angular"]
|
||||||
},
|
},
|
||||||
|
"EventEmitter": {
|
||||||
|
"exports": "EventEmitter"
|
||||||
|
},
|
||||||
"moment-duration-format": {
|
"moment-duration-format": {
|
||||||
"deps": ["moment"]
|
"deps": ["moment"]
|
||||||
},
|
},
|
||||||
@ -51,6 +56,9 @@ requirejs.config({
|
|||||||
},
|
},
|
||||||
"zepto": {
|
"zepto": {
|
||||||
"exports": "Zepto"
|
"exports": "Zepto"
|
||||||
|
},
|
||||||
|
"d3": {
|
||||||
|
"exports": "d3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -82,6 +90,7 @@ define([
|
|||||||
'./platform/features/pages/bundle',
|
'./platform/features/pages/bundle',
|
||||||
'./platform/features/plot/bundle',
|
'./platform/features/plot/bundle',
|
||||||
'./platform/features/timeline/bundle',
|
'./platform/features/timeline/bundle',
|
||||||
|
'./platform/features/conductor-v2/bundle',
|
||||||
'./platform/features/table/bundle',
|
'./platform/features/table/bundle',
|
||||||
'./platform/forms/bundle',
|
'./platform/forms/bundle',
|
||||||
'./platform/identity/bundle',
|
'./platform/identity/bundle',
|
||||||
@ -92,7 +101,8 @@ define([
|
|||||||
'./platform/entanglement/bundle',
|
'./platform/entanglement/bundle',
|
||||||
'./platform/search/bundle',
|
'./platform/search/bundle',
|
||||||
'./platform/status/bundle',
|
'./platform/status/bundle',
|
||||||
'./platform/commonUI/regions/bundle'
|
'./platform/commonUI/regions/bundle',
|
||||||
|
'./example/msl/bundle'
|
||||||
], function (Main, legacyRegistry) {
|
], function (Main, legacyRegistry) {
|
||||||
return {
|
return {
|
||||||
legacyRegistry: legacyRegistry,
|
legacyRegistry: legacyRegistry,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "openmct",
|
"name": "openmct",
|
||||||
"version": "0.10.2-SNAPSHOT",
|
"version": "0.11.0-SNAPSHOT",
|
||||||
"description": "The Open MCT core platform",
|
"description": "The Open MCT core platform",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.13.1",
|
"express": "^4.13.1",
|
||||||
|
@ -27,6 +27,7 @@ define([
|
|||||||
"./src/MenuArrowController",
|
"./src/MenuArrowController",
|
||||||
"./src/navigation/NavigationService",
|
"./src/navigation/NavigationService",
|
||||||
"./src/navigation/NavigateAction",
|
"./src/navigation/NavigateAction",
|
||||||
|
"./src/navigation/OrphanNavigationHandler",
|
||||||
"./src/windowing/NewTabAction",
|
"./src/windowing/NewTabAction",
|
||||||
"./src/windowing/FullscreenAction",
|
"./src/windowing/FullscreenAction",
|
||||||
"./src/windowing/WindowTitler",
|
"./src/windowing/WindowTitler",
|
||||||
@ -47,6 +48,7 @@ define([
|
|||||||
MenuArrowController,
|
MenuArrowController,
|
||||||
NavigationService,
|
NavigationService,
|
||||||
NavigateAction,
|
NavigateAction,
|
||||||
|
OrphanNavigationHandler,
|
||||||
NewTabAction,
|
NewTabAction,
|
||||||
FullscreenAction,
|
FullscreenAction,
|
||||||
WindowTitler,
|
WindowTitler,
|
||||||
@ -91,11 +93,9 @@ define([
|
|||||||
"$scope",
|
"$scope",
|
||||||
"$route",
|
"$route",
|
||||||
"$location",
|
"$location",
|
||||||
"$window",
|
|
||||||
"objectService",
|
"objectService",
|
||||||
"navigationService",
|
"navigationService",
|
||||||
"urlService",
|
"urlService",
|
||||||
"policyService",
|
|
||||||
"DEFAULT_PATH"
|
"DEFAULT_PATH"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -199,7 +199,9 @@ define([
|
|||||||
"implementation": NavigateAction,
|
"implementation": NavigateAction,
|
||||||
"depends": [
|
"depends": [
|
||||||
"navigationService",
|
"navigationService",
|
||||||
"$q"
|
"$q",
|
||||||
|
"policyService",
|
||||||
|
"$window"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -253,6 +255,14 @@ define([
|
|||||||
"$rootScope",
|
"$rootScope",
|
||||||
"$document"
|
"$document"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"implementation": OrphanNavigationHandler,
|
||||||
|
"depends": [
|
||||||
|
"throttle",
|
||||||
|
"topic",
|
||||||
|
"navigationService"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"licenses": [
|
"licenses": [
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="holder l-flex-col flex-elem grows l-object-wrapper-inner">
|
||||||
<!-- Toolbar and Save/Cancel buttons -->
|
<!-- Toolbar and Save/Cancel buttons -->
|
||||||
<div class="l-edit-controls flex-elem l-flex-row flex-align-end">
|
<div class="l-edit-controls flex-elem l-flex-row flex-align-end">
|
||||||
@ -59,4 +59,9 @@
|
|||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- put time conductor in here? -->
|
||||||
|
<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>
|
</div>
|
||||||
|
@ -44,11 +44,9 @@ define(
|
|||||||
$scope,
|
$scope,
|
||||||
$route,
|
$route,
|
||||||
$location,
|
$location,
|
||||||
$window,
|
|
||||||
objectService,
|
objectService,
|
||||||
navigationService,
|
navigationService,
|
||||||
urlService,
|
urlService,
|
||||||
policyService,
|
|
||||||
defaultPath
|
defaultPath
|
||||||
) {
|
) {
|
||||||
var path = [ROOT_ID].concat(
|
var path = [ROOT_ID].concat(
|
||||||
@ -75,25 +73,10 @@ define(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Callback for updating the in-scope reference to the object
|
function setScopeObjects(domainObject, navigationAllowed) {
|
||||||
// that is currently navigated-to.
|
|
||||||
function setNavigation(domainObject) {
|
|
||||||
var navigationAllowed = true;
|
|
||||||
|
|
||||||
if (domainObject === $scope.navigatedObject) {
|
|
||||||
//do nothing;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
policyService.allow("navigation", $scope.navigatedObject, domainObject, function (message) {
|
|
||||||
navigationAllowed = $window.confirm(message + "\r\n\r\n" +
|
|
||||||
" Are you sure you want to continue?");
|
|
||||||
});
|
|
||||||
|
|
||||||
if (navigationAllowed) {
|
if (navigationAllowed) {
|
||||||
$scope.navigatedObject = domainObject;
|
$scope.navigatedObject = domainObject;
|
||||||
$scope.treeModel.selectedObject = domainObject;
|
$scope.treeModel.selectedObject = domainObject;
|
||||||
navigationService.setNavigation(domainObject);
|
|
||||||
updateRoute(domainObject);
|
updateRoute(domainObject);
|
||||||
} else {
|
} else {
|
||||||
//If navigation was unsuccessful (ie. blocked), reset
|
//If navigation was unsuccessful (ie. blocked), reset
|
||||||
@ -103,6 +86,20 @@ define(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Callback for updating the in-scope reference to the object
|
||||||
|
// that is currently navigated-to.
|
||||||
|
function setNavigation(domainObject) {
|
||||||
|
if (domainObject === $scope.navigatedObject) {
|
||||||
|
//do nothing;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (domainObject) {
|
||||||
|
domainObject.getCapability("action").perform("navigate").then(setScopeObjects.bind(undefined, domainObject));
|
||||||
|
} else {
|
||||||
|
setScopeObjects(domainObject, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function navigateTo(domainObject) {
|
function navigateTo(domainObject) {
|
||||||
|
|
||||||
// Check if an object has been navigated-to already...
|
// Check if an object has been navigated-to already...
|
||||||
|
@ -33,10 +33,12 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @implements {Action}
|
* @implements {Action}
|
||||||
*/
|
*/
|
||||||
function NavigateAction(navigationService, $q, context) {
|
function NavigateAction(navigationService, $q, policyService, $window, context) {
|
||||||
this.domainObject = context.domainObject;
|
this.domainObject = context.domainObject;
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
this.navigationService = navigationService;
|
this.navigationService = navigationService;
|
||||||
|
this.policyService = policyService;
|
||||||
|
this.$window = $window;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,9 +47,20 @@ define(
|
|||||||
* navigation has been updated
|
* navigation has been updated
|
||||||
*/
|
*/
|
||||||
NavigateAction.prototype.perform = function () {
|
NavigateAction.prototype.perform = function () {
|
||||||
|
var self = this,
|
||||||
|
navigationAllowed = true;
|
||||||
|
|
||||||
|
function allow() {
|
||||||
|
self.policyService.allow("navigation", self.navigationService.getNavigation(), self.domainObject, function (message) {
|
||||||
|
navigationAllowed = self.$window.confirm(message + "\r\n\r\n" +
|
||||||
|
" Are you sure you want to continue?");
|
||||||
|
});
|
||||||
|
return navigationAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
// Set navigation, and wrap like a promise
|
// Set navigation, and wrap like a promise
|
||||||
return this.$q.when(
|
return this.$q.when(
|
||||||
this.navigationService.setNavigation(this.domainObject)
|
allow() && this.navigationService.setNavigation(this.domainObject)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigates away from orphan objects whenever they are detected.
|
||||||
|
*
|
||||||
|
* An orphan object is an object whose apparent parent does not
|
||||||
|
* actually contain it. This may occur in certain circumstances, such
|
||||||
|
* as when persistence succeeds for a newly-created object but fails
|
||||||
|
* for its parent.
|
||||||
|
*
|
||||||
|
* @param throttle the `throttle` service
|
||||||
|
* @param topic the `topic` service
|
||||||
|
* @param navigationService the `navigationService`
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function OrphanNavigationHandler(throttle, topic, navigationService) {
|
||||||
|
var throttledCheckNavigation;
|
||||||
|
|
||||||
|
function getParent(domainObject) {
|
||||||
|
var context = domainObject.getCapability('context');
|
||||||
|
return context.getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOrphan(domainObject) {
|
||||||
|
var parent = getParent(domainObject),
|
||||||
|
composition = parent.getModel().composition,
|
||||||
|
id = domainObject.getId();
|
||||||
|
return !composition || (composition.indexOf(id) === -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function navigateToParent(domainObject) {
|
||||||
|
var parent = getParent(domainObject);
|
||||||
|
return parent.getCapability('action').perform('navigate');
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkNavigation() {
|
||||||
|
var navigatedObject = navigationService.getNavigation();
|
||||||
|
if (navigatedObject.hasCapability('context') &&
|
||||||
|
isOrphan(navigatedObject)) {
|
||||||
|
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
|
||||||
|
navigateToParent(navigatedObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throttledCheckNavigation = throttle(checkNavigation);
|
||||||
|
|
||||||
|
navigationService.addListener(throttledCheckNavigation);
|
||||||
|
topic('mutation').listen(throttledCheckNavigation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OrphanNavigationHandler;
|
||||||
|
});
|
@ -37,9 +37,8 @@ define(
|
|||||||
mockUrlService,
|
mockUrlService,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockNextObject,
|
mockNextObject,
|
||||||
mockWindow,
|
|
||||||
mockPolicyService,
|
|
||||||
testDefaultRoot,
|
testDefaultRoot,
|
||||||
|
mockActionCapability,
|
||||||
controller;
|
controller;
|
||||||
|
|
||||||
function mockPromise(value) {
|
function mockPromise(value) {
|
||||||
@ -55,25 +54,14 @@ define(
|
|||||||
mockScope,
|
mockScope,
|
||||||
mockRoute,
|
mockRoute,
|
||||||
mockLocation,
|
mockLocation,
|
||||||
mockWindow,
|
|
||||||
mockObjectService,
|
mockObjectService,
|
||||||
mockNavigationService,
|
mockNavigationService,
|
||||||
mockUrlService,
|
mockUrlService,
|
||||||
mockPolicyService,
|
|
||||||
testDefaultRoot
|
testDefaultRoot
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockWindow = jasmine.createSpyObj('$window', [
|
|
||||||
"confirm"
|
|
||||||
]);
|
|
||||||
mockWindow.confirm.andReturn(true);
|
|
||||||
|
|
||||||
mockPolicyService = jasmine.createSpyObj('policyService', [
|
|
||||||
'allow'
|
|
||||||
]);
|
|
||||||
|
|
||||||
testDefaultRoot = "some-root-level-domain-object";
|
testDefaultRoot = "some-root-level-domain-object";
|
||||||
|
|
||||||
mockScope = jasmine.createSpyObj(
|
mockScope = jasmine.createSpyObj(
|
||||||
@ -128,6 +116,8 @@ define(
|
|||||||
mockNextObject.getId.andReturn("next");
|
mockNextObject.getId.andReturn("next");
|
||||||
mockDomainObject.getId.andReturn(testDefaultRoot);
|
mockDomainObject.getId.andReturn(testDefaultRoot);
|
||||||
|
|
||||||
|
mockActionCapability = jasmine.createSpyObj('actionCapability', ['perform']);
|
||||||
|
|
||||||
instantiateController();
|
instantiateController();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -211,8 +201,13 @@ define(
|
|||||||
mockContext.getPath.andReturn(
|
mockContext.getPath.andReturn(
|
||||||
[mockRootObject, mockDomainObject, mockNextObject]
|
[mockRootObject, mockDomainObject, mockNextObject]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//Return true from navigate action
|
||||||
|
mockActionCapability.perform.andReturn(mockPromise(true));
|
||||||
|
|
||||||
mockNextObject.getCapability.andCallFake(function (c) {
|
mockNextObject.getCapability.andCallFake(function (c) {
|
||||||
return c === 'context' && mockContext;
|
return (c === 'context' && mockContext) ||
|
||||||
|
(c === 'action' && mockActionCapability);
|
||||||
});
|
});
|
||||||
mockScope.$on.andReturn(mockUnlisten);
|
mockScope.$on.andReturn(mockUnlisten);
|
||||||
// Provide a navigation change
|
// Provide a navigation change
|
||||||
@ -225,6 +220,7 @@ define(
|
|||||||
mockLocation.path.andReturn("/browse/");
|
mockLocation.path.andReturn("/browse/");
|
||||||
|
|
||||||
mockNavigationService.setNavigation.andReturn(true);
|
mockNavigationService.setNavigation.andReturn(true);
|
||||||
|
mockActionCapability.perform.andReturn(mockPromise(true));
|
||||||
|
|
||||||
// Exercise the Angular workaround
|
// Exercise the Angular workaround
|
||||||
mockNavigationService.addListener.mostRecentCall.args[0]();
|
mockNavigationService.addListener.mostRecentCall.args[0]();
|
||||||
@ -243,6 +239,9 @@ define(
|
|||||||
mockScope.navigatedObject = mockDomainObject;
|
mockScope.navigatedObject = mockDomainObject;
|
||||||
mockNavigationService.setNavigation.andReturn(true);
|
mockNavigationService.setNavigation.andReturn(true);
|
||||||
|
|
||||||
|
mockActionCapability.perform.andReturn(mockPromise(true));
|
||||||
|
mockNextObject.getCapability.andReturn(mockActionCapability);
|
||||||
|
|
||||||
//Simulate a change in selected tree object
|
//Simulate a change in selected tree object
|
||||||
mockScope.treeModel = {selectedObject: mockDomainObject};
|
mockScope.treeModel = {selectedObject: mockDomainObject};
|
||||||
mockScope.$watch.mostRecentCall.args[1](mockNextObject);
|
mockScope.$watch.mostRecentCall.args[1](mockNextObject);
|
||||||
@ -254,11 +253,10 @@ define(
|
|||||||
it("after failed navigation event resets the selected tree" +
|
it("after failed navigation event resets the selected tree" +
|
||||||
" object", function () {
|
" object", function () {
|
||||||
mockScope.navigatedObject = mockDomainObject;
|
mockScope.navigatedObject = mockDomainObject;
|
||||||
mockWindow.confirm.andReturn(false);
|
|
||||||
mockPolicyService.allow.andCallFake(function (category, object, context, callback) {
|
//Return false from navigation action
|
||||||
callback("unsaved changes");
|
mockActionCapability.perform.andReturn(mockPromise(false));
|
||||||
return false;
|
mockNextObject.getCapability.andReturn(mockActionCapability);
|
||||||
});
|
|
||||||
|
|
||||||
//Simulate a change in selected tree object
|
//Simulate a change in selected tree object
|
||||||
mockScope.treeModel = {selectedObject: mockDomainObject};
|
mockScope.treeModel = {selectedObject: mockDomainObject};
|
||||||
|
@ -31,6 +31,8 @@ define(
|
|||||||
var mockNavigationService,
|
var mockNavigationService,
|
||||||
mockQ,
|
mockQ,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
|
mockPolicyService,
|
||||||
|
mockWindow,
|
||||||
action;
|
action;
|
||||||
|
|
||||||
function mockPromise(value) {
|
function mockPromise(value) {
|
||||||
@ -44,25 +46,70 @@ define(
|
|||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockNavigationService = jasmine.createSpyObj(
|
mockNavigationService = jasmine.createSpyObj(
|
||||||
"navigationService",
|
"navigationService",
|
||||||
["setNavigation"]
|
[
|
||||||
|
"setNavigation",
|
||||||
|
"getNavigation"
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
mockNavigationService.getNavigation.andReturn({});
|
||||||
mockQ = { when: mockPromise };
|
mockQ = { when: mockPromise };
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
"domainObject",
|
"domainObject",
|
||||||
["getId", "getModel", "getCapability"]
|
["getId", "getModel", "getCapability"]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
mockPolicyService = jasmine.createSpyObj("policyService",
|
||||||
|
[
|
||||||
|
"allow"
|
||||||
|
]);
|
||||||
|
mockWindow = jasmine.createSpyObj("$window",
|
||||||
|
[
|
||||||
|
"confirm"
|
||||||
|
]);
|
||||||
|
|
||||||
action = new NavigateAction(
|
action = new NavigateAction(
|
||||||
mockNavigationService,
|
mockNavigationService,
|
||||||
mockQ,
|
mockQ,
|
||||||
|
mockPolicyService,
|
||||||
|
mockWindow,
|
||||||
{ domainObject: mockDomainObject }
|
{ domainObject: mockDomainObject }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("invokes the navigate service when performed", function () {
|
it("invokes the policy service to determine if navigation" +
|
||||||
|
" allowed", function () {
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(mockNavigationService.setNavigation)
|
expect(mockPolicyService.allow)
|
||||||
.toHaveBeenCalledWith(mockDomainObject);
|
.toHaveBeenCalledWith("navigation", jasmine.any(Object), jasmine.any(Object), jasmine.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prompts user if policy rejection", function () {
|
||||||
|
action.perform();
|
||||||
|
expect(mockPolicyService.allow).toHaveBeenCalled();
|
||||||
|
mockPolicyService.allow.mostRecentCall.args[3]();
|
||||||
|
expect(mockWindow.confirm).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("shows a prompt", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
// Ensure the allow callback is called synchronously
|
||||||
|
mockPolicyService.allow.andCallFake(function () {
|
||||||
|
return arguments[3]();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("does not navigate on prompt rejection", function () {
|
||||||
|
mockWindow.confirm.andReturn(false);
|
||||||
|
action.perform();
|
||||||
|
expect(mockNavigationService.setNavigation)
|
||||||
|
.not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does navigate on prompt acceptance", function () {
|
||||||
|
mockWindow.confirm.andReturn(true);
|
||||||
|
action.perform();
|
||||||
|
expect(mockNavigationService.setNavigation)
|
||||||
|
.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("is only applicable when a domain object is in context", function () {
|
it("is only applicable when a domain object is in context", function () {
|
||||||
|
@ -0,0 +1,180 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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/navigation/OrphanNavigationHandler'
|
||||||
|
], function (OrphanNavigationHandler) {
|
||||||
|
describe("OrphanNavigationHandler", function () {
|
||||||
|
var mockTopic,
|
||||||
|
mockThrottle,
|
||||||
|
mockMutationTopic,
|
||||||
|
mockNavigationService,
|
||||||
|
mockDomainObject,
|
||||||
|
mockParentObject,
|
||||||
|
mockContext,
|
||||||
|
mockActionCapability,
|
||||||
|
mockEditor,
|
||||||
|
testParentModel,
|
||||||
|
testId,
|
||||||
|
mockThrottledFns;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
testId = 'some-identifier';
|
||||||
|
|
||||||
|
mockThrottledFns = [];
|
||||||
|
testParentModel = {};
|
||||||
|
|
||||||
|
mockTopic = jasmine.createSpy('topic');
|
||||||
|
mockThrottle = jasmine.createSpy('throttle');
|
||||||
|
mockNavigationService = jasmine.createSpyObj('navigationService', [
|
||||||
|
'getNavigation',
|
||||||
|
'addListener'
|
||||||
|
]);
|
||||||
|
mockMutationTopic = jasmine.createSpyObj('mutationTopic', [
|
||||||
|
'listen'
|
||||||
|
]);
|
||||||
|
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
||||||
|
'getId',
|
||||||
|
'getCapability',
|
||||||
|
'getModel',
|
||||||
|
'hasCapability'
|
||||||
|
]);
|
||||||
|
mockParentObject = jasmine.createSpyObj('domainObject', [
|
||||||
|
'getId',
|
||||||
|
'getCapability',
|
||||||
|
'getModel',
|
||||||
|
'hasCapability'
|
||||||
|
]);
|
||||||
|
mockContext = jasmine.createSpyObj('context', ['getParent']);
|
||||||
|
mockActionCapability = jasmine.createSpyObj('action', ['perform']);
|
||||||
|
mockEditor = jasmine.createSpyObj('editor', ['isEditContextRoot']);
|
||||||
|
|
||||||
|
mockThrottle.andCallFake(function (fn) {
|
||||||
|
var mockThrottledFn =
|
||||||
|
jasmine.createSpy('throttled-' + mockThrottledFns.length);
|
||||||
|
mockThrottledFn.andCallFake(fn);
|
||||||
|
mockThrottledFns.push(mockThrottledFn);
|
||||||
|
return mockThrottledFn;
|
||||||
|
});
|
||||||
|
mockTopic.andCallFake(function (k) {
|
||||||
|
return k === 'mutation' && mockMutationTopic;
|
||||||
|
});
|
||||||
|
mockDomainObject.getId.andReturn(testId);
|
||||||
|
mockDomainObject.getCapability.andCallFake(function (c) {
|
||||||
|
return {
|
||||||
|
context: mockContext,
|
||||||
|
editor: mockEditor
|
||||||
|
}[c];
|
||||||
|
});
|
||||||
|
mockDomainObject.hasCapability.andCallFake(function (c) {
|
||||||
|
return !!mockDomainObject.getCapability(c);
|
||||||
|
});
|
||||||
|
mockParentObject.getModel.andReturn(testParentModel);
|
||||||
|
mockParentObject.getCapability.andCallFake(function (c) {
|
||||||
|
return {
|
||||||
|
action: mockActionCapability
|
||||||
|
}[c];
|
||||||
|
});
|
||||||
|
mockContext.getParent.andReturn(mockParentObject);
|
||||||
|
mockNavigationService.getNavigation.andReturn(mockDomainObject);
|
||||||
|
mockEditor.isEditContextRoot.andReturn(false);
|
||||||
|
|
||||||
|
return new OrphanNavigationHandler(
|
||||||
|
mockThrottle,
|
||||||
|
mockTopic,
|
||||||
|
mockNavigationService
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("listens for mutation with a throttled function", function () {
|
||||||
|
expect(mockMutationTopic.listen)
|
||||||
|
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||||
|
expect(mockThrottledFns.indexOf(
|
||||||
|
mockMutationTopic.listen.mostRecentCall.args[0]
|
||||||
|
)).not.toEqual(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("listens for navigation changes with a throttled function", function () {
|
||||||
|
expect(mockNavigationService.addListener)
|
||||||
|
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||||
|
expect(mockThrottledFns.indexOf(
|
||||||
|
mockNavigationService.addListener.mostRecentCall.args[0]
|
||||||
|
)).not.toEqual(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
[false, true].forEach(function (isOrphan) {
|
||||||
|
var prefix = isOrphan ? "" : "non-";
|
||||||
|
describe("for " + prefix + "orphan objects", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
testParentModel.composition = isOrphan ? [] : [testId];
|
||||||
|
});
|
||||||
|
|
||||||
|
[false, true].forEach(function (isEditRoot) {
|
||||||
|
var caseName = isEditRoot ?
|
||||||
|
"that are being edited" : "that are not being edited";
|
||||||
|
|
||||||
|
function itNavigatesAsExpected() {
|
||||||
|
if (isOrphan && !isEditRoot) {
|
||||||
|
it("navigates to the parent", function () {
|
||||||
|
expect(mockActionCapability.perform)
|
||||||
|
.toHaveBeenCalledWith('navigate');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
it("does nothing", function () {
|
||||||
|
expect(mockActionCapability.perform)
|
||||||
|
.not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe(caseName, function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
mockEditor.isEditContextRoot.andReturn(isEditRoot);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when navigation changes", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
mockNavigationService.addListener.mostRecentCall
|
||||||
|
.args[0](mockDomainObject);
|
||||||
|
});
|
||||||
|
|
||||||
|
itNavigatesAsExpected();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when mutation occurs", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
mockMutationTopic.listen.mostRecentCall
|
||||||
|
.args[0](mockParentObject);
|
||||||
|
});
|
||||||
|
|
||||||
|
itNavigatesAsExpected();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -170,7 +170,7 @@ define([
|
|||||||
"navigationService",
|
"navigationService",
|
||||||
"$log"
|
"$log"
|
||||||
],
|
],
|
||||||
"description": "Edit this object.",
|
"description": "Edit",
|
||||||
"category": "view-control",
|
"category": "view-control",
|
||||||
"glyph": "p"
|
"glyph": "p"
|
||||||
},
|
},
|
||||||
@ -206,7 +206,9 @@ define([
|
|||||||
"implementation": SaveAction,
|
"implementation": SaveAction,
|
||||||
"name": "Save",
|
"name": "Save",
|
||||||
"description": "Save changes made to these objects.",
|
"description": "Save changes made to these objects.",
|
||||||
"depends": [],
|
"depends": [
|
||||||
|
"dialogService"
|
||||||
|
],
|
||||||
"priority": "mandatory"
|
"priority": "mandatory"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -36,14 +36,14 @@
|
|||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="pane right menu-item-description">
|
<div class="pane right menu-item-description l-flex-col">
|
||||||
<div class="desc-area ui-symbol icon type-icon">
|
<div class="desc-area flex-elem holder ui-symbol icon type-icon">
|
||||||
{{representation.activeMetadata.glyph}}
|
{{representation.activeMetadata.glyph}}
|
||||||
</div>
|
</div>
|
||||||
<div class="desc-area title">
|
<div class="desc-area flex-elem holder title">
|
||||||
{{representation.activeMetadata.name}}
|
{{representation.activeMetadata.name}}
|
||||||
</div>
|
</div>
|
||||||
<div class="desc-area description">
|
<div class="desc-area flex-elem holder description">
|
||||||
{{representation.activeMetadata.description}}
|
{{representation.activeMetadata.description}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,18 +50,24 @@ define(
|
|||||||
//If the object existed already, navigate to refresh view
|
//If the object existed already, navigate to refresh view
|
||||||
// with previous object state.
|
// with previous object state.
|
||||||
if (domainObject.getModel().persisted) {
|
if (domainObject.getModel().persisted) {
|
||||||
domainObject.getCapability("action").perform("navigate");
|
return domainObject.getCapability("action").perform("navigate");
|
||||||
} else {
|
} else {
|
||||||
//If the object was new, and user has cancelled, then
|
//If the object was new, and user has cancelled, then
|
||||||
//navigate back to parent because nothing to show.
|
//navigate back to parent because nothing to show.
|
||||||
domainObject.getCapability("location").getOriginal().then(function (original) {
|
return domainObject.getCapability("location").getOriginal().then(function (original) {
|
||||||
parent = original.getCapability("context").getParent();
|
parent = original.getCapability("context").getParent();
|
||||||
parent.getCapability("action").perform("navigate");
|
parent.getCapability("action").perform("navigate");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.domainObject.getCapability("editor").cancel()
|
|
||||||
.then(returnToBrowse);
|
function cancel(allowed) {
|
||||||
|
return allowed && domainObject.getCapability("editor").cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Do navigation first in order to trigger unsaved changes dialog
|
||||||
|
return returnToBrowse()
|
||||||
|
.then(cancel);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,8 +21,8 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
[],
|
['./SaveInProgressDialog'],
|
||||||
function () {
|
function (SaveInProgressDialog) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "Save" action; the action triggered by clicking Save from
|
* The "Save" action; the action triggered by clicking Save from
|
||||||
@ -33,9 +33,11 @@ define(
|
|||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
*/
|
*/
|
||||||
function SaveAction(
|
function SaveAction(
|
||||||
|
dialogService,
|
||||||
context
|
context
|
||||||
) {
|
) {
|
||||||
this.domainObject = (context || {}).domainObject;
|
this.domainObject = (context || {}).domainObject;
|
||||||
|
this.dialogService = dialogService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,7 +48,8 @@ define(
|
|||||||
* @memberof platform/commonUI/edit.SaveAction#
|
* @memberof platform/commonUI/edit.SaveAction#
|
||||||
*/
|
*/
|
||||||
SaveAction.prototype.perform = function () {
|
SaveAction.prototype.perform = function () {
|
||||||
var domainObject = this.domainObject;
|
var domainObject = this.domainObject,
|
||||||
|
dialog = new SaveInProgressDialog(this.dialogService);
|
||||||
|
|
||||||
function resolveWith(object) {
|
function resolveWith(object) {
|
||||||
return function () {
|
return function () {
|
||||||
@ -72,8 +75,17 @@ define(
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
//return doSave().then(returnToBrowse);
|
function hideBlockingDialog(object) {
|
||||||
return doSave().then(returnToBrowse);
|
dialog.hide();
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
return doSave()
|
||||||
|
.then(hideBlockingDialog)
|
||||||
|
.then(returnToBrowse)
|
||||||
|
.catch(hideBlockingDialog);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,9 +21,14 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
define(
|
define([
|
||||||
['../creation/CreateWizard'],
|
'../creation/CreateWizard',
|
||||||
function (CreateWizard) {
|
'./SaveInProgressDialog'
|
||||||
|
],
|
||||||
|
function (
|
||||||
|
CreateWizard,
|
||||||
|
SaveInProgressDialog
|
||||||
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "Save" action; the action triggered by clicking Save from
|
* The "Save" action; the action triggered by clicking Save from
|
||||||
@ -105,7 +110,8 @@ define(
|
|||||||
SaveAsAction.prototype.save = function () {
|
SaveAsAction.prototype.save = function () {
|
||||||
var self = this,
|
var self = this,
|
||||||
domainObject = this.domainObject,
|
domainObject = this.domainObject,
|
||||||
copyService = this.copyService;
|
copyService = this.copyService,
|
||||||
|
dialog = new SaveInProgressDialog(this.dialogService);
|
||||||
|
|
||||||
function doWizardSave(parent) {
|
function doWizardSave(parent) {
|
||||||
var wizard = self.createWizard(parent);
|
var wizard = self.createWizard(parent);
|
||||||
@ -116,6 +122,16 @@ define(
|
|||||||
).then(wizard.populateObjectFromInput.bind(wizard));
|
).then(wizard.populateObjectFromInput.bind(wizard));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showBlockingDialog(object) {
|
||||||
|
dialog.show();
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideBlockingDialog(object) {
|
||||||
|
dialog.hide();
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
function fetchObject(objectId) {
|
function fetchObject(objectId) {
|
||||||
return self.getObjectService().getObjects([objectId]).then(function (objects) {
|
return self.getObjectService().getObjects([objectId]).then(function (objects) {
|
||||||
return objects[objectId];
|
return objects[objectId];
|
||||||
@ -140,14 +156,22 @@ define(
|
|||||||
.then(resolveWith(clonedObject));
|
.then(resolveWith(clonedObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onFailure() {
|
||||||
|
hideBlockingDialog();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return getParent(domainObject)
|
return getParent(domainObject)
|
||||||
.then(doWizardSave)
|
.then(doWizardSave)
|
||||||
|
.then(showBlockingDialog)
|
||||||
.then(getParent)
|
.then(getParent)
|
||||||
.then(cloneIntoParent)
|
.then(cloneIntoParent)
|
||||||
.then(commitEditingAfterClone)
|
.then(commitEditingAfterClone)
|
||||||
.catch(resolveWith(false));
|
.then(hideBlockingDialog)
|
||||||
|
.catch(onFailure);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this action is applicable in a given context.
|
* Check if this action is applicable in a given context.
|
||||||
* This will ensure that a domain object is present in the context,
|
* This will ensure that a domain object is present in the context,
|
||||||
|
20
platform/commonUI/edit/src/actions/SaveInProgressDialog.js
Normal file
20
platform/commonUI/edit/src/actions/SaveInProgressDialog.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
define([], function () {
|
||||||
|
function SaveInProgressDialog(dialogService) {
|
||||||
|
this.dialogService = dialogService;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveInProgressDialog.prototype.show = function () {
|
||||||
|
this.dialogService.showBlockingMessage({
|
||||||
|
title: "Saving...",
|
||||||
|
hint: "Do not navigate away from this page or close this browser tab while this message is displayed.",
|
||||||
|
unknownProgress: true,
|
||||||
|
severity: "info"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
SaveInProgressDialog.prototype.hide = function () {
|
||||||
|
this.dialogService.dismiss();
|
||||||
|
};
|
||||||
|
|
||||||
|
return SaveInProgressDialog;
|
||||||
|
});
|
@ -67,10 +67,17 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onCancel() {
|
function onCancel() {
|
||||||
return self.persistenceCapability.refresh().then(function (result) {
|
if (self.domainObject.getModel().persisted !== undefined) {
|
||||||
|
//Fetch clean model from persistence
|
||||||
|
return self.persistenceCapability.refresh().then(function (result) {
|
||||||
|
self.persistPending = false;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
self.persistPending = false;
|
self.persistPending = false;
|
||||||
return result;
|
//Model is undefined in persistence, so return undefined.
|
||||||
});
|
return self.$q.when(undefined);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.transactionService.isActive()) {
|
if (this.transactionService.isActive()) {
|
||||||
|
@ -24,12 +24,11 @@ define(
|
|||||||
["../../src/actions/CancelAction"],
|
["../../src/actions/CancelAction"],
|
||||||
function (CancelAction) {
|
function (CancelAction) {
|
||||||
|
|
||||||
//TODO: Disabled for NEM Beta
|
describe("The Cancel action", function () {
|
||||||
xdescribe("The Cancel action", function () {
|
var mockDomainObject,
|
||||||
var mockLocation,
|
mockParentObject,
|
||||||
mockDomainObject,
|
capabilities = {},
|
||||||
mockEditorCapability,
|
parentCapabilities = {},
|
||||||
mockUrlService,
|
|
||||||
actionContext,
|
actionContext,
|
||||||
action;
|
action;
|
||||||
|
|
||||||
@ -42,61 +41,114 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockLocation = jasmine.createSpyObj(
|
|
||||||
"$location",
|
|
||||||
["path"]
|
|
||||||
);
|
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
"domainObject",
|
"domainObject",
|
||||||
["getCapability", "hasCapability"]
|
[
|
||||||
|
"getCapability",
|
||||||
|
"hasCapability",
|
||||||
|
"getModel"
|
||||||
|
]
|
||||||
);
|
);
|
||||||
mockEditorCapability = jasmine.createSpyObj(
|
mockDomainObject.getModel.andReturn({});
|
||||||
|
|
||||||
|
mockParentObject = jasmine.createSpyObj(
|
||||||
|
"parentObject",
|
||||||
|
[
|
||||||
|
"getCapability"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
mockParentObject.getCapability.andCallFake(function (name) {
|
||||||
|
return parentCapabilities[name];
|
||||||
|
});
|
||||||
|
|
||||||
|
capabilities.editor = jasmine.createSpyObj(
|
||||||
"editor",
|
"editor",
|
||||||
["save", "cancel"]
|
["save", "cancel", "isEditContextRoot"]
|
||||||
);
|
);
|
||||||
mockUrlService = jasmine.createSpyObj(
|
capabilities.action = jasmine.createSpyObj(
|
||||||
"urlService",
|
"actionCapability",
|
||||||
["urlForLocation"]
|
[
|
||||||
|
"perform"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
capabilities.location = jasmine.createSpyObj(
|
||||||
|
"locationCapability",
|
||||||
|
[
|
||||||
|
"getOriginal"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
capabilities.location.getOriginal.andReturn(mockPromise(mockDomainObject));
|
||||||
|
capabilities.context = jasmine.createSpyObj(
|
||||||
|
"contextCapability",
|
||||||
|
[
|
||||||
|
"getParent"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
capabilities.context.getParent.andReturn(mockParentObject);
|
||||||
|
|
||||||
|
parentCapabilities.action = jasmine.createSpyObj(
|
||||||
|
"actionCapability",
|
||||||
|
[
|
||||||
|
"perform"
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
actionContext = {
|
actionContext = {
|
||||||
domainObject: mockDomainObject
|
domainObject: mockDomainObject
|
||||||
};
|
};
|
||||||
|
|
||||||
mockDomainObject.hasCapability.andReturn(true);
|
mockDomainObject.getCapability.andCallFake(function (name) {
|
||||||
mockDomainObject.getCapability.andReturn(mockEditorCapability);
|
return capabilities[name];
|
||||||
mockEditorCapability.cancel.andReturn(mockPromise(true));
|
});
|
||||||
|
|
||||||
action = new CancelAction(mockLocation, mockUrlService, actionContext);
|
mockDomainObject.hasCapability.andCallFake(function (name) {
|
||||||
|
return !!capabilities[name];
|
||||||
|
});
|
||||||
|
|
||||||
|
capabilities.editor.cancel.andReturn(mockPromise(true));
|
||||||
|
|
||||||
|
action = new CancelAction(actionContext);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("only applies to domain object with an editor capability", function () {
|
it("only applies to domain object that is being edited", function () {
|
||||||
|
capabilities.editor.isEditContextRoot.andReturn(true);
|
||||||
expect(CancelAction.appliesTo(actionContext)).toBeTruthy();
|
expect(CancelAction.appliesTo(actionContext)).toBeTruthy();
|
||||||
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
|
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
|
||||||
|
|
||||||
|
capabilities.editor.isEditContextRoot.andReturn(false);
|
||||||
|
expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
|
||||||
|
|
||||||
mockDomainObject.hasCapability.andReturn(false);
|
mockDomainObject.hasCapability.andReturn(false);
|
||||||
mockDomainObject.getCapability.andReturn(undefined);
|
|
||||||
expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
|
expect(CancelAction.appliesTo(actionContext)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("invokes the editor capability's save functionality when performed", function () {
|
it("invokes the editor capability's cancel functionality when" +
|
||||||
// Verify precondition
|
" performed", function () {
|
||||||
expect(mockEditorCapability.cancel).not.toHaveBeenCalled();
|
mockDomainObject.getModel.andReturn({persisted: 1});
|
||||||
|
//Return true from navigate action
|
||||||
|
capabilities.action.perform.andReturn(mockPromise(true));
|
||||||
action.perform();
|
action.perform();
|
||||||
|
|
||||||
// Should have called cancel
|
// Should have called cancel
|
||||||
expect(mockEditorCapability.cancel).toHaveBeenCalled();
|
expect(capabilities.editor.cancel).toHaveBeenCalled();
|
||||||
|
|
||||||
// Definitely shouldn't call save!
|
// Definitely shouldn't call save!
|
||||||
expect(mockEditorCapability.save).not.toHaveBeenCalled();
|
expect(capabilities.editor.save).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns to browse when performed", function () {
|
it("navigates to object if existing using navigate action", function () {
|
||||||
|
mockDomainObject.getModel.andReturn({persisted: 1});
|
||||||
|
//Return true from navigate action
|
||||||
|
capabilities.action.perform.andReturn(mockPromise(true));
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(mockLocation.path).toHaveBeenCalledWith(
|
expect(capabilities.action.perform).toHaveBeenCalledWith("navigate");
|
||||||
mockUrlService.urlForLocation("browse", mockDomainObject)
|
});
|
||||||
);
|
|
||||||
|
it("navigates to parent if new using navigate action", function () {
|
||||||
|
mockDomainObject.getModel.andReturn({persisted: undefined});
|
||||||
|
action.perform();
|
||||||
|
expect(parentCapabilities.action.perform).toHaveBeenCalledWith("navigate");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ define(
|
|||||||
var mockDomainObject,
|
var mockDomainObject,
|
||||||
mockEditorCapability,
|
mockEditorCapability,
|
||||||
actionContext,
|
actionContext,
|
||||||
|
dialogService,
|
||||||
mockActionCapability,
|
mockActionCapability,
|
||||||
capabilities = {},
|
capabilities = {},
|
||||||
action;
|
action;
|
||||||
@ -36,6 +37,9 @@ define(
|
|||||||
return {
|
return {
|
||||||
then: function (callback) {
|
then: function (callback) {
|
||||||
return mockPromise(callback(value));
|
return mockPromise(callback(value));
|
||||||
|
},
|
||||||
|
catch: function (callback) {
|
||||||
|
return mockPromise(callback(value));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -64,6 +68,10 @@ define(
|
|||||||
actionContext = {
|
actionContext = {
|
||||||
domainObject: mockDomainObject
|
domainObject: mockDomainObject
|
||||||
};
|
};
|
||||||
|
dialogService = jasmine.createSpyObj(
|
||||||
|
"dialogService",
|
||||||
|
["showBlockingMessage", "dismiss"]
|
||||||
|
);
|
||||||
|
|
||||||
mockDomainObject.hasCapability.andReturn(true);
|
mockDomainObject.hasCapability.andReturn(true);
|
||||||
mockDomainObject.getCapability.andCallFake(function (capability) {
|
mockDomainObject.getCapability.andCallFake(function (capability) {
|
||||||
@ -73,8 +81,7 @@ define(
|
|||||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||||
|
|
||||||
action = new SaveAction(actionContext);
|
action = new SaveAction(dialogService, actionContext);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("only applies to domain object with an editor capability", function () {
|
it("only applies to domain object with an editor capability", function () {
|
||||||
@ -104,6 +111,19 @@ define(
|
|||||||
expect(mockActionCapability.perform).toHaveBeenCalledWith("navigate");
|
expect(mockActionCapability.perform).toHaveBeenCalledWith("navigate");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("shows a dialog while saving", function () {
|
||||||
|
mockEditorCapability.save.andReturn(new Promise(function () {}));
|
||||||
|
action.perform();
|
||||||
|
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
|
||||||
|
expect(dialogService.dismiss).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hides a dialog when saving is complete", function () {
|
||||||
|
action.perform();
|
||||||
|
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
|
||||||
|
expect(dialogService.dismiss).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -100,7 +100,9 @@ define(
|
|||||||
mockDialogService = jasmine.createSpyObj(
|
mockDialogService = jasmine.createSpyObj(
|
||||||
"dialogService",
|
"dialogService",
|
||||||
[
|
[
|
||||||
"getUserInput"
|
"getUserInput",
|
||||||
|
"showBlockingMessage",
|
||||||
|
"dismiss"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
mockDialogService.getUserInput.andReturn(mockPromise(undefined));
|
mockDialogService.getUserInput.andReturn(mockPromise(undefined));
|
||||||
@ -169,6 +171,19 @@ define(
|
|||||||
expect(mockDialogService.getUserInput).toHaveBeenCalled();
|
expect(mockDialogService.getUserInput).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("shows a blocking dialog while waiting for save", function () {
|
||||||
|
mockEditorCapability.save.andReturn(new Promise(function () {}));
|
||||||
|
action.perform();
|
||||||
|
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
||||||
|
expect(mockDialogService.dismiss).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hides the blocking dialog after saving", function () {
|
||||||
|
action.perform();
|
||||||
|
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
||||||
|
expect(mockDialogService.dismiss).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -57,6 +57,15 @@ define(
|
|||||||
);
|
);
|
||||||
mockPersistence.persist.andReturn(fastPromise());
|
mockPersistence.persist.andReturn(fastPromise());
|
||||||
mockPersistence.refresh.andReturn(fastPromise());
|
mockPersistence.refresh.andReturn(fastPromise());
|
||||||
|
|
||||||
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
|
"domainObject",
|
||||||
|
[
|
||||||
|
"getModel"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
mockDomainObject.getModel.andReturn({persisted: 1});
|
||||||
|
|
||||||
capability = new TransactionalPersistenceCapability(mockQ, mockTransactionService, mockPersistence, mockDomainObject);
|
capability = new TransactionalPersistenceCapability(mockQ, mockTransactionService, mockPersistence, mockDomainObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -78,6 +87,20 @@ define(
|
|||||||
expect(mockPersistence.refresh).toHaveBeenCalled();
|
expect(mockPersistence.refresh).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("if transaction is active, cancel call is queued that refreshes model when appropriate", function () {
|
||||||
|
mockTransactionService.isActive.andReturn(true);
|
||||||
|
capability.persist();
|
||||||
|
expect(mockTransactionService.addToTransaction).toHaveBeenCalled();
|
||||||
|
|
||||||
|
mockDomainObject.getModel.andReturn({});
|
||||||
|
mockTransactionService.addToTransaction.mostRecentCall.args[1]();
|
||||||
|
expect(mockPersistence.refresh).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
mockDomainObject.getModel.andReturn({persisted: 1});
|
||||||
|
mockTransactionService.addToTransaction.mostRecentCall.args[1]();
|
||||||
|
expect(mockPersistence.refresh).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it("persist call is only added to transaction once", function () {
|
it("persist call is only added to transaction once", function () {
|
||||||
mockTransactionService.isActive.andReturn(true);
|
mockTransactionService.isActive.andReturn(true);
|
||||||
capability.persist();
|
capability.persist();
|
||||||
|
@ -23,10 +23,12 @@
|
|||||||
define([
|
define([
|
||||||
"./src/FormatProvider",
|
"./src/FormatProvider",
|
||||||
"./src/UTCTimeFormat",
|
"./src/UTCTimeFormat",
|
||||||
|
"./src/DurationFormat",
|
||||||
'legacyRegistry'
|
'legacyRegistry'
|
||||||
], function (
|
], function (
|
||||||
FormatProvider,
|
FormatProvider,
|
||||||
UTCTimeFormat,
|
UTCTimeFormat,
|
||||||
|
DurationFormat,
|
||||||
legacyRegistry
|
legacyRegistry
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -48,6 +50,10 @@ define([
|
|||||||
{
|
{
|
||||||
"key": "utc",
|
"key": "utc",
|
||||||
"implementation": UTCTimeFormat
|
"implementation": UTCTimeFormat
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "duration",
|
||||||
|
"implementation": DurationFormat
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"constants": [
|
"constants": [
|
||||||
|
60
platform/commonUI/formats/src/DurationFormat.js
Normal file
60
platform/commonUI/formats/src/DurationFormat.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 UTC timestamps. Interprets numeric values as
|
||||||
|
* milliseconds since the start of 1970. Displays only the utc time. Can
|
||||||
|
* be used, with care, for specifying time intervals.
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
});
|
@ -204,6 +204,7 @@ define([
|
|||||||
"implementation": TimeRangeController,
|
"implementation": TimeRangeController,
|
||||||
"depends": [
|
"depends": [
|
||||||
"$scope",
|
"$scope",
|
||||||
|
"$timeout",
|
||||||
"formatService",
|
"formatService",
|
||||||
"DEFAULT_TIME_FORMAT",
|
"DEFAULT_TIME_FORMAT",
|
||||||
"now"
|
"now"
|
||||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -100,5 +100,6 @@
|
|||||||
<glyph unicode="" glyph-name="icon-tabular-realtime" d="M896 960h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128zM448 668l25.060-25.32c7.916-7.922 18.856-12.822 30.94-12.822s23.024 4.9 30.94 12.822l75.5 76.3c29.97 30.338 71.571 49.128 117.56 49.128s87.59-18.79 117.544-49.112l50.456-50.997v-152.2c-24.111 8.83-44.678 22.255-61.542 39.342l-75.518 76.318c-7.916 7.922-18.856 12.822-30.94 12.822s-23.024-4.9-30.94-12.822l-75.5-76.3c-29.971-30.343-71.575-49.137-117.568-49.137-20.084 0-39.331 3.584-57.137 10.146l1.145 151.831zM320 0h-192c-35.26 0.214-63.786 28.74-64 63.98v128.020h256v-192zM320 256h-256v192h256v-192zM320 512h-256v192h256v-192zM640 0h-256v192h256v-192zM448 323.38v174.5c1.88-1.74 3.74-3.5 5.56-5.34l75.5-76.3c7.916-7.922 18.856-12.822 30.94-12.822s23.024 4.9 30.94 12.822l75.5 76.3c29.966 30.333 71.56 49.119 117.542 49.119 43.28 0 82.673-16.644 112.128-43.879l-0.11-174.399c-1.88 1.74-3.74 3.5-5.56 5.34l-75.5 76.3c-7.916 7.922-18.856 12.822-30.94 12.822s-23.024-4.9-30.94-12.822l-75.5-76.3c-29.966-30.333-71.56-49.119-117.542-49.119-43.28 0-82.673 16.644-112.128 43.879zM960 64c-0.214-35.26-28.74-63.786-63.98-64h-192.020v192h256v-128z" />
|
<glyph unicode="" glyph-name="icon-tabular-realtime" d="M896 960h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128zM448 668l25.060-25.32c7.916-7.922 18.856-12.822 30.94-12.822s23.024 4.9 30.94 12.822l75.5 76.3c29.97 30.338 71.571 49.128 117.56 49.128s87.59-18.79 117.544-49.112l50.456-50.997v-152.2c-24.111 8.83-44.678 22.255-61.542 39.342l-75.518 76.318c-7.916 7.922-18.856 12.822-30.94 12.822s-23.024-4.9-30.94-12.822l-75.5-76.3c-29.971-30.343-71.575-49.137-117.568-49.137-20.084 0-39.331 3.584-57.137 10.146l1.145 151.831zM320 0h-192c-35.26 0.214-63.786 28.74-64 63.98v128.020h256v-192zM320 256h-256v192h256v-192zM320 512h-256v192h256v-192zM640 0h-256v192h256v-192zM448 323.38v174.5c1.88-1.74 3.74-3.5 5.56-5.34l75.5-76.3c7.916-7.922 18.856-12.822 30.94-12.822s23.024 4.9 30.94 12.822l75.5 76.3c29.966 30.333 71.56 49.119 117.542 49.119 43.28 0 82.673-16.644 112.128-43.879l-0.11-174.399c-1.88 1.74-3.74 3.5-5.56 5.34l-75.5 76.3c-7.916 7.922-18.856 12.822-30.94 12.822s-23.024-4.9-30.94-12.822l-75.5-76.3c-29.966-30.333-71.56-49.119-117.542-49.119-43.28 0-82.673 16.644-112.128 43.879zM960 64c-0.214-35.26-28.74-63.786-63.98-64h-192.020v192h256v-128z" />
|
||||||
<glyph unicode="" glyph-name="icon-tabular-lad" d="M896 960h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128zM64 704h256v-192h-256v192zM64 448h256v-192h-256v192zM128 0c-35.26 0.214-63.786 28.74-64 63.98v128.020h256v-192h-192zM384 0v192h256v-192h-256zM960 64c-0.214-35.26-28.74-63.786-63.98-64h-192.020v192h256v-128zM960 448v-192h-576v192h64v64h-64v192h576v-192h-64v-64h64zM782.32 412.62l-110.32 55.16v172.22c0 17.673-14.327 32-32 32s-32-14.327-32-32v-211.78l145.68-72.84c4.172-2.133 9.1-3.383 14.32-3.383 17.675 0 32.003 14.328 32.003 32.003 0 12.454-7.114 23.247-17.501 28.536z" />
|
<glyph unicode="" glyph-name="icon-tabular-lad" d="M896 960h-768c-70.606-0.215-127.785-57.394-128-127.979v-768.021c0.215-70.606 57.394-127.785 127.979-128h768.021c70.606 0.215 127.785 57.394 128 127.979v768.021c-0.215 70.606-57.394 127.785-127.979 128zM64 704h256v-192h-256v192zM64 448h256v-192h-256v192zM128 0c-35.26 0.214-63.786 28.74-64 63.98v128.020h256v-192h-192zM384 0v192h256v-192h-256zM960 64c-0.214-35.26-28.74-63.786-63.98-64h-192.020v192h256v-128zM960 448v-192h-576v192h64v64h-64v192h576v-192h-64v-64h64zM782.32 412.62l-110.32 55.16v172.22c0 17.673-14.327 32-32 32s-32-14.327-32-32v-211.78l145.68-72.84c4.172-2.133 9.1-3.383 14.32-3.383 17.675 0 32.003 14.328 32.003 32.003 0 12.454-7.114 23.247-17.501 28.536z" />
|
||||||
<glyph unicode="" glyph-name="icon-tabular-lad-set" d="M128 192v576c-70.606-0.215-127.785-57.394-128-127.979v-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979l-576 0.021c-70.606 0.215-127.785 57.394-128 127.979zM896 960h-576c-70.606-0.215-127.785-57.394-128-127.979v-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v576.021c-0.215 70.606-57.394 127.785-127.979 128zM256 768h192v-128h-192v128zM256 576h192v-192h-192v192zM320 192c-35.26 0.214-63.786 28.74-64 63.98v64.020h192v-128h-128zM512 192v128h192v-128h-192zM960 256c-0.214-35.26-28.74-63.786-63.98-64h-128.020v128h192v-64zM960 384h-448v384h448v-384zM832 480c0.002 0 0.005 0 0.007 0 17.673 0 32 14.327 32 32 0 14.055-9.062 25.994-21.662 30.293l-74.345 24.767v104.94c0 17.673-14.327 32-32 32s-32-14.327-32-32v-151.060l117.88-39.3c3.018-1.040 6.495-1.64 10.113-1.64 0.003 0 0.005 0 0.008 0z" />
|
<glyph unicode="" glyph-name="icon-tabular-lad-set" d="M128 192v576c-70.606-0.215-127.785-57.394-128-127.979v-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979l-576 0.021c-70.606 0.215-127.785 57.394-128 127.979zM896 960h-576c-70.606-0.215-127.785-57.394-128-127.979v-576.021c0.215-70.606 57.394-127.785 127.979-128h576.021c70.606 0.215 127.785 57.394 128 127.979v576.021c-0.215 70.606-57.394 127.785-127.979 128zM256 768h192v-128h-192v128zM256 576h192v-192h-192v192zM320 192c-35.26 0.214-63.786 28.74-64 63.98v64.020h192v-128h-128zM512 192v128h192v-128h-192zM960 256c-0.214-35.26-28.74-63.786-63.98-64h-128.020v128h192v-64zM960 384h-448v384h448v-384zM832 480c0.002 0 0.005 0 0.007 0 17.673 0 32 14.327 32 32 0 14.055-9.062 25.994-21.662 30.293l-74.345 24.767v104.94c0 17.673-14.327 32-32 32s-32-14.327-32-32v-151.060l117.88-39.3c3.018-1.040 6.495-1.64 10.113-1.64 0.003 0 0.005 0 0.008 0z" />
|
||||||
|
<glyph unicode="" glyph-name="icon-download" d="M832 384v-255.66l-0.34-0.34-639.66 0.34v255.66h-192v-256c0-105.6 86.4-192 192-192h640c105.6 0 192 86.4 192 192v256h-192zM512 320l448 448h-256v192h-384v-192h-256l448-448z" />
|
||||||
<glyph unicode="" glyph-name="icon-x" d="M384 448l-365.332-365.332c-24.89-24.89-24.89-65.62 0-90.51l37.49-37.49c24.89-24.89 65.62-24.89 90.51 0 0 0 365.332 365.332 365.332 365.332l365.332-365.332c24.89-24.89 65.62-24.89 90.51 0l37.49 37.49c24.89 24.89 24.89 65.62 0 90.51l-365.332 365.332c0 0 365.332 365.332 365.332 365.332 24.89 24.89 24.89 65.62 0 90.51l-37.49 37.49c-24.89 24.89-65.62 24.89-90.51 0 0 0-365.332-365.332-365.332-365.332l-365.332 365.332c-24.89 24.89-65.62 24.89-90.51 0l-37.49-37.49c-24.89-24.89-24.89-65.62 0-90.51 0 0 365.332-365.332 365.332-365.332z" />
|
<glyph unicode="" glyph-name="icon-x" d="M384 448l-365.332-365.332c-24.89-24.89-24.89-65.62 0-90.51l37.49-37.49c24.89-24.89 65.62-24.89 90.51 0 0 0 365.332 365.332 365.332 365.332l365.332-365.332c24.89-24.89 65.62-24.89 90.51 0l37.49 37.49c24.89 24.89 24.89 65.62 0 90.51l-365.332 365.332c0 0 365.332 365.332 365.332 365.332 24.89 24.89 24.89 65.62 0 90.51l-37.49 37.49c-24.89 24.89-65.62 24.89-90.51 0 0 0-365.332-365.332-365.332-365.332l-365.332 365.332c-24.89 24.89-65.62 24.89-90.51 0l-37.49-37.49c-24.89-24.89-24.89-65.62 0-90.51 0 0 365.332-365.332 365.332-365.332z" />
|
||||||
</font></defs></svg>
|
</font></defs></svg>
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Binary file not shown.
Binary file not shown.
91
platform/commonUI/general/res/sass/_animations.scss
Normal file
91
platform/commonUI/general/res/sass/_animations.scss
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
@ -108,6 +108,9 @@
|
|||||||
&.grows {
|
&.grows {
|
||||||
@include flex(1 1 auto);
|
@include flex(1 1 auto);
|
||||||
}
|
}
|
||||||
|
&.contents-align-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.flex-container {
|
.flex-container {
|
||||||
// Apply to wrapping elements, mct-includes, etc.
|
// Apply to wrapping elements, mct-includes, etc.
|
||||||
@ -121,17 +124,18 @@
|
|||||||
.l-flex-row {
|
.l-flex-row {
|
||||||
@include flex-direction(row);
|
@include flex-direction(row);
|
||||||
&.flex-elem { @include flex(1 1 auto); }
|
&.flex-elem { @include flex(1 1 auto); }
|
||||||
.flex-elem {
|
> .flex-elem {
|
||||||
height: inherit;
|
height: inherit;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
&.holder:not(:last-child) { margin-right: $interiorMargin; }
|
||||||
}
|
}
|
||||||
.flex-container { @include flex-direction(row); }
|
.flex-container { @include flex-direction(row); }
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-flex-col {
|
.l-flex-col {
|
||||||
@include flex-direction(column);
|
@include flex-direction(column);
|
||||||
.flex-elem {
|
> .flex-elem {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
&.holder:not(:last-child) { margin-bottom: $interiorMarginLg; }
|
&.holder:not(:last-child) { margin-bottom: $interiorMarginLg; }
|
||||||
}
|
}
|
||||||
|
@ -48,8 +48,8 @@ $uePaneMiniTabW: 10px;
|
|||||||
$uePaneMiniTabCollapsedW: 11px;
|
$uePaneMiniTabCollapsedW: 11px;
|
||||||
$ueEditLeftPaneW: 75%;
|
$ueEditLeftPaneW: 75%;
|
||||||
$treeSearchInputBarH: 25px;
|
$treeSearchInputBarH: 25px;
|
||||||
$ueTimeControlH: (33px, 18px, 20px);
|
$ueTimeControlH: (25px, 6px, 20px);
|
||||||
// Panes
|
/*************** Panes */
|
||||||
$ueBrowseLeftPaneTreeMinW: 150px;
|
$ueBrowseLeftPaneTreeMinW: 150px;
|
||||||
$ueBrowseLeftPaneTreeMaxW: 35%;
|
$ueBrowseLeftPaneTreeMaxW: 35%;
|
||||||
$ueBrowseLeftPaneTreeW: 25%;
|
$ueBrowseLeftPaneTreeW: 25%;
|
||||||
@ -57,48 +57,59 @@ $ueBrowseRightPaneInspectMinW: 200px;
|
|||||||
$ueBrowseRightPaneInspectMaxW: 35%;
|
$ueBrowseRightPaneInspectMaxW: 35%;
|
||||||
$ueBrowseRightPaneInspectW: 20%;
|
$ueBrowseRightPaneInspectW: 20%;
|
||||||
$ueDesktopMinW: 600px;
|
$ueDesktopMinW: 600px;
|
||||||
|
/*************** Overlay */
|
||||||
// Overlay
|
|
||||||
$ovrTopBarH: 45px;
|
$ovrTopBarH: 45px;
|
||||||
$ovrFooterH: 24px;
|
$ovrFooterH: 24px;
|
||||||
$overlayMargin: 25px;
|
$overlayMargin: 25px;
|
||||||
// Items
|
/*************** Items */
|
||||||
$ueBrowseGridItemLg: 200px;
|
$ueBrowseGridItemLg: 200px;
|
||||||
$ueBrowseGridItemTopBarH: 20px;
|
$ueBrowseGridItemTopBarH: 20px;
|
||||||
$ueBrowseGridItemBottomBarH: 30px;
|
$ueBrowseGridItemBottomBarH: 30px;
|
||||||
$itemPadLR: 5px;
|
$itemPadLR: 5px;
|
||||||
// Tree
|
/*************** Tree */
|
||||||
$treeVCW: 10px;
|
$treeVCW: 10px;
|
||||||
$treeTypeIconH: 1.4em; // was 16px
|
$treeTypeIconH: 1.4em; // was 16px
|
||||||
$treeTypeIconHPx: 16px;
|
$treeTypeIconHPx: 16px;
|
||||||
$treeTypeIconW: 18px;
|
$treeTypeIconW: 18px;
|
||||||
$treeContextTriggerW: 20px;
|
$treeContextTriggerW: 20px;
|
||||||
// Tabular
|
/*************** Tabular */
|
||||||
$tabularHeaderH: 22px; //18px
|
$tabularHeaderH: 22px; //18px
|
||||||
$tabularTdPadLR: $itemPadLR;
|
$tabularTdPadLR: $itemPadLR;
|
||||||
$tabularTdPadTB: 3px;
|
$tabularTdPadTB: 3px;
|
||||||
// Imagery
|
/*************** Imagery */
|
||||||
$imageMainControlBarH: 25px;
|
$imageMainControlBarH: 25px;
|
||||||
$imageThumbsD: 120px;
|
$imageThumbsD: 120px;
|
||||||
$imageThumbsWrapperH: $imageThumbsD * 1.4;
|
$imageThumbsWrapperH: $imageThumbsD * 1.4;
|
||||||
$imageThumbPad: 1px;
|
$imageThumbPad: 1px;
|
||||||
// Ticks
|
/*************** Ticks */
|
||||||
$ticksH: 25px;
|
$ticksH: 25px;
|
||||||
$tickLblVMargin: 3px;
|
$tickLblVMargin: 3px;
|
||||||
$tickLblH: 15px;
|
$tickLblH: 15px;
|
||||||
$tickLblW: 50px;
|
$tickLblW: 50px;
|
||||||
$tickH: $ticksH - $tickLblVMargin - $tickLblH;
|
$tickH: $ticksH - $tickLblVMargin - $tickLblH;
|
||||||
$tickW: 1px;
|
$tickW: 1px;
|
||||||
// Bubbles
|
/*************** Plots */
|
||||||
|
$plotYBarW: 60px;
|
||||||
|
$plotYLabelMinH: 20px;
|
||||||
|
$plotYLabelW: 10px;
|
||||||
|
$plotXBarH: 32px;
|
||||||
|
$plotLegendH: 20px;
|
||||||
|
$plotSwatchD: 8px;
|
||||||
|
// 1: Top, 2: right, 3: bottom, 4: left
|
||||||
|
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH + $interiorMargin, $plotYBarW);
|
||||||
|
/* min plot height is based on user testing to find minimum useful height */
|
||||||
|
$plotMinH: 95px;
|
||||||
|
/*************** Bubbles */
|
||||||
$bubbleArwSize: 10px;
|
$bubbleArwSize: 10px;
|
||||||
$bubblePad: $interiorMargin;
|
$bubblePad: $interiorMargin;
|
||||||
$bubbleMinW: 100px;
|
$bubbleMinW: 100px;
|
||||||
$bubbleMaxW: 300px;
|
$bubbleMaxW: 300px;
|
||||||
// Forms
|
/*************** Forms */
|
||||||
$reqSymbolW: 15px;
|
$reqSymbolW: 15px;
|
||||||
$reqSymbolM: $interiorMargin * 2;
|
$reqSymbolM: $interiorMargin * 2;
|
||||||
$reqSymbolFontSize: 0.7em;
|
$reqSymbolFontSize: 0.7em;
|
||||||
// Wait Spinner Defaults
|
$inputTextP: 3px 5px;
|
||||||
|
/*************** Wait Spinner Defaults */
|
||||||
$waitSpinnerD: 32px;
|
$waitSpinnerD: 32px;
|
||||||
$waitSpinnerTreeD: 20px;
|
$waitSpinnerTreeD: 20px;
|
||||||
$waitSpinnerBorderW: 5px;
|
$waitSpinnerBorderW: 5px;
|
||||||
@ -124,6 +135,8 @@ $dirImgs: $dirCommonRes + 'images/';
|
|||||||
|
|
||||||
/************************** TIMINGS */
|
/************************** TIMINGS */
|
||||||
$controlFadeMs: 100ms;
|
$controlFadeMs: 100ms;
|
||||||
|
$browseToEditAnimMs: 400ms;
|
||||||
|
$editBorderPulseMs: 500ms;
|
||||||
|
|
||||||
/************************** LIMITS */
|
/************************** LIMITS */
|
||||||
$glyphLimit: '\e603';
|
$glyphLimit: '\e603';
|
||||||
|
@ -39,15 +39,20 @@
|
|||||||
@include pulse($animName: pulse-subtle, $dur: 500ms, $opacity0: 0.7);
|
@include pulse($animName: pulse-subtle, $dur: 500ms, $opacity0: 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin pulseBorder($c: red, $dur: 500ms, $iteration: infinite, $delay: 0s, $opacity0: 0, $opacity100: 1) {
|
@mixin animTo($animName, $propName, $propValStart, $propValEnd, $dur: 500ms, $delay: 0) {
|
||||||
@include keyframes(pulseBorder) {
|
@include keyframes($animName) {
|
||||||
0% { border-color: rgba($c, $opacity0); }
|
from { #{propName}: $propValStart; }
|
||||||
100% { border-color: rgba($c, $opacity100); }
|
to { #{$propName}: $propValEnd; }
|
||||||
}
|
}
|
||||||
@include animation-name(pulseBorder);
|
@include animToParams($animName, $dur: 500ms, $delay: 0)
|
||||||
@include animation-duration($dur);
|
|
||||||
@include animation-direction(alternate);
|
|
||||||
@include animation-iteration-count($iteration);
|
|
||||||
@include animation-timing-function(ease);
|
|
||||||
@include animation-delay($delay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin animToParams($animName, $dur: 500ms, $delay: 0) {
|
||||||
|
@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-timing-function(ease-in-out);
|
||||||
|
}
|
@ -66,7 +66,7 @@ input, textarea {
|
|||||||
input[type="text"],
|
input[type="text"],
|
||||||
input[type="search"] {
|
input[type="search"] {
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
padding: 3px 5px;
|
padding: $inputTextP;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3 {
|
h1, h2, h3 {
|
||||||
|
@ -66,8 +66,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu .type-icon,
|
.menu .type-icon,
|
||||||
.tree-item .type-icon,
|
.tree-item .type-icon {
|
||||||
.super-menu.menu .type-icon {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
@import "effects";
|
@import "effects";
|
||||||
@import "global";
|
@import "global";
|
||||||
|
@import "animations";
|
||||||
@import "archetypes";
|
@import "archetypes";
|
||||||
@import "about";
|
@import "about";
|
||||||
@import "text";
|
@import "text";
|
||||||
@ -40,7 +41,7 @@
|
|||||||
@import "controls/lists";
|
@import "controls/lists";
|
||||||
@import "controls/menus";
|
@import "controls/menus";
|
||||||
@import "controls/messages";
|
@import "controls/messages";
|
||||||
@import "controls/time-controller";
|
@import "controls/time-conductor";
|
||||||
@import "mobile/controls/menus";
|
@import "mobile/controls/menus";
|
||||||
|
|
||||||
/********************************* FORMS */
|
/********************************* FORMS */
|
||||||
|
@ -185,21 +185,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mixin sliderTrack($bg: $scrollbarTrackColorBg) {
|
@mixin sliderTrack($bg: $scrollbarTrackColorBg) {
|
||||||
//$b: 1px solid lighten($bg, 30%);
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@include boxIncised(0.7);
|
@include boxIncised(0.7);
|
||||||
background-color: $bg;
|
background-color: $bg;
|
||||||
//border-bottom: $b;
|
|
||||||
//border-right: $b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin controlGrippy($b, $direction: horizontal, $w: 1px, $style: dotted) {
|
@mixin controlGrippy($b, $direction: horizontal, $w: 1px, $style: dotted) {
|
||||||
//&:before {
|
|
||||||
//@include trans-prop-nice("border-color", 25ms);
|
|
||||||
content: '';
|
content: '';
|
||||||
display: block;
|
display: block;
|
||||||
//height: auto;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
@ -305,7 +299,6 @@
|
|||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: $fg;
|
color: $fg;
|
||||||
display: inline-block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin btnBase($bg: $colorBodyBg, $bgHovColor: none, $fg: $colorBodyFg, $ic: $colorBtnIcon) {
|
@mixin btnBase($bg: $colorBodyBg, $bgHovColor: none, $fg: $colorBodyFg, $ic: $colorBtnIcon) {
|
||||||
@ -348,7 +341,6 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-family: 'symbolsfont';
|
font-family: 'symbolsfont';
|
||||||
margin-left: $interiorMarginSm;
|
margin-left: $interiorMarginSm;
|
||||||
vertical-align: top;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin nice-textarea($bg: $colorBodyBg, $fg: $colorBodyFg) {
|
@mixin nice-textarea($bg: $colorBodyBg, $fg: $colorBodyFg) {
|
||||||
|
@ -33,18 +33,11 @@ $pad: $interiorMargin * $baseRatio;
|
|||||||
|
|
||||||
.s-btn {
|
.s-btn {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
padding: 0 $pad;
|
padding: 0 $pad;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
@include btnSubtle($colorBtnBg, $colorBtnBgHov, $colorBtnFg, $colorBtnIcon);
|
||||||
.icon {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
color: $colorKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-label {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.lg {
|
&.lg {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
@ -58,19 +51,14 @@ $pad: $interiorMargin * $baseRatio;
|
|||||||
padding: 0 ($pad / $baseRatio) / 2;
|
padding: 0 ($pad / $baseRatio) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.major {
|
&.major,
|
||||||
|
&.key-edit,
|
||||||
|
&.key-properties {
|
||||||
$bg: $colorBtnMajorBg;
|
$bg: $colorBtnMajorBg;
|
||||||
$hc: lighten($bg, 10%);
|
$hc: lighten($bg, 10%);
|
||||||
@include btnSubtle($bg, $hc, $colorBtnMajorFg, $colorBtnMajorFg);
|
@include btnSubtle($bg, $hc, $colorBtnMajorFg, $colorBtnMajorFg);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.major) {
|
|
||||||
// bg, bgHov, fg, ic
|
|
||||||
@include btnSubtle($colorBtnBg, $colorBtnBgHov, $colorBtnFg, $colorBtnIcon);
|
|
||||||
}
|
|
||||||
&.pause-play {
|
|
||||||
|
|
||||||
}
|
|
||||||
&.t-save:before {
|
&.t-save:before {
|
||||||
content:'\e612';
|
content:'\e612';
|
||||||
font-family: symbolsfont;
|
font-family: symbolsfont;
|
||||||
@ -109,6 +97,22 @@ $pad: $interiorMargin * $baseRatio;
|
|||||||
content: "\000039";
|
content: "\000039";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.t-export {
|
||||||
|
&:before {
|
||||||
|
@extend .ui-symbol;
|
||||||
|
@extend .icon;
|
||||||
|
content: '\e623';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-label {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-icon-btn {
|
.s-icon-btn {
|
||||||
@ -275,4 +279,3 @@ body.desktop .mini-tab-icon {
|
|||||||
color: $colorPausedBg !important;
|
color: $colorPausedBg !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,6 +420,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 */
|
/******************************************************** DATETIME PICKER */
|
||||||
.l-datetime-picker {
|
.l-datetime-picker {
|
||||||
$r1H: 15px;
|
$r1H: 15px;
|
||||||
|
@ -167,7 +167,7 @@
|
|||||||
}
|
}
|
||||||
.pane {
|
.pane {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
&.left {
|
&.menu-items {
|
||||||
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
||||||
left: 0;
|
left: 0;
|
||||||
padding-right: $interiorMargin;
|
padding-right: $interiorMargin;
|
||||||
@ -183,38 +183,53 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.right {
|
&.menu-item-description {
|
||||||
left: auto;
|
left: auto;
|
||||||
right: 0;
|
right: 0;
|
||||||
padding: $interiorMargin * 5;
|
padding: $interiorMargin * 5;
|
||||||
width: $prw;
|
width: $prw;
|
||||||
|
.desc-area {
|
||||||
|
&.icon {
|
||||||
|
color: $colorCreateMenuLgIcon;
|
||||||
|
font-size: 8em;
|
||||||
|
margin-bottom: $interiorMargin * 3;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
&.title {
|
||||||
|
color: $colorCreateMenuText;
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin-bottom: $interiorMargin * 2;
|
||||||
|
}
|
||||||
|
&.description {
|
||||||
|
color: pushBack($colorCreateMenuText, 20%);
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.menu-item-description {
|
|
||||||
.desc-area {
|
&.mini {
|
||||||
&.icon {
|
width: 400px;
|
||||||
$h: 150px;
|
height: 300px;
|
||||||
color: $colorCreateMenuLgIcon;
|
.pane {
|
||||||
position: relative;
|
&.menu-items {
|
||||||
font-size: 8em;
|
font-size: 0.8em;
|
||||||
left: 0;
|
}
|
||||||
height: $h;
|
&.menu-item-description {
|
||||||
line-height: $h;
|
padding: $interiorMargin * 3;
|
||||||
margin-bottom: $interiorMargin * 5;
|
.desc-area {
|
||||||
text-align: center;
|
&.icon {
|
||||||
}
|
font-size: 4em;
|
||||||
&.title {
|
}
|
||||||
color: $colorCreateMenuText;
|
&.title {
|
||||||
font-size: 1.2em;
|
font-size: 1em;
|
||||||
margin-bottom: 0.5em;
|
}
|
||||||
}
|
}
|
||||||
&.description {
|
}
|
||||||
color: $colorCreateMenuText;
|
}
|
||||||
font-size: 0.8em;
|
}
|
||||||
line-height: 1.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.context-menu {
|
.context-menu {
|
||||||
font-size: 0.80rem;
|
font-size: 0.80rem;
|
||||||
@ -251,3 +266,7 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menus-up .menu {
|
||||||
|
bottom: $btnStdH; top: auto;
|
||||||
|
}
|
||||||
|
376
platform/commonUI/general/res/sass/controls/_time-conductor.scss
Normal file
376
platform/commonUI/general/res/sass/controls/_time-conductor.scss
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
@mixin toiLineHovEffects() {
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
background-color: $timeControllerToiLineColorHov;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-time-conductor-holder {
|
||||||
|
$minW: 500px;
|
||||||
|
border-top: 1px solid $colorInteriorBorder;
|
||||||
|
min-width: $minW;
|
||||||
|
padding-top: $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-conductor-icon {
|
||||||
|
$c: $colorObjHdrIc;
|
||||||
|
$d: 20px;
|
||||||
|
background: $c;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: $d !important;
|
||||||
|
width: $d;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
// Icon shape: brackets
|
||||||
|
&:before,
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
background: $colorBodyBg;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
$in: 7px;
|
||||||
|
left: $in;
|
||||||
|
top: 0;
|
||||||
|
right: $in;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
$in: 4px;
|
||||||
|
left: $in;
|
||||||
|
top: $in;
|
||||||
|
right: $in;
|
||||||
|
bottom: $in;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clock hands
|
||||||
|
div[class*="hand"] {
|
||||||
|
$handW: 2px;
|
||||||
|
$handH: 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-color: $c;
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
bottom: -1px;
|
||||||
|
}
|
||||||
|
&.hand-little {
|
||||||
|
z-index: 2;
|
||||||
|
@include animation-duration(12s);
|
||||||
|
&:before {
|
||||||
|
//background: red;
|
||||||
|
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($ueTimeControlH, 1);
|
||||||
|
$r2H: nth($ueTimeControlH, 2);
|
||||||
|
$r3H: nth($ueTimeControlH, 3);
|
||||||
|
|
||||||
|
// Glyphs Todo: replace with refactored CSS approach when that is merged into master
|
||||||
|
$glyphIconFixed: '\e604';
|
||||||
|
$glyphIconRealtime: '\43';
|
||||||
|
$glyphIconLatest: '\44';
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> .l-row-elem {
|
||||||
|
// First order row elements
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mode-selector .s-menu-btn,
|
||||||
|
.time-delta {
|
||||||
|
&:before {
|
||||||
|
@extend .ui-symbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-delta {
|
||||||
|
&:before {
|
||||||
|
color: $colorTimeCondKeyBg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-time-conductor-inputs-holder,
|
||||||
|
.l-time-conductor-ticks,
|
||||||
|
.l-time-conductor-zoom-w {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-time-conductor-inputs-holder {
|
||||||
|
$trInputW: 180px;
|
||||||
|
$hmInputW: 60px;
|
||||||
|
$ticksBlockerFadeW: 50px;
|
||||||
|
$iconCalendarW: 16px;
|
||||||
|
$wBgColor: $colorBodyBg;
|
||||||
|
|
||||||
|
height: $r1H;
|
||||||
|
z-index: 1;
|
||||||
|
.l-time-range-w {
|
||||||
|
// Wraps a datetime text input field
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
&.start-w {
|
||||||
|
@include background-image(linear-gradient(270deg, transparent, $wBgColor $ticksBlockerFadeW));
|
||||||
|
padding-right: $ticksBlockerFadeW;
|
||||||
|
}
|
||||||
|
&.end-w {
|
||||||
|
@include background-image(linear-gradient(90deg, transparent, $wBgColor $ticksBlockerFadeW));
|
||||||
|
padding-left: $ticksBlockerFadeW;
|
||||||
|
right: 0;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
input[type="text"] {
|
||||||
|
@include trans-prop-nice(padding, 250ms);
|
||||||
|
}
|
||||||
|
.time-range-input input {
|
||||||
|
width: $trInputW;
|
||||||
|
}
|
||||||
|
.hrs-min-input input {
|
||||||
|
width: $hmInputW;
|
||||||
|
}
|
||||||
|
.icon-calendar {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-time-conductor-ticks {
|
||||||
|
$c: $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 {
|
||||||
|
height: $r3H;
|
||||||
|
min-width: 100px;
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
.time-conductor-zoom-current-range {
|
||||||
|
color: $colorTick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// Fixed
|
||||||
|
&.fixed-mode {
|
||||||
|
.time-conductor-icon div[class*="hand"] {
|
||||||
|
&.hand-little {
|
||||||
|
@include transform(rotate(120deg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Realtime, latest modes
|
||||||
|
&.realtime-mode,
|
||||||
|
&.latest-mode {
|
||||||
|
.time-conductor-icon {
|
||||||
|
background: $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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.start-date {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.icon-calendar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&.end-date {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.realtime-mode .time-conductor-icon div[class*="hand"] { @include animation-name(clock-hands); }
|
||||||
|
&.latest-mode .time-conductor-icon div[class*="hand"] {
|
||||||
|
@include animation-name(clock-hands-sticky);
|
||||||
|
&.hand-big { @include animation-duration(5s); }
|
||||||
|
&.hand-little { @include animation-duration(60s); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-data-visualization {
|
||||||
|
background: $colorTimeCondDataVisRtBg !important
|
||||||
|
}
|
||||||
|
.mode-selector .s-menu-btn {
|
||||||
|
@include btnSubtle($colorTimeCondKeyBg, pullForward($colorTimeCondKeyBg, $ltGamma), $colorTimeCondKeyFg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.fixed-mode {
|
||||||
|
$i: $glyphIconFixed;
|
||||||
|
.mode-selector .s-menu-btn:before {
|
||||||
|
content: $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.realtime-mode {
|
||||||
|
$i: $glyphIconRealtime;
|
||||||
|
.time-delta:before {
|
||||||
|
content: $i;
|
||||||
|
}
|
||||||
|
.mode-selector .s-menu-btn:before {
|
||||||
|
content: $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.latest-mode {
|
||||||
|
$i: $glyphIconLatest;
|
||||||
|
.time-delta:before {
|
||||||
|
content: $i;
|
||||||
|
}
|
||||||
|
.mode-selector .s-menu-btn:before {
|
||||||
|
content: $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-time-range-val {
|
||||||
|
border-radius: $controlCr;
|
||||||
|
background-color: $colorInputBg;
|
||||||
|
padding: 1px 1px 0 $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************** MOBILE */
|
||||||
|
|
||||||
|
@include phoneandtablet {
|
||||||
|
.l-time-conductor {
|
||||||
|
min-width: 0;
|
||||||
|
.l-time-range-slider-holder,
|
||||||
|
.l-time-conductor-ticks {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include phone {
|
||||||
|
.l-time-conductor {
|
||||||
|
.l-time-conductor-inputs-holder {
|
||||||
|
&.l-flex-row,
|
||||||
|
.l-flex-row {
|
||||||
|
@include align-items(flex-start);
|
||||||
|
}
|
||||||
|
.l-time-range-inputs-elem {
|
||||||
|
&.type-icon {
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.l-time-conductor-inputs-holder {
|
||||||
|
@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-conductor {
|
||||||
|
.l-time-conductor-inputs-holder {
|
||||||
|
.l-time-conductor-inputs-holder {
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +1,10 @@
|
|||||||
.l-image-main-wrapper,
|
.l-image-main-wrapper,
|
||||||
.l-image-main,
|
|
||||||
.l-image-main-controlbar,
|
|
||||||
.l-image-thumbs-wrapper {
|
.l-image-thumbs-wrapper {
|
||||||
@include absPosDefault(0, false);
|
@include absPosDefault(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************** MAIN LAYOUT */
|
/*************************************** MAIN LAYOUT */
|
||||||
.l-image-main-wrapper {
|
.l-image-main-wrapper {
|
||||||
//@include test();
|
|
||||||
@if $enableImageryThumbs == true {
|
@if $enableImageryThumbs == true {
|
||||||
bottom: $interiorMargin*2 + $imageThumbsWrapperH;
|
bottom: $interiorMargin*2 + $imageThumbsWrapperH;
|
||||||
}
|
}
|
||||||
@ -15,16 +12,14 @@
|
|||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
.l-image-main {
|
.l-image-main {
|
||||||
background-color: $colorPlotBg;
|
background-color: $colorPlotBg;
|
||||||
bottom: $imageMainControlBarH + $interiorMargin;
|
margin-bottom: $interiorMargin;
|
||||||
}
|
}
|
||||||
.l-image-main-controlbar {
|
.l-image-main-controlbar {
|
||||||
top: auto;
|
&.l-flex-row { @include align-items(center); }
|
||||||
height: $imageMainControlBarH;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-image-thumbs-wrapper {
|
.l-image-thumbs-wrapper {
|
||||||
//@include test(red);
|
|
||||||
top: auto;
|
top: auto;
|
||||||
height: $imageThumbsWrapperH;
|
height: $imageThumbsWrapperH;
|
||||||
}
|
}
|
||||||
@ -44,24 +39,17 @@
|
|||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-image-main {
|
|
||||||
//cursor: crosshair;
|
|
||||||
}
|
|
||||||
|
|
||||||
.l-image-main-controlbar {
|
.l-image-main-controlbar {
|
||||||
//@include test();
|
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
line-height: $imageMainControlBarH;
|
line-height: inherit;
|
||||||
.left, .right {
|
.left, .right {
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.left {
|
.left {
|
||||||
//@include test(red);
|
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
.right {
|
.right {
|
||||||
//@include test(green);
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
.l-date,
|
.l-date,
|
||||||
@ -71,7 +59,6 @@
|
|||||||
.l-mag {
|
.l-mag {
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
//white-space: nowrap;
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "\000049";
|
content: "\000049";
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,6 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* 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) {
|
@mixin spinner($b: 5px, $c: $colorKey) {
|
||||||
@include transform-origin(center);
|
@include transform-origin(center);
|
||||||
@include animation-name(rotation-centered);
|
@include animation-name(rotation-centered);
|
||||||
|
@ -24,6 +24,10 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tabular-holder {
|
||||||
|
@include absPosDefault();
|
||||||
|
}
|
||||||
|
|
||||||
.tabular,
|
.tabular,
|
||||||
table {
|
table {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -162,4 +166,41 @@ table {
|
|||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************** SPECIFIC TABULAR VIEWS */
|
||||||
|
.tabular-holder {
|
||||||
|
&.t-exportable {
|
||||||
|
$btnExportH: 25px;
|
||||||
|
.l-view-section {
|
||||||
|
top: $btnExportH + $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.child-frame {
|
||||||
|
.tabular-holder {
|
||||||
|
&.t-exportable {
|
||||||
|
$btnExportH: $btnFrameH;
|
||||||
|
.s-btn.t-export {
|
||||||
|
@include trans-prop-nice(opacity, $dur: 50ms);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.l-view-section {
|
||||||
|
@include trans-prop-nice(top, $dur: 150ms, $delay: 50ms);
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
.s-btn.t-export {
|
||||||
|
@include trans-prop-nice(opacity, 150ms, 100ms);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.l-view-section {
|
||||||
|
@include trans-prop-nice(top, $dur: 150ms);
|
||||||
|
top: $btnExportH + $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -19,12 +19,10 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
$yBarW: 60px;
|
.abs.holder-plot {
|
||||||
$yLabelW: 10px;
|
// Fend off the scrollbar when less than min-height;
|
||||||
$xBarH: 32px;
|
right: $interiorMargin;
|
||||||
$legendH: 20px;
|
}
|
||||||
$swatchD: 8px;
|
|
||||||
$plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBarW); // Top, right, bottom, left
|
|
||||||
|
|
||||||
.gl-plot {
|
.gl-plot {
|
||||||
color: $colorPlotFg;
|
color: $colorPlotFg;
|
||||||
@ -32,6 +30,7 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
min-height: $plotMinH;
|
||||||
|
|
||||||
.gl-plot-local-controls {
|
.gl-plot-local-controls {
|
||||||
@include trans-prop-nice(opacity, 150ms);
|
@include trans-prop-nice(opacity, 150ms);
|
||||||
@ -54,17 +53,17 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
|
|||||||
top: auto;
|
top: auto;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: $interiorMargin;
|
bottom: $interiorMargin;
|
||||||
left: $yBarW;
|
left: $plotYBarW;
|
||||||
height: $xBarH;
|
height: $plotXBarH;
|
||||||
width: auto;
|
width: auto;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
&.gl-plot-y {
|
&.gl-plot-y {
|
||||||
top: $legendH + $interiorMargin;
|
top: $plotLegendH + $interiorMargin;
|
||||||
right: auto;
|
right: auto;
|
||||||
bottom: nth($plotDisplayArea, 3);
|
bottom: nth($plotDisplayArea, 3);
|
||||||
left: 0;
|
left: 0;
|
||||||
width: $yBarW;
|
width: $plotYBarW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +145,7 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
|
|||||||
@include transform(translateY(-50%));
|
@include transform(translateY(-50%));
|
||||||
min-width: 150px; // Need this due to enclosure of .select
|
min-width: 150px; // Need this due to enclosure of .select
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: $yLabelW + $interiorMargin * 2;
|
left: $plotYLabelW + $interiorMargin * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.t-plot-display-controls {
|
.t-plot-display-controls {
|
||||||
@ -174,7 +173,7 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
left: 0;
|
left: 0;
|
||||||
height: $legendH;
|
height: $plotLegendH;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
@ -236,8 +235,8 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
|
|||||||
.color-swatch {
|
.color-swatch {
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: $swatchD;
|
height: $plotSwatchD;
|
||||||
width: $swatchD;
|
width: $plotSwatchD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,8 +248,8 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
|
|||||||
padding: 0px $itemPadLR;
|
padding: 0px $itemPadLR;
|
||||||
.plot-color-swatch {
|
.plot-color-swatch {
|
||||||
border: 1px solid $colorBodyBg;
|
border: 1px solid $colorBodyBg;
|
||||||
height: $swatchD + 1;
|
height: $plotSwatchD + 1;
|
||||||
width: $swatchD + 1;
|
width: $plotSwatchD + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,8 @@
|
|||||||
height: $ohH;
|
height: $ohH;
|
||||||
line-height: $ohH;
|
line-height: $ohH;
|
||||||
padding: 0 $interiorMargin;
|
padding: 0 $interiorMargin;
|
||||||
> span {
|
> span,
|
||||||
|
&:before {
|
||||||
font-size: 0.65rem;
|
font-size: 0.65rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,6 +129,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.primary-pane {
|
.primary-pane {
|
||||||
|
// Clip element that have min-widths
|
||||||
|
overflow: hidden;
|
||||||
// Need to lift up this pane to ensure that 'collapsed' panes don't block user interactions
|
// Need to lift up this pane to ensure that 'collapsed' panes don't block user interactions
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
}
|
}
|
||||||
@ -237,30 +239,10 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
|
|||||||
top: $ueTopBarH + $interiorMarginLg;
|
top: $ueTopBarH + $interiorMarginLg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-object-wrapper {
|
|
||||||
@extend .abs;
|
|
||||||
|
|
||||||
.object-holder-main {
|
|
||||||
@extend .abs;
|
|
||||||
}
|
|
||||||
.l-edit-controls {
|
|
||||||
//@include trans-prop-nice((opacity, height), 0.25s);
|
|
||||||
border-bottom: 1px solid $colorInteriorBorder;
|
|
||||||
line-height: $ueEditToolBarH;
|
|
||||||
height: 0px;
|
|
||||||
opacity: 0;
|
|
||||||
.tool-bar {
|
|
||||||
right: $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.l-object-wrapper-inner {
|
.l-object-wrapper-inner {
|
||||||
@include trans-prop-nice-resize(0.25s);
|
@include trans-prop-nice-resize(0.25s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.object-browse-bar .s-btn,
|
.object-browse-bar .s-btn,
|
||||||
.top-bar .buttons-main .s-btn,
|
.top-bar .buttons-main .s-btn,
|
||||||
.top-bar .s-menu-btn,
|
.top-bar .s-menu-btn,
|
||||||
@ -288,8 +270,9 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
|
|||||||
|
|
||||||
.left {
|
.left {
|
||||||
padding-right: $interiorMarginLg;
|
padding-right: $interiorMarginLg;
|
||||||
.l-back:not(.s-status-editing) {
|
.l-back {
|
||||||
margin-right: $interiorMarginLg;
|
margin-right: $interiorMarginLg;
|
||||||
|
&.s-status-editing { display: none; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,19 +359,49 @@ body.desktop {
|
|||||||
|
|
||||||
.s-status-editing {
|
.s-status-editing {
|
||||||
.l-object-wrapper {
|
.l-object-wrapper {
|
||||||
@include pulseBorder($colorEditAreaFg, $dur: 1s, $opacity0: 0.3);
|
$t2Dur: $browseToEditAnimMs;
|
||||||
border-radius: $controlCr;
|
$t1Dur: $t2Dur / 2;
|
||||||
|
$pulseDur: $editBorderPulseMs;
|
||||||
|
$bC0: rgba($colorEditAreaFg, 0.5);
|
||||||
|
$bC100: rgba($colorEditAreaFg, 1);
|
||||||
|
|
||||||
background-color: $colorEditAreaBg;
|
background-color: $colorEditAreaBg;
|
||||||
border-color: $colorEditAreaFg;
|
border-radius: $controlCr;
|
||||||
border-width: 2px;
|
border: 1px dotted $bC0;
|
||||||
border-style: dotted;
|
|
||||||
.l-object-wrapper-inner {
|
// Transition 1
|
||||||
@include absPosDefault(3px, hidden);
|
@include keyframes(wrapperIn) {
|
||||||
|
from { border: 0px dotted transparent; padding: 0; }
|
||||||
|
to { border: 1px dotted $bC0; padding: 5px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do last
|
||||||
|
@include keyframes(pulseNew) {
|
||||||
|
from { border-color: $bC0; }
|
||||||
|
to { border-color: $bC100; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@include animation-name(wrapperIn, pulseNew);
|
||||||
|
@include animation-duration($t1Dur, $pulseDur);
|
||||||
|
@include animation-delay(0s, $t1Dur + $t2Dur);
|
||||||
|
@include animation-direction(normal, alternate);
|
||||||
|
@include animation-fill-mode(both, none);
|
||||||
|
@include animation-iteration-count(1, infinite);
|
||||||
|
@include animation-timing-function(ease-in-out, linear);
|
||||||
|
|
||||||
|
|
||||||
.l-edit-controls {
|
.l-edit-controls {
|
||||||
height: $ueEditToolBarH + $interiorMargin;
|
height: 0;
|
||||||
margin-bottom: $interiorMargin;
|
border-bottom: 1px solid $colorInteriorBorder;
|
||||||
opacity: 1;
|
// Transition 2: reveal edit controls
|
||||||
|
@include keyframes(editIn) {
|
||||||
|
from { border-bottom: 0px solid transparent; height: 0; margin-bottom: 0; }
|
||||||
|
to { border-bottom: 1px solid $colorInteriorBorder; height: $ueEditToolBarH + $interiorMargin; margin-bottom: $interiorMargin; }
|
||||||
|
}
|
||||||
|
@include animToParams(editIn, $dur: $t2Dur, $delay: $t1Dur);
|
||||||
|
.tool-bar {
|
||||||
|
right: $interiorMargin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,16 +23,19 @@
|
|||||||
<input type="text"
|
<input type="text"
|
||||||
ng-model="textValue"
|
ng-model="textValue"
|
||||||
ng-blur="restoreTextValue(); ngBlur()"
|
ng-blur="restoreTextValue(); ngBlur()"
|
||||||
|
ng-mouseup="ngMouseup()"
|
||||||
ng-class="{
|
ng-class="{
|
||||||
error: textInvalid ||
|
error: textInvalid ||
|
||||||
(structure.validate &&
|
(structure.validate &&
|
||||||
!structure.validate(ngModel[field])),
|
!structure.validate(ngModel[field])),
|
||||||
'picker-icon': structure.format === 'utc' || !structure.format
|
'picker-icon': structure.format === 'utc' || !structure.format
|
||||||
}">
|
}">
|
||||||
</input><a class="ui-symbol icon icon-calendar"
|
</input>
|
||||||
ng-if="structure.format === 'utc' || !structure.format"
|
<a class="ui-symbol icon icon-calendar"
|
||||||
ng-click="picker.active = !picker.active">
|
ng-if="!picker.active && (structure.format === 'utc' || !structure.format)"
|
||||||
</a>
|
ng-click="picker.active = !picker.active"></a>
|
||||||
|
<!-- If picker active show icon with no onclick to prevent double registration of clicks -->
|
||||||
|
<a class="ui-symbol icon icon-calendar" ng-if="picker.active"></a>
|
||||||
<mct-popup ng-if="picker.active">
|
<mct-popup ng-if="picker.active">
|
||||||
<div mct-click-elsewhere="picker.active = false">
|
<div mct-click-elsewhere="picker.active = false">
|
||||||
<mct-control key="'datetime-picker'"
|
<mct-control key="'datetime-picker'"
|
||||||
|
@ -20,10 +20,10 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div ng-controller="TimeRangeController as trCtrl" class="l-flex-col">
|
<div ng-controller="TimeRangeController as trCtrl" class="l-flex-col">
|
||||||
<form class="l-time-range-inputs-holder l-flex-row flex-elem"
|
<form class="l-time-conductor-inputs-holder l-flex-row flex-elem"
|
||||||
ng-submit="trCtrl.updateBoundsFromForm()">
|
ng-submit="trCtrl.updateBoundsFromForm()">
|
||||||
<span class="l-time-range-inputs-elem ui-symbol type-icon flex-elem">C</span>
|
<span class="l-time-range-inputs-elem ui-symbol type-icon flex-elem">C</span>
|
||||||
<span class="l-time-range-inputs-elem t-inputs-w l-flex-row flex-elem">
|
<span class="l-time-range-inputs-elem l-flex-row flex-elem">
|
||||||
<span class="l-time-range-input-w flex-elem">
|
<span class="l-time-range-input-w flex-elem">
|
||||||
<mct-control key="'datetime-field'"
|
<mct-control key="'datetime-field'"
|
||||||
structure="{
|
structure="{
|
||||||
@ -86,7 +86,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="l-time-range-ticks-holder flex-elem">
|
<div class="l-time-conductor-ticks flex-elem">
|
||||||
<div class="l-time-range-ticks">
|
<div class="l-time-range-ticks">
|
||||||
<div
|
<div
|
||||||
ng-repeat="tick in ticks track by $index"
|
ng-repeat="tick in ticks track by $index"
|
||||||
|
@ -72,6 +72,17 @@ define(
|
|||||||
if ($scope.ngBlur) {
|
if ($scope.ngBlur) {
|
||||||
$scope.ngBlur();
|
$scope.ngBlur();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If picker is active, dismiss it when valid value has been selected
|
||||||
|
// This 'if' is to avoid unnecessary validation if picker is not active
|
||||||
|
if ($scope.picker.active) {
|
||||||
|
if ($scope.structure.validate && $scope.structure.validate($scope.ngModel[$scope.field])) {
|
||||||
|
$scope.picker.active = false;
|
||||||
|
} else if (!$scope.structure.validate) {
|
||||||
|
//If picker visible, but no validation function, hide picker
|
||||||
|
$scope.picker.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +104,6 @@ define(
|
|||||||
$scope.$watch('ngModel[field]', updateFromModel);
|
$scope.$watch('ngModel[field]', updateFromModel);
|
||||||
$scope.$watch('pickerModel.value', updateFromPicker);
|
$scope.$watch('pickerModel.value', updateFromPicker);
|
||||||
$scope.$watch('textValue', updateFromView);
|
$scope.$watch('textValue', updateFromView);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DateTimeFieldController;
|
return DateTimeFieldController;
|
||||||
|
@ -53,7 +53,7 @@ define([
|
|||||||
* format has been otherwise specified
|
* format has been otherwise specified
|
||||||
* @param {Function} now a function to return current system time
|
* @param {Function} now a function to return current system time
|
||||||
*/
|
*/
|
||||||
function TimeRangeController($scope, formatService, defaultFormat, now) {
|
function TimeRangeController($scope, $timeout, formatService, defaultFormat, now) {
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
this.formatService = formatService;
|
this.formatService = formatService;
|
||||||
this.defaultFormat = defaultFormat;
|
this.defaultFormat = defaultFormat;
|
||||||
@ -66,6 +66,7 @@ define([
|
|||||||
this.formatter = formatService.getFormat(defaultFormat);
|
this.formatter = formatService.getFormat(defaultFormat);
|
||||||
this.formStartChanged = false;
|
this.formStartChanged = false;
|
||||||
this.formEndChanged = false;
|
this.formEndChanged = false;
|
||||||
|
this.$timeout = $timeout;
|
||||||
|
|
||||||
this.$scope.ticks = [];
|
this.$scope.ticks = [];
|
||||||
|
|
||||||
@ -259,18 +260,23 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
TimeRangeController.prototype.updateBoundsFromForm = function () {
|
TimeRangeController.prototype.updateBoundsFromForm = function () {
|
||||||
if (this.formStartChanged) {
|
var self = this;
|
||||||
this.$scope.ngModel.outer.start =
|
|
||||||
this.$scope.ngModel.inner.start =
|
//Allow Angular to trigger watches and determine whether values have changed.
|
||||||
this.$scope.formModel.start;
|
this.$timeout(function () {
|
||||||
this.formStartChanged = false;
|
if (self.formStartChanged) {
|
||||||
}
|
self.$scope.ngModel.outer.start =
|
||||||
if (this.formEndChanged) {
|
self.$scope.ngModel.inner.start =
|
||||||
this.$scope.ngModel.outer.end =
|
self.$scope.formModel.start;
|
||||||
this.$scope.ngModel.inner.end =
|
self.formStartChanged = false;
|
||||||
this.$scope.formModel.end;
|
}
|
||||||
this.formEndChanged = false;
|
if (self.formEndChanged) {
|
||||||
}
|
self.$scope.ngModel.outer.end =
|
||||||
|
self.$scope.ngModel.inner.end =
|
||||||
|
self.$scope.formModel.end;
|
||||||
|
self.formEndChanged = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
TimeRangeController.prototype.onFormStartChange = function (
|
TimeRangeController.prototype.onFormStartChange = function (
|
||||||
|
@ -51,7 +51,9 @@ define(
|
|||||||
yMax = yMin + rect.height;
|
yMax = yMin + rect.height;
|
||||||
|
|
||||||
if (x < xMin || x > xMax || y < yMin || y > yMax) {
|
if (x < xMin || x > xMax || y < yMin || y > yMax) {
|
||||||
scope.$eval(attrs.mctClickElsewhere);
|
scope.$apply(function () {
|
||||||
|
scope.$eval(attrs.mctClickElsewhere);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ define(
|
|||||||
var mockScope,
|
var mockScope,
|
||||||
mockFormatService,
|
mockFormatService,
|
||||||
testDefaultFormat,
|
testDefaultFormat,
|
||||||
|
mockTimeout,
|
||||||
mockNow,
|
mockNow,
|
||||||
mockFormat,
|
mockFormat,
|
||||||
controller;
|
controller;
|
||||||
@ -54,6 +55,10 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
mockTimeout = function (fn) {
|
||||||
|
return fn();
|
||||||
|
};
|
||||||
|
|
||||||
mockScope = jasmine.createSpyObj(
|
mockScope = jasmine.createSpyObj(
|
||||||
"$scope",
|
"$scope",
|
||||||
["$apply", "$watch", "$watchCollection"]
|
["$apply", "$watch", "$watchCollection"]
|
||||||
@ -78,6 +83,7 @@ define(
|
|||||||
|
|
||||||
controller = new TimeRangeController(
|
controller = new TimeRangeController(
|
||||||
mockScope,
|
mockScope,
|
||||||
|
mockTimeout,
|
||||||
mockFormatService,
|
mockFormatService,
|
||||||
testDefaultFormat,
|
testDefaultFormat,
|
||||||
mockNow
|
mockNow
|
||||||
|
@ -104,6 +104,8 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("triggers an evaluation of its related Angular expression", function () {
|
it("triggers an evaluation of its related Angular expression", function () {
|
||||||
|
expect(mockScope.$apply).toHaveBeenCalled();
|
||||||
|
mockScope.$apply.mostRecentCall.args[0]();
|
||||||
expect(mockScope.$eval)
|
expect(mockScope.$eval)
|
||||||
.toHaveBeenCalledWith(testAttrs.mctClickElsewhere);
|
.toHaveBeenCalledWith(testAttrs.mctClickElsewhere);
|
||||||
});
|
});
|
||||||
|
@ -14,7 +14,7 @@ $colorAHov: #fff;
|
|||||||
$contrastRatioPercent: 7%;
|
$contrastRatioPercent: 7%;
|
||||||
$hoverRatioPercent: 10%;
|
$hoverRatioPercent: 10%;
|
||||||
$basicCr: 3px;
|
$basicCr: 3px;
|
||||||
$controlCr: 3px;
|
$controlCr: 2px;
|
||||||
$smallCr: 2px;
|
$smallCr: 2px;
|
||||||
|
|
||||||
// Buttons and Controls
|
// Buttons and Controls
|
||||||
@ -52,7 +52,7 @@ $colorGridLines: rgba(#fff, 0.05);
|
|||||||
$colorInvokeMenu: #fff;
|
$colorInvokeMenu: #fff;
|
||||||
$colorObjHdrTxt: $colorBodyFg;
|
$colorObjHdrTxt: $colorBodyFg;
|
||||||
$colorObjHdrIc: pullForward($colorObjHdrTxt, 20%);
|
$colorObjHdrIc: pullForward($colorObjHdrTxt, 20%);
|
||||||
$colorTick: rgba(white, 0.2);
|
$colorTick: pullForward($colorBodyBg, 20%);
|
||||||
|
|
||||||
// Menu colors
|
// Menu colors
|
||||||
$colorMenuBg: pullForward($colorBodyBg, 23%);
|
$colorMenuBg: pullForward($colorBodyBg, 23%);
|
||||||
@ -183,12 +183,13 @@ $scrollbarThumbColorOverlay: lighten($colorOvrBg, 10%);
|
|||||||
$scrollbarThumbColorOverlayHov: lighten($scrollbarThumbColorOverlay, 2%);
|
$scrollbarThumbColorOverlayHov: lighten($scrollbarThumbColorOverlay, 2%);
|
||||||
|
|
||||||
// Splitter
|
// Splitter
|
||||||
$splitterD: 25px; // splitterD and HandleD should both be odd, or even
|
$splitterD: 17px; // splitterD and $splitterHandleD should both be odd, or even
|
||||||
$splitterHandleD: 1px;
|
$splitterHandleD: 1px;
|
||||||
|
$splitterDSm: 17px; // Smaller splitter, used inside elements like a Timeline view
|
||||||
$colorSplitterBg: rgba(#fff, 0.1); //pullForward($colorBodyBg, 5%);
|
$colorSplitterBg: rgba(#fff, 0.1); //pullForward($colorBodyBg, 5%);
|
||||||
$splitterShdw: rgba(black, 0.4) 0 0 3px;
|
$splitterShdw: rgba(black, 0.4) 0 0 3px;
|
||||||
$splitterEndCr: none;
|
$splitterEndCr: none;
|
||||||
$colorSplitterHover: pullForward($colorBodyBg, 15%);
|
$colorSplitterHover: pullForward($colorBodyBg, 40%);
|
||||||
$colorSplitterActive: $colorKey;
|
$colorSplitterActive: $colorKey;
|
||||||
|
|
||||||
// Mobile
|
// Mobile
|
||||||
@ -206,4 +207,10 @@ $colorAboutLink: #84b3ff;
|
|||||||
|
|
||||||
// Loading
|
// Loading
|
||||||
$colorLoadingFg: $colorAlt1;
|
$colorLoadingFg: $colorAlt1;
|
||||||
$colorLoadingBg: rgba($colorBodyFg, 0.2);
|
$colorLoadingBg: rgba($colorBodyFg, 0.2);
|
||||||
|
|
||||||
|
// Time Conductor
|
||||||
|
$colorTimeCondKeyBg: #4e70dc;
|
||||||
|
$colorTimeCondKeyFg: #fff;
|
||||||
|
$colorTimeCondDataVisBg: pullForward($colorBodyBg, 10%);
|
||||||
|
$colorTimeCondDataVisRtBg: pushBack($colorTimeCondKeyBg, 10%);
|
@ -52,7 +52,7 @@ $colorGridLines: rgba(#000, 0.05);
|
|||||||
$colorInvokeMenu: #fff;
|
$colorInvokeMenu: #fff;
|
||||||
$colorObjHdrTxt: $colorBodyFg;
|
$colorObjHdrTxt: $colorBodyFg;
|
||||||
$colorObjHdrIc: pushBack($colorObjHdrTxt, 30%);
|
$colorObjHdrIc: pushBack($colorObjHdrTxt, 30%);
|
||||||
$colorTick: rgba(black, 0.2);
|
$colorTick: pullForward($colorBodyBg, 30%);
|
||||||
|
|
||||||
// Menu colors
|
// Menu colors
|
||||||
$colorMenuBg: pushBack($colorBodyBg, 10%);
|
$colorMenuBg: pushBack($colorBodyBg, 10%);
|
||||||
@ -183,12 +183,13 @@ $scrollbarThumbColorOverlay: darken($colorOvrBg, 50%);
|
|||||||
$scrollbarThumbColorOverlayHov: $scrollbarThumbColorHov;
|
$scrollbarThumbColorOverlayHov: $scrollbarThumbColorHov;
|
||||||
|
|
||||||
// Splitter
|
// Splitter
|
||||||
$splitterD: 24px;
|
$splitterD: 16px; // splitterD and $splitterHandleD should both be odd, or even
|
||||||
$splitterHandleD: 2px;
|
$splitterHandleD: 2px;
|
||||||
|
$splitterDSm: 16px; // Smaller splitter, used inside elements like a Timeline view
|
||||||
$colorSplitterBg: pullForward($colorBodyBg, 10%);
|
$colorSplitterBg: pullForward($colorBodyBg, 10%);
|
||||||
$splitterShdw: none;
|
$splitterShdw: none;
|
||||||
$splitterEndCr: none;
|
$splitterEndCr: none;
|
||||||
$colorSplitterHover: none;
|
$colorSplitterHover: pullForward($colorBodyBg, 30%);
|
||||||
$colorSplitterActive: $colorKey;
|
$colorSplitterActive: $colorKey;
|
||||||
|
|
||||||
// Mobile
|
// Mobile
|
||||||
@ -207,3 +208,9 @@ $colorAboutLink: #84b3ff;
|
|||||||
// Loading
|
// Loading
|
||||||
$colorLoadingFg: $colorAlt1;
|
$colorLoadingFg: $colorAlt1;
|
||||||
$colorLoadingBg: rgba($colorLoadingFg, 0.1);
|
$colorLoadingBg: rgba($colorLoadingFg, 0.1);
|
||||||
|
|
||||||
|
// Time Conductor
|
||||||
|
$colorTimeCondKeyBg: #6178dc;
|
||||||
|
$colorTimeCondKeyFg: #fff;
|
||||||
|
$colorTimeCondDataVisBg: pullForward($colorBodyBg, 10%);
|
||||||
|
$colorTimeCondDataVisRtBg: pushBack($colorTimeCondKeyBg, 30%);
|
||||||
|
@ -60,11 +60,6 @@ define(
|
|||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getKey(id) {
|
|
||||||
var parts = id.split(":");
|
|
||||||
return parts.length > 1 ? parts.slice(1).join(":") : id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the value returned is falsey, and if so returns a
|
* Checks if the value returned is falsey, and if so returns a
|
||||||
* rejected promise
|
* rejected promise
|
||||||
@ -131,7 +126,7 @@ define(
|
|||||||
// ...and persist
|
// ...and persist
|
||||||
return persistenceFn.apply(persistenceService, [
|
return persistenceFn.apply(persistenceService, [
|
||||||
this.getSpace(),
|
this.getSpace(),
|
||||||
getKey(domainObject.getId()),
|
this.getKey(),
|
||||||
domainObject.getModel()
|
domainObject.getModel()
|
||||||
]).then(function (result) {
|
]).then(function (result) {
|
||||||
return rejectIfFalsey(result, self.$q);
|
return rejectIfFalsey(result, self.$q);
|
||||||
@ -159,7 +154,7 @@ define(
|
|||||||
|
|
||||||
return this.persistenceService.readObject(
|
return this.persistenceService.readObject(
|
||||||
this.getSpace(),
|
this.getSpace(),
|
||||||
this.domainObject.getId()
|
this.getKey()
|
||||||
).then(updateModel);
|
).then(updateModel);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,6 +173,17 @@ define(
|
|||||||
return this.identifierService.parse(id).getSpace();
|
return this.identifierService.parse(id).getSpace();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the key for this domain object in the given space.
|
||||||
|
*
|
||||||
|
* @returns {string} the key of the object in it's space.
|
||||||
|
*/
|
||||||
|
PersistenceCapability.prototype.getKey = function () {
|
||||||
|
var id = this.domainObject.getId();
|
||||||
|
return this.identifierService.parse(id).getKey();
|
||||||
|
};
|
||||||
|
|
||||||
return PersistenceCapability;
|
return PersistenceCapability;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -35,7 +35,8 @@ define(
|
|||||||
mockNofificationService,
|
mockNofificationService,
|
||||||
mockCacheService,
|
mockCacheService,
|
||||||
mockQ,
|
mockQ,
|
||||||
id = "object id",
|
key = "persistence key",
|
||||||
|
id = "object identifier",
|
||||||
model,
|
model,
|
||||||
SPACE = "some space",
|
SPACE = "some space",
|
||||||
persistence,
|
persistence,
|
||||||
@ -101,6 +102,7 @@ define(
|
|||||||
});
|
});
|
||||||
mockIdentifierService.parse.andReturn(mockIdentifier);
|
mockIdentifierService.parse.andReturn(mockIdentifier);
|
||||||
mockIdentifier.getSpace.andReturn(SPACE);
|
mockIdentifier.getSpace.andReturn(SPACE);
|
||||||
|
mockIdentifier.getKey.andReturn(key);
|
||||||
persistence = new PersistenceCapability(
|
persistence = new PersistenceCapability(
|
||||||
mockCacheService,
|
mockCacheService,
|
||||||
mockPersistenceService,
|
mockPersistenceService,
|
||||||
@ -124,7 +126,7 @@ define(
|
|||||||
|
|
||||||
expect(mockPersistenceService.createObject).toHaveBeenCalledWith(
|
expect(mockPersistenceService.createObject).toHaveBeenCalledWith(
|
||||||
SPACE,
|
SPACE,
|
||||||
id,
|
key,
|
||||||
model
|
model
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -138,7 +140,7 @@ define(
|
|||||||
|
|
||||||
expect(mockPersistenceService.updateObject).toHaveBeenCalledWith(
|
expect(mockPersistenceService.updateObject).toHaveBeenCalledWith(
|
||||||
SPACE,
|
SPACE,
|
||||||
id,
|
key,
|
||||||
model
|
model
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -82,16 +82,6 @@ define(
|
|||||||
expect(result.a.getModel()).toEqual(model);
|
expect(result.a.getModel()).toEqual(model);
|
||||||
});
|
});
|
||||||
|
|
||||||
//TODO: Disabled for NEM Beta
|
|
||||||
xit("provides a new, fully constituted domain object for a" +
|
|
||||||
" provided model", function () {
|
|
||||||
var model = { someKey: "some value"},
|
|
||||||
result;
|
|
||||||
result = provider.newObject("a", model);
|
|
||||||
expect(result.getId()).toEqual("a");
|
|
||||||
expect(result.getModel()).toEqual(model);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -81,6 +81,7 @@ define(
|
|||||||
if (phase.toLowerCase() === 'preparing' && !this.dialog) {
|
if (phase.toLowerCase() === 'preparing' && !this.dialog) {
|
||||||
this.dialog = this.dialogService.showBlockingMessage({
|
this.dialog = this.dialogService.showBlockingMessage({
|
||||||
title: "Preparing to copy objects",
|
title: "Preparing to copy objects",
|
||||||
|
hint: "Do not navigate away from this page or close this browser tab while this message is displayed.",
|
||||||
unknownProgress: true,
|
unknownProgress: true,
|
||||||
severity: "info"
|
severity: "info"
|
||||||
});
|
});
|
||||||
|
@ -24,10 +24,7 @@ define(
|
|||||||
[],
|
[],
|
||||||
function () {
|
function () {
|
||||||
|
|
||||||
var DISALLOWED_ACTIONS = [
|
var DISALLOWED_ACTIONS = ["move"];
|
||||||
"move",
|
|
||||||
"copy"
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This policy prevents performing move/copy/link actions across
|
* This policy prevents performing move/copy/link actions across
|
||||||
|
@ -70,27 +70,25 @@ define(
|
|||||||
policy = new CrossSpacePolicy();
|
policy = new CrossSpacePolicy();
|
||||||
});
|
});
|
||||||
|
|
||||||
['move', 'copy'].forEach(function (key) {
|
describe("for move actions", function () {
|
||||||
describe("for " + key + " actions", function () {
|
beforeEach(function () {
|
||||||
beforeEach(function () {
|
testActionMetadata.key = 'move';
|
||||||
testActionMetadata.key = key;
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it("allows same-space changes", function () {
|
it("allows same-space changes", function () {
|
||||||
expect(policy.allow(mockAction, sameSpaceContext))
|
expect(policy.allow(mockAction, sameSpaceContext))
|
||||||
.toBe(true);
|
.toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("disallows cross-space changes", function () {
|
it("disallows cross-space changes", function () {
|
||||||
expect(policy.allow(mockAction, crossSpaceContext))
|
expect(policy.allow(mockAction, crossSpaceContext))
|
||||||
.toBe(false);
|
.toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows actions with no selectedObject", function () {
|
it("allows actions with no selectedObject", function () {
|
||||||
expect(policy.allow(mockAction, {
|
expect(policy.allow(mockAction, {
|
||||||
domainObject: makeObject('a')
|
domainObject: makeObject('a')
|
||||||
})).toBe(true);
|
})).toBe(true);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
93
platform/features/conductor-v2/bundle.js
Normal file
93
platform/features/conductor-v2/bundle.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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/TimeConductor",
|
||||||
|
"./src/ui/TimeConductorController",
|
||||||
|
"./src/ui/MCTConductorAxis",
|
||||||
|
"./src/timeSystems/UTCTimeSystem",
|
||||||
|
"text!./res/templates/time-conductor.html",
|
||||||
|
"text!./res/templates/mode-selector/mode-selector.html",
|
||||||
|
"text!./res/templates/mode-selector/mode-menu.html",
|
||||||
|
'legacyRegistry'
|
||||||
|
], function (
|
||||||
|
TimeConductor,
|
||||||
|
TimeConductorController,
|
||||||
|
MCTConductorAxis,
|
||||||
|
UTCTimeSystem,
|
||||||
|
timeConductorTemplate,
|
||||||
|
modeSelectorTemplate,
|
||||||
|
modeMenuTemplate,
|
||||||
|
legacyRegistry
|
||||||
|
) {
|
||||||
|
|
||||||
|
legacyRegistry.register("platform/features/conductor-v2", {
|
||||||
|
"extensions": {
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"key": "timeConductor",
|
||||||
|
"implementation": TimeConductor
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"controllers": [
|
||||||
|
{
|
||||||
|
"key": "TimeConductorController",
|
||||||
|
"implementation": TimeConductorController,
|
||||||
|
"depends": [
|
||||||
|
"$scope",
|
||||||
|
"timeConductor",
|
||||||
|
"timeSystems[]"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"directives": [
|
||||||
|
{
|
||||||
|
"key": "mctConductorAxis",
|
||||||
|
"implementation": MCTConductorAxis,
|
||||||
|
"depends": [
|
||||||
|
"timeConductor"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"representations": [
|
||||||
|
{
|
||||||
|
"key": "time-conductor",
|
||||||
|
"template": timeConductorTemplate
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "mode-selector",
|
||||||
|
"template": modeSelectorTemplate
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "mode-menu",
|
||||||
|
"template": modeMenuTemplate
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeSystems": [
|
||||||
|
{
|
||||||
|
"implementation": UTCTimeSystem,
|
||||||
|
"depends": ["$timeout"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
@ -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.
|
||||||
|
-->
|
||||||
|
<div class="contents">
|
||||||
|
<div class="pane left menu-items">
|
||||||
|
<ul>
|
||||||
|
<li ng-repeat="option in ngModel.options"
|
||||||
|
ng-click="ngModel.selected=option">
|
||||||
|
<a
|
||||||
|
ng-mouseover="representation.activeMetadata = option.metadata"
|
||||||
|
ng-mouseleave="representation.activeMetadata = undefined">
|
||||||
|
<span class="ui-symbol icon type-icon">
|
||||||
|
{{option.metadata.glyph}}
|
||||||
|
</span>
|
||||||
|
{{option.metadata.name}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="pane right menu-item-description">
|
||||||
|
<div class="desc-area ui-symbol icon type-icon">
|
||||||
|
{{representation.activeMetadata.glyph}}
|
||||||
|
</div>
|
||||||
|
<div class="desc-area title">
|
||||||
|
{{representation.activeMetadata.name}}
|
||||||
|
</div>
|
||||||
|
<div class="desc-area description">
|
||||||
|
{{representation.activeMetadata.description}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -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-btn"
|
||||||
|
ng-click="modeController.toggle()">
|
||||||
|
<span class="title-label">{{ngModel.selected.metadata.label}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="menu super-menu mini mode-selector-menu"
|
||||||
|
ng-show="modeController.isActive()">
|
||||||
|
<mct-representation mct-object="domainObject"
|
||||||
|
key="'mode-menu'"
|
||||||
|
ng-model="ngModel">
|
||||||
|
</mct-representation>
|
||||||
|
</div>
|
||||||
|
</span>
|
107
platform/features/conductor-v2/res/templates/time-conductor.html
Normal file
107
platform/features/conductor-v2/res/templates/time-conductor.html
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<!-- Parent holder for time conductor. follow-mode | fixed-mode -->
|
||||||
|
<div ng-controller="TimeConductorController as tcController"
|
||||||
|
class="holder grows flex-elem l-flex-row l-time-conductor {{modeModel.selected.metadata.key}}-mode {{timeSystemModel.selected.metadata.key}}-time-system">
|
||||||
|
|
||||||
|
<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-ticks l-row-elem l-flex-row flex-elem no-margin">
|
||||||
|
<form class="abs l-time-conductor-inputs-holder"
|
||||||
|
ng-submit="tcController.updateBoundsFromForm(formModel)">
|
||||||
|
<span class="l-time-range-w start-w">
|
||||||
|
<span class="l-time-range-input-w start-date">
|
||||||
|
<mct-control key="'datetime-field'"
|
||||||
|
structure="{
|
||||||
|
format: timeSystemModel.format,
|
||||||
|
validate: tcController.validation.validateStart
|
||||||
|
}"
|
||||||
|
ng-model="formModel"
|
||||||
|
ng-mouseup="changing['start'] = true"
|
||||||
|
ng-blur="changing['start'] = false; tcController.updateBoundsFromForm(formModel)"
|
||||||
|
field="'start'"
|
||||||
|
class="time-range-input">
|
||||||
|
</mct-control>
|
||||||
|
</span>
|
||||||
|
<span class="l-time-range-input-w time-delta start-delta"
|
||||||
|
ng-class="{'hide':(modeModel.selected.metadata.key === 'fixed')}">
|
||||||
|
<mct-control key="'datetime-field'"
|
||||||
|
structure="{
|
||||||
|
format: 'duration',
|
||||||
|
validate: tcController.validation.validateStartDelta
|
||||||
|
}"
|
||||||
|
ng-model="formModel"
|
||||||
|
ng-blur="tcController.updateDeltasFromForm(formModel)"
|
||||||
|
field="'startDelta'"
|
||||||
|
class="hrs-min-input">
|
||||||
|
</mct-control>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="l-time-range-w end-w">
|
||||||
|
<span class="l-time-range-input-w time-delta end-delta"
|
||||||
|
ng-class="{'hide':(modeModel.selected.metadata.key === 'fixed')}">
|
||||||
|
|
||||||
|
<mct-control key="'datetime-field'"
|
||||||
|
structure="{
|
||||||
|
format: 'duration',
|
||||||
|
validate: tcController.validation.validateEndDelta
|
||||||
|
}"
|
||||||
|
ng-model="formModel"
|
||||||
|
ng-blur="tcController.updateDeltasFromForm(formModel)"
|
||||||
|
field="'endDelta'"
|
||||||
|
class="hrs-min-input">
|
||||||
|
</mct-control>
|
||||||
|
</span>
|
||||||
|
<span class="l-time-range-input-w end-date"
|
||||||
|
ng-controller="ToggleController as t2">
|
||||||
|
<mct-control key="'datetime-field'"
|
||||||
|
structure="{
|
||||||
|
format: timeSystemModel.format,
|
||||||
|
validate: tcController.validation.validateEnd
|
||||||
|
}"
|
||||||
|
ng-model="formModel"
|
||||||
|
ng-mouseup="changing['end'] = true"
|
||||||
|
ng-blur="changing['end'] = false; tcController.updateBoundsFromForm(formModel)"
|
||||||
|
field="'end'"
|
||||||
|
class="time-range-input">
|
||||||
|
</mct-control>
|
||||||
|
</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 l-flex-row 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-representation
|
||||||
|
key="'mode-selector'"
|
||||||
|
mct-object="domainObject"
|
||||||
|
ng-model="modeModel"
|
||||||
|
class="holder flex-elem menus-up mode-selector">
|
||||||
|
</mct-representation>
|
||||||
|
<mct-control
|
||||||
|
key="'menu-button'"
|
||||||
|
class="holder flex-elem menus-up time-system"
|
||||||
|
structure="{
|
||||||
|
text: timeSystemModel.selected.metadata.name,
|
||||||
|
click: tcController.selectTimeSystem,
|
||||||
|
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">Mars Minutes</span>
|
||||||
|
<input class="time-conductor-zoom flex-elem" type="range" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
185
platform/features/conductor-v2/src/TimeConductor.js
Normal file
185
platform/features/conductor-v2/src/TimeConductor.js
Normal 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(['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;
|
||||||
|
};
|
||||||
|
|
||||||
|
function throwOnError(validationResult) {
|
||||||
|
if (validationResult !== true) {
|
||||||
|
throw new Error(validationResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
throwOnError(this.validateBounds(newBounds));
|
||||||
|
//Create a copy to avoid direct mutation of conductor bounds
|
||||||
|
this.boundsVal = JSON.parse(JSON.stringify(newBounds));
|
||||||
|
/**
|
||||||
|
* @event TimeConductor#bounds The start time, end time, or
|
||||||
|
* both have been updated
|
||||||
|
* @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);
|
||||||
|
// Do something with bounds here. Try and convert between
|
||||||
|
// time systems? Or just set defaults when time system changes?
|
||||||
|
// eg.
|
||||||
|
this.bounds(bounds);
|
||||||
|
} else if (arguments.length === 1) {
|
||||||
|
throw new Error('Must set bounds when changing time system');
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
});
|
110
platform/features/conductor-v2/src/TimeConductorSpec.js
Normal file
110
platform/features/conductor-v2/src/TimeConductorSpec.js
Normal 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.toBe(bounds);
|
||||||
|
expect(tc.bounds.bind(tc, bounds)).not.toThrow();
|
||||||
|
expect(tc.bounds()).toBe(bounds);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Disallows setting of invalid bounds", function () {
|
||||||
|
bounds = {start: 1, end: 0};
|
||||||
|
expect(tc.bounds()).not.toBe(bounds);
|
||||||
|
expect(tc.bounds.bind(tc, bounds)).toThrow();
|
||||||
|
expect(tc.bounds()).not.toBe(bounds);
|
||||||
|
|
||||||
|
bounds = {start: 1};
|
||||||
|
expect(tc.bounds()).not.toBe(bounds);
|
||||||
|
expect(tc.bounds.bind(tc, bounds)).toThrow();
|
||||||
|
expect(tc.bounds()).not.toBe(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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
92
platform/features/conductor-v2/src/timeSystems/LocalClock.js
Normal file
92
platform/features/conductor-v2/src/timeSystems/LocalClock.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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: 'real-time',
|
||||||
|
glyph: '\u0043',
|
||||||
|
label: 'Real-time',
|
||||||
|
name: 'Real-time Mode',
|
||||||
|
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
LocalClock.prototype.type = function () {
|
||||||
|
return 'clock';
|
||||||
|
};
|
||||||
|
|
||||||
|
return LocalClock;
|
||||||
|
});
|
54
platform/features/conductor-v2/src/timeSystems/TickSource.js
Normal file
54
platform/features/conductor-v2/src/timeSystems/TickSource.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 a timing of timing signals. 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');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What does this source tick on? A clock, or data availability.
|
||||||
|
* Information is required to support time conductor modes.
|
||||||
|
* @returns {string} type One of 'clock' or 'data'
|
||||||
|
*/
|
||||||
|
TickSource.prototype.type = function () {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
return TickSource;
|
||||||
|
});
|
59
platform/features/conductor-v2/src/timeSystems/TimeSystem.js
Normal file
59
platform/features/conductor-v2/src/timeSystems/TimeSystem.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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;
|
||||||
|
this._tickSources = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSystem.prototype.formats = function () {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
};
|
||||||
|
|
||||||
|
TimeSystem.prototype.tickSources = function () {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns {TimeSystemDefault[]} At least one set of default values for
|
||||||
|
* this time system.
|
||||||
|
*/
|
||||||
|
TimeSystem.prototype.defaults = function () {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
};
|
||||||
|
|
||||||
|
return TimeSystem;
|
||||||
|
});
|
@ -0,0 +1,78 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define([
|
||||||
|
'./TimeSystem',
|
||||||
|
'./LocalClock'
|
||||||
|
], function (TimeSystem, LocalClock, UTCTimeFormat) {
|
||||||
|
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, glyph: string}}
|
||||||
|
*/
|
||||||
|
this.metadata = {
|
||||||
|
'key': 'utc',
|
||||||
|
'name': 'UTC',
|
||||||
|
'glyph': '\u0043'
|
||||||
|
};
|
||||||
|
|
||||||
|
//Time formats are defined as extensions. Include the key
|
||||||
|
// for the corresponding time format here
|
||||||
|
this._formats = ['utc'];
|
||||||
|
this._tickSources = [new LocalClock($timeout, DEFAULT_PERIOD)];
|
||||||
|
}
|
||||||
|
|
||||||
|
UTCTimeSystem.prototype = Object.create(TimeSystem.prototype);
|
||||||
|
|
||||||
|
UTCTimeSystem.prototype.formats = function () {
|
||||||
|
return this._formats;
|
||||||
|
};
|
||||||
|
|
||||||
|
UTCTimeSystem.prototype.tickSources = function () {
|
||||||
|
return this._tickSources;
|
||||||
|
};
|
||||||
|
|
||||||
|
UTCTimeSystem.prototype.defaults = function () {
|
||||||
|
var now = Math.ceil(Date.now() / 1000) * 1000;
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'utc-default',
|
||||||
|
name: 'UTC time system defaults',
|
||||||
|
deltas: {start: FIFTEEN_MINUTES, end: 0},
|
||||||
|
bounds: {start: now - FIFTEEN_MINUTES, end: now}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
return UTCTimeSystem;
|
||||||
|
});
|
89
platform/features/conductor-v2/src/ui/MctConductorAxis.js
Normal file
89
platform/features/conductor-v2/src/ui/MctConductorAxis.js
Normal 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(
|
||||||
|
[
|
||||||
|
"zepto",
|
||||||
|
"d3"
|
||||||
|
],
|
||||||
|
function ($, d3) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mct-conductor-axis renders a horizontal axis with regular
|
||||||
|
* labelled 'ticks'. It requires 'start' and 'end' integer values to
|
||||||
|
* be specified as attributes.
|
||||||
|
*/
|
||||||
|
function MCTConductorAxis(conductor) {
|
||||||
|
|
||||||
|
function link(scope, element, attrs, ngModelController) {
|
||||||
|
var target = element[0].firstChild,
|
||||||
|
height = target.offsetHeight,
|
||||||
|
padding = 1;
|
||||||
|
|
||||||
|
var vis = d3.select(target)
|
||||||
|
.append('svg:svg')
|
||||||
|
.attr('width', '100%')
|
||||||
|
.attr('height', height);
|
||||||
|
var xScale = d3.scaleUtc();
|
||||||
|
var xAxis = d3.axisTop();
|
||||||
|
// draw x axis with labels and move to the bottom of the chart area
|
||||||
|
var axisElement = vis.append("g")
|
||||||
|
.attr("transform", "translate(0," + (height - padding) + ")");
|
||||||
|
|
||||||
|
function setScale(start, end) {
|
||||||
|
var width = target.offsetWidth;
|
||||||
|
xScale.domain([new Date(start), new Date(end)])
|
||||||
|
.range([padding, width - padding * 2]);
|
||||||
|
xAxis.scale(xScale);
|
||||||
|
axisElement.call(xAxis);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.resize = function () {
|
||||||
|
setScale(conductor.bounds().start, conductor.bounds().end);
|
||||||
|
};
|
||||||
|
|
||||||
|
//On conductor bounds changes, redraw ticks
|
||||||
|
conductor.on('bounds', function (bounds) {
|
||||||
|
setScale(bounds.start, bounds.end);
|
||||||
|
});
|
||||||
|
|
||||||
|
//Set initial scale.
|
||||||
|
setScale(conductor.bounds().start, conductor.bounds().end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Only show at the element level
|
||||||
|
restrict: "E",
|
||||||
|
|
||||||
|
template: "<div class=\"l-axis-holder\" mct-resize=\"resize()\"></div>",
|
||||||
|
|
||||||
|
// ngOptions is terminal, so we need to be higher priority
|
||||||
|
priority: 1000,
|
||||||
|
|
||||||
|
// Link function
|
||||||
|
link: link
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return MCTConductorAxis;
|
||||||
|
}
|
||||||
|
);
|
223
platform/features/conductor-v2/src/ui/TimeConductorController.js
Normal file
223
platform/features/conductor-v2/src/ui/TimeConductorController.js
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[
|
||||||
|
'./modes/FixedMode',
|
||||||
|
'./modes/RealtimeMode',
|
||||||
|
'./modes/LADMode',
|
||||||
|
'./TimeConductorValidation'
|
||||||
|
],
|
||||||
|
function (FixedMode, RealtimeMode, LADMode, TimeConductorValidation) {
|
||||||
|
|
||||||
|
function TimeConductorController($scope, conductor, timeSystems) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
//Bind all class functions to 'this'
|
||||||
|
Object.keys(TimeConductorController.prototype).filter(function (key) {
|
||||||
|
return typeof TimeConductorController.prototype[key] === 'function';
|
||||||
|
}).forEach(function (key) {
|
||||||
|
self[key] = self[key].bind(self);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.conductor = conductor;
|
||||||
|
// Construct the provided time system definitions
|
||||||
|
this.timeSystems = timeSystems.map(function (timeSystemConstructor){
|
||||||
|
return timeSystemConstructor();
|
||||||
|
});
|
||||||
|
// Populate a list of modes supported by the time conductor
|
||||||
|
this.modes = [
|
||||||
|
new FixedMode(this.conductor, this.timeSystems),
|
||||||
|
new RealtimeMode(this.conductor, this.timeSystems),
|
||||||
|
new LADMode(this.conductor, this.timeSystems)
|
||||||
|
];
|
||||||
|
|
||||||
|
this.validation = new TimeConductorValidation(conductor);
|
||||||
|
this.$scope = $scope;
|
||||||
|
this.initializeScope($scope);
|
||||||
|
|
||||||
|
conductor.on('bounds', this.setBounds);
|
||||||
|
conductor.on('follow', function (follow){
|
||||||
|
$scope.followMode = follow;
|
||||||
|
});
|
||||||
|
|
||||||
|
//Set the time conductor mode to the first one in the list,
|
||||||
|
// effectively initializing the time conductor
|
||||||
|
this.setMode(this.modes[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
TimeConductorController.prototype.initializeScope = function ($scope) {
|
||||||
|
$scope.timeSystemModel = {
|
||||||
|
selected: undefined,
|
||||||
|
format: undefined,
|
||||||
|
options: []
|
||||||
|
};
|
||||||
|
$scope.modeModel = {
|
||||||
|
selected: undefined,
|
||||||
|
options: this.modes
|
||||||
|
};
|
||||||
|
$scope.formModel = {
|
||||||
|
start: 0,
|
||||||
|
end: 0
|
||||||
|
};
|
||||||
|
$scope.changing = {
|
||||||
|
'start': false,
|
||||||
|
'end': false
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$watch('modeModel.selected', this.setMode);
|
||||||
|
$scope.$watch('timeSystem', this.setTimeSystem);
|
||||||
|
|
||||||
|
$scope.$on('$destroy', function() {
|
||||||
|
var mode = $scope.modeModel.selected;
|
||||||
|
if (mode && mode.destroy) {
|
||||||
|
mode.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the bounds change in the time conductor. Synchronizes
|
||||||
|
* the bounds values in the time conductor with those in the form
|
||||||
|
* @param bounds
|
||||||
|
*/
|
||||||
|
TimeConductorController.prototype.setBounds = function (bounds) {
|
||||||
|
if (!this.$scope.changing['start']) {
|
||||||
|
this.$scope.formModel.start = bounds.start;
|
||||||
|
}
|
||||||
|
if (!this.$scope.changing['end']) {
|
||||||
|
this.$scope.formModel.end = bounds.end;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when form values are changed. Synchronizes the form with
|
||||||
|
* the time conductor
|
||||||
|
* @param formModel
|
||||||
|
*/
|
||||||
|
TimeConductorController.prototype.updateBoundsFromForm = function (formModel) {
|
||||||
|
var newBounds = {start: formModel.start, end: formModel.end};
|
||||||
|
|
||||||
|
if (this.conductor.validateBounds(newBounds) === true) {
|
||||||
|
this.conductor.bounds(newBounds);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the delta values in the form change. Validates and
|
||||||
|
* sets the new deltas on the Mode.
|
||||||
|
* @param formModel
|
||||||
|
* @see TimeConductorMode
|
||||||
|
*/
|
||||||
|
TimeConductorController.prototype.updateDeltasFromForm = function (formModel) {
|
||||||
|
var mode = this.$scope.modeModel.selected,
|
||||||
|
deltas = mode.deltas();
|
||||||
|
|
||||||
|
if (deltas !== undefined && this.validation.validateDeltas(formModel)) {
|
||||||
|
//Sychronize deltas between form and mode
|
||||||
|
mode.deltas({start: formModel.startDelta, end: formModel.endDelta});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the selected Time Conductor mode. This will call destroy
|
||||||
|
* and initialization functions on the relevant modes, setting
|
||||||
|
* default values for bound and deltas in the form.
|
||||||
|
* @param newMode
|
||||||
|
* @param oldMode
|
||||||
|
*/
|
||||||
|
TimeConductorController.prototype.setMode = function (newMode, oldMode) {
|
||||||
|
if (newMode !== oldMode) {
|
||||||
|
if (oldMode && oldMode.destroy) {
|
||||||
|
oldMode.destroy();
|
||||||
|
}
|
||||||
|
newMode.initialize();
|
||||||
|
|
||||||
|
var timeSystem = newMode.selectedTimeSystem();
|
||||||
|
|
||||||
|
this.$scope.modeModel.selected = newMode;
|
||||||
|
|
||||||
|
//Synchronize scope with time system on mode
|
||||||
|
this.$scope.timeSystemModel.options = newMode.timeSystems().map(function (timeSystem) {
|
||||||
|
return timeSystem.metadata;
|
||||||
|
});
|
||||||
|
this.$scope.timeSystemModel.selected = timeSystem;
|
||||||
|
//Use default format
|
||||||
|
this.$scope.timeSystemModel.format = timeSystem.formats()[0];
|
||||||
|
this.setDefaultsFromTimeSystem(newMode.selectedTimeSystem());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
TimeConductorController.prototype.setDefaultsFromTimeSystem = function (timeSystem) {
|
||||||
|
var defaults = timeSystem.defaults()[0];
|
||||||
|
var deltas = defaults.deltas;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the selected mode defines deltas, set them in the form
|
||||||
|
*/
|
||||||
|
if (deltas !== undefined) {
|
||||||
|
this.$scope.formModel.startDelta = deltas.start;
|
||||||
|
this.$scope.formModel.endDelta = deltas.end;
|
||||||
|
} else {
|
||||||
|
this.$scope.formModel.startDelta = 0;
|
||||||
|
this.$scope.formModel.endDelta = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows time system to be changed by key. This supports selection
|
||||||
|
* from the menu. Resolves a TimeSystem object and then invokes
|
||||||
|
* TimeConductorController#setTimeSystem
|
||||||
|
* @param key
|
||||||
|
* @see TimeConductorController#setTimeSystem
|
||||||
|
*/
|
||||||
|
TimeConductorController.prototype.selectTimeSystem = function(key){
|
||||||
|
var selected = this.timeSystems.find(function (timeSystem){
|
||||||
|
return timeSystem.metadata.key === key;
|
||||||
|
});
|
||||||
|
this.setTimeSystem(selected);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the selected time system. Will populate form with the default
|
||||||
|
* bounds and deltas defined in the selected time system.
|
||||||
|
* @param newTimeSystem
|
||||||
|
*/
|
||||||
|
TimeConductorController.prototype.setTimeSystem = function (newTimeSystem) {
|
||||||
|
if (newTimeSystem && newTimeSystem !== this.$scope.timeSystemModel.selected) {
|
||||||
|
this.$scope.timeSystemModel.selected = newTimeSystem;
|
||||||
|
var mode = this.$scope.modeModel.selected;
|
||||||
|
mode.selectedTimeSystem(newTimeSystem);
|
||||||
|
this.setDefaultsFromTimeSystem(newTimeSystem);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return TimeConductorController;
|
||||||
|
}
|
||||||
|
);
|
@ -0,0 +1,79 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form validation for the TimeConductorController.
|
||||||
|
* @param conductor
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function TimeConductorValidation(conductor) {
|
||||||
|
var self = this;
|
||||||
|
this.conductor = conductor
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bind all class functions to 'this'
|
||||||
|
*/
|
||||||
|
Object.keys(TimeConductorValidation.prototype).filter(function (key) {
|
||||||
|
return typeof TimeConductorValidation.prototype[key] === 'function';
|
||||||
|
}).forEach(function (key) {
|
||||||
|
self[key] = self[key].bind(self);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeConductorValidation.prototype.validateStart = function (start) {
|
||||||
|
var bounds = this.conductor.bounds();
|
||||||
|
return this.conductor.validateBounds({start: start, end: bounds.end}) === true;
|
||||||
|
};
|
||||||
|
|
||||||
|
TimeConductorValidation.prototype.validateEnd = function (end) {
|
||||||
|
var bounds = this.conductor.bounds();
|
||||||
|
return this.conductor.validateBounds({start: bounds.start, end: end}) === true;
|
||||||
|
};
|
||||||
|
|
||||||
|
TimeConductorValidation.prototype.validateStartDelta = function (startDelta) {
|
||||||
|
return startDelta > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
TimeConductorValidation.prototype.validateEndDelta = function (endDelta) {
|
||||||
|
return endDelta >= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the delta values in the form model. Deltas are offsets
|
||||||
|
* from a fixed point in time, and are used in follow modes as the
|
||||||
|
* primary determinant of conductor bounds.
|
||||||
|
* @param formModel
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
TimeConductorValidation.prototype.validateDeltas = function (formModel) {
|
||||||
|
// Validate that start Delta is some non-zero value, and that end
|
||||||
|
// delta is zero or positive (ie. 'now' or some time in the future).
|
||||||
|
return this.validateStartDelta(formModel.startDelta) && this.validateEndDelta(formModel.endDelta);
|
||||||
|
};
|
||||||
|
|
||||||
|
return TimeConductorValidation;
|
||||||
|
}
|
||||||
|
);
|
78
platform/features/conductor-v2/src/ui/modes/FixedMode.js
Normal file
78
platform/features/conductor-v2/src/ui/modes/FixedMode.js
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['./TimeConductorMode'],
|
||||||
|
function (TimeConductorMode) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles time conductor behavior when it is in 'fixed' mode. In
|
||||||
|
* fixed mode, the time conductor is bound by two dates and does not
|
||||||
|
* progress.
|
||||||
|
* @param conductor
|
||||||
|
* @param timeSystems
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function FixedMode(conductor, timeSystems) {
|
||||||
|
var metadata = {
|
||||||
|
key: 'fixed',
|
||||||
|
glyph: '\ue604',
|
||||||
|
label: 'Fixed',
|
||||||
|
name: 'Fixed Timespan Mode',
|
||||||
|
description: 'Query and explore data that falls between two fixed datetimes.'
|
||||||
|
};
|
||||||
|
|
||||||
|
TimeConductorMode.call(this, metadata, conductor, timeSystems);
|
||||||
|
}
|
||||||
|
|
||||||
|
FixedMode.prototype = Object.create(TimeConductorMode.prototype);
|
||||||
|
|
||||||
|
FixedMode.prototype.initialize = function () {
|
||||||
|
TimeConductorMode.prototype.initialize.apply(this);
|
||||||
|
this.conductor.follow(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines behavior to occur when selected time system changes. In
|
||||||
|
* this case, sets default bounds on the time conductor.
|
||||||
|
* @param timeSystem
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
FixedMode.prototype.selectedTimeSystem = function (timeSystem){
|
||||||
|
TimeConductorMode.prototype.selectedTimeSystem.apply(this, arguments);
|
||||||
|
|
||||||
|
if (timeSystem) {
|
||||||
|
var defaults = timeSystem.defaults()[0];
|
||||||
|
|
||||||
|
var bounds = {
|
||||||
|
start: defaults.bounds.start,
|
||||||
|
end: defaults.bounds.end
|
||||||
|
};
|
||||||
|
|
||||||
|
this.conductor.timeSystem(timeSystem, bounds);
|
||||||
|
}
|
||||||
|
return this._selectedTimeSystem;
|
||||||
|
};
|
||||||
|
|
||||||
|
return FixedMode;
|
||||||
|
}
|
||||||
|
);
|
147
platform/features/conductor-v2/src/ui/modes/FollowMode.js
Normal file
147
platform/features/conductor-v2/src/ui/modes/FollowMode.js
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['./TimeConductorMode'],
|
||||||
|
function (TimeConductorMode) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A parent class for Realtime and LAD modes, which both advance the
|
||||||
|
* time conductor bounds over time. The event that advances the time
|
||||||
|
* conductor is abstracted to a TickSource. Unlike FixedMode, the
|
||||||
|
* two 'follow' modes define 'deltas' which are offsets from a fixed
|
||||||
|
* end point. Thus, in follow mode, the end time of the conductor is
|
||||||
|
* the mode relevant, with both offsets defined relative to it.
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function FollowMode(metadata, conductor, timeSystems) {
|
||||||
|
TimeConductorMode.call(this, metadata, conductor, timeSystems);
|
||||||
|
|
||||||
|
this._deltas = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
FollowMode.prototype = Object.create(TimeConductorMode.prototype);
|
||||||
|
|
||||||
|
FollowMode.prototype.initialize = function () {
|
||||||
|
TimeConductorMode.prototype.initialize.apply(this);
|
||||||
|
this.conductor.follow(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param time
|
||||||
|
*/
|
||||||
|
FollowMode.prototype.tick = function (time) {
|
||||||
|
var deltas = this.deltas();
|
||||||
|
this.conductor.bounds({
|
||||||
|
start: time - deltas.start,
|
||||||
|
end: time + deltas.end
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param tickSource
|
||||||
|
*/
|
||||||
|
FollowMode.prototype.listenToTickSource = function () {
|
||||||
|
if (this._selectedTimeSystem &&
|
||||||
|
this._timeSystems[0]) {
|
||||||
|
|
||||||
|
var tickSource = this._timeSystems[0].tickSources()[0];
|
||||||
|
if (tickSource) {
|
||||||
|
this.tickSourceUnlisten = tickSource.listen(this.tick.bind(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On time system change, default the bounds values in the time
|
||||||
|
* conductor, using the deltas associated with this mode.
|
||||||
|
* @param timeSystem
|
||||||
|
* @returns {TimeSystem}
|
||||||
|
*/
|
||||||
|
FollowMode.prototype.selectedTimeSystem = function (timeSystem) {
|
||||||
|
TimeConductorMode.prototype.selectedTimeSystem.apply(this, arguments);
|
||||||
|
|
||||||
|
if (timeSystem) {
|
||||||
|
var defaults = timeSystem.defaults()[0];
|
||||||
|
|
||||||
|
if (arguments.length > 0) {
|
||||||
|
var bounds = {
|
||||||
|
start: defaults.bounds.start,
|
||||||
|
end: defaults.bounds.end
|
||||||
|
};
|
||||||
|
|
||||||
|
if (defaults.deltas) {
|
||||||
|
bounds.start = bounds.end - defaults.deltas.start;
|
||||||
|
bounds.end = bounds.end + defaults.deltas.end;
|
||||||
|
this._deltas = JSON.parse(JSON.stringify(defaults.deltas));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.conductor.timeSystem(timeSystem, bounds);
|
||||||
|
|
||||||
|
this.listenToTickSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._selectedTimeSystem;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or set the current value for the deltas used by this time system.
|
||||||
|
* On change, the new deltas will be used to calculate and set the
|
||||||
|
* bounds on the time conductor.
|
||||||
|
* @param deltas
|
||||||
|
* @returns {TimeSystemDeltas}
|
||||||
|
*/
|
||||||
|
FollowMode.prototype.deltas = function (deltas) {
|
||||||
|
if (arguments.length !== 0) {
|
||||||
|
var oldEnd = this.conductor.bounds().end;
|
||||||
|
|
||||||
|
if (this._deltas && this._deltas.end){
|
||||||
|
//Calculate the previous 'true' end value (without delta)
|
||||||
|
oldEnd = oldEnd - this._deltas.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._deltas = deltas;
|
||||||
|
|
||||||
|
var newBounds = {
|
||||||
|
start: oldEnd - this._deltas.start,
|
||||||
|
end: oldEnd + this._deltas.end
|
||||||
|
};
|
||||||
|
|
||||||
|
this.conductor.bounds(newBounds);
|
||||||
|
}
|
||||||
|
return this._deltas;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop listening to tick sources
|
||||||
|
*/
|
||||||
|
FollowMode.prototype.destroy = function () {
|
||||||
|
if (this.tickSourceUnlisten) {
|
||||||
|
this.tickSourceUnlisten();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return FollowMode;
|
||||||
|
}
|
||||||
|
);
|
56
platform/features/conductor-v2/src/ui/modes/LADMode.js
Normal file
56
platform/features/conductor-v2/src/ui/modes/LADMode.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['./FollowMode'],
|
||||||
|
function (FollowMode) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supports the 'Latest Available Data' mode of the time conductor.
|
||||||
|
* This is a special case of FollowMode that advances on 'data' type
|
||||||
|
* tick sources.
|
||||||
|
* @param conductor
|
||||||
|
* @param timeSystems
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function LADMode(conductor, timeSystems) {
|
||||||
|
var metadata = {
|
||||||
|
key: 'latest',
|
||||||
|
glyph: '\u0044',
|
||||||
|
label: 'LAD',
|
||||||
|
name: 'LAD Mode',
|
||||||
|
description: 'Latest Available Data mode monitors real-time streaming data as it comes in. The Time Conductor and displays will only advance when data becomes available.'
|
||||||
|
};
|
||||||
|
var filteredTimeSystems = timeSystems.filter(function (timeSystem){
|
||||||
|
return timeSystem.tickSources().some(function (tickSource){
|
||||||
|
return tickSource.type() === 'data';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
FollowMode.call(this, metadata, conductor, filteredTimeSystems);
|
||||||
|
}
|
||||||
|
|
||||||
|
LADMode.prototype = Object.create(FollowMode.prototype);
|
||||||
|
|
||||||
|
return LADMode;
|
||||||
|
}
|
||||||
|
);
|
55
platform/features/conductor-v2/src/ui/modes/RealtimeMode.js
Normal file
55
platform/features/conductor-v2/src/ui/modes/RealtimeMode.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['./FollowMode'],
|
||||||
|
function (FollowMode) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing the 'realtime' mode of the time conductor.
|
||||||
|
* This is a special case of FollowMode that only supports 'clock'
|
||||||
|
* type tick sources.
|
||||||
|
* @param conductor
|
||||||
|
* @param timeSystems
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function RealtimeMode(conductor, timeSystems) {
|
||||||
|
var metadata = {
|
||||||
|
key: 'realtime',
|
||||||
|
glyph: '\u0043',
|
||||||
|
label: 'Real-time',
|
||||||
|
name: 'Real-time Mode',
|
||||||
|
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
|
||||||
|
};
|
||||||
|
var filteredTimeSystems = timeSystems.filter(function (timeSystem){
|
||||||
|
return timeSystem.tickSources().some(function (tickSource){
|
||||||
|
return tickSource.type() === 'clock';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
FollowMode.call(this, metadata, conductor, filteredTimeSystems);
|
||||||
|
}
|
||||||
|
|
||||||
|
RealtimeMode.prototype = Object.create(FollowMode.prototype);
|
||||||
|
|
||||||
|
return RealtimeMode;
|
||||||
|
}
|
||||||
|
);
|
@ -0,0 +1,82 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT Web includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supports mode-specific time conductor behavior. This class
|
||||||
|
* defines a parent with default behavior that specific modes are
|
||||||
|
* expected to override.
|
||||||
|
*
|
||||||
|
* @interface
|
||||||
|
* @constructor
|
||||||
|
* @param {TimeConductorMetadata} metadata
|
||||||
|
*/
|
||||||
|
function TimeConductorMode(metadata, conductor, timeSystems) {
|
||||||
|
this.metadata = metadata;
|
||||||
|
|
||||||
|
this.conductor = conductor;
|
||||||
|
this._timeSystems = timeSystems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function is called when mode becomes active (ie. is selected)
|
||||||
|
*/
|
||||||
|
TimeConductorMode.prototype.initialize = function () {
|
||||||
|
this.selectedTimeSystem(this._timeSystems[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the time systems supported by this mode. Modes are
|
||||||
|
* expected to determine which time systems they will support by
|
||||||
|
* filtering a provided list of all time systems.
|
||||||
|
*
|
||||||
|
* @returns {TimeSystem[]} The time systems supported by this mode
|
||||||
|
*/
|
||||||
|
TimeConductorMode.prototype.timeSystems = function () {
|
||||||
|
return this._timeSystems;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or set the currently selected time system
|
||||||
|
* @param timeSystem
|
||||||
|
* @returns {TimeSystem} the currently selected time system
|
||||||
|
*/
|
||||||
|
TimeConductorMode.prototype.selectedTimeSystem = function (timeSystem) {
|
||||||
|
if (arguments.length > 0) {
|
||||||
|
if (this._timeSystems.indexOf(timeSystem) === -1){
|
||||||
|
throw new Error("Unsupported time system");
|
||||||
|
} else {
|
||||||
|
this._selectedTimeSystem = timeSystem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._selectedTimeSystem;
|
||||||
|
};
|
||||||
|
|
||||||
|
TimeConductorMode.prototype.destroy = function () {
|
||||||
|
};
|
||||||
|
|
||||||
|
return TimeConductorMode;
|
||||||
|
}
|
||||||
|
);
|
@ -53,7 +53,7 @@ define([
|
|||||||
"provides": "telemetryService",
|
"provides": "telemetryService",
|
||||||
"implementation": ConductorTelemetryDecorator,
|
"implementation": ConductorTelemetryDecorator,
|
||||||
"depends": [
|
"depends": [
|
||||||
"conductorService"
|
"timeConductor"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -34,22 +34,21 @@ define(
|
|||||||
* the service which exposes the global time conductor
|
* the service which exposes the global time conductor
|
||||||
* @param {TelemetryService} telemetryService the decorated service
|
* @param {TelemetryService} telemetryService the decorated service
|
||||||
*/
|
*/
|
||||||
function ConductorTelemetryDecorator(conductorService, telemetryService) {
|
function ConductorTelemetryDecorator(conductor, telemetryService) {
|
||||||
this.conductorService = conductorService;
|
this.conductor = conductor;
|
||||||
this.telemetryService = telemetryService;
|
this.telemetryService = telemetryService;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConductorTelemetryDecorator.prototype.amendRequests = function (requests) {
|
ConductorTelemetryDecorator.prototype.amendRequests = function (requests) {
|
||||||
var conductor = this.conductorService.getConductor(),
|
var conductor = this.conductor,
|
||||||
start = conductor.displayStart(),
|
bounds = conductor.bounds(),
|
||||||
end = conductor.displayEnd(),
|
start = bounds.start,
|
||||||
domain = conductor.domain();
|
end = bounds.end;
|
||||||
|
|
||||||
function amendRequest(request) {
|
function amendRequest(request) {
|
||||||
request = request || {};
|
request = request || {};
|
||||||
request.start = start;
|
request.start = start;
|
||||||
request.end = end;
|
request.end = end;
|
||||||
request.domain = domain.key;
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ define(
|
|||||||
'parent'
|
'parent'
|
||||||
];
|
];
|
||||||
|
|
||||||
xdescribe("ConductorRepresenter", function () {
|
describe("ConductorRepresenter", function () {
|
||||||
var mockThrottle,
|
var mockThrottle,
|
||||||
mockConductorService,
|
mockConductorService,
|
||||||
mockCompile,
|
mockCompile,
|
||||||
|
@ -1,23 +1,18 @@
|
|||||||
<div class="t-imagery" ng-controller="ImageryController as imagery">
|
<div class="t-imagery" ng-controller="ImageryController as imagery">
|
||||||
<div
|
<div class="l-image-main-wrapper l-flex-col"
|
||||||
class="l-image-main-wrapper"
|
|
||||||
ng-mouseenter="showLocalControls = true;"
|
ng-mouseenter="showLocalControls = true;"
|
||||||
ng-mouseleave="showLocalControls = false;"
|
ng-mouseleave="showLocalControls = false;">
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="l-local-controls s-local-controls"
|
class="l-local-controls s-local-controls"
|
||||||
ng-show="false && showLocalControls"
|
ng-show="false && showLocalControls">
|
||||||
>
|
<a class="s-btn"
|
||||||
<a
|
|
||||||
class="s-btn"
|
|
||||||
ng-click="plot.stepBackPanZoom()"
|
ng-click="plot.stepBackPanZoom()"
|
||||||
ng-show="1"
|
ng-show="1"
|
||||||
title="Restore previous pan/zoom">
|
title="Restore previous pan/zoom">
|
||||||
<span class="ui-symbol icon"><</span>
|
<span class="ui-symbol icon"><</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a
|
<a class="s-btn"
|
||||||
class="s-btn"
|
|
||||||
ng-click="plot.unzoom()"
|
ng-click="plot.unzoom()"
|
||||||
ng-show="1"
|
ng-show="1"
|
||||||
title="Reset pan/zoom">
|
title="Reset pan/zoom">
|
||||||
@ -25,29 +20,23 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="l-image-main s-image-main flex-elem grows"
|
||||||
class="l-image-main s-image-main"
|
|
||||||
ng-class="{ paused: imagery.paused(), stale:false }"
|
ng-class="{ paused: imagery.paused(), stale:false }"
|
||||||
mct-background-image="imagery.getImageUrl()"
|
mct-background-image="imagery.getImageUrl()">
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="l-image-main-controlbar l-flex-row">
|
<div class="l-image-main-controlbar flex-elem l-flex-row">
|
||||||
<div class="left flex-elem grows">
|
<div class="left flex-elem grows">
|
||||||
<a
|
<a class="s-btn show-thumbs sm hidden"
|
||||||
class="s-btn show-thumbs sm hidden"
|
ng-click="showThumbsBubble = (showThumbsBubble)? false:true"><span class="ui-symbol icon"></span></a>
|
||||||
ng-click="showThumbsBubble = (showThumbsBubble)? false:true"
|
|
||||||
><span class="ui-symbol icon"></span></a>
|
|
||||||
<span class="l-timezone">{{imagery.getZone()}}</span>
|
<span class="l-timezone">{{imagery.getZone()}}</span>
|
||||||
<span class="l-time">{{imagery.getTime()}}</span>
|
<span class="l-time">{{imagery.getTime()}}</span>
|
||||||
<span class="l-date">{{imagery.getDate()}}</span>
|
<span class="l-date">{{imagery.getDate()}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="right flex-elem">
|
<div class="right flex-elem">
|
||||||
<a
|
<a class="s-btn pause-play"
|
||||||
class="s-btn pause-play"
|
|
||||||
ng-click="imagery.paused(!imagery.paused())"
|
ng-click="imagery.paused(!imagery.paused())"
|
||||||
ng-class="{ paused: imagery.paused() }"
|
ng-class="{ paused: imagery.paused() }"><span class="ui-symbol icon"></span></a>
|
||||||
><span class="ui-symbol icon"></span></a>
|
|
||||||
<a href="{{imagery.getImageUrl()}}"
|
<a href="{{imagery.getImageUrl()}}"
|
||||||
ng-if="imagery.getImageUrl()"
|
ng-if="imagery.getImageUrl()"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@ -58,8 +47,7 @@
|
|||||||
class="s-btn l-mag s-mag ui-symbol vsm"
|
class="s-btn l-mag s-mag ui-symbol vsm"
|
||||||
ng-click="clipped = false"
|
ng-click="clipped = false"
|
||||||
ng-show="clipped === true"
|
ng-show="clipped === true"
|
||||||
title="Not all of image is visible; click to reset."
|
title="Not all of image is visible; click to reset."></a>
|
||||||
></a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -72,6 +72,7 @@ define([
|
|||||||
"$scope",
|
"$scope",
|
||||||
"telemetryFormatter",
|
"telemetryFormatter",
|
||||||
"telemetryHandler",
|
"telemetryHandler",
|
||||||
|
"timeConductor",
|
||||||
"throttle",
|
"throttle",
|
||||||
"PLOT_FIXED_DURATION"
|
"PLOT_FIXED_DURATION"
|
||||||
]
|
]
|
||||||
|
@ -65,6 +65,7 @@ define(
|
|||||||
$scope,
|
$scope,
|
||||||
telemetryFormatter,
|
telemetryFormatter,
|
||||||
telemetryHandler,
|
telemetryHandler,
|
||||||
|
conductor,
|
||||||
throttle,
|
throttle,
|
||||||
PLOT_FIXED_DURATION
|
PLOT_FIXED_DURATION
|
||||||
) {
|
) {
|
||||||
@ -125,6 +126,7 @@ define(
|
|||||||
duration = PLOT_FIXED_DURATION;
|
duration = PLOT_FIXED_DURATION;
|
||||||
|
|
||||||
updater = new PlotUpdater(handle, domain, range, duration);
|
updater = new PlotUpdater(handle, domain, range, duration);
|
||||||
|
|
||||||
lastDomain = domain;
|
lastDomain = domain;
|
||||||
lastRange = range;
|
lastRange = range;
|
||||||
|
|
||||||
@ -227,13 +229,20 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Respond to a display bounds change (requery for data)
|
// Respond to a display bounds change (requery for data)
|
||||||
function changeDisplayBounds(event, bounds) {
|
function changeDisplayBounds(bounds) {
|
||||||
var domainAxis = $scope.axes[0];
|
//var domainAxis = $scope.axes[0];
|
||||||
|
|
||||||
domainAxis.chooseOption(bounds.domain);
|
//domainAxis.chooseOption(bounds.domain);
|
||||||
updateDomainFormat();
|
//updateDomainFormat();
|
||||||
setBasePanZoom(bounds);
|
setBasePanZoom(bounds);
|
||||||
requery();
|
|
||||||
|
// re-query historical. What do we do about ticks? Don't want
|
||||||
|
// to do a historical re-query on every tick. Need a
|
||||||
|
// forward-buffer I think...
|
||||||
|
// For now, if follow mode, don't requery
|
||||||
|
if (!conductor.follow()) {
|
||||||
|
requery();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modeOptions = new PlotModeOptions([], subPlotFactory);
|
this.modeOptions = new PlotModeOptions([], subPlotFactory);
|
||||||
@ -262,7 +271,8 @@ define(
|
|||||||
$scope.$watch('domainObject', subscribe);
|
$scope.$watch('domainObject', subscribe);
|
||||||
|
|
||||||
// Respond to external bounds changes
|
// Respond to external bounds changes
|
||||||
$scope.$on("telemetry:display:bounds", changeDisplayBounds);
|
//$scope.$on("telemetry:display:bounds", changeDisplayBounds);
|
||||||
|
conductor.on('bounds', changeDisplayBounds);
|
||||||
|
|
||||||
// Unsubscribe when the plot is destroyed
|
// Unsubscribe when the plot is destroyed
|
||||||
$scope.$on("$destroy", releaseSubscription);
|
$scope.$on("$destroy", releaseSubscription);
|
||||||
|
@ -164,9 +164,11 @@ define(
|
|||||||
priorDomainDimensions = this.dimensions[0];
|
priorDomainDimensions = this.dimensions[0];
|
||||||
|
|
||||||
if (bufferArray.length > 0) {
|
if (bufferArray.length > 0) {
|
||||||
this.domainExtrema = bufferArray.map(function (lineBuffer) {
|
if (!this.domainExtrema) {
|
||||||
return lineBuffer.getDomainExtrema();
|
this.domainExtrema = bufferArray.map(function (lineBuffer) {
|
||||||
}).reduce(reduceExtrema);
|
return lineBuffer.getDomainExtrema();
|
||||||
|
}).reduce(reduceExtrema);
|
||||||
|
}
|
||||||
|
|
||||||
this.rangeExtrema = bufferArray.map(function (lineBuffer) {
|
this.rangeExtrema = bufferArray.map(function (lineBuffer) {
|
||||||
return lineBuffer.getRangeExtrema();
|
return lineBuffer.getRangeExtrema();
|
||||||
|
@ -109,7 +109,7 @@ define([
|
|||||||
{
|
{
|
||||||
"key": "HistoricalTableController",
|
"key": "HistoricalTableController",
|
||||||
"implementation": HistoricalTableController,
|
"implementation": HistoricalTableController,
|
||||||
"depends": ["$scope", "telemetryHandler", "telemetryFormatter"]
|
"depends": ["$scope", "telemetryHandler", "telemetryFormatter", "$timeout"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "RealtimeTableController",
|
"key": "RealtimeTableController",
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
<div ng-controller="HistoricalTableController">
|
<div ng-controller="HistoricalTableController" ng-class="{'loading': loading}">
|
||||||
<mct-table
|
<mct-table
|
||||||
headers="headers"
|
headers="headers"
|
||||||
rows="rows"
|
rows="rows"
|
||||||
enableFilter="true"
|
enableFilter="true"
|
||||||
enableSort="true">
|
enableSort="true"
|
||||||
|
class="tabular-holder t-exportable">
|
||||||
</mct-table>
|
</mct-table>
|
||||||
</div>
|
</div>
|
@ -1,4 +1,9 @@
|
|||||||
<div class="l-view-section scrolling" style="overflow: auto;">
|
<a class="t-btn l-btn s-btn t-export"
|
||||||
|
ng-click="exportAsCSV()"
|
||||||
|
title="Export This View's Data">
|
||||||
|
Export
|
||||||
|
</a>
|
||||||
|
<div class="l-view-section scrolling" style="overflow: auto;" mct-resize="resize()">
|
||||||
<table class="sizing-table">
|
<table class="sizing-table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
rows="rows"
|
rows="rows"
|
||||||
enableFilter="true"
|
enableFilter="true"
|
||||||
enableSort="true"
|
enableSort="true"
|
||||||
auto-scroll="autoScroll">
|
class="tabular-holder t-exportable"
|
||||||
|
auto-scroll="true">
|
||||||
</mct-table>
|
</mct-table>
|
||||||
</div>
|
</div>
|
@ -25,6 +25,7 @@ define(
|
|||||||
'./TelemetryTableController'
|
'./TelemetryTableController'
|
||||||
],
|
],
|
||||||
function (TableController) {
|
function (TableController) {
|
||||||
|
var BATCH_SIZE = 1000;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extends TelemetryTableController and adds real-time streaming
|
* Extends TelemetryTableController and adds real-time streaming
|
||||||
@ -35,32 +36,82 @@ define(
|
|||||||
* @param telemetryFormatter
|
* @param telemetryFormatter
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function HistoricalTableController($scope, telemetryHandler, telemetryFormatter) {
|
function HistoricalTableController($scope, telemetryHandler, telemetryFormatter, $timeout) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
this.$timeout = $timeout;
|
||||||
|
this.timeoutHandle = undefined;
|
||||||
|
this.batchSize = BATCH_SIZE;
|
||||||
|
|
||||||
|
$scope.$on("$destroy", function () {
|
||||||
|
if (self.timeoutHandle) {
|
||||||
|
self.$timeout.cancel(self.timeoutHandle);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
TableController.call(this, $scope, telemetryHandler, telemetryFormatter);
|
TableController.call(this, $scope, telemetryHandler, telemetryFormatter);
|
||||||
}
|
}
|
||||||
|
|
||||||
HistoricalTableController.prototype = Object.create(TableController.prototype);
|
HistoricalTableController.prototype = Object.create(TableController.prototype);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates historical data on scope when it becomes available from
|
* Set provided row data on scope, and cancel loading spinner
|
||||||
* the telemetry API
|
* @private
|
||||||
*/
|
*/
|
||||||
HistoricalTableController.prototype.addHistoricalData = function () {
|
HistoricalTableController.prototype.doneProcessing = function (rowData) {
|
||||||
var rowData = [],
|
|
||||||
self = this;
|
|
||||||
|
|
||||||
this.handle.getTelemetryObjects().forEach(function (telemetryObject) {
|
|
||||||
var series = self.handle.getSeries(telemetryObject) || {},
|
|
||||||
pointCount = series.getPointCount ? series.getPointCount() : 0,
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
for (; i < pointCount; i++) {
|
|
||||||
rowData.push(self.table.getRowValues(telemetryObject,
|
|
||||||
self.handle.makeDatum(telemetryObject, series, i)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$scope.rows = rowData;
|
this.$scope.rows = rowData;
|
||||||
|
this.$scope.loading = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes an array of objects, formatting the telemetry available
|
||||||
|
* for them and setting it on scope when done
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
HistoricalTableController.prototype.processTelemetryObjects = function (objects, offset, start, rowData) {
|
||||||
|
var telemetryObject = objects[offset],
|
||||||
|
series,
|
||||||
|
i = start,
|
||||||
|
pointCount,
|
||||||
|
end;
|
||||||
|
|
||||||
|
//No more objects to process
|
||||||
|
if (!telemetryObject) {
|
||||||
|
return this.doneProcessing(rowData);
|
||||||
|
}
|
||||||
|
|
||||||
|
series = this.handle.getSeries(telemetryObject);
|
||||||
|
|
||||||
|
pointCount = series.getPointCount();
|
||||||
|
end = Math.min(start + this.batchSize, pointCount);
|
||||||
|
|
||||||
|
//Process rows in a batch with size not exceeding a maximum length
|
||||||
|
for (; i < end; i++) {
|
||||||
|
rowData.push(this.table.getRowValues(telemetryObject,
|
||||||
|
this.handle.makeDatum(telemetryObject, series, i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Done processing all rows for this object.
|
||||||
|
if (end >= pointCount) {
|
||||||
|
offset++;
|
||||||
|
end = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done processing either a batch or an object, yield process
|
||||||
|
// before continuing processing
|
||||||
|
this.timeoutHandle = this.$timeout(this.processTelemetryObjects.bind(this, objects, offset, end, rowData));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates historical data on scope when it becomes available from
|
||||||
|
* the telemetry API
|
||||||
|
*/
|
||||||
|
HistoricalTableController.prototype.addHistoricalData = function () {
|
||||||
|
if (this.timeoutHandle) {
|
||||||
|
this.$timeout.cancel(this.timeoutHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.timeoutHandle = this.$timeout(this.processTelemetryObjects.bind(this, this.handle.getTelemetryObjects(), 0, 0, []));
|
||||||
};
|
};
|
||||||
|
|
||||||
return HistoricalTableController;
|
return HistoricalTableController;
|
||||||
|
@ -12,7 +12,7 @@ define(
|
|||||||
* @param element
|
* @param element
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function MCTTableController($scope, $timeout, element) {
|
function MCTTableController($scope, $timeout, element, exportService) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
@ -46,6 +46,16 @@ define(
|
|||||||
|
|
||||||
setDefaults($scope);
|
setDefaults($scope);
|
||||||
|
|
||||||
|
$scope.exportAsCSV = function () {
|
||||||
|
var headers = $scope.displayHeaders;
|
||||||
|
exportService.exportCSV($scope.displayRows.map(function (row) {
|
||||||
|
return headers.reduce(function (r, header) {
|
||||||
|
r[header] = row[header].text;
|
||||||
|
return r;
|
||||||
|
}, {});
|
||||||
|
}), { headers: headers });
|
||||||
|
};
|
||||||
|
|
||||||
$scope.toggleSort = function (key) {
|
$scope.toggleSort = function (key) {
|
||||||
if (!$scope.enableSort) {
|
if (!$scope.enableSort) {
|
||||||
return;
|
return;
|
||||||
@ -76,6 +86,12 @@ define(
|
|||||||
*/
|
*/
|
||||||
$scope.$on('add:row', this.addRow.bind(this));
|
$scope.$on('add:row', this.addRow.bind(this));
|
||||||
$scope.$on('remove:row', this.removeRow.bind(this));
|
$scope.$on('remove:row', this.removeRow.bind(this));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Listen for resize events to trigger recalculation of table width
|
||||||
|
*/
|
||||||
|
$scope.resize = this.setElementSizes.bind(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,30 +38,7 @@ define(
|
|||||||
function RealtimeTableController($scope, telemetryHandler, telemetryFormatter) {
|
function RealtimeTableController($scope, telemetryHandler, telemetryFormatter) {
|
||||||
TableController.call(this, $scope, telemetryHandler, telemetryFormatter);
|
TableController.call(this, $scope, telemetryHandler, telemetryFormatter);
|
||||||
|
|
||||||
$scope.autoScroll = false;
|
|
||||||
this.maxRows = 100000;
|
this.maxRows = 100000;
|
||||||
|
|
||||||
/*
|
|
||||||
* Determine if auto-scroll should be enabled. Is enabled
|
|
||||||
* automatically when telemetry type is string
|
|
||||||
*/
|
|
||||||
function hasStringTelemetry(domainObject) {
|
|
||||||
var telemetry = domainObject &&
|
|
||||||
domainObject.getCapability('telemetry'),
|
|
||||||
metadata = telemetry ? telemetry.getMetadata() : {},
|
|
||||||
ranges = metadata.ranges || [];
|
|
||||||
|
|
||||||
return ranges.some(function (range) {
|
|
||||||
return range.format === 'string';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
$scope.$watch('domainObject', function (domainObject) {
|
|
||||||
//When a domain object becomes available, check whether the
|
|
||||||
// view should auto-scroll to the bottom.
|
|
||||||
if (domainObject && hasStringTelemetry(domainObject)) {
|
|
||||||
$scope.autoScroll = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RealtimeTableController.prototype = Object.create(TableController.prototype);
|
RealtimeTableController.prototype = Object.create(TableController.prototype);
|
||||||
@ -91,6 +68,7 @@ define(
|
|||||||
self.$scope.rows.length - 1);
|
self.$scope.rows.length - 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.$scope.loading = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
return RealtimeTableController;
|
return RealtimeTableController;
|
||||||
|
@ -72,10 +72,10 @@ define(
|
|||||||
* Maintain a configuration object on scope that stores column
|
* Maintain a configuration object on scope that stores column
|
||||||
* configuration. On change, synchronize with object model.
|
* configuration. On change, synchronize with object model.
|
||||||
*/
|
*/
|
||||||
$scope.$watchCollection('configuration.table.columns', function (columns) {
|
$scope.$watchCollection('configuration.table.columns', function (newColumns, oldColumns) {
|
||||||
if (columns) {
|
if (newColumns !== oldColumns) {
|
||||||
self.domainObject.useCapability('mutation', function (model) {
|
self.domainObject.useCapability('mutation', function (model) {
|
||||||
model.configuration.table.columns = columns;
|
model.configuration.table.columns = newColumns;
|
||||||
});
|
});
|
||||||
self.domainObject.getCapability('persistence').persist();
|
self.domainObject.getCapability('persistence').persist();
|
||||||
}
|
}
|
||||||
|
@ -83,16 +83,24 @@ define(
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
TelemetryTableController.prototype.registerChangeListeners = function () {
|
TelemetryTableController.prototype.registerChangeListeners = function () {
|
||||||
|
var self = this;
|
||||||
this.unregisterChangeListeners();
|
this.unregisterChangeListeners();
|
||||||
|
|
||||||
// When composition changes, re-subscribe to the various
|
// When composition changes, re-subscribe to the various
|
||||||
// telemetry subscriptions
|
// telemetry subscriptions
|
||||||
this.changeListeners.push(this.$scope.$watchCollection(
|
this.changeListeners.push(this.$scope.$watchCollection(
|
||||||
'domainObject.getModel().composition', this.subscribe.bind(this)));
|
'domainObject.getModel().composition',
|
||||||
|
function (newVal, oldVal) {
|
||||||
|
if (newVal !== oldVal) {
|
||||||
|
self.subscribe();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
//Change of bounds in time conductor
|
//Change of bounds in time conductor
|
||||||
this.changeListeners.push(this.$scope.$on('telemetry:display:bounds',
|
this.changeListeners.push(this.$scope.$on('telemetry:display:bounds',
|
||||||
this.subscribe.bind(this)));
|
this.subscribe.bind(this))
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,6 +140,7 @@ define(
|
|||||||
if (this.handle) {
|
if (this.handle) {
|
||||||
this.handle.unsubscribe();
|
this.handle.unsubscribe();
|
||||||
}
|
}
|
||||||
|
this.$scope.loading = true;
|
||||||
|
|
||||||
this.handle = this.$scope.domainObject && this.telemetryHandler.handle(
|
this.handle = this.$scope.domainObject && this.telemetryHandler.handle(
|
||||||
this.$scope.domainObject,
|
this.$scope.domainObject,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user