mirror of
https://github.com/nasa/openmct.git
synced 2025-06-25 18:50:11 +00:00
Compare commits
199 Commits
cli
...
prototype-
Author | SHA1 | Date | |
---|---|---|---|
5013646e89 | |||
9925b91405 | |||
fdfb524eef | |||
ef250f58de | |||
15ed91f651 | |||
074254a513 | |||
4c84789d5d | |||
92573b817f | |||
5382cca435 | |||
15c1bf20ab | |||
685dd2114d | |||
6e30a25a6f | |||
42fa5bfd7e | |||
fb0ce1eca8 | |||
d7bd793bf3 | |||
9cc0c0b06f | |||
c703714cb3 | |||
b48a07cd3e | |||
8c29c8ed0e | |||
5763511ec8 | |||
2accf21518 | |||
28c42fcd4c | |||
1efa97e6f5 | |||
756f728865 | |||
d8276c532b | |||
fc0bfa77db | |||
12c6e53939 | |||
4f716ad5c5 | |||
8093fcbda1 | |||
0ea5721f76 | |||
d902943552 | |||
5f7c8ccadb | |||
520d17f9db | |||
7532db5f49 | |||
ebd8fdeee3 | |||
f738f84075 | |||
cc19a0acba | |||
134b749bbf | |||
36d06e8b54 | |||
5520d90984 | |||
77b0086d18 | |||
146e948097 | |||
669b434c36 | |||
431c74ca49 | |||
cd0c0f77cc | |||
8cba321886 | |||
dd83816035 | |||
1ca2b769d9 | |||
a1d1261179 | |||
99048a4ee3 | |||
553b17fafe | |||
c4aff95341 | |||
13095b4135 | |||
fd927d4c03 | |||
445f22ccb0 | |||
1ad0bf337c | |||
73dc16d398 | |||
bebe53820f | |||
dfe909d6b5 | |||
3cf62ded08 | |||
6cbd3e5fae | |||
3050b265fb | |||
5104a7990a | |||
571beb8df2 | |||
bea5002752 | |||
d04c5e6858 | |||
0d1f3bf87a | |||
b632926d8e | |||
78f3f8367e | |||
85ac4a9a32 | |||
ef527df381 | |||
c2868a4573 | |||
77c66053f3 | |||
0891e15936 | |||
2979ee90a3 | |||
77c399f2a2 | |||
fe8543158e | |||
a4f3e0d776 | |||
866c8882ca | |||
ad7d3d642e | |||
333f7cb848 | |||
f198c281bc | |||
23de3917bb | |||
badaca53d3 | |||
00ac249ee2 | |||
00aa6821d1 | |||
e33485ec59 | |||
afb1202865 | |||
f5a4a370f9 | |||
2848a8458b | |||
cbaf45afe9 | |||
7a677062e4 | |||
a7153f320f | |||
b3da6edd0c | |||
ff1fd26efc | |||
4ced6c44a6 | |||
67f627b51f | |||
1d83516982 | |||
9e64dfe3b9 | |||
09f5fa42ab | |||
54a077a4e2 | |||
13525a67c2 | |||
cc6b6538d5 | |||
0c7de98195 | |||
404d1e7801 | |||
1214a32c26 | |||
6bd8e7a47c | |||
825d93cee3 | |||
9f7dc1da9b | |||
3d8aec2d01 | |||
928e31b548 | |||
f182d1f2c4 | |||
d238b669a5 | |||
5d5a7c26c5 | |||
0b0cee3afb | |||
0260e6fff4 | |||
9811443c71 | |||
7dc13dab66 | |||
e67a2e63cf | |||
f4e53a946d | |||
de71bde62f | |||
8f24e014e0 | |||
190f5fd0ea | |||
ad29fb0f92 | |||
fcd073c010 | |||
071368c3b9 | |||
7a97588aa5 | |||
f776561303 | |||
e34fe1a289 | |||
70d9587c9b | |||
9a78b63065 | |||
6c497f3c36 | |||
d951b794e3 | |||
797046aca4 | |||
cf76583ed7 | |||
6f28ab0145 | |||
9ebf157ec0 | |||
493c63be44 | |||
f29951140f | |||
d0b5bb2d21 | |||
cd98886a43 | |||
4549828cae | |||
d0478c3433 | |||
53369ec0dc | |||
de99969f0a | |||
24449d2dcc | |||
f42c5ca1e5 | |||
890aafc203 | |||
2a14cf2dfc | |||
62962e119e | |||
2229e868ce | |||
8d209f4d19 | |||
86bb89a162 | |||
2758250833 | |||
7d20351a6a | |||
78fae345da | |||
4c79c9a1b1 | |||
2ec9956d44 | |||
e3b191b5dc | |||
a4dda695dd | |||
0d710209b1 | |||
fdbc91131b | |||
d2dfec3ce7 | |||
351181d38e | |||
760f4b818f | |||
c026bfa17d | |||
47b97a504e | |||
29c460556a | |||
4d276888e1 | |||
142af3db77 | |||
b66759e519 | |||
c58ffb4a52 | |||
600ff1a3ee | |||
77d11e1bcf | |||
d158aa6028 | |||
c2985d61b7 | |||
3ce40ab870 | |||
bfb19dea74 | |||
01a6d2e6a7 | |||
af462ff3ee | |||
5c1d209eff | |||
8a76c3a425 | |||
9ccd0b9188 | |||
f83588d980 | |||
a481b377cb | |||
35ff4efbca | |||
436e010738 | |||
bf4765fcb6 | |||
dbfb8b9861 | |||
681cd0bb9c | |||
b668fb58fb | |||
f74da6b935 | |||
e4dec21ceb | |||
fc2860810b | |||
9d6b70f433 | |||
57a947eaef | |||
a18cc50a43 | |||
91fe3d798f | |||
e873389655 |
15
README.md
15
README.md
@ -103,6 +103,21 @@ This build will:
|
||||
|
||||
Run as `mvn clean install`.
|
||||
|
||||
### Building Documentation
|
||||
|
||||
Open MCT Web's documentation is generated by an
|
||||
[npm](https://www.npmjs.com/)-based build:
|
||||
|
||||
* `npm install` _(only needs to run once)_
|
||||
* `npm run docs`
|
||||
|
||||
Documentation will be generated in `target/docs`. Note that diagram
|
||||
generation is dependent on having [Cairo](http://cairographics.org/download/)
|
||||
installed; see
|
||||
[node-canvas](https://github.com/Automattic/node-canvas#installation)'s
|
||||
documentation for help with installation.
|
||||
|
||||
|
||||
# Glossary
|
||||
|
||||
Certain terms are used throughout Open MCT Web with consistent meanings
|
||||
|
@ -34,6 +34,10 @@
|
||||
{
|
||||
"key": "time",
|
||||
"name": "Time"
|
||||
},
|
||||
{
|
||||
"key": "yesterday",
|
||||
"name": "Yesterday"
|
||||
}
|
||||
],
|
||||
"ranges": [
|
||||
@ -61,4 +65,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,8 @@
|
||||
* Module defining SinewaveTelemetryProvider. Created by vwoeltje on 11/12/14.
|
||||
*/
|
||||
define(
|
||||
["./SinewaveTelemetry"],
|
||||
function (SinewaveTelemetry) {
|
||||
["./SinewaveTelemetrySeries"],
|
||||
function (SinewaveTelemetrySeries) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
@ -45,7 +45,7 @@ define(
|
||||
function generateData(request) {
|
||||
return {
|
||||
key: request.key,
|
||||
telemetry: new SinewaveTelemetry(request)
|
||||
telemetry: new SinewaveTelemetrySeries(request)
|
||||
};
|
||||
}
|
||||
|
||||
@ -112,4 +112,4 @@ define(
|
||||
|
||||
return SinewaveTelemetryProvider;
|
||||
}
|
||||
);
|
||||
);
|
||||
|
@ -29,35 +29,47 @@ define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
var firstObservedTime = Date.now();
|
||||
var ONE_DAY = 60 * 60 * 24,
|
||||
firstObservedTime = Math.floor(Date.now() / 1000) - ONE_DAY;
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SinewaveTelemetry(request) {
|
||||
var latestObservedTime = Date.now(),
|
||||
count = Math.floor((latestObservedTime - firstObservedTime) / 1000),
|
||||
period = request.period || 30,
|
||||
generatorData = {};
|
||||
function SinewaveTelemetrySeries(request) {
|
||||
var timeOffset = (request.domain === 'yesterday') ? ONE_DAY : 0,
|
||||
latestTime = Math.floor(Date.now() / 1000) - timeOffset,
|
||||
firstTime = firstObservedTime - timeOffset,
|
||||
endTime = (request.end !== undefined) ?
|
||||
Math.floor(request.end / 1000) : latestTime,
|
||||
count = Math.min(endTime, latestTime) - firstTime,
|
||||
period = +request.period || 30,
|
||||
generatorData = {},
|
||||
requestStart = (request.start === undefined) ? firstTime :
|
||||
Math.max(Math.floor(request.start / 1000), firstTime),
|
||||
offset = requestStart - firstTime;
|
||||
|
||||
if (request.size !== undefined) {
|
||||
offset = Math.max(offset, count - request.size);
|
||||
}
|
||||
|
||||
generatorData.getPointCount = function () {
|
||||
return count;
|
||||
return count - offset;
|
||||
};
|
||||
|
||||
generatorData.getDomainValue = function (i, domain) {
|
||||
return i * 1000 +
|
||||
(domain !== 'delta' ? firstObservedTime : 0);
|
||||
return (i + offset) * 1000 + firstTime * 1000 -
|
||||
(domain === 'yesterday' ? ONE_DAY : 0);
|
||||
};
|
||||
|
||||
generatorData.getRangeValue = function (i, range) {
|
||||
range = range || "sin";
|
||||
return Math[range](i * Math.PI * 2 / period);
|
||||
return Math[range]((i + offset) * Math.PI * 2 / period);
|
||||
};
|
||||
|
||||
return generatorData;
|
||||
}
|
||||
|
||||
return SinewaveTelemetry;
|
||||
return SinewaveTelemetrySeries;
|
||||
}
|
||||
);
|
||||
);
|
@ -4,7 +4,11 @@
|
||||
{
|
||||
"implementation": "WatchIndicator.js",
|
||||
"depends": ["$interval", "$rootScope"]
|
||||
},
|
||||
{
|
||||
"implementation": "DigestIndicator.js",
|
||||
"depends": ["$interval", "$rootScope"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
77
example/profiling/src/DigestIndicator.js
Normal file
77
example/profiling/src/DigestIndicator.js
Normal file
@ -0,0 +1,77 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Displays the number of digests that have occurred since the
|
||||
* indicator was first instantiated.
|
||||
* @constructor
|
||||
* @param $interval Angular's $interval
|
||||
* @implements {Indicator}
|
||||
*/
|
||||
function DigestIndicator($interval, $rootScope) {
|
||||
var digests = 0,
|
||||
displayed = 0,
|
||||
start = Date.now();
|
||||
|
||||
function update() {
|
||||
var secs = (Date.now() - start) / 1000;
|
||||
displayed = Math.round(digests / secs);
|
||||
}
|
||||
|
||||
function increment() {
|
||||
digests += 1;
|
||||
}
|
||||
|
||||
$rootScope.$watch(increment);
|
||||
|
||||
// Update state every second
|
||||
$interval(update, 1000);
|
||||
|
||||
// Provide initial state, too
|
||||
update();
|
||||
|
||||
return {
|
||||
getGlyph: function () {
|
||||
return ".";
|
||||
},
|
||||
getGlyphClass: function () {
|
||||
return undefined;
|
||||
},
|
||||
getText: function () {
|
||||
return displayed + " digests/sec";
|
||||
},
|
||||
getDescription: function () {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return DigestIndicator;
|
||||
|
||||
}
|
||||
);
|
@ -20,6 +20,7 @@
|
||||
"$scope",
|
||||
"$route",
|
||||
"$location",
|
||||
"$q",
|
||||
"objectService",
|
||||
"navigationService",
|
||||
"urlService"
|
||||
@ -34,7 +35,8 @@
|
||||
{
|
||||
"key": "BrowseObjectController",
|
||||
"implementation": "BrowseObjectController.js",
|
||||
"depends": [ "$scope", "$location", "$route" ]
|
||||
"depends": [ "$scope", "$location", "$route", "$q",
|
||||
"navigationService" ]
|
||||
},
|
||||
{
|
||||
"key": "CreateMenuController",
|
||||
@ -62,6 +64,7 @@
|
||||
{
|
||||
"key": "browse-object",
|
||||
"templateUrl": "templates/browse-object.html",
|
||||
"gestures": ["drop"],
|
||||
"uses": [ "view" ]
|
||||
},
|
||||
{
|
||||
|
@ -19,7 +19,15 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<span ng-controller="BrowseObjectController">
|
||||
<div ng-init="
|
||||
editBtns = [{ cssclass: 'save',title: 'Save' },{ cssclass: 'cancel',title: 'Discard Changes', action:'cancelEditing' }];
|
||||
"></div>
|
||||
<span ng-controller="BrowseObjectController"
|
||||
mct-before-unload="getUnloadWarning()">
|
||||
<a class="s-btn"
|
||||
style="opacity: 0.9; position:absolute; right: 250px; z-index: 100"
|
||||
ng-class="{ major:!editMode }"
|
||||
ng-click="editMode = !editMode">Set EditMode to {{!editMode}}</a>
|
||||
<div class="object-browse-bar bar l-flex">
|
||||
<div class="items-select left">
|
||||
<mct-representation key="'back-arrow'"
|
||||
@ -42,9 +50,69 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='object-holder abs vscroll'>
|
||||
<mct-representation key="representation.selected.key"
|
||||
mct-object="representation.selected.key && domainObject">
|
||||
</mct-representation>
|
||||
<div class="l-object-wrapper"
|
||||
ng-class="{ active:editMode, 'edit-main':editMode}">
|
||||
<div class="l-object-wrapper-inner l-flex flex-col">
|
||||
<!-- Toolbar and Save/Cancel buttons -->
|
||||
<div class="l-edit-controls flex-elem l-flex flex-row flex-align-end"
|
||||
ng-class="{ active:editMode }">
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- <mct-toolbar name="mctToolbar"
|
||||
structure="toolbar.structure"
|
||||
ng-model="toolbar.state"
|
||||
class="flex-elem grow">
|
||||
</mct-toolbar>-->
|
||||
<!-- from toolbar.html -->
|
||||
<mct-toolbar-x class="flex-elem grow ellipsis">
|
||||
<form novalidate>
|
||||
<div class="tool-bar btn-bar">
|
||||
<span class="l-control-group">
|
||||
<span class="s-menu-btn">Foo</span>
|
||||
<span class="s-menu-btn">Bar</span>
|
||||
</span>
|
||||
<span class="l-control-group">
|
||||
<span class="s-menu-btn">Foo</span>
|
||||
<span class="s-menu-btn">Bar</span>
|
||||
<span class="s-menu-btn">Foo</span>
|
||||
<span class="s-menu-btn">Lorem</span>
|
||||
</span>
|
||||
<span class="l-control-group">
|
||||
<span class="s-menu-btn">Foo</span>
|
||||
<span class="s-menu-btn">Bar</span>
|
||||
<span class="s-menu-btn">Lorem</span>
|
||||
</span>
|
||||
<span class="s-menu-btn">Lorem</span>
|
||||
</div>
|
||||
</form>
|
||||
</mct-toolbar-x>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- <mct-representation key="'edit-action-buttons'"
|
||||
mct-object="domainObject"
|
||||
class='flex-elem conclude-editing'>
|
||||
</mct-representation>-->
|
||||
<!-- from edit-action-buttons.html -->
|
||||
<span class='flex-elem conclude-editing'>
|
||||
<span ng-repeat="btn in editBtns">
|
||||
<a class='s-btn t-{{btn.cssclass}}'
|
||||
title='{{btn.title}}'
|
||||
ng-click="doAction(btn.action)"
|
||||
ng-class="{ major: $index === 0 }">
|
||||
<span class="title-label">{{btn.title}}</span>
|
||||
</a>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<mct-representation key="representation.selected.key"
|
||||
mct-object="representation.selected.key && domainObject"
|
||||
class="flex-elem grow object-holder">
|
||||
</mct-representation>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
|
@ -28,7 +28,9 @@
|
||||
<mct-split-pane class='contents abs' anchor='left'>
|
||||
<div class='split-pane-component treeview pane left'>
|
||||
<div class="holder abs l-mobile">
|
||||
<mct-representation key="'create-button'" mct-object="navigatedObject">
|
||||
<mct-representation key="'create-button'"
|
||||
mct-object="navigatedObject"
|
||||
mct-device="desktop">
|
||||
</mct-representation>
|
||||
<div class='holder search-holder abs'
|
||||
ng-class="{active: treeModel.search}">
|
||||
|
@ -20,7 +20,7 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="menu-element wrapper" ng-controller="ClickAwayController as createController">
|
||||
<div class="s-menu major create-btn" ng-click="createController.toggle()">
|
||||
<div class="s-menu-btn major create-btn" ng-click="createController.toggle()">
|
||||
<span class="ui-symbol icon type-icon">+</span>
|
||||
<span class="title-label">Create</span>
|
||||
</div>
|
||||
|
@ -26,8 +26,11 @@
|
||||
* @namespace platform/commonUI/browse
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
[
|
||||
'../../../representation/src/gestures/GestureConstants',
|
||||
'../../edit/src/objects/EditableDomainObject'
|
||||
],
|
||||
function (GestureConstants, EditableDomainObject) {
|
||||
"use strict";
|
||||
|
||||
var ROOT_ID = "ROOT",
|
||||
@ -43,7 +46,7 @@ define(
|
||||
* @memberof platform/commonUI/browse
|
||||
* @constructor
|
||||
*/
|
||||
function BrowseController($scope, $route, $location, objectService, navigationService, urlService) {
|
||||
function BrowseController($scope, $route, $location, $q, objectService, navigationService, urlService) {
|
||||
var path = [ROOT_ID].concat(
|
||||
($route.current.params.ids || DEFAULT_PATH).split("/")
|
||||
);
|
||||
@ -71,13 +74,17 @@ define(
|
||||
// Callback for updating the in-scope reference to the object
|
||||
// that is currently navigated-to.
|
||||
function setNavigation(domainObject) {
|
||||
$scope.navigatedObject = domainObject;
|
||||
|
||||
var wrappedObject = domainObject;
|
||||
|
||||
$scope.navigatedObject = wrappedObject;
|
||||
$scope.treeModel.selectedObject = domainObject;
|
||||
navigationService.setNavigation(domainObject);
|
||||
updateRoute(domainObject);
|
||||
}
|
||||
|
||||
function navigateTo(domainObject) {
|
||||
|
||||
// Check if an object has been navigated-to already...
|
||||
// If not, or if an ID path has been explicitly set in the URL,
|
||||
// navigate to the URL-specified object.
|
||||
@ -148,12 +155,12 @@ define(
|
||||
|
||||
// Also listen for changes which come from the tree
|
||||
$scope.$watch("treeModel.selectedObject", setNavigation);
|
||||
|
||||
|
||||
// Clean up when the scope is destroyed
|
||||
$scope.$on("$destroy", function () {
|
||||
navigationService.removeListener(setNavigation);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
return BrowseController;
|
||||
|
@ -22,8 +22,9 @@
|
||||
/*global define,Promise*/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
['../../../representation/src/gestures/GestureConstants',
|
||||
'../../edit/src/objects/EditableDomainObject'],
|
||||
function (GestureConstants, EditableDomainObject) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
@ -32,8 +33,10 @@ define(
|
||||
* @memberof platform/commonUI/browse
|
||||
* @constructor
|
||||
*/
|
||||
function BrowseObjectController($scope, $location, $route) {
|
||||
function BrowseObjectController($scope, $location, $route, $q, navigationService) {
|
||||
var navigatedObject;
|
||||
function setViewForDomainObject(domainObject) {
|
||||
|
||||
var locationViewKey = $location.search().view;
|
||||
|
||||
function selectViewIfMatching(view) {
|
||||
@ -47,6 +50,8 @@ define(
|
||||
((domainObject && domainObject.useCapability('view')) || [])
|
||||
.forEach(selectViewIfMatching);
|
||||
}
|
||||
$scope.editMode = domainObject.getDomainObject ? true : false;
|
||||
navigatedObject = domainObject;
|
||||
}
|
||||
|
||||
function updateQueryParam(viewKey) {
|
||||
@ -67,6 +72,15 @@ define(
|
||||
|
||||
$scope.$watch('domainObject', setViewForDomainObject);
|
||||
$scope.$watch('representation.selected.key', updateQueryParam);
|
||||
|
||||
$scope.cancelEditing = function() {
|
||||
navigationService.setNavigation($scope.domainObject.getDomainObject());
|
||||
}
|
||||
|
||||
$scope.doAction = function (action){
|
||||
$scope[action] && $scope[action]();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return BrowseObjectController;
|
||||
|
@ -21,10 +21,11 @@
|
||||
-->
|
||||
<span ng-controller="EditActionController">
|
||||
<span ng-repeat="currentAction in editActions">
|
||||
<a class='s-btn'
|
||||
<a class='s-btn t-{{currentAction.getMetadata().key}}'
|
||||
title='{{currentAction.getMetadata().name}}'
|
||||
ng-click="currentAction.perform()"
|
||||
ng-class="{ major: $index === 0, subtle: $index !== 0 }">
|
||||
{{currentAction.getMetadata().name}}
|
||||
ng-class="{ major: $index === 0 }">
|
||||
<span class="title-label">{{currentAction.getMetadata().name}}</span>
|
||||
</a>
|
||||
</span>
|
||||
</span>
|
@ -30,12 +30,11 @@
|
||||
structure="toolbar.structure"
|
||||
ng-model="toolbar.state">
|
||||
</mct-toolbar>
|
||||
<div class='holder abs object-holder work-area'>
|
||||
<mct-representation key="representation.selected.key"
|
||||
toolbar="toolbar"
|
||||
mct-object="representation.selected.key && domainObject">
|
||||
</mct-representation>
|
||||
</div>
|
||||
<mct-representation key="representation.selected.key"
|
||||
toolbar="toolbar"
|
||||
mct-object="representation.selected.key && domainObject"
|
||||
class="holder abs object-holder work-area">
|
||||
</mct-representation>
|
||||
</div>
|
||||
<mct-splitter></mct-splitter>
|
||||
<div
|
||||
|
@ -25,8 +25,8 @@
|
||||
* Module defining EditAction. Created by vwoeltje on 11/14/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
['../objects/EditableDomainObject'],
|
||||
function (EditableDomainObject) {
|
||||
"use strict";
|
||||
|
||||
// A no-op action to return in the event that the action cannot
|
||||
@ -71,8 +71,10 @@ define(
|
||||
* Enter edit mode.
|
||||
*/
|
||||
EditAction.prototype.perform = function () {
|
||||
this.navigationService.setNavigation(this.domainObject);
|
||||
this.$location.path("/edit");
|
||||
if (!this.domainObject.getDomainObject) {
|
||||
this.navigationService.setNavigation(new EditableDomainObject(this.domainObject));
|
||||
}
|
||||
//this.$location.path("/edit");
|
||||
};
|
||||
|
||||
/**
|
||||
@ -83,10 +85,11 @@ define(
|
||||
*/
|
||||
EditAction.appliesTo = function (context) {
|
||||
var domainObject = (context || {}).domainObject,
|
||||
type = domainObject && domainObject.getCapability('type');
|
||||
type = domainObject && domainObject.getCapability('type'),
|
||||
isEditMode = domainObject && domainObject.getDomainObject ? true : false;
|
||||
|
||||
// Only allow creatable types to be edited
|
||||
return type && type.hasFeature('creation');
|
||||
return type && type.hasFeature('creation') && !isEditMode;
|
||||
};
|
||||
|
||||
return EditAction;
|
||||
|
@ -101,6 +101,10 @@ define(
|
||||
new Factory(capability, editableObject, domainObject, cache) :
|
||||
capability;
|
||||
};
|
||||
|
||||
editableObject.getDomainObject = function() {
|
||||
return domainObject;
|
||||
}
|
||||
|
||||
return editableObject;
|
||||
}
|
||||
|
@ -8,6 +8,11 @@
|
||||
"key": "urlService",
|
||||
"implementation": "services/UrlService.js",
|
||||
"depends": [ "$location" ]
|
||||
},
|
||||
{
|
||||
"key": "popupService",
|
||||
"implementation": "services/PopupService.js",
|
||||
"depends": [ "$document", "$window" ]
|
||||
}
|
||||
],
|
||||
"runs": [
|
||||
@ -45,6 +50,16 @@
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "TimeRangeController",
|
||||
"implementation": "controllers/TimeRangeController.js",
|
||||
"depends": [ "$scope", "now" ]
|
||||
},
|
||||
{
|
||||
"key": "DateTimePickerController",
|
||||
"implementation": "controllers/DateTimePickerController.js",
|
||||
"depends": [ "$scope", "now" ]
|
||||
},
|
||||
{
|
||||
"key": "TreeNodeController",
|
||||
"implementation": "controllers/TreeNodeController.js",
|
||||
@ -105,11 +120,21 @@
|
||||
"implementation": "directives/MCTDrag.js",
|
||||
"depends": [ "$document" ]
|
||||
},
|
||||
{
|
||||
"key": "mctClickElsewhere",
|
||||
"implementation": "directives/MCTClickElsewhere.js",
|
||||
"depends": [ "$document" ]
|
||||
},
|
||||
{
|
||||
"key": "mctResize",
|
||||
"implementation": "directives/MCTResize.js",
|
||||
"depends": [ "$timeout" ]
|
||||
},
|
||||
{
|
||||
"key": "mctPopup",
|
||||
"implementation": "directives/MCTPopup.js",
|
||||
"depends": [ "$compile", "popupService" ]
|
||||
},
|
||||
{
|
||||
"key": "mctScrollX",
|
||||
"implementation": "directives/MCTScroll.js",
|
||||
@ -213,6 +238,10 @@
|
||||
{
|
||||
"key": "selector",
|
||||
"templateUrl": "templates/controls/selector.html"
|
||||
},
|
||||
{
|
||||
"key": "datetime-picker",
|
||||
"templateUrl": "templates/controls/datetime-picker.html"
|
||||
}
|
||||
],
|
||||
"licenses": [
|
||||
|
@ -1,14 +1,46 @@
|
||||
{
|
||||
"metadata": {
|
||||
"name": "WTD Symbols v24",
|
||||
"lastOpened": 1441992412958,
|
||||
"created": 1441992410384
|
||||
"name": "WTD-Symbols-v25",
|
||||
"lastOpened": 1445369623428,
|
||||
"created": 1445367449289
|
||||
},
|
||||
"iconSets": [
|
||||
{
|
||||
"selection": [
|
||||
{
|
||||
"order": 86,
|
||||
"order": 109,
|
||||
"id": 89,
|
||||
"prevSize": 32,
|
||||
"code": 58898,
|
||||
"name": "icon-save",
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 108,
|
||||
"id": 88,
|
||||
"prevSize": 32,
|
||||
"code": 58897,
|
||||
"name": "icon-dataset",
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 90,
|
||||
"id": 87,
|
||||
"prevSize": 32,
|
||||
"code": 58896,
|
||||
"name": "icon-bell",
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 91,
|
||||
"id": 86,
|
||||
"prevSize": 32,
|
||||
"code": 58889,
|
||||
"name": "icon-hourglass",
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 92,
|
||||
"id": 85,
|
||||
"prevSize": 32,
|
||||
"code": 58888,
|
||||
@ -18,119 +50,119 @@
|
||||
58890
|
||||
],
|
||||
"name": "icon-info-v15",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 82,
|
||||
"order": 93,
|
||||
"id": 84,
|
||||
"prevSize": 32,
|
||||
"code": 58887,
|
||||
"name": "icon-x-in-circle",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 77,
|
||||
"order": 94,
|
||||
"id": 83,
|
||||
"prevSize": 32,
|
||||
"code": 58881,
|
||||
"name": "icon-datatable",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 78,
|
||||
"order": 95,
|
||||
"id": 82,
|
||||
"prevSize": 32,
|
||||
"code": 58882,
|
||||
"name": "icon-tabular-scrolling",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 79,
|
||||
"order": 96,
|
||||
"id": 81,
|
||||
"prevSize": 32,
|
||||
"code": 58884,
|
||||
"name": "icon-tabular",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 80,
|
||||
"order": 97,
|
||||
"id": 80,
|
||||
"prevSize": 32,
|
||||
"code": 58885,
|
||||
"name": "icon-calendar",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 83,
|
||||
"order": 98,
|
||||
"id": 78,
|
||||
"prevSize": 32,
|
||||
"code": 58886,
|
||||
"name": "icon-paint-bucket",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 1,
|
||||
"order": 99,
|
||||
"id": 75,
|
||||
"prevSize": 32,
|
||||
"code": 123,
|
||||
"name": "icon-pointer-left",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 3,
|
||||
"order": 100,
|
||||
"id": 74,
|
||||
"prevSize": 32,
|
||||
"code": 125,
|
||||
"name": "icon-pointer-right",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 4,
|
||||
"order": 101,
|
||||
"id": 73,
|
||||
"prevSize": 32,
|
||||
"code": 80,
|
||||
"name": "icon-person",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 5,
|
||||
"order": 102,
|
||||
"id": 72,
|
||||
"prevSize": 32,
|
||||
"code": 232,
|
||||
"name": "icon-chain-links",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 6,
|
||||
"order": 103,
|
||||
"id": 71,
|
||||
"prevSize": 32,
|
||||
"code": 115,
|
||||
"name": "icon-database-in-brackets",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 7,
|
||||
"order": 104,
|
||||
"id": 70,
|
||||
"prevSize": 32,
|
||||
"code": 114,
|
||||
"name": "icon-refresh",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 8,
|
||||
"order": 105,
|
||||
"id": 69,
|
||||
"prevSize": 32,
|
||||
"code": 108,
|
||||
"name": "icon-lock",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 9,
|
||||
"order": 106,
|
||||
"id": 68,
|
||||
"prevSize": 32,
|
||||
"code": 51,
|
||||
"name": "icon-box-with-dashed-lines",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 10,
|
||||
@ -138,7 +170,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 58880,
|
||||
"name": "icon-box-with-arrow-cursor",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 11,
|
||||
@ -146,7 +178,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 65,
|
||||
"name": "icon-activity-mode",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 12,
|
||||
@ -154,15 +186,15 @@
|
||||
"prevSize": 32,
|
||||
"code": 97,
|
||||
"name": "icon-activity",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 13,
|
||||
"order": 87,
|
||||
"id": 64,
|
||||
"prevSize": 32,
|
||||
"code": 33,
|
||||
"name": "icon-alert-rect",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 14,
|
||||
@ -170,7 +202,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 58883,
|
||||
"name": "icon-alert-triangle",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 15,
|
||||
@ -178,7 +210,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 238,
|
||||
"name": "icon-arrow-double-down",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 16,
|
||||
@ -186,7 +218,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 235,
|
||||
"name": "icon-arrow-double-up",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 2,
|
||||
@ -194,7 +226,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 118,
|
||||
"name": "icon-arrow-down",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 19,
|
||||
@ -202,7 +234,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 60,
|
||||
"name": "icon-arrow-left",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 20,
|
||||
@ -210,7 +242,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 62,
|
||||
"name": "icon-arrow-right",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 21,
|
||||
@ -218,7 +250,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 236,
|
||||
"name": "icon-arrow-tall-down",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 22,
|
||||
@ -226,7 +258,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 237,
|
||||
"name": "icon-arrow-tall-up",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 23,
|
||||
@ -234,7 +266,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 94,
|
||||
"name": "icon-arrow-up",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 24,
|
||||
@ -242,7 +274,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 73,
|
||||
"name": "icon-arrows-out",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 25,
|
||||
@ -250,7 +282,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 58893,
|
||||
"name": "icon-arrows-right-left",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 33,
|
||||
@ -258,7 +290,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 53,
|
||||
"name": "icon-arrows-up-down",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 26,
|
||||
@ -266,7 +298,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 42,
|
||||
"name": "icon-asterisk",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 27,
|
||||
@ -274,7 +306,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 72,
|
||||
"name": "icon-autoflow-tabular",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 28,
|
||||
@ -282,7 +314,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 224,
|
||||
"name": "icon-box",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 29,
|
||||
@ -290,7 +322,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 50,
|
||||
"name": "icon-check",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 30,
|
||||
@ -298,7 +330,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 67,
|
||||
"name": "icon-clock",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 31,
|
||||
@ -306,7 +338,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 46,
|
||||
"name": "icon-connectivity",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 32,
|
||||
@ -314,7 +346,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 100,
|
||||
"name": "icon-database-query",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 17,
|
||||
@ -322,7 +354,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 68,
|
||||
"name": "icon-database",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 35,
|
||||
@ -330,7 +362,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 81,
|
||||
"name": "icon-dictionary",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 36,
|
||||
@ -338,7 +370,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 242,
|
||||
"name": "icon-duplicate",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 37,
|
||||
@ -346,7 +378,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 102,
|
||||
"name": "icon-folder-new",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 38,
|
||||
@ -354,7 +386,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 70,
|
||||
"name": "icon-folder",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 39,
|
||||
@ -362,7 +394,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 95,
|
||||
"name": "icon-fullscreen-collapse",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 40,
|
||||
@ -370,7 +402,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 122,
|
||||
"name": "icon-fullscreen-expand",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 41,
|
||||
@ -378,7 +410,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 71,
|
||||
"name": "icon-gear",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 49,
|
||||
@ -386,7 +418,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 227,
|
||||
"name": "icon-image",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 42,
|
||||
@ -394,7 +426,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 225,
|
||||
"name": "icon-layers",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 43,
|
||||
@ -402,7 +434,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 76,
|
||||
"name": "icon-layout",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 44,
|
||||
@ -410,7 +442,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 226,
|
||||
"name": "icon-line-horz",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 75,
|
||||
@ -418,7 +450,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 244,
|
||||
"name": "icon-link",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 46,
|
||||
@ -426,7 +458,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 88,
|
||||
"name": "icon-magnify-in",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 47,
|
||||
@ -434,7 +466,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 89,
|
||||
"name": "icon-magnify-out",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 48,
|
||||
@ -442,7 +474,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 77,
|
||||
"name": "icon-magnify",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 34,
|
||||
@ -450,7 +482,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 109,
|
||||
"name": "icon-menu",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 50,
|
||||
@ -458,7 +490,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 243,
|
||||
"name": "icon-move",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 51,
|
||||
@ -466,7 +498,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 121,
|
||||
"name": "icon-new-window",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 52,
|
||||
@ -474,7 +506,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 111,
|
||||
"name": "icon-object",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 73,
|
||||
@ -482,7 +514,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 63,
|
||||
"name": "icon-object-unknown",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 53,
|
||||
@ -490,7 +522,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 86,
|
||||
"name": "icon-packet",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 54,
|
||||
@ -498,7 +530,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 234,
|
||||
"name": "icon-page",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 55,
|
||||
@ -506,7 +538,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 241,
|
||||
"name": "icon-pause",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 56,
|
||||
@ -514,7 +546,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 112,
|
||||
"name": "icon-pencil",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 65,
|
||||
@ -522,7 +554,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 79,
|
||||
"name": "icon-people",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 57,
|
||||
@ -530,7 +562,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 239,
|
||||
"name": "icon-play",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 58,
|
||||
@ -538,7 +570,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 233,
|
||||
"name": "icon-plot-resource",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 59,
|
||||
@ -546,7 +578,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 43,
|
||||
"name": "icon-plus",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 60,
|
||||
@ -554,7 +586,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 45,
|
||||
"name": "icon-minus",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 61,
|
||||
@ -562,7 +594,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 54,
|
||||
"name": "icon-sine",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 62,
|
||||
@ -570,7 +602,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 228,
|
||||
"name": "icon-T",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 63,
|
||||
@ -578,7 +610,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 116,
|
||||
"name": "icon-telemetry-panel",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 64,
|
||||
@ -586,7 +618,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 84,
|
||||
"name": "icon-telemetry",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 18,
|
||||
@ -594,7 +626,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 246,
|
||||
"name": "icon-thumbs-strip",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 67,
|
||||
@ -602,7 +634,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 83,
|
||||
"name": "icon-timeline",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 68,
|
||||
@ -610,7 +642,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 245,
|
||||
"name": "icon-timer",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 69,
|
||||
@ -618,7 +650,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 90,
|
||||
"name": "icon-trash",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 70,
|
||||
@ -626,7 +658,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 229,
|
||||
"name": "icon-two-parts-both",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 71,
|
||||
@ -634,7 +666,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 231,
|
||||
"name": "icon-two-parts-one-only",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 72,
|
||||
@ -642,7 +674,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 120,
|
||||
"name": "icon-x-heavy",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 66,
|
||||
@ -650,7 +682,7 @@
|
||||
"prevSize": 32,
|
||||
"code": 58946,
|
||||
"name": "icon-x",
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
}
|
||||
],
|
||||
"id": 2,
|
||||
@ -665,6 +697,100 @@
|
||||
"height": 1024,
|
||||
"prevSize": 32,
|
||||
"icons": [
|
||||
{
|
||||
"id": 89,
|
||||
"paths": [
|
||||
"M192.2 576c-0.2 0-0.2 0 0 0l-0.2 448h640v-447.8c0 0 0 0-0.2-0.2h-639.6z",
|
||||
"M978.8 210.8l-165.4-165.4c-25-25-74.2-45.4-109.4-45.4h-576c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128v-448c0-35.2 28.8-64 64-64h640c35.2 0 64 28.8 64 64v448c70.4 0 128-57.6 128-128v-576c0-35.2-20.4-84.4-45.2-109.2zM704 256c0 35.2-28.8 64-64 64h-448c-35.2 0-64-28.8-64-64v-192h320v192h128v-192h128v192z"
|
||||
],
|
||||
"attrs": [
|
||||
{
|
||||
"fill": "rgb(0, 0, 0)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 0, 0)"
|
||||
}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"grid": 0,
|
||||
"tags": [
|
||||
"icon-save-v2"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"125525525516161751": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 88,
|
||||
"paths": [
|
||||
"M896 192h-320c-16.4-16.4-96.8-96.8-109.2-109.2l-37.4-37.4c-25-25-74.2-45.4-109.4-45.4h-256c-35.2 0-64 28.8-64 64v384c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v-128c0-70.4-57.6-128-128-128z",
|
||||
"M896 448h-768c-70.4 0-128 57.6-128 128v320c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-320c0-70.4-57.6-128-128-128zM320 896h-128v-320h128v320zM576 896h-128v-320h128v320zM832 896h-128v-320h128v320z"
|
||||
],
|
||||
"attrs": [],
|
||||
"isMulticolor": false,
|
||||
"grid": 0,
|
||||
"tags": [
|
||||
"icon-dataset"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"125525525516161751": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 87,
|
||||
"paths": [
|
||||
"M512 1024c106 0 192-86 192-192h-384c0 106 86 192 192 192z",
|
||||
"M896 448v-64c0-212-172-384-384-384s-384 172-384 384v64c0 70.6-57.4 128-128 128v128h1024v-128c-70.6 0-128-57.4-128-128z"
|
||||
],
|
||||
"attrs": [
|
||||
{
|
||||
"fill": "rgb(6, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(6, 161, 75)"
|
||||
}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"grid": 0,
|
||||
"tags": [
|
||||
"icon-bell"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"125525525516161751": [
|
||||
1,
|
||||
1
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 86,
|
||||
"paths": [
|
||||
"M1024 0h-1024c0 282.8 229.2 512 512 512s512-229.2 512-512zM512 384c-102.6 0-199-40-271.6-112.4-41.2-41.2-72-90.2-90.8-143.6h724.6c-18.8 53.4-49.6 102.4-90.8 143.6-72.4 72.4-168.8 112.4-271.4 112.4z",
|
||||
"M512 512c-282.8 0-512 229.2-512 512h1024c0-282.8-229.2-512-512-512z"
|
||||
],
|
||||
"attrs": [
|
||||
{
|
||||
"fill": "rgb(6, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(6, 161, 75)"
|
||||
}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"grid": 0,
|
||||
"tags": [
|
||||
"icon-hourglass"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"125525525516161751": [
|
||||
1,
|
||||
1
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 85,
|
||||
"paths": [
|
||||
@ -698,7 +824,8 @@
|
||||
"icon-x-in-circle"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"16161751": []
|
||||
"16161751": [],
|
||||
"125525525516161751": []
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -899,6 +1026,11 @@
|
||||
1,
|
||||
1,
|
||||
1
|
||||
],
|
||||
"125525525516161751": [
|
||||
1,
|
||||
1,
|
||||
1
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -1051,18 +1183,28 @@
|
||||
{
|
||||
"id": 67,
|
||||
"paths": [
|
||||
"M832 512.4c0-0.2 0-0.2 0-0.4v-320c0-105.6-86.4-192-192-192h-448c-105.6 0-192 86.4-192 192v320c0 105.6 86.4 192 192 192h263.6l-197.2-445.6 573.6 254z",
|
||||
"M766.8 659.8l193.8-20.4-576.6-255.4 255.4 576.6 20.4-193.8 257 257.2 107.2-107.2z"
|
||||
"M894-2h-768c-70.4 0-128 57.6-128 128v768c0 70.4 57.6 128 128 128h400c-2.2-3.8-4-7.6-5.8-11.4l-255.2-576.8c-21.4-48.4-10.8-105 26.6-142.4 24.4-24.4 57.2-37.4 90.4-37.4 17.4 0 35.2 3.6 51.8 11l576.6 255.4c4 1.8 7.8 3.8 11.4 5.8v-400.2c0.2-70.4-57.4-128-127.8-128z",
|
||||
"M958.6 637.4l-576.6-255.4 255.4 576.6 64.6-128.6 192 192 128-128-192-192z"
|
||||
],
|
||||
"attrs": [
|
||||
{},
|
||||
{}
|
||||
{
|
||||
"fill": "rgb(0, 0, 0)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 0, 0)"
|
||||
}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"grid": 0,
|
||||
"tags": [
|
||||
"icon-box-with-arrow-cursor"
|
||||
]
|
||||
],
|
||||
"colorPermutations": {
|
||||
"125525525516161751": [
|
||||
0,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 66,
|
||||
@ -1338,6 +1480,9 @@
|
||||
"colorPermutations": {
|
||||
"16161751": [
|
||||
0
|
||||
],
|
||||
"125525525516161751": [
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -14853,6 +14998,5 @@
|
||||
"gridSize": 16,
|
||||
"showLiga": false
|
||||
},
|
||||
"uid": -1,
|
||||
"time": 1441993324496
|
||||
"uid": -1
|
||||
}
|
Binary file not shown.
@ -76,7 +76,7 @@
|
||||
<glyph unicode="ô" glyph-name="icon-link" d="M1024 448l-512 512v-307.2l-512-204.8v-256h512v-256z" />
|
||||
<glyph unicode="õ" glyph-name="icon-timer" d="M638 898c0 35.4-28.6 64-64 64h-128c-35.4 0-64-28.6-64-64s28.6-64 64-64h128c35.4 0 64 28.6 64 64zM510 834c-247.4 0-448-200.6-448-448s200.6-448 448-448 448 200.6 448 448-200.6 448-448 448zM510 386h-336c0 185.2 150.8 336 336 336v-336z" />
|
||||
<glyph unicode="ö" glyph-name="icon-thumbs-strip" d="M448 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM448 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320z" />
|
||||
<glyph unicode="" glyph-name="icon-box-with-arrow-cursor" d="M832 447.6c0 0.2 0 0.2 0 0.4v320c0 105.6-86.4 192-192 192h-448c-105.6 0-192-86.4-192-192v-320c0-105.6 86.4-192 192-192h263.6l-197.2 445.6 573.6-254zM766.8 300.2l193.8 20.4-576.6 255.4 255.4-576.6 20.4 193.8 257-257.2 107.2 107.2z" />
|
||||
<glyph unicode="" glyph-name="icon-box-with-arrow-cursor" d="M894 962h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h400c-2.2 3.8-4 7.6-5.8 11.4l-255.2 576.8c-21.4 48.4-10.8 105 26.6 142.4 24.4 24.4 57.2 37.4 90.4 37.4 17.4 0 35.2-3.6 51.8-11l576.6-255.4c4-1.8 7.8-3.8 11.4-5.8v400.2c0.2 70.4-57.4 128-127.8 128zM958.6 322.6l-576.6 255.4 255.4-576.6 64.6 128.6 192-192 128 128-192 192z" />
|
||||
<glyph unicode="" glyph-name="icon-datatable" d="M1024 768c0-106.039-229.23-192-512-192s-512 85.961-512 192c0 106.039 229.23 192 512 192s512-85.961 512-192zM512 448c-282.8 0-512 86-512 192v-512c0-106 229.2-192 512-192s512 86 512 192v512c0-106-229.2-192-512-192zM896 385v-256c-36.6-15.6-79.8-28.8-128-39.4v256c48.2 10.6 91.4 23.8 128 39.4zM256 345.6v-256c-48.2 10.4-91.4 23.8-128 39.4v256c36.6-15.6 79.8-28.8 128-39.4zM384 70v256c41-4 83.8-6 128-6s87 2.2 128 6v-256c-41-4-83.8-6-128-6s-87 2.2-128 6z" />
|
||||
<glyph unicode="" glyph-name="icon-tabular-scrolling" d="M64 960c-35.2 0-64-28.8-64-64v-192h448v256h-384zM1024 704v192c0 35.2-28.8 64-64 64h-384v-256h448zM0 576v-192c0-35.2 28.8-64 64-64h384v256h-448zM960 320c35.2 0 64 28.8 64 64v192h-448v-256h384zM512-64l-256 256h512z" />
|
||||
<glyph unicode="" glyph-name="icon-alert-triangle" d="M998.208 111.136l-422.702 739.728c-34.928 61.124-92.084 61.124-127.012 0l-422.702-739.728c-34.928-61.126-5.906-111.136 64.494-111.136h843.428c70.4 0 99.422 50.010 64.494 111.136zM512 128c-35.2 0-64 28.8-64 64s28.8 64 64 64 64-28.8 64-64c0-35.2-28.8-64-64-64zM627.448 577.242l-38.898-194.486c-6.902-34.516-41.35-62.756-76.55-62.756s-69.648 28.24-76.552 62.758l-38.898 194.486c-6.902 34.516 16.25 62.756 51.45 62.756h128c35.2 0 58.352-28.24 51.448-62.758z" />
|
||||
@ -85,6 +85,10 @@
|
||||
<glyph unicode="" glyph-name="icon-paint-bucket" d="M544 736v-224c0-88.4-71.6-160-160-160s-160 71.6-160 160v97.2l-197.4-196.4c-50-50-12.4-215.2 112.4-340s290-162.4 340-112.4l417 423.6-352 352zM896-64c70.6 0 128 57.4 128 128 0 108.6-128 192-128 192s-128-83.4-128-192c0-70.6 57.4-128 128-128zM384 448c-35.4 0-64 28.6-64 64v384c0 35.4 28.6 64 64 64s64-28.6 64-64v-384c0-35.4-28.6-64-64-64z" />
|
||||
<glyph unicode="" glyph-name="icon-x-in-circle" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM832 256l-128-128-192 192-192-192-128 128 192 192-192 192 128 128 192-192 192 192 128-128-192-192 192-192z" />
|
||||
<glyph unicode="" glyph-name="icon-info-v15" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM512 832c70.6 0 128-57.4 128-128s-57.4-128-128-128c-70.6 0-128 57.4-128 128s57.4 128 128 128zM704 128h-384v128h64v256h256v-256h64v-128z" />
|
||||
<glyph unicode="" glyph-name="icon-hourglass" d="M1024 960h-1024c0-282.8 229.2-512 512-512s512 229.2 512 512zM512 576c-102.6 0-199 40-271.6 112.4-41.2 41.2-72 90.2-90.8 143.6h724.6c-18.8-53.4-49.6-102.4-90.8-143.6-72.4-72.4-168.8-112.4-271.4-112.4zM512 448c-282.8 0-512-229.2-512-512h1024c0 282.8-229.2 512-512 512z" />
|
||||
<glyph unicode="" glyph-name="icon-arrows-right-left" d="M1024 448l-448-512v1024zM448 960l-448-512 448-512z" />
|
||||
<glyph unicode="" glyph-name="icon-bell" d="M512-64c106 0 192 86 192 192h-384c0-106 86-192 192-192zM896 512v64c0 212-172 384-384 384s-384-172-384-384v-64c0-70.6-57.4-128-128-128v-128h1024v128c-70.6 0-128 57.4-128 128z" />
|
||||
<glyph unicode="" glyph-name="icon-dataset" d="M896 768h-320c-16.4 16.4-96.8 96.8-109.2 109.2l-37.4 37.4c-25 25-74.2 45.4-109.4 45.4h-256c-35.2 0-64-28.8-64-64v-384c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v128c0 70.4-57.6 128-128 128zM896 512h-768c-70.4 0-128-57.6-128-128v-320c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v320c0 70.4-57.6 128-128 128zM320 64h-128v320h128v-320zM576 64h-128v320h128v-320zM832 64h-128v320h128v-320z" />
|
||||
<glyph unicode="" glyph-name="icon-save" d="M192.2 384c-0.2 0-0.2 0 0 0l-0.2-448h640v447.8c0 0 0 0-0.2 0.2h-639.6zM978.8 749.2l-165.4 165.4c-25 25-74.2 45.4-109.4 45.4h-576c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128v448c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64v-448c70.4 0 128 57.6 128 128v576c0 35.2-20.4 84.4-45.2 109.2zM704 704c0-35.2-28.8-64-64-64h-448c-35.2 0-64 28.8-64 64v192h320v-192h128v192h128v-192z" />
|
||||
<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>
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
Binary file not shown.
Binary file not shown.
@ -45,6 +45,7 @@ $ueEditToolBarH: 25px;
|
||||
$ueBrowseLeftPaneW: 25%;
|
||||
$ueEditLeftPaneW: 75%;
|
||||
$treeSearchInputBarH: 25px;
|
||||
$ueTimeControlH: (33px, 20px, 20px);
|
||||
// Overlay
|
||||
$ovrTopBarH: 60px;
|
||||
$ovrFooterH: 30px;
|
||||
|
@ -66,6 +66,19 @@ a.disabled {
|
||||
@include animation-timing-function(ease-in-out);
|
||||
}
|
||||
|
||||
@mixin pulseBorder($c: red, $dur: 500ms, $iteration: infinite, $delay: 0s) {
|
||||
@include keyframes(pulseBorder) {
|
||||
0% { border-color: transparent; }
|
||||
100% { border-color: $c; }
|
||||
}
|
||||
@include animation-name(pulseBorder);
|
||||
@include animation-duration($dur);
|
||||
@include animation-direction(alternate);
|
||||
@include animation-iteration-count($iteration);
|
||||
@include animation-timing-function(ease);
|
||||
@include animation-delay($delay);
|
||||
}
|
||||
|
||||
.pulse {
|
||||
@include pulse(750ms);
|
||||
}
|
@ -40,11 +40,11 @@
|
||||
|
||||
/************************** HTML ENTITIES */
|
||||
a {
|
||||
color: #ccc;
|
||||
color: $colorA;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
color: #fff;
|
||||
color: $colorAHov;
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,6 +125,18 @@ mct-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
@include ellipsize();
|
||||
}
|
||||
|
||||
.scrolling {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.vscroll {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -29,6 +29,9 @@
|
||||
}
|
||||
|
||||
.ui-symbol {
|
||||
&.type-icon {
|
||||
color: $colorObjHdrIc;
|
||||
}
|
||||
&.icon {
|
||||
color: $colorKey;
|
||||
&.alert {
|
||||
@ -41,6 +44,9 @@
|
||||
font-size: 1.65em;
|
||||
}
|
||||
}
|
||||
&.icon-calendar:after {
|
||||
content: "\e605";
|
||||
}
|
||||
}
|
||||
|
||||
.bar .ui-symbol {
|
||||
@ -52,7 +58,7 @@
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.s-menu .invoke-menu,
|
||||
.s-menu-btn .invoke-menu,
|
||||
.icon.major .invoke-menu {
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
|
@ -61,6 +61,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
@mixin trans-prop-nice-resize($t: 0.5s, $tf: ease-in-out) {
|
||||
@include transition-property(height, width, top, right, bottom, left, opacity);
|
||||
@include transition-duration($t);
|
||||
@include transition-timing-function($tf);
|
||||
}
|
||||
|
||||
@mixin trans-prop-nice-resize-h($t: 0.5s) {
|
||||
@include transition-property(height, bottom, top);
|
||||
@include transition-duration($t);
|
||||
@ -347,9 +353,10 @@
|
||||
/* This doesn't work on an element inside an element with absolute positioning that has height: auto */
|
||||
//position: relative;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
@include webkitProp(transform, translateY(-50%));
|
||||
//-webkit-transform: translateY(-50%);
|
||||
//-ms-transform: translateY(-50%);
|
||||
//transform: translateY(-50%);
|
||||
}
|
||||
|
||||
@mixin verticalCenterBlock($holderH, $itemH) {
|
||||
@ -374,22 +381,8 @@
|
||||
overflow-y: $showBar;
|
||||
}
|
||||
|
||||
@mixin wait-spinner($b: 5px, $c: $colorAlt1) {
|
||||
display: block;
|
||||
position: absolute;
|
||||
-webkit-animation: rotation .6s infinite linear;
|
||||
-moz-animation: rotation .6s infinite linear;
|
||||
-o-animation: rotation .6s infinite linear;
|
||||
animation: rotation .6s infinite linear;
|
||||
border-color: rgba($c, 0.25);
|
||||
border-top-color: rgba($c, 1.0);
|
||||
border-style: solid;
|
||||
border-width: $b;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
@mixin test($c: #ffcc00, $a: 0.2) {
|
||||
background-color: rgba($c, $a);
|
||||
background-color: rgba($c, $a) !important;
|
||||
}
|
||||
|
||||
@mixin tmpBorder($c: #ffcc00, $a: 0.75) {
|
||||
|
@ -10,9 +10,6 @@
|
||||
&.fixed {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
&.scrolling {
|
||||
overflow: auto;
|
||||
}
|
||||
.controls,
|
||||
label,
|
||||
.inline-block {
|
||||
|
@ -31,6 +31,7 @@ $pad: $interiorMargin * $baseRatio;
|
||||
line-height: $btnStdH;
|
||||
padding: 0 $pad;
|
||||
font-size: 0.7rem;
|
||||
vertical-align: top;
|
||||
|
||||
.icon {
|
||||
font-size: 0.8rem;
|
||||
@ -66,6 +67,18 @@ $pad: $interiorMargin * $baseRatio;
|
||||
&.pause-play {
|
||||
|
||||
}
|
||||
&.t-save:before {
|
||||
content:'\e612';
|
||||
font-family: symbolsfont;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
&.t-cancel {
|
||||
.title-label { display: none; }
|
||||
&:before {
|
||||
content:'\78';
|
||||
font-family: symbolsfont;
|
||||
}
|
||||
}
|
||||
|
||||
&.pause-play {
|
||||
.icon:before {
|
||||
|
@ -115,7 +115,7 @@
|
||||
@include box-sizing(border-box);
|
||||
border-left: 1px solid $colorInteriorBorder;
|
||||
display: inline-block;
|
||||
padding: 0 $interiorMargin;
|
||||
padding: 0 0 0 $interiorMargin;
|
||||
position: relative;
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
@ -205,7 +205,7 @@ label.checkbox.custom {
|
||||
}
|
||||
}
|
||||
|
||||
.s-menu label.checkbox.custom {
|
||||
.s-menu-btn label.checkbox.custom {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@ -295,49 +295,155 @@ label.checkbox.custom {
|
||||
|
||||
.slider {
|
||||
$knobH: 100%; //14px;
|
||||
$knobW: 12px;
|
||||
$slotH: 50%;
|
||||
.slot {
|
||||
// @include border-radius($basicCr * .75);
|
||||
@include sliderTrack();
|
||||
height: $slotH;
|
||||
//@include sliderTrack();
|
||||
width: auto;
|
||||
position: absolute;
|
||||
top: ($knobH - $slotH) / 2;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: auto;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
.knob {
|
||||
@include btnSubtle();
|
||||
@include controlGrippy(rgba(black, 0.3), vertical, 1px, solid);
|
||||
cursor: ew-resize;
|
||||
//@include btnSubtle();
|
||||
//@include controlGrippy(rgba(black, 0.3), vertical, 1px, solid);
|
||||
@include trans-prop-nice-fade(.25s);
|
||||
background-color: $sliderColorKnob;
|
||||
&:hover {
|
||||
background-color: $sliderColorKnobHov;
|
||||
}
|
||||
position: absolute;
|
||||
height: $knobH;
|
||||
width: $knobW;
|
||||
width: $sliderKnobW;
|
||||
top: 0;
|
||||
auto: 0;
|
||||
bottom: auto;
|
||||
left: auto;
|
||||
&:before {
|
||||
top: 1px;
|
||||
bottom: 3px;
|
||||
left: ($knobW / 2) - 1;
|
||||
}
|
||||
|
||||
}
|
||||
.knob-l {
|
||||
@include border-left-radius($sliderKnobW);
|
||||
cursor: w-resize;
|
||||
}
|
||||
.knob-r {
|
||||
@include border-right-radius($sliderKnobW);
|
||||
cursor: e-resize;
|
||||
}
|
||||
.range {
|
||||
background: rgba($colorKey, 0.6);
|
||||
@include trans-prop-nice-fade(.25s);
|
||||
background-color: $sliderColorRange;
|
||||
cursor: ew-resize;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
top: 0; //$tbOffset;
|
||||
right: auto;
|
||||
bottom: 0;
|
||||
left: auto;
|
||||
height: auto;
|
||||
width: auto;
|
||||
&:hover {
|
||||
background: rgba($colorKey, 0.7);
|
||||
background-color: $sliderColorRangeHov;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** DATETIME PICKER */
|
||||
.l-datetime-picker {
|
||||
$r1H: 15px;
|
||||
@include user-select(none);
|
||||
font-size: 0.8rem;
|
||||
padding: $interiorMarginLg !important;
|
||||
width: 230px;
|
||||
.l-month-year-pager {
|
||||
$pagerW: 20px;
|
||||
//@include test();
|
||||
//font-size: 0.8rem;
|
||||
height: $r1H;
|
||||
margin-bottom: $interiorMargin;
|
||||
position: relative;
|
||||
.pager,
|
||||
.val {
|
||||
//@include test(red);
|
||||
@extend .abs;
|
||||
}
|
||||
.pager {
|
||||
width: $pagerW;
|
||||
@extend .ui-symbol;
|
||||
&.prev {
|
||||
right: auto;
|
||||
&:before {
|
||||
content: "\3c";
|
||||
}
|
||||
}
|
||||
&.next {
|
||||
left: auto;
|
||||
text-align: right;
|
||||
&:before {
|
||||
content: "\3e";
|
||||
}
|
||||
}
|
||||
}
|
||||
.val {
|
||||
text-align: center;
|
||||
left: $pagerW + $interiorMargin;
|
||||
right: $pagerW + $interiorMargin;
|
||||
}
|
||||
}
|
||||
.l-calendar,
|
||||
.l-time-selects {
|
||||
border-top: 1px solid $colorInteriorBorder
|
||||
}
|
||||
.l-time-selects {
|
||||
line-height: $formInputH;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** CALENDAR */
|
||||
.l-calendar {
|
||||
$colorMuted: pushBack($colorMenuFg, 30%);
|
||||
ul.l-cal-row {
|
||||
@include display-flex;
|
||||
@include flex-flow(row nowrap);
|
||||
margin-top: 1px;
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
li {
|
||||
@include flex(1 0);
|
||||
//@include test();
|
||||
margin-left: 1px;
|
||||
padding: $interiorMargin;
|
||||
text-align: center;
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
&.l-header li {
|
||||
color: $colorMuted;
|
||||
}
|
||||
&.l-body li {
|
||||
@include trans-prop-nice(background-color, .25s);
|
||||
cursor: pointer;
|
||||
&.in-month {
|
||||
background-color: $colorCalCellInMonthBg;
|
||||
}
|
||||
.sub {
|
||||
color: $colorMuted;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
&.selected {
|
||||
background: $colorCalCellSelectedBg;
|
||||
color: $colorCalCellSelectedFg;
|
||||
.sub {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background-color: $colorCalCellHovBg;
|
||||
color: $colorCalCellHovFg;
|
||||
.sub {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/******************************************************** MENU BUTTONS */
|
||||
.s-menu {
|
||||
.s-menu-btn {
|
||||
// Formerly .btn-menu
|
||||
@extend .s-btn;
|
||||
span.l-click-area {
|
||||
@ -62,186 +62,192 @@
|
||||
|
||||
/******************************************************** MENUS THEMSELVES */
|
||||
.menu-element {
|
||||
$bg: $colorMenuBg;
|
||||
$fg: $colorMenuFg;
|
||||
$ic: $colorMenuIc;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
.menu {
|
||||
@include border-radius($basicCr);
|
||||
@include containerSubtle($bg, $fg);
|
||||
@include boxShdw($shdwMenu);
|
||||
@include txtShdw($shdwMenuText);
|
||||
display: block; // set to block via jQuery
|
||||
padding: $interiorMarginSm 0;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
ul {
|
||||
@include menuUlReset();
|
||||
li {
|
||||
@include box-sizing(border-box);
|
||||
border-top: 1px solid lighten($bg, 20%);
|
||||
color: pullForward($bg, 60%);
|
||||
line-height: $menuLineH;
|
||||
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
&:first-child {
|
||||
border: none;
|
||||
}
|
||||
&:hover {
|
||||
background: $colorMenuHovBg;
|
||||
color: $colorMenuHovFg;
|
||||
.icon {
|
||||
color: $colorMenuHovIc;
|
||||
}
|
||||
}
|
||||
.type-icon {
|
||||
left: $interiorMargin * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu,
|
||||
.context-menu,
|
||||
.super-menu {
|
||||
pointer-events: auto;
|
||||
ul li {
|
||||
//padding-left: 25px;
|
||||
a {
|
||||
color: $fg;
|
||||
}
|
||||
.icon {
|
||||
color: $ic;
|
||||
}
|
||||
.type-icon {
|
||||
left: $interiorMargin;
|
||||
}
|
||||
&:hover .icon {
|
||||
//color: lighten($ic, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
.s-menu {
|
||||
@include border-radius($basicCr);
|
||||
@include containerSubtle($colorMenuBg, $colorMenuFg);
|
||||
@include boxShdw($shdwMenu);
|
||||
@include txtShdw($shdwMenuText);
|
||||
padding: $interiorMarginSm 0;
|
||||
}
|
||||
|
||||
.checkbox-menu {
|
||||
// Used in search dropdown in tree
|
||||
@extend .context-menu;
|
||||
ul li {
|
||||
padding-left: 50px;
|
||||
.checkbox {
|
||||
$d: 0.7rem;
|
||||
position: absolute;
|
||||
left: $interiorMargin;
|
||||
top: ($menuLineH - $d) / 1.5;
|
||||
em {
|
||||
height: $d;
|
||||
width: $d;
|
||||
&:before {
|
||||
font-size: 7px !important;// $d/2;
|
||||
height: $d;
|
||||
width: $d;
|
||||
line-height: $d;
|
||||
}
|
||||
}
|
||||
}
|
||||
.type-icon {
|
||||
left: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.super-menu {
|
||||
$w: 500px;
|
||||
$h: $w - 20;
|
||||
$plw: 50%;
|
||||
$prw: 50%;
|
||||
display: block;
|
||||
width: $w;
|
||||
height: $h;
|
||||
.contents {
|
||||
@include absPosDefault($interiorMargin);
|
||||
}
|
||||
.pane {
|
||||
.menu {
|
||||
@extend .s-menu;
|
||||
display: block;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
ul {
|
||||
@include menuUlReset();
|
||||
li {
|
||||
@include box-sizing(border-box);
|
||||
&.left {
|
||||
//@include test();
|
||||
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
||||
left: 0;
|
||||
padding-right: $interiorMargin;
|
||||
right: auto;
|
||||
width: $plw;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
ul {
|
||||
li {
|
||||
@include border-radius($controlCr);
|
||||
padding-left: 30px;
|
||||
border-top: none;
|
||||
}
|
||||
border-top: 1px solid lighten($colorMenuBg, 20%);
|
||||
color: pullForward($colorMenuBg, 60%);
|
||||
line-height: $menuLineH;
|
||||
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
&:first-child {
|
||||
border: none;
|
||||
}
|
||||
&:hover {
|
||||
background: $colorMenuHovBg;
|
||||
color: $colorMenuHovFg;
|
||||
.icon {
|
||||
color: $colorMenuHovIc;
|
||||
}
|
||||
}
|
||||
&.right {
|
||||
//@include test(red);
|
||||
left: auto;
|
||||
right: 0;
|
||||
padding: $interiorMargin * 5;
|
||||
width: $prw;
|
||||
.type-icon {
|
||||
left: $interiorMargin * 2;
|
||||
}
|
||||
}
|
||||
.menu-item-description {
|
||||
.desc-area {
|
||||
&.icon {
|
||||
$h: 150px;
|
||||
color: $colorCreateMenuLgIcon;
|
||||
position: relative;
|
||||
font-size: 8em;
|
||||
left: 0;
|
||||
height: $h;
|
||||
line-height: $h;
|
||||
margin-bottom: $interiorMargin * 5;
|
||||
text-align: center;
|
||||
}
|
||||
&.title {
|
||||
color: $colorCreateMenuText;
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
&.description {
|
||||
//color: lighten($bg, 30%);
|
||||
color: $colorCreateMenuText;
|
||||
font-size: 0.8em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.context-menu {
|
||||
font-size: 0.80rem;
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu-holder {
|
||||
pointer-events: none;
|
||||
.menu,
|
||||
.context-menu,
|
||||
.super-menu {
|
||||
pointer-events: auto;
|
||||
ul li {
|
||||
//padding-left: 25px;
|
||||
a {
|
||||
color: $colorMenuFg;
|
||||
}
|
||||
.icon {
|
||||
color: $colorMenuIc;
|
||||
}
|
||||
.type-icon {
|
||||
left: $interiorMargin;
|
||||
}
|
||||
&:hover .icon {
|
||||
//color: lighten($colorMenuIc, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.checkbox-menu {
|
||||
// Used in search dropdown in tree
|
||||
@extend .context-menu;
|
||||
ul li {
|
||||
padding-left: 50px;
|
||||
.checkbox {
|
||||
$d: 0.7rem;
|
||||
position: absolute;
|
||||
left: $interiorMargin;
|
||||
top: ($menuLineH - $d) / 1.5;
|
||||
em {
|
||||
height: $d;
|
||||
width: $d;
|
||||
&:before {
|
||||
font-size: 7px !important;// $d/2;
|
||||
height: $d;
|
||||
width: $d;
|
||||
line-height: $d;
|
||||
}
|
||||
}
|
||||
}
|
||||
.type-icon {
|
||||
left: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.super-menu {
|
||||
$w: 500px;
|
||||
$h: $w - 20;
|
||||
$plw: 50%;
|
||||
$prw: 50%;
|
||||
display: block;
|
||||
width: $w;
|
||||
height: $h;
|
||||
.contents {
|
||||
@include absPosDefault($interiorMargin);
|
||||
}
|
||||
.pane {
|
||||
@include box-sizing(border-box);
|
||||
&.left {
|
||||
//@include test();
|
||||
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
||||
left: 0;
|
||||
padding-right: $interiorMargin;
|
||||
right: auto;
|
||||
width: $plw;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
ul {
|
||||
li {
|
||||
@include border-radius($controlCr);
|
||||
padding-left: 30px;
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.right {
|
||||
//@include test(red);
|
||||
left: auto;
|
||||
right: 0;
|
||||
padding: $interiorMargin * 5;
|
||||
width: $prw;
|
||||
}
|
||||
}
|
||||
.menu-item-description {
|
||||
.desc-area {
|
||||
&.icon {
|
||||
$h: 150px;
|
||||
color: $colorCreateMenuLgIcon;
|
||||
position: relative;
|
||||
font-size: 8em;
|
||||
left: 0;
|
||||
height: $h;
|
||||
line-height: $h;
|
||||
margin-bottom: $interiorMargin * 5;
|
||||
text-align: center;
|
||||
}
|
||||
&.title {
|
||||
color: $colorCreateMenuText;
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
&.description {
|
||||
//color: lighten($colorMenuBg, 30%);
|
||||
color: $colorCreateMenuText;
|
||||
font-size: 0.8em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.context-menu {
|
||||
font-size: 0.80rem;
|
||||
}
|
||||
|
||||
.context-menu-holder,
|
||||
.menu-holder {
|
||||
position: absolute;
|
||||
height: 200px;
|
||||
width: 170px;
|
||||
z-index: 70;
|
||||
.context-menu-wrapper {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.context-menu {
|
||||
}
|
||||
}
|
||||
&.go-left .context-menu {
|
||||
&.go-left .context-menu,
|
||||
&.go-left .menu {
|
||||
right: 0;
|
||||
}
|
||||
&.go-up .context-menu {
|
||||
&.go-up .context-menu,
|
||||
&.go-up .menu {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.context-menu-holder {
|
||||
pointer-events: none;
|
||||
height: 200px;
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
.btn-bar.right .menu,
|
||||
.menus-to-left .menu {
|
||||
left: auto;
|
||||
|
@ -1,72 +1,155 @@
|
||||
.l-time-controller {
|
||||
$inputTxtW: 90px;
|
||||
$knobW: 9px;
|
||||
$r1H: 20px;
|
||||
$r2H: 30px;
|
||||
$r3H: 10px;
|
||||
@mixin toiLineHovEffects() {
|
||||
//@include pulse(.25s);
|
||||
&:before,
|
||||
&:after {
|
||||
background-color: $timeControllerToiLineColorHov;
|
||||
}
|
||||
}
|
||||
|
||||
position: relative;
|
||||
margin: $interiorMarginLg 0;
|
||||
min-width: 400px;
|
||||
.l-time-controller-visible {
|
||||
|
||||
}
|
||||
|
||||
mct-include.l-time-controller {
|
||||
$minW: 500px;
|
||||
$knobHOffset: 0px;
|
||||
$knobM: ($sliderKnobW + $knobHOffset) * -1;
|
||||
$rangeValPad: $interiorMargin;
|
||||
$rangeValOffset: $sliderKnobW;
|
||||
//$knobCr: $sliderKnobW;
|
||||
$timeRangeSliderLROffset: 130px + $sliderKnobW + $rangeValOffset;
|
||||
$r1H: nth($ueTimeControlH,1);
|
||||
$r2H: nth($ueTimeControlH,2);
|
||||
$r3H: nth($ueTimeControlH,3);
|
||||
|
||||
@include absPosDefault();
|
||||
//@include test();
|
||||
display: block;
|
||||
top: auto;
|
||||
height: $r1H + $r2H + $r3H + ($interiorMargin * 2);
|
||||
min-width: $minW;
|
||||
font-size: 0.8rem;
|
||||
|
||||
.l-time-range-inputs-holder,
|
||||
.l-time-range-slider {
|
||||
font-size: 0.8em;
|
||||
//font-size: 0.8em;
|
||||
}
|
||||
|
||||
.l-time-range-inputs-holder,
|
||||
.l-time-range-slider-holder,
|
||||
.l-time-range-ticks-holder
|
||||
{
|
||||
margin-bottom: $interiorMargin;
|
||||
position: relative;
|
||||
//@include test();
|
||||
@include absPosDefault(0, visible);
|
||||
@include box-sizing(border-box);
|
||||
top: auto;
|
||||
}
|
||||
.l-time-range-slider,
|
||||
.l-time-range-ticks {
|
||||
//@include test(red, 0.1);
|
||||
@include absPosDefault(0, visible);
|
||||
left: $timeRangeSliderLROffset; right: $timeRangeSliderLROffset;
|
||||
}
|
||||
|
||||
.l-time-range-inputs-holder {
|
||||
height: $r1H;
|
||||
}
|
||||
|
||||
.l-time-range-slider,
|
||||
.l-time-range-ticks {
|
||||
left: $inputTxtW; right: $inputTxtW;
|
||||
|
||||
//@include test(red);
|
||||
height: $r1H; bottom: $r2H + $r3H + ($interiorMarginSm * 2);
|
||||
padding-top: $interiorMargin;
|
||||
border-top: 1px solid $colorInteriorBorder;
|
||||
.type-icon {
|
||||
font-size: 120%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.l-time-range-input,
|
||||
.l-time-range-inputs-elem {
|
||||
margin-right: $interiorMargin;
|
||||
.lbl {
|
||||
color: $colorPlotLabelFg;
|
||||
}
|
||||
.ui-symbol.icon {
|
||||
font-size: 11px;
|
||||
width: 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.l-time-range-slider-holder {
|
||||
height: $r2H;
|
||||
//@include test(green);
|
||||
height: $r2H; bottom: $r3H + ($interiorMarginSm * 1);
|
||||
.range-holder {
|
||||
@include box-shadow(none);
|
||||
background: none;
|
||||
border: none;
|
||||
height: 75%;
|
||||
.range {
|
||||
.toi-line {
|
||||
$myC: $timeControllerToiLineColor;
|
||||
$myW: 8px;
|
||||
@include transform(translateX(50%));
|
||||
position: absolute;
|
||||
//@include test();
|
||||
top: 0; right: 0; bottom: 0px; left: auto;
|
||||
width: $myW;
|
||||
height: auto;
|
||||
z-index: 2;
|
||||
&:before,
|
||||
&:after {
|
||||
background-color: $myC;
|
||||
content: "";
|
||||
position: absolute;
|
||||
}
|
||||
&:before {
|
||||
// Vert line
|
||||
top: 0; right: auto; bottom: -10px; left: floor($myW/2) - 1;
|
||||
width: 2px;
|
||||
//top: 0; right: 3px; bottom: 0; left: 3px;
|
||||
}
|
||||
&:after {
|
||||
// Circle element
|
||||
@include border-radius($myW);
|
||||
@include transform(translateY(-50%));
|
||||
top: 50%; right: 0; bottom: auto; left: 0;
|
||||
width: auto;
|
||||
height: $myW;
|
||||
}
|
||||
}
|
||||
&:hover .toi-line {
|
||||
@include toiLineHovEffects;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:not(:active) {
|
||||
//@include test(#ff00cc);
|
||||
.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 $colorInteriorBorder;
|
||||
border-top: 1px solid $colorTick;
|
||||
.tick {
|
||||
background-color: $colorInteriorBorder;
|
||||
background-color: $colorTick;
|
||||
border:none;
|
||||
height: 5px;
|
||||
width: 1px;
|
||||
margin-left: -1px;
|
||||
position: absolute;
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
.l-time-range-tick-label {
|
||||
color: lighten($colorInteriorBorder, 20%);
|
||||
font-size: 0.7em;
|
||||
@include webkitProp(transform, translateX(-50%));
|
||||
color: $colorPlotLabelFg;
|
||||
display: inline-block;
|
||||
font-size: 0.9em;
|
||||
position: absolute;
|
||||
margin-left: -0.5 * $tickLblW;
|
||||
text-align: center;
|
||||
top: $r3H;
|
||||
width: $tickLblW;
|
||||
top: 8px;
|
||||
white-space: nowrap;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
@ -74,31 +157,47 @@
|
||||
}
|
||||
|
||||
.knob {
|
||||
width: $knobW;
|
||||
z-index: 2;
|
||||
.range-value {
|
||||
$w: 75px;
|
||||
//@include test();
|
||||
//@include test($sliderColorRange);
|
||||
@include trans-prop-nice-fade(.25s);
|
||||
padding: 0 $rangeValOffset;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -7px; // Label is 13px high
|
||||
height: $r2H;
|
||||
line-height: $r2H;
|
||||
white-space: nowrap;
|
||||
width: $w;
|
||||
}
|
||||
&:hover .range-value {
|
||||
color: $colorKey;
|
||||
color: $sliderColorKnobHov;
|
||||
}
|
||||
&.knob-l {
|
||||
margin-left: $knobW / -2;
|
||||
//@include border-bottom-left-radius($knobCr); // MOVED TO _CONTROLS.SCSS
|
||||
margin-left: $knobM;
|
||||
.range-value {
|
||||
text-align: right;
|
||||
right: $knobW + $interiorMargin;
|
||||
right: $rangeValOffset;
|
||||
}
|
||||
}
|
||||
&.knob-r {
|
||||
margin-right: $knobW / -2;
|
||||
//@include border-bottom-right-radius($knobCr);
|
||||
margin-right: $knobM;
|
||||
.range-value {
|
||||
left: $knobW + $interiorMargin;
|
||||
left: $rangeValOffset;
|
||||
}
|
||||
&:hover + .range-holder .range .toi-line {
|
||||
@include toiLineHovEffects;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//.slot.range-holder {
|
||||
// background-color: $sliderColorRangeHolder;
|
||||
//}
|
||||
|
||||
.s-time-range-val {
|
||||
//@include test();
|
||||
@include border-radius($controlCr);
|
||||
background-color: $colorInputBg;
|
||||
padding: 1px 1px 0 $interiorMargin;
|
||||
}
|
@ -19,39 +19,44 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
@mixin complexFieldHolder($myW) {
|
||||
width: $myW + $interiorMargin;
|
||||
input[type="text"] {
|
||||
width: $myW;
|
||||
}
|
||||
}
|
||||
|
||||
.complex.datetime {
|
||||
span {
|
||||
display: inline-block;
|
||||
margin-right: $interiorMargin;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
.field-hints,
|
||||
.fields {
|
||||
}
|
||||
|
||||
|
||||
.field-hints {
|
||||
|
||||
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
.fields {
|
||||
margin-top: $interiorMarginSm 0;
|
||||
padding: $interiorMarginSm 0;
|
||||
}
|
||||
|
||||
.date {
|
||||
$myW: 80px;
|
||||
width: $myW + $interiorMargin;
|
||||
input {
|
||||
width: $myW;
|
||||
}
|
||||
|
||||
@include complexFieldHolder(80px);
|
||||
}
|
||||
|
||||
.time.md {
|
||||
@include complexFieldHolder(60px);
|
||||
}
|
||||
|
||||
.time.sm {
|
||||
$myW: 40px;
|
||||
width: $myW + $interiorMargin;
|
||||
input {
|
||||
width: $myW;
|
||||
}
|
||||
@include complexFieldHolder(40px);
|
||||
}
|
||||
}
|
@ -21,10 +21,13 @@
|
||||
*****************************************************************************/
|
||||
.select {
|
||||
@include btnSubtle($colorSelectBg);
|
||||
margin: 0 0 2px 2px; // Needed to avoid dropshadow from being clipped by parent containers
|
||||
@if $shdwBtns != none {
|
||||
margin: 0 0 2px 0; // Needed to avoid dropshadow from being clipped by parent containers
|
||||
}
|
||||
padding: 0 $interiorMargin;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
line-height: $formInputH;
|
||||
select {
|
||||
@include appearance(none);
|
||||
@include box-sizing(border-box);
|
||||
@ -40,11 +43,8 @@
|
||||
}
|
||||
&:after {
|
||||
@include contextArrow();
|
||||
pointer-events: none;
|
||||
color: rgba($colorSelectFg, percentToDecimal($contrastInvokeMenuPercent));
|
||||
//content:"v";
|
||||
//display: block;
|
||||
//font-family: 'symbolsfont';
|
||||
//pointer-events: none;
|
||||
position: absolute;
|
||||
right: $interiorMargin; top: 0;
|
||||
}
|
||||
|
@ -19,24 +19,45 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
@-webkit-keyframes rotation {
|
||||
from {-webkit-transform: rotate(0deg);}
|
||||
to {-webkit-transform: rotate(359deg);}
|
||||
@include keyframes(rotation) {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(359deg); }
|
||||
}
|
||||
|
||||
@-moz-keyframes rotation {
|
||||
from {-moz-transform: rotate(0deg);}
|
||||
to {-moz-transform: rotate(359deg);}
|
||||
@mixin wait-spinner2($b: 5px, $c: $colorAlt1) {
|
||||
@include keyframes(rotateCentered) {
|
||||
0% { transform: translateX(-50%) translateY(-50%) rotate(0deg); }
|
||||
100% { transform: translateX(-50%) translateY(-50%) rotate(359deg); }
|
||||
}
|
||||
@include animation-name(rotateCentered);
|
||||
@include animation-duration(0.5s);
|
||||
@include animation-iteration-count(infinite);
|
||||
@include animation-timing-function(linear);
|
||||
border-color: rgba($c, 0.25);
|
||||
border-top-color: rgba($c, 1.0);
|
||||
border-style: solid;
|
||||
border-width: 5px;
|
||||
@include border-radius(100%);
|
||||
@include box-sizing(border-box);
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 0; width: 0;
|
||||
padding: 7%;
|
||||
left: 50%; top: 50%;
|
||||
}
|
||||
|
||||
@-o-keyframes rotation {
|
||||
from {-o-transform: rotate(0deg);}
|
||||
to {-o-transform: rotate(359deg);}
|
||||
}
|
||||
|
||||
@keyframes rotation {
|
||||
from {transform: rotate(0deg);}
|
||||
to {transform: rotate(359deg);}
|
||||
@mixin wait-spinner($b: 5px, $c: $colorAlt1) {
|
||||
display: block;
|
||||
position: absolute;
|
||||
-webkit-animation: rotation .6s infinite linear;
|
||||
-moz-animation: rotation .6s infinite linear;
|
||||
-o-animation: rotation .6s infinite linear;
|
||||
animation: rotation .6s infinite linear;
|
||||
border-color: rgba($c, 0.25);
|
||||
border-top-color: rgba($c, 1.0);
|
||||
border-style: solid;
|
||||
border-width: $b;
|
||||
@include border-radius(100%);
|
||||
}
|
||||
|
||||
.t-wait-spinner,
|
||||
@ -96,4 +117,28 @@
|
||||
margin-top: 0 !important;
|
||||
padding: 0 !important;
|
||||
top: 0; left: 0;
|
||||
}
|
||||
|
||||
.loading {
|
||||
// Can be applied to any block element with height and width
|
||||
pointer-events: none;
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
}
|
||||
&:before {
|
||||
@include wait-spinner2(5px, $colorLoadingFg);
|
||||
z-index: 10;
|
||||
}
|
||||
&:after {
|
||||
@include absPosDefault();
|
||||
background: $colorLoadingBg;
|
||||
display: block;
|
||||
z-index: 9;
|
||||
}
|
||||
&.tree-item:before {
|
||||
padding: $menuLineH / 4;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
}
|
@ -40,6 +40,11 @@ table {
|
||||
thead, .thead {
|
||||
border-bottom: 1px solid $colorTabHeaderBorder;
|
||||
}
|
||||
|
||||
&:not(.fixed-header) tr th {
|
||||
background-color: $colorTabHeaderBg;
|
||||
}
|
||||
|
||||
tbody, .tbody {
|
||||
display: table-row-group;
|
||||
tr, .tr {
|
||||
@ -64,7 +69,6 @@ table {
|
||||
display: table-cell;
|
||||
}
|
||||
th, .th {
|
||||
background-color: $colorTabHeaderBg;
|
||||
border-left: 1px solid $colorTabHeaderBorder;
|
||||
color: $colorTabHeaderFg;
|
||||
padding: $tabularTdPadLR $tabularTdPadLR;
|
||||
@ -143,7 +147,7 @@ table {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: $tabularHeaderH;
|
||||
background: rgba(#fff, 0.15);
|
||||
background-color: $colorTabHeaderBg;
|
||||
}
|
||||
}
|
||||
tbody, .tbody {
|
||||
|
@ -89,7 +89,7 @@ $plotDisplayArea: ($legendH + $interiorMargin, 0, $xBarH + $interiorMargin, $yBa
|
||||
.gl-plot-label,
|
||||
.l-plot-label {
|
||||
// @include test(yellow);
|
||||
color: lighten($colorBodyFg, 20%);
|
||||
color: $colorPlotLabelFg;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
// text-transform: uppercase;
|
||||
|
@ -214,8 +214,6 @@
|
||||
|
||||
.search-scroll {
|
||||
order: 3;
|
||||
|
||||
//padding-right: $rightPadding;
|
||||
margin-top: 4px;
|
||||
|
||||
// Adjustable scrolling size
|
||||
@ -227,28 +225,6 @@
|
||||
|
||||
.load-icon {
|
||||
position: relative;
|
||||
&.loading {
|
||||
pointer-events: none;
|
||||
margin-left: $leftMargin;
|
||||
|
||||
.title-label {
|
||||
// Text styling
|
||||
font-style: italic;
|
||||
font-size: .9em;
|
||||
opacity: 0.5;
|
||||
|
||||
// Text positioning
|
||||
margin-left: $iconWidth + $leftMargin;
|
||||
line-height: 24px;
|
||||
}
|
||||
.wait-spinner {
|
||||
margin-left: $leftMargin;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.loading) {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.load-more-button {
|
||||
|
@ -83,7 +83,6 @@ ul.tree {
|
||||
.icon {
|
||||
&.l-icon-link,
|
||||
&.l-icon-alert {
|
||||
//@include txtShdw($shdwItemTreeIcon);
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
}
|
||||
@ -105,26 +104,12 @@ ul.tree {
|
||||
@include absPosDefault();
|
||||
display: block;
|
||||
left: $runningItemW + ($interiorMargin * 3);
|
||||
//right: $treeContextTriggerW + $interiorMargin;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&.loading {
|
||||
pointer-events: none;
|
||||
.label {
|
||||
opacity: 0.5;
|
||||
.title-label {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
.wait-spinner {
|
||||
margin-left: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background: $colorItemTreeSelectedBg;
|
||||
color: $colorItemTreeSelectedFg;
|
||||
@ -142,9 +127,6 @@ ul.tree {
|
||||
&:hover {
|
||||
background: rgba($colorBodyFg, 0.1); //lighten($colorBodyBg, 5%);
|
||||
color: pullForward($colorBodyFg, 20%);
|
||||
//.context-trigger {
|
||||
// display: block;
|
||||
//}
|
||||
.icon {
|
||||
color: $colorItemTreeIconHover;
|
||||
}
|
||||
@ -158,7 +140,6 @@ ul.tree {
|
||||
|
||||
.context-trigger {
|
||||
$h: 0.9rem;
|
||||
//display: none;
|
||||
top: -1px;
|
||||
position: absolute;
|
||||
right: $interiorMarginSm;
|
||||
|
@ -47,7 +47,7 @@
|
||||
}
|
||||
&.frame-template {
|
||||
.s-btn,
|
||||
.s-menu {
|
||||
.s-menu-btn {
|
||||
height: $ohH;
|
||||
line-height: $ohH;
|
||||
padding: 0 $interiorMargin;
|
||||
@ -56,7 +56,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.s-menu:after {
|
||||
.s-menu-btn:after {
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
|
@ -30,21 +30,21 @@
|
||||
}
|
||||
|
||||
.holder-all {
|
||||
$myM: 0; // $interiorMarginSm;
|
||||
top: $myM;
|
||||
right: $myM;
|
||||
bottom: $myM;
|
||||
left: $myM;
|
||||
$myM: 0; // $interiorMarginSm;
|
||||
top: $myM;
|
||||
right: $myM;
|
||||
bottom: $myM;
|
||||
left: $myM;
|
||||
}
|
||||
|
||||
.browse-area,
|
||||
.edit-area,
|
||||
.editor {
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.editor {
|
||||
@include border-radius($basicCr * 1.5);
|
||||
@include border-radius($basicCr * 1.5);
|
||||
}
|
||||
|
||||
.contents {
|
||||
@ -68,8 +68,8 @@
|
||||
margin-right: $interiorMargin;
|
||||
}
|
||||
&.abs {
|
||||
text-wrap: none;
|
||||
white-space: nowrap;
|
||||
text-wrap: none;
|
||||
white-space: nowrap;
|
||||
&.left,
|
||||
.left {
|
||||
width: 45%;
|
||||
@ -95,51 +95,52 @@
|
||||
}
|
||||
|
||||
.user-environ {
|
||||
.browse-area,
|
||||
.edit-area,
|
||||
.editor {
|
||||
top: $bodyMargin + $ueTopBarH + ($interiorMargin);
|
||||
right: $bodyMargin;
|
||||
bottom: $ueFooterH + $bodyMargin;
|
||||
left: $bodyMargin;
|
||||
}
|
||||
.browse-area,
|
||||
.edit-area,
|
||||
.editor {
|
||||
top: $bodyMargin + $ueTopBarH + ($interiorMargin);
|
||||
right: $bodyMargin;
|
||||
bottom: $ueFooterH + $bodyMargin;
|
||||
left: $bodyMargin;
|
||||
}
|
||||
|
||||
.browse-area,
|
||||
.edit-area {
|
||||
> .contents {
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
.browse-area,
|
||||
.edit-area {
|
||||
> .contents {
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.edit-area {
|
||||
$tbH: $btnToolbarH + $interiorMargin;
|
||||
top: $bodyMargin + $ueTopBarEditH + ($interiorMargin);
|
||||
.tool-bar {
|
||||
bottom: auto;
|
||||
height: $tbH;
|
||||
line-height: $btnToolbarH;
|
||||
}
|
||||
.work-area {
|
||||
top: $tbH + $interiorMargin * 2;
|
||||
}
|
||||
}
|
||||
.edit-area {
|
||||
$tbH: $btnToolbarH + $interiorMargin;
|
||||
top: $bodyMargin + $ueTopBarEditH + ($interiorMargin);
|
||||
.tool-bar {
|
||||
border-bottom: 1px solid $colorInteriorBorder;
|
||||
bottom: auto;
|
||||
height: $tbH;
|
||||
line-height: $btnToolbarH;
|
||||
}
|
||||
.work-area {
|
||||
top: $tbH + $interiorMargin * 2;
|
||||
}
|
||||
}
|
||||
|
||||
.ue-bottom-bar {
|
||||
.ue-bottom-bar {
|
||||
//@include absPosDefault($bodyMargin);
|
||||
@include absPosDefault(0);// New status bar design
|
||||
top: auto;
|
||||
height: $ueFooterH;
|
||||
.status-holder {
|
||||
//right: $ueAppLogoW + $bodyMargin; New status bar design
|
||||
z-index: 1;
|
||||
}
|
||||
.app-logo {
|
||||
left: auto;
|
||||
width: $ueAppLogoW;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
@include absPosDefault(0); // New status bar design
|
||||
top: auto;
|
||||
height: $ueFooterH;
|
||||
.status-holder {
|
||||
//right: $ueAppLogoW + $bodyMargin; New status bar design
|
||||
z-index: 1;
|
||||
}
|
||||
.app-logo {
|
||||
left: auto;
|
||||
width: $ueAppLogoW;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cols {
|
||||
@ -225,89 +226,127 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.pane {
|
||||
position: absolute;
|
||||
&.treeview.left {
|
||||
.create-btn-holder {
|
||||
bottom: auto; top: 0;
|
||||
height: $ueTopBarH;
|
||||
.wrapper.menu-element {
|
||||
position: absolute;
|
||||
bottom: $interiorMargin;
|
||||
}
|
||||
}
|
||||
.search-holder {
|
||||
top: $ueTopBarH + $interiorMarginLg;
|
||||
}
|
||||
.tree-holder {
|
||||
overflow: auto;
|
||||
top: $ueTopBarH + $interiorMarginLg + $treeSearchInputBarH + $interiorMargin;
|
||||
}
|
||||
.create-btn-holder {
|
||||
bottom: auto;
|
||||
top: 0;
|
||||
height: $ueTopBarH;
|
||||
.wrapper.menu-element {
|
||||
position: absolute;
|
||||
bottom: $interiorMargin;
|
||||
}
|
||||
}
|
||||
.search-holder {
|
||||
top: $ueTopBarH + $interiorMarginLg;
|
||||
}
|
||||
.tree-holder {
|
||||
overflow: auto;
|
||||
top: $ueTopBarH + $interiorMarginLg + $treeSearchInputBarH + $interiorMargin;
|
||||
}
|
||||
}
|
||||
&.items {
|
||||
.object-browse-bar {
|
||||
.left.abs,
|
||||
.right.abs {
|
||||
top: auto;
|
||||
}
|
||||
//.left.abs {
|
||||
// @include tmpBorder(green);
|
||||
//}
|
||||
//.right.abs {
|
||||
// @include tmpBorder(red);
|
||||
//}
|
||||
}
|
||||
.object-holder {
|
||||
top: $ueTopBarH + $interiorMarginLg;
|
||||
.left.abs,
|
||||
.right.abs {
|
||||
top: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
.object-holder {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.split-layout {
|
||||
&.horizontal {
|
||||
// Slides up and down
|
||||
>.pane {
|
||||
// @include test();
|
||||
margin-top: $interiorMargin;
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.vertical {
|
||||
// Slides left and right
|
||||
>.pane {
|
||||
// @include test();
|
||||
margin-left: $interiorMargin;
|
||||
>.holder {
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
.holder {
|
||||
right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.horizontal {
|
||||
// Slides up and down
|
||||
> .pane {
|
||||
// @include test();
|
||||
margin-top: $interiorMargin;
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.vertical {
|
||||
// Slides left and right
|
||||
> .pane {
|
||||
// @include test();
|
||||
margin-left: $interiorMargin;
|
||||
> .holder {
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
.holder {
|
||||
right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.object-holder {
|
||||
@include trans-prop-nice-resize(0.25s);
|
||||
overflow: hidden; // Contained objects need to handle their own overflow now
|
||||
> ng-include {
|
||||
@include absPosDefault(0, auto);
|
||||
}
|
||||
&.l-controls-visible {
|
||||
&.l-time-controller-visible {
|
||||
bottom: nth($ueTimeControlH,1) + nth($ueTimeControlH,2) +nth($ueTimeControlH,3) + ($interiorMargin * 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.l-object-wrapper {
|
||||
@extend .abs;
|
||||
top: $ueTopBarH + $interiorMarginLg;
|
||||
&.active {
|
||||
@include pulseBorder($colorKey, 150ms, 8, 0.5s);
|
||||
@include border-radius($controlCr);
|
||||
border-color: $colorKey;
|
||||
border-width: 2px;
|
||||
border-style: dotted;
|
||||
.l-object-wrapper-inner {
|
||||
$m: 3px;
|
||||
top: $m;
|
||||
right: $m;
|
||||
bottom: $m;
|
||||
left: $m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.l-object-wrapper-inner {
|
||||
@extend .abs;
|
||||
@include display-flex(column nowrap);
|
||||
@include trans-prop-nice-resize(0.25s);
|
||||
}
|
||||
|
||||
.l-edit-controls {
|
||||
@include trans-prop-nice-resize(0.25s);
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
&.active {
|
||||
border-bottom: 1px solid $colorInteriorBorder;
|
||||
height: $ueEditToolBarH + $interiorMargin;
|
||||
line-height: $ueEditToolBarH;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.object-browse-bar .s-btn,
|
||||
.top-bar .buttons-main .s-btn,
|
||||
.top-bar .s-menu,
|
||||
.top-bar .s-menu-btn,
|
||||
.tool-bar .s-btn,
|
||||
.tool-bar .s-menu {
|
||||
$h: $btnToolbarH;
|
||||
height: $h;
|
||||
line-height: $h;
|
||||
vertical-align: top;
|
||||
.tool-bar .s-menu-btn {
|
||||
$h: $btnToolbarH;
|
||||
height: $h;
|
||||
line-height: $h;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.object-browse-bar,
|
||||
@ -318,33 +357,59 @@
|
||||
}
|
||||
|
||||
.object-browse-bar {
|
||||
//@include test(blue);
|
||||
@include absPosDefault(0, visible);
|
||||
@include box-sizing(border-box);
|
||||
height: $ueTopBarH;
|
||||
line-height: $ueTopBarH;
|
||||
white-space: nowrap;
|
||||
//@include test(blue);
|
||||
@include absPosDefault(0, visible);
|
||||
@include box-sizing(border-box);
|
||||
height: $ueTopBarH;
|
||||
line-height: $ueTopBarH;
|
||||
white-space: nowrap;
|
||||
|
||||
.left {
|
||||
padding-right: $interiorMarginLg * 2;
|
||||
.l-back {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
margin-right: $interiorMarginLg;
|
||||
}
|
||||
}
|
||||
.left {
|
||||
padding-right: $interiorMarginLg * 2;
|
||||
.l-back {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
margin-right: $interiorMarginLg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.l-flex {
|
||||
@include webkitVal('display', 'flex');
|
||||
@include webkitProp('flex-flow', 'row nowrap');
|
||||
.left {
|
||||
//@include test(red);
|
||||
@include webkitProp(flex, '1 1 0');
|
||||
padding-right: $interiorMarginLg;
|
||||
}
|
||||
}
|
||||
|
||||
.vscroll {
|
||||
overflow-y: auto;
|
||||
}
|
||||
@include display-flex;
|
||||
&.flex-row {
|
||||
@include flex-flow(row nowrap);
|
||||
//@include align-items(center);
|
||||
.flex-elem {
|
||||
white-space: nowrap;
|
||||
margin-right: $interiorMargin;
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
&.flex-align-end {
|
||||
@include justify-content(flex-end);
|
||||
}
|
||||
}
|
||||
}
|
||||
&.flex-col {
|
||||
@include flex-flow(column nowrap);
|
||||
//@include align-items(stretch);
|
||||
.flex-elem.active {
|
||||
margin-bottom: $interiorMarginLg;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.flex-elem {
|
||||
//@include test(purple);
|
||||
@include flex(0 1 auto);
|
||||
position: relative;
|
||||
&.grow {
|
||||
@include flex(1 1 auto);
|
||||
}
|
||||
}
|
||||
.left {
|
||||
@include flex(1 1 0);
|
||||
padding-right: $interiorMarginLg;
|
||||
}
|
||||
}
|
@ -20,7 +20,6 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
.tool-bar {
|
||||
border-bottom: 1px solid $colorInteriorBorder;
|
||||
.l-control-group {
|
||||
height: $btnToolbarH;
|
||||
}
|
||||
|
@ -0,0 +1,66 @@
|
||||
<!--
|
||||
Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
|
||||
Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Open MCT Web includes source code licensed under additional open source
|
||||
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
|
||||
<div ng-controller="DateTimePickerController" class="l-datetime-picker s-datetime-picker s-menu">
|
||||
<div class="holder">
|
||||
<div class="l-month-year-pager">
|
||||
<a class="pager prev" ng-click="changeMonth(-1)"></a>
|
||||
<span class="val">{{month}} {{year}}</span>
|
||||
<a class="pager next" ng-click="changeMonth(1)"></a>
|
||||
</div>
|
||||
<div class="l-calendar">
|
||||
<ul class="l-cal-row l-header">
|
||||
<li ng-repeat="day in ['Su','Mo','Tu','We','Th','Fr','Sa']">{{day}}</li>
|
||||
</ul>
|
||||
<ul class="l-cal-row l-body" ng-repeat="row in table">
|
||||
<li ng-repeat="cell in row"
|
||||
ng-click="select(cell)"
|
||||
ng-class='{ "in-month": isInCurrentMonth(cell), selected: isSelected(cell) }'>
|
||||
<div class="prime">{{cell.day}}</div>
|
||||
<div class="sub">{{cell.dayOfYear}}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="l-time-selects complex datetime"
|
||||
ng-show="options">
|
||||
<div class="field-hints">
|
||||
<span class="hint time md"
|
||||
ng-repeat="key in ['hours', 'minutes', 'seconds']"
|
||||
ng-if="options[key]">
|
||||
{{nameFor(key)}}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="field control time md"
|
||||
ng-repeat="key in ['hours', 'minutes', 'seconds']"
|
||||
ng-if="options[key]">
|
||||
<div class='form-control select'>
|
||||
<select size="1"
|
||||
ng-model="time[key]"
|
||||
ng-options="i for i in optionsFor(key)">
|
||||
</select>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -21,7 +21,7 @@
|
||||
-->
|
||||
<span ng-controller="ViewSwitcherController">
|
||||
<div
|
||||
class="view-switcher menu-element s-menu"
|
||||
class="view-switcher menu-element s-menu-btn"
|
||||
ng-if="view.length > 1"
|
||||
ng-controller="ClickAwayController as toggle"
|
||||
>
|
||||
|
@ -1,55 +1,88 @@
|
||||
<!--
|
||||
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.
|
||||
|
||||
NOTES
|
||||
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.
|
||||
|
||||
Ticks:
|
||||
The thinking is to divide whatever the current time span is by 5,
|
||||
and assign values accordingly to 5 statically-positioned ticks. So the tick x-position is a static percentage
|
||||
of the total width available, and the labels change dynamically. This is consistent
|
||||
with our current approach to the time axis of plots.
|
||||
I'm keeping the number of ticks low so that when the view portal gets narrow,
|
||||
the tick labels won't collide with each other. For extra credit, add/remove ticks as the user resizes the view area.
|
||||
Note: this eval needs to be based on the whatever is containing the
|
||||
time-controller component, not the whole browser window.
|
||||
|
||||
Range indicator and slider knobs:
|
||||
The left and right properties used in .slider .range-holder and the .knobs are
|
||||
CSS offsets from the left and right of their respective containers. You
|
||||
may want or need to calculate those positions as pure offsets from the start datetime
|
||||
(or left, as it were) and set them as left properties. No problem if so, but
|
||||
we'll need to tweak the CSS tiny bit to get the center of the knobs to line up
|
||||
properly on the range left and right bounds.
|
||||
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 ng-init="
|
||||
notes = 'Temporarily using an array to populate ticks so I can see what I\'m doing';
|
||||
ticks = [
|
||||
'00:00',
|
||||
'00:30',
|
||||
'01:00',
|
||||
'01:30',
|
||||
'02:00'
|
||||
];
|
||||
"></div>
|
||||
|
||||
<div class="l-time-controller">
|
||||
<!-- MINE -->
|
||||
<div ng-controller="TimeRangeController">
|
||||
<div class="l-time-range-inputs-holder">
|
||||
Start: <input type="date" />
|
||||
End: <input type="date" />
|
||||
<span class="l-time-range-inputs-elem ui-symbol type-icon">C</span>
|
||||
<span class="l-time-range-input" ng-controller="ToggleController as t1">
|
||||
<!--<span class="lbl">Start</span>-->
|
||||
<span class="s-btn time-range-start" ng-click="t1.toggle()">
|
||||
<span class="val">{{startOuterText}}</span>
|
||||
<a class="ui-symbol icon icon-calendar"></a>
|
||||
<mct-popup ng-if="t1.isActive()">
|
||||
<div mct-click-elsewhere="t1.setState(false)">
|
||||
<mct-control key="'datetime-picker'"
|
||||
ng-model="ngModel.outer"
|
||||
field="'start'"
|
||||
options="{ hours: true }">
|
||||
</mct-control>
|
||||
</div>
|
||||
</mct-popup>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="l-time-range-inputs-elem lbl">to</span>
|
||||
|
||||
<span class="l-time-range-input" ng-controller="ToggleController as t2">
|
||||
<!--<span class="lbl">End</span>-->
|
||||
<span class="s-btn l-time-range-input" ng-click="t2.toggle()">
|
||||
<span class="val">{{endOuterText}}</span>
|
||||
<a class="ui-symbol icon icon-calendar"></a>
|
||||
<mct-popup ng-if="t2.isActive()">
|
||||
<div mct-click-elsewhere="t2.setState(false)">
|
||||
<mct-control key="'datetime-picker'"
|
||||
ng-model="ngModel.outer"
|
||||
field="'end'"
|
||||
options="{ hours: true }">
|
||||
</mct-control>
|
||||
</div>
|
||||
</mct-popup>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="l-time-range-slider-holder">
|
||||
<div class="l-time-range-slider">
|
||||
<div class="slider">
|
||||
<div class="slider"
|
||||
mct-resize="spanWidth = bounds.width">
|
||||
<div class="knob knob-l"
|
||||
mct-drag-down="startLeftDrag()"
|
||||
mct-drag="leftDrag(delta[0])"
|
||||
ng-style="{ left: startInnerPct }">
|
||||
<div class="range-value">{{startInnerText}}</div>
|
||||
</div>
|
||||
<div class="knob knob-r"
|
||||
mct-drag-down="startRightDrag()"
|
||||
mct-drag="rightDrag(delta[0])"
|
||||
ng-style="{ right: endInnerPct }">
|
||||
<div class="range-value">{{endInnerText}}</div>
|
||||
</div>
|
||||
<div class="slot range-holder">
|
||||
<div class="range" style="left: 0%; right: 30%;"></div>
|
||||
</div>
|
||||
<div class="knob knob-l" style="left: 0%;">
|
||||
<div class="range-value">05/22 14:46</div>
|
||||
</div>
|
||||
<div class="knob knob-r" style="right: 30%;">
|
||||
<div class="range-value">07/22 01:21</div>
|
||||
<div class="range"
|
||||
mct-drag-down="startMiddleDrag()"
|
||||
mct-drag="middleDrag(delta[0])"
|
||||
ng-style="{ left: startInnerPct, right: endInnerPct}">
|
||||
<div class="toi-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -59,7 +92,7 @@ ticks = [
|
||||
<div class="l-time-range-ticks">
|
||||
<div
|
||||
ng-repeat="tick in ticks"
|
||||
ng-style="{ left: $index * 25 + '%' }"
|
||||
ng-style="{ left: $index * (100 / (ticks.length - 1)) + '%' }"
|
||||
class="tick tick-x"
|
||||
>
|
||||
<span class="l-time-range-tick-label">{{tick}}</span>
|
||||
|
@ -0,0 +1,202 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
define(
|
||||
[ 'moment' ],
|
||||
function (moment) {
|
||||
'use strict';
|
||||
|
||||
var TIME_NAMES = {
|
||||
'hours': "Hour",
|
||||
'minutes': "Minute",
|
||||
'seconds': "Second"
|
||||
},
|
||||
MONTHS = moment.months(),
|
||||
TIME_OPTIONS = (function makeRanges() {
|
||||
var arr = [];
|
||||
while (arr.length < 60) {
|
||||
arr.push(arr.length);
|
||||
}
|
||||
return {
|
||||
hours: arr.slice(0, 24),
|
||||
minutes: arr,
|
||||
seconds: arr
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* Controller to support the date-time picker.
|
||||
*
|
||||
* Adds/uses the following properties in scope:
|
||||
* * `year`: Year being displayed in picker
|
||||
* * `month`: Month being displayed
|
||||
* * `table`: Table being displayed; array of arrays of
|
||||
* * `day`: Day of month
|
||||
* * `dayOfYear`: Day of year
|
||||
* * `month`: Month associated with the day
|
||||
* * `year`: Year associated with the day.
|
||||
* * `date`: Date chosen
|
||||
* * `year`: Year selected
|
||||
* * `month`: Month selected (0-indexed)
|
||||
* * `day`: Day of month selected
|
||||
* * `time`: Chosen time (hours/minutes/seconds)
|
||||
* * `hours`: Hours chosen
|
||||
* * `minutes`: Minutes chosen
|
||||
* * `seconds`: Seconds chosen
|
||||
*
|
||||
* Months are zero-indexed, day-of-months are one-indexed.
|
||||
*/
|
||||
function DateTimePickerController($scope, now) {
|
||||
var year,
|
||||
month, // For picker state, not model state
|
||||
interacted = false;
|
||||
|
||||
function generateTable() {
|
||||
var m = moment.utc({ year: year, month: month }).day(0),
|
||||
table = [],
|
||||
row,
|
||||
col;
|
||||
|
||||
for (row = 0; row < 6; row += 1) {
|
||||
table.push([]);
|
||||
for (col = 0; col < 7; col += 1) {
|
||||
table[row].push({
|
||||
year: m.year(),
|
||||
month: m.month(),
|
||||
day: m.date(),
|
||||
dayOfYear: m.dayOfYear()
|
||||
});
|
||||
m.add(1, 'days'); // Next day!
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
function updateScopeForMonth() {
|
||||
$scope.month = MONTHS[month];
|
||||
$scope.year = year;
|
||||
$scope.table = generateTable();
|
||||
}
|
||||
|
||||
function updateFromModel(ngModel) {
|
||||
var m;
|
||||
|
||||
m = moment.utc(ngModel);
|
||||
|
||||
$scope.date = {
|
||||
year: m.year(),
|
||||
month: m.month(),
|
||||
day: m.date()
|
||||
};
|
||||
$scope.time = {
|
||||
hours: m.hour(),
|
||||
minutes: m.minute(),
|
||||
seconds: m.second()
|
||||
};
|
||||
|
||||
//window.alert($scope.date.day + " " + ngModel);
|
||||
|
||||
// Zoom to that date in the picker, but
|
||||
// only if the user hasn't interacted with it yet.
|
||||
if (!interacted) {
|
||||
year = m.year();
|
||||
month = m.month();
|
||||
updateScopeForMonth();
|
||||
}
|
||||
}
|
||||
|
||||
function updateFromView() {
|
||||
var m = moment.utc({
|
||||
year: $scope.date.year,
|
||||
month: $scope.date.month,
|
||||
day: $scope.date.day,
|
||||
hour: $scope.time.hours,
|
||||
minute: $scope.time.minutes,
|
||||
second: $scope.time.seconds
|
||||
});
|
||||
$scope.ngModel[$scope.field] = m.valueOf();
|
||||
}
|
||||
|
||||
$scope.isInCurrentMonth = function (cell) {
|
||||
return cell.month === month;
|
||||
};
|
||||
|
||||
$scope.isSelected = function (cell) {
|
||||
var date = $scope.date || {};
|
||||
return cell.day === date.day &&
|
||||
cell.month === date.month &&
|
||||
cell.year === date.year;
|
||||
};
|
||||
|
||||
$scope.select = function (cell) {
|
||||
$scope.date = $scope.date || {};
|
||||
$scope.date.month = cell.month;
|
||||
$scope.date.year = cell.year;
|
||||
$scope.date.day = cell.day;
|
||||
updateFromView();
|
||||
};
|
||||
|
||||
$scope.dateEquals = function (d1, d2) {
|
||||
return d1.year === d2.year &&
|
||||
d1.month === d2.month &&
|
||||
d1.day === d2.day;
|
||||
};
|
||||
|
||||
$scope.changeMonth = function (delta) {
|
||||
month += delta;
|
||||
if (month > 11) {
|
||||
month = 0;
|
||||
year += 1;
|
||||
}
|
||||
if (month < 0) {
|
||||
month = 11;
|
||||
year -= 1;
|
||||
}
|
||||
interacted = true;
|
||||
updateScopeForMonth();
|
||||
};
|
||||
|
||||
$scope.nameFor = function (key) {
|
||||
return TIME_NAMES[key];
|
||||
};
|
||||
|
||||
$scope.optionsFor = function (key) {
|
||||
return TIME_OPTIONS[key];
|
||||
};
|
||||
|
||||
updateScopeForMonth();
|
||||
|
||||
// Ensure some useful default
|
||||
$scope.ngModel[$scope.field] =
|
||||
$scope.ngModel[$scope.field] === undefined ?
|
||||
now() : $scope.ngModel[$scope.field];
|
||||
|
||||
$scope.$watch('ngModel[field]', updateFromModel);
|
||||
$scope.$watchCollection('date', updateFromView);
|
||||
$scope.$watchCollection('time', updateFromView);
|
||||
}
|
||||
|
||||
return DateTimePickerController;
|
||||
}
|
||||
);
|
239
platform/commonUI/general/src/controllers/TimeRangeController.js
Normal file
239
platform/commonUI/general/src/controllers/TimeRangeController.js
Normal file
@ -0,0 +1,239 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
define(
|
||||
['moment'],
|
||||
function (moment) {
|
||||
"use strict";
|
||||
|
||||
var
|
||||
DATE_FORMAT = "YYYY-MM-DD HH:mm:ss",
|
||||
TICK_SPACING_PX = 150;
|
||||
|
||||
/**
|
||||
* @memberof platform/commonUI/general
|
||||
* @constructor
|
||||
*/
|
||||
function TimeConductorController($scope, now) {
|
||||
var tickCount = 2,
|
||||
innerMinimumSpan = 1000, // 1 second
|
||||
outerMinimumSpan = 1000 * 60 * 60, // 1 hour
|
||||
initialDragValue;
|
||||
|
||||
function formatTimestamp(ts) {
|
||||
return moment.utc(ts).format(DATE_FORMAT);
|
||||
}
|
||||
|
||||
// From 0.0-1.0 to "0%"-"1%"
|
||||
function toPercent(p) {
|
||||
return (100 * p) + "%";
|
||||
}
|
||||
|
||||
function updateTicks() {
|
||||
var i, p, ts, start, end, span;
|
||||
end = $scope.ngModel.outer.end;
|
||||
start = $scope.ngModel.outer.start;
|
||||
span = end - start;
|
||||
$scope.ticks = [];
|
||||
for (i = 0; i < tickCount; i += 1) {
|
||||
p = i / (tickCount - 1);
|
||||
ts = p * span + start;
|
||||
$scope.ticks.push(formatTimestamp(ts));
|
||||
}
|
||||
}
|
||||
|
||||
function updateSpanWidth(w) {
|
||||
tickCount = Math.max(Math.floor(w / TICK_SPACING_PX), 2);
|
||||
updateTicks();
|
||||
}
|
||||
|
||||
function updateViewForInnerSpanFromModel(ngModel) {
|
||||
var span = ngModel.outer.end - ngModel.outer.start;
|
||||
|
||||
// Expose readable dates for the knobs
|
||||
$scope.startInnerText = formatTimestamp(ngModel.inner.start);
|
||||
$scope.endInnerText = formatTimestamp(ngModel.inner.end);
|
||||
|
||||
// And positions for the knobs
|
||||
$scope.startInnerPct =
|
||||
toPercent((ngModel.inner.start - ngModel.outer.start) / span);
|
||||
$scope.endInnerPct =
|
||||
toPercent((ngModel.outer.end - ngModel.inner.end) / span);
|
||||
}
|
||||
|
||||
function defaultBounds() {
|
||||
var t = now();
|
||||
return {
|
||||
start: t - 24 * 3600 * 1000, // One day
|
||||
end: t
|
||||
};
|
||||
}
|
||||
|
||||
function copyBounds(bounds) {
|
||||
return { start: bounds.start, end: bounds.end };
|
||||
}
|
||||
|
||||
function updateViewFromModel(ngModel) {
|
||||
var t = now();
|
||||
|
||||
ngModel = ngModel || {};
|
||||
ngModel.outer = ngModel.outer || defaultBounds();
|
||||
ngModel.inner = ngModel.inner || copyBounds(ngModel.outer);
|
||||
|
||||
// First, dates for the date pickers for outer bounds
|
||||
$scope.startOuterDate = new Date(ngModel.outer.start);
|
||||
$scope.endOuterDate = new Date(ngModel.outer.end);
|
||||
|
||||
// Then various updates for the inner span
|
||||
updateViewForInnerSpanFromModel(ngModel);
|
||||
|
||||
// Stick it back is scope (in case we just set defaults)
|
||||
$scope.ngModel = ngModel;
|
||||
|
||||
updateTicks();
|
||||
}
|
||||
|
||||
function startLeftDrag() {
|
||||
initialDragValue = $scope.ngModel.inner.start;
|
||||
}
|
||||
|
||||
function startRightDrag() {
|
||||
initialDragValue = $scope.ngModel.inner.end;
|
||||
}
|
||||
|
||||
function startMiddleDrag() {
|
||||
initialDragValue = {
|
||||
start: $scope.ngModel.inner.start,
|
||||
end: $scope.ngModel.inner.end
|
||||
};
|
||||
}
|
||||
|
||||
function toMillis(pixels) {
|
||||
var span = $scope.ngModel.outer.end - $scope.ngModel.outer.start;
|
||||
return (pixels / $scope.spanWidth) * span;
|
||||
}
|
||||
|
||||
function clamp(value, low, high) {
|
||||
return Math.max(low, Math.min(high, value));
|
||||
}
|
||||
|
||||
function leftDrag(pixels) {
|
||||
var delta = toMillis(pixels);
|
||||
$scope.ngModel.inner.start = clamp(
|
||||
initialDragValue + delta,
|
||||
$scope.ngModel.outer.start,
|
||||
$scope.ngModel.inner.end - innerMinimumSpan
|
||||
);
|
||||
updateViewFromModel($scope.ngModel);
|
||||
}
|
||||
|
||||
function rightDrag(pixels) {
|
||||
var delta = toMillis(pixels);
|
||||
$scope.ngModel.inner.end = clamp(
|
||||
initialDragValue + delta,
|
||||
$scope.ngModel.inner.start + innerMinimumSpan,
|
||||
$scope.ngModel.outer.end
|
||||
);
|
||||
updateViewFromModel($scope.ngModel);
|
||||
}
|
||||
|
||||
function middleDrag(pixels) {
|
||||
var delta = toMillis(pixels),
|
||||
edge = delta < 0 ? 'start' : 'end',
|
||||
opposite = delta < 0 ? 'end' : 'start';
|
||||
|
||||
// Adjust the position of the edge in the direction of drag
|
||||
$scope.ngModel.inner[edge] = clamp(
|
||||
initialDragValue[edge] + delta,
|
||||
$scope.ngModel.outer.start,
|
||||
$scope.ngModel.outer.end
|
||||
);
|
||||
// Adjust opposite knob to maintain span
|
||||
$scope.ngModel.inner[opposite] = $scope.ngModel.inner[edge] +
|
||||
initialDragValue[opposite] - initialDragValue[edge];
|
||||
|
||||
updateViewFromModel($scope.ngModel);
|
||||
}
|
||||
|
||||
function updateOuterStart(t) {
|
||||
var ngModel = $scope.ngModel;
|
||||
|
||||
ngModel.outer.end = Math.max(
|
||||
ngModel.outer.start + outerMinimumSpan,
|
||||
ngModel.outer.end
|
||||
);
|
||||
|
||||
ngModel.inner.start =
|
||||
Math.max(ngModel.outer.start, ngModel.inner.start);
|
||||
ngModel.inner.end = Math.max(
|
||||
ngModel.inner.start + innerMinimumSpan,
|
||||
ngModel.inner.end
|
||||
);
|
||||
|
||||
$scope.startOuterText = formatTimestamp(t);
|
||||
|
||||
updateViewForInnerSpanFromModel(ngModel);
|
||||
}
|
||||
|
||||
function updateOuterEnd(t) {
|
||||
var ngModel = $scope.ngModel;
|
||||
|
||||
ngModel.outer.start = Math.min(
|
||||
ngModel.outer.end - outerMinimumSpan,
|
||||
ngModel.outer.start
|
||||
);
|
||||
|
||||
ngModel.inner.end =
|
||||
Math.min(ngModel.outer.end, ngModel.inner.end);
|
||||
ngModel.inner.start = Math.min(
|
||||
ngModel.inner.end - innerMinimumSpan,
|
||||
ngModel.inner.start
|
||||
);
|
||||
|
||||
$scope.endOuterText = formatTimestamp(t);
|
||||
|
||||
updateViewForInnerSpanFromModel(ngModel);
|
||||
}
|
||||
|
||||
$scope.startLeftDrag = startLeftDrag;
|
||||
$scope.startRightDrag = startRightDrag;
|
||||
$scope.startMiddleDrag = startMiddleDrag;
|
||||
$scope.leftDrag = leftDrag;
|
||||
$scope.rightDrag = rightDrag;
|
||||
$scope.middleDrag = middleDrag;
|
||||
|
||||
$scope.state = false;
|
||||
$scope.ticks = [];
|
||||
|
||||
// Initialize scope to defaults
|
||||
updateViewFromModel($scope.ngModel);
|
||||
|
||||
$scope.$watchCollection("ngModel", updateViewFromModel);
|
||||
$scope.$watch("spanWidth", updateSpanWidth);
|
||||
$scope.$watch("ngModel.outer.start", updateOuterStart);
|
||||
$scope.$watch("ngModel.outer.end", updateOuterEnd);
|
||||
}
|
||||
|
||||
return TimeConductorController;
|
||||
}
|
||||
);
|
@ -0,0 +1,77 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* The `mct-click-elsewhere` directive will evaluate its
|
||||
* associated expression whenever a `mousedown` occurs anywhere
|
||||
* outside of the element that has the `mct-click-elsewhere`
|
||||
* directive attached. This is useful for dismissing popups
|
||||
* and the like.
|
||||
*/
|
||||
function MCTClickElsewhere($document) {
|
||||
|
||||
// Link; install event handlers.
|
||||
function link(scope, element, attrs) {
|
||||
// Keep a reference to the body, to attach/detach
|
||||
// mouse event handlers; mousedown and mouseup cannot
|
||||
// only be attached to the element being linked, as the
|
||||
// mouse may leave this element during the drag.
|
||||
var body = $document.find('body');
|
||||
|
||||
function clickBody(event) {
|
||||
var x = event.clientX,
|
||||
y = event.clientY,
|
||||
rect = element[0].getBoundingClientRect(),
|
||||
xMin = rect.left,
|
||||
xMax = xMin + rect.width,
|
||||
yMin = rect.top,
|
||||
yMax = yMin + rect.height;
|
||||
|
||||
if (x < xMin || x > xMax || y < yMin || y > yMax) {
|
||||
scope.$eval(attrs.mctClickElsewhere);
|
||||
}
|
||||
}
|
||||
|
||||
body.on("mousedown", clickBody);
|
||||
scope.$on("$destroy", function () {
|
||||
body.off("mousedown", clickBody);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
// mct-drag only makes sense as an attribute
|
||||
restrict: "A",
|
||||
// Link function, to install event handlers
|
||||
link: link
|
||||
};
|
||||
}
|
||||
|
||||
return MCTClickElsewhere;
|
||||
}
|
||||
);
|
||||
|
73
platform/commonUI/general/src/directives/MCTPopup.js
Normal file
73
platform/commonUI/general/src/directives/MCTPopup.js
Normal file
@ -0,0 +1,73 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
function () {
|
||||
'use strict';
|
||||
|
||||
var TEMPLATE = "<div></div>";
|
||||
|
||||
/**
|
||||
* The `mct-popup` directive may be used to display elements
|
||||
* which "pop up" over other parts of the page. Typically, this is
|
||||
* done in conjunction with an `ng-if` to control the visibility
|
||||
* of the popup.
|
||||
*
|
||||
* Example of usage:
|
||||
*
|
||||
* <mct-popup ng-if="someExpr">
|
||||
* <span>These are the contents of the popup!</span>
|
||||
* </mct-popup>
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform/commonUI/general
|
||||
* @param $compile Angular's $compile service
|
||||
* @param {platform/commonUI/general.PopupService} popupService
|
||||
*/
|
||||
function MCTPopup($compile, popupService) {
|
||||
function link(scope, element, attrs, ctrl, transclude) {
|
||||
var div = $compile(TEMPLATE)(scope),
|
||||
rect = element.parent()[0].getBoundingClientRect(),
|
||||
position = [ rect.left, rect.top ],
|
||||
popup = popupService.display(div, position);
|
||||
|
||||
transclude(function (clone) {
|
||||
div.append(clone);
|
||||
});
|
||||
|
||||
scope.$on('$destroy', function () {
|
||||
popup.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: "E",
|
||||
transclude: true,
|
||||
link: link,
|
||||
scope: {}
|
||||
};
|
||||
}
|
||||
|
||||
return MCTPopup;
|
||||
}
|
||||
);
|
@ -58,6 +58,7 @@ define(
|
||||
// Link; start listening for changes to an element's size
|
||||
function link(scope, element, attrs) {
|
||||
var lastBounds,
|
||||
linking = true,
|
||||
active = true;
|
||||
|
||||
// Determine how long to wait before the next update
|
||||
@ -74,7 +75,9 @@ define(
|
||||
lastBounds.width !== bounds.width ||
|
||||
lastBounds.height !== bounds.height) {
|
||||
scope.$eval(attrs.mctResize, { bounds: bounds });
|
||||
scope.$apply(); // Trigger a digest
|
||||
if (!linking) { // Avoid apply-in-a-digest
|
||||
scope.$apply();
|
||||
}
|
||||
lastBounds = bounds;
|
||||
}
|
||||
}
|
||||
@ -101,6 +104,9 @@ define(
|
||||
|
||||
// Handle the initial callback
|
||||
onInterval();
|
||||
|
||||
// Trigger scope.$apply on subsequent changes
|
||||
linking = false;
|
||||
}
|
||||
|
||||
return {
|
||||
|
89
platform/commonUI/general/src/services/Popup.js
Normal file
89
platform/commonUI/general/src/services/Popup.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.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* A popup is an element that has been displayed at a particular
|
||||
* location within the page.
|
||||
* @constructor
|
||||
* @memberof platform/commonUI/general
|
||||
* @param element the jqLite-wrapped element
|
||||
* @param {object} styles an object containing key-value pairs
|
||||
* of styles used to position the element.
|
||||
*/
|
||||
function Popup(element, styles) {
|
||||
this.styles = styles;
|
||||
this.element = element;
|
||||
|
||||
element.css(styles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop showing this popup.
|
||||
*/
|
||||
Popup.prototype.dismiss = function () {
|
||||
this.element.remove();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if this popup is positioned such that it appears to the
|
||||
* left of its original location.
|
||||
* @returns {boolean} true if the popup goes left
|
||||
*/
|
||||
Popup.prototype.goesLeft = function () {
|
||||
return !this.styles.left;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if this popup is positioned such that it appears to the
|
||||
* right of its original location.
|
||||
* @returns {boolean} true if the popup goes right
|
||||
*/
|
||||
Popup.prototype.goesRight = function () {
|
||||
return !this.styles.right;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if this popup is positioned such that it appears above
|
||||
* its original location.
|
||||
* @returns {boolean} true if the popup goes up
|
||||
*/
|
||||
Popup.prototype.goesUp = function () {
|
||||
return !this.styles.top;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if this popup is positioned such that it appears below
|
||||
* its original location.
|
||||
* @returns {boolean} true if the popup goes down
|
||||
*/
|
||||
Popup.prototype.goesDown = function () {
|
||||
return !this.styles.bottom;
|
||||
};
|
||||
|
||||
return Popup;
|
||||
}
|
||||
);
|
127
platform/commonUI/general/src/services/PopupService.js
Normal file
127
platform/commonUI/general/src/services/PopupService.js
Normal file
@ -0,0 +1,127 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
['./Popup'],
|
||||
function (Popup) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Displays popup elements at specific positions within the document.
|
||||
* @memberof platform/commonUI/general
|
||||
* @constructor
|
||||
*/
|
||||
function PopupService($document, $window) {
|
||||
this.$document = $document;
|
||||
this.$window = $window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options controlling how the popup is displaed.
|
||||
*
|
||||
* @typedef PopupOptions
|
||||
* @memberof platform/commonUI/general
|
||||
* @property {number} [offsetX] the horizontal distance, in pixels,
|
||||
* to offset the element in whichever direction it is
|
||||
* displayed. Defaults to 0.
|
||||
* @property {number} [offsetY] the vertical distance, in pixels,
|
||||
* to offset the element in whichever direction it is
|
||||
* displayed. Defaults to 0.
|
||||
* @property {number} [marginX] the horizontal position, in pixels,
|
||||
* after which to prefer to display the element to the left.
|
||||
* If negative, this is relative to the right edge of the
|
||||
* page. Defaults to half the window's width.
|
||||
* @property {number} [marginY] the vertical position, in pixels,
|
||||
* after which to prefer to display the element upward.
|
||||
* If negative, this is relative to the right edge of the
|
||||
* page. Defaults to half the window's height.
|
||||
* @property {string} [leftClass] class to apply when shifting to the left
|
||||
* @property {string} [rightClass] class to apply when shifting to the right
|
||||
* @property {string} [upClass] class to apply when shifting upward
|
||||
* @property {string} [downClass] class to apply when shifting downward
|
||||
*/
|
||||
|
||||
/**
|
||||
* Display a popup at a particular location. The location chosen will
|
||||
* be the corner of the element; the element will be positioned either
|
||||
* to the left or the right of this point depending on available
|
||||
* horizontal space, and will similarly be shifted upward or downward
|
||||
* depending on available vertical space.
|
||||
*
|
||||
* @param element the jqLite-wrapped DOM element to pop up
|
||||
* @param {number[]} position x,y position of the element, in
|
||||
* pixel coordinates. Negative values are interpreted as
|
||||
* relative to the right or bottom of the window.
|
||||
* @param {PopupOptions} [options] additional options to control
|
||||
* positioning of the popup
|
||||
* @returns {platform/commonUI/general.Popup} the popup
|
||||
*/
|
||||
PopupService.prototype.display = function (element, position, options) {
|
||||
var $document = this.$document,
|
||||
$window = this.$window,
|
||||
body = $document.find('body'),
|
||||
winDim = [ $window.innerWidth, $window.innerHeight ],
|
||||
styles = { position: 'absolute' },
|
||||
margin,
|
||||
offset,
|
||||
bubble;
|
||||
|
||||
function adjustNegatives(value, index) {
|
||||
return value < 0 ? (value + winDim[index]) : value;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
options = options || {};
|
||||
offset = [
|
||||
options.offsetX !== undefined ? options.offsetX : 0,
|
||||
options.offsetY !== undefined ? options.offsetY : 0
|
||||
];
|
||||
margin = [ options.marginX, options.marginY ].map(function (m, i) {
|
||||
return m === undefined ? (winDim[i] / 2) : m;
|
||||
}).map(adjustNegatives);
|
||||
|
||||
position = position.map(adjustNegatives);
|
||||
|
||||
if (position[0] > margin[0]) {
|
||||
styles.right = (winDim[0] - position[0] + offset[0]) + 'px';
|
||||
} else {
|
||||
styles.left = (position[0] + offset[0]) + 'px';
|
||||
}
|
||||
|
||||
if (position[1] > margin[1]) {
|
||||
styles.bottom = (winDim[1] - position[1] + offset[1]) + 'px';
|
||||
} else {
|
||||
styles.top = (position[1] + offset[1]) + 'px';
|
||||
}
|
||||
|
||||
// Add the menu to the body
|
||||
body.append(element);
|
||||
|
||||
// Return a function to dismiss the bubble
|
||||
return new Popup(element, styles);
|
||||
};
|
||||
|
||||
return PopupService;
|
||||
}
|
||||
);
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||
|
||||
define(
|
||||
["../../src/controllers/DateTimePickerController"],
|
||||
function (DateTimePickerController) {
|
||||
"use strict";
|
||||
|
||||
describe("The DateTimePickerController", function () {
|
||||
var mockScope,
|
||||
mockNow,
|
||||
controller;
|
||||
|
||||
function fireWatch(expr, value) {
|
||||
mockScope.$watch.calls.forEach(function (call) {
|
||||
if (call.args[0] === expr) {
|
||||
call.args[1](value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
[ "$apply", "$watch", "$watchCollection" ]
|
||||
);
|
||||
mockScope.ngModel = {};
|
||||
mockScope.field = "testField";
|
||||
mockNow = jasmine.createSpy('now');
|
||||
controller = new DateTimePickerController(mockScope, mockNow);
|
||||
});
|
||||
|
||||
it("watches the model that was passed in", function () {
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||
"ngModel[field]",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -0,0 +1,173 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||
|
||||
define(
|
||||
["../../src/controllers/TimeRangeController"],
|
||||
function (TimeRangeController) {
|
||||
"use strict";
|
||||
|
||||
var SEC = 1000,
|
||||
MIN = 60 * SEC,
|
||||
HOUR = 60 * MIN,
|
||||
DAY = 24 * HOUR;
|
||||
|
||||
describe("The TimeRangeController", function () {
|
||||
var mockScope,
|
||||
mockNow,
|
||||
controller;
|
||||
|
||||
function fireWatch(expr, value) {
|
||||
mockScope.$watch.calls.forEach(function (call) {
|
||||
if (call.args[0] === expr) {
|
||||
call.args[1](value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fireWatchCollection(expr, value) {
|
||||
mockScope.$watchCollection.calls.forEach(function (call) {
|
||||
if (call.args[0] === expr) {
|
||||
call.args[1](value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
[ "$apply", "$watch", "$watchCollection" ]
|
||||
);
|
||||
mockNow = jasmine.createSpy('now');
|
||||
controller = new TimeRangeController(mockScope, mockNow);
|
||||
});
|
||||
|
||||
it("watches the model that was passed in", function () {
|
||||
expect(mockScope.$watchCollection)
|
||||
.toHaveBeenCalledWith("ngModel", jasmine.any(Function));
|
||||
});
|
||||
|
||||
describe("when dragged", function () {
|
||||
beforeEach(function () {
|
||||
mockScope.ngModel = {
|
||||
outer: {
|
||||
start: DAY * 1000,
|
||||
end: DAY * 1001
|
||||
},
|
||||
inner: {
|
||||
start: DAY * 1000 + HOUR * 3,
|
||||
end: DAY * 1001 - HOUR * 3
|
||||
}
|
||||
};
|
||||
mockScope.spanWidth = 1000;
|
||||
fireWatch("spanWidth", mockScope.spanWidth);
|
||||
fireWatchCollection("ngModel", mockScope.ngModel);
|
||||
});
|
||||
|
||||
it("updates the start time for left drags", function () {
|
||||
mockScope.startLeftDrag();
|
||||
mockScope.leftDrag(250);
|
||||
expect(mockScope.ngModel.inner.start)
|
||||
.toEqual(DAY * 1000 + HOUR * 9);
|
||||
});
|
||||
|
||||
it("updates the end time for right drags", function () {
|
||||
mockScope.startRightDrag();
|
||||
mockScope.rightDrag(-250);
|
||||
expect(mockScope.ngModel.inner.end)
|
||||
.toEqual(DAY * 1000 + HOUR * 15);
|
||||
});
|
||||
|
||||
it("updates both start and end for middle drags", function () {
|
||||
mockScope.startMiddleDrag();
|
||||
mockScope.middleDrag(-125);
|
||||
expect(mockScope.ngModel.inner).toEqual({
|
||||
start: DAY * 1000,
|
||||
end: DAY * 1000 + HOUR * 18
|
||||
});
|
||||
mockScope.middleDrag(250);
|
||||
expect(mockScope.ngModel.inner).toEqual({
|
||||
start: DAY * 1000 + HOUR * 6,
|
||||
end: DAY * 1001
|
||||
});
|
||||
});
|
||||
|
||||
it("enforces a minimum inner span", function () {
|
||||
mockScope.startRightDrag();
|
||||
mockScope.rightDrag(-9999999);
|
||||
expect(mockScope.ngModel.inner.end)
|
||||
.toBeGreaterThan(mockScope.ngModel.inner.start);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when outer bounds are changed", function () {
|
||||
beforeEach(function () {
|
||||
mockScope.ngModel = {
|
||||
outer: {
|
||||
start: DAY * 1000,
|
||||
end: DAY * 1001
|
||||
},
|
||||
inner: {
|
||||
start: DAY * 1000 + HOUR * 3,
|
||||
end: DAY * 1001 - HOUR * 3
|
||||
}
|
||||
};
|
||||
mockScope.spanWidth = 1000;
|
||||
fireWatch("spanWidth", mockScope.spanWidth);
|
||||
fireWatchCollection("ngModel", mockScope.ngModel);
|
||||
});
|
||||
|
||||
it("enforces a minimum outer span", function () {
|
||||
mockScope.ngModel.outer.end =
|
||||
mockScope.ngModel.outer.start - DAY * 100;
|
||||
fireWatch(
|
||||
"ngModel.outer.end",
|
||||
mockScope.ngModel.outer.end
|
||||
);
|
||||
expect(mockScope.ngModel.outer.end)
|
||||
.toBeGreaterThan(mockScope.ngModel.outer.start);
|
||||
|
||||
mockScope.ngModel.outer.start =
|
||||
mockScope.ngModel.outer.end + DAY * 100;
|
||||
fireWatch(
|
||||
"ngModel.outer.start",
|
||||
mockScope.ngModel.outer.start
|
||||
);
|
||||
expect(mockScope.ngModel.outer.end)
|
||||
.toBeGreaterThan(mockScope.ngModel.outer.start);
|
||||
});
|
||||
|
||||
it("enforces a minimum inner span when outer span changes", function () {
|
||||
mockScope.ngModel.outer.end =
|
||||
mockScope.ngModel.outer.start - DAY * 100;
|
||||
fireWatch(
|
||||
"ngModel.outer.end",
|
||||
mockScope.ngModel.outer.end
|
||||
);
|
||||
expect(mockScope.ngModel.inner.end)
|
||||
.toBeGreaterThan(mockScope.ngModel.inner.start);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -0,0 +1,84 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,jasmine*/
|
||||
|
||||
define(
|
||||
["../../src/directives/MCTClickElsewhere"],
|
||||
function (MCTClickElsewhere) {
|
||||
"use strict";
|
||||
|
||||
var JQLITE_METHODS = [ "on", "off", "find", "parent" ];
|
||||
|
||||
describe("The mct-click-elsewhere directive", function () {
|
||||
var mockDocument,
|
||||
mockScope,
|
||||
mockElement,
|
||||
testAttrs,
|
||||
mockBody,
|
||||
mockParentEl,
|
||||
testRect,
|
||||
mctClickElsewhere;
|
||||
|
||||
function testEvent(x, y) {
|
||||
return {
|
||||
pageX: x,
|
||||
pageY: y,
|
||||
preventDefault: jasmine.createSpy("preventDefault")
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockDocument =
|
||||
jasmine.createSpyObj("$document", JQLITE_METHODS);
|
||||
mockScope =
|
||||
jasmine.createSpyObj("$scope", [ "$eval", "$apply", "$on" ]);
|
||||
mockElement =
|
||||
jasmine.createSpyObj("element", JQLITE_METHODS);
|
||||
mockBody =
|
||||
jasmine.createSpyObj("body", JQLITE_METHODS);
|
||||
mockParentEl =
|
||||
jasmine.createSpyObj("parent", ["getBoundingClientRect"]);
|
||||
|
||||
testAttrs = {
|
||||
mctClickElsewhere: "some Angular expression"
|
||||
};
|
||||
testRect = {
|
||||
left: 20,
|
||||
top: 42,
|
||||
width: 60,
|
||||
height: 75
|
||||
};
|
||||
|
||||
mockDocument.find.andReturn(mockBody);
|
||||
|
||||
mctClickElsewhere = new MCTClickElsewhere(mockDocument);
|
||||
mctClickElsewhere.link(mockScope, mockElement, testAttrs);
|
||||
});
|
||||
|
||||
it("is valid as an attribute", function () {
|
||||
expect(mctClickElsewhere.restrict).toEqual("A");
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
);
|
136
platform/commonUI/general/test/directives/MCTPopupSpec.js
Normal file
136
platform/commonUI/general/test/directives/MCTPopupSpec.js
Normal file
@ -0,0 +1,136 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,jasmine*/
|
||||
|
||||
define(
|
||||
["../../src/directives/MCTPopup"],
|
||||
function (MCTPopup) {
|
||||
"use strict";
|
||||
|
||||
var JQLITE_METHODS = [ "on", "off", "find", "parent", "css", "append" ];
|
||||
|
||||
describe("The mct-popup directive", function () {
|
||||
var mockCompile,
|
||||
mockPopupService,
|
||||
mockPopup,
|
||||
mockScope,
|
||||
mockElement,
|
||||
testAttrs,
|
||||
mockBody,
|
||||
mockTransclude,
|
||||
mockParentEl,
|
||||
mockNewElement,
|
||||
testRect,
|
||||
mctPopup;
|
||||
|
||||
function testEvent(x, y) {
|
||||
return {
|
||||
pageX: x,
|
||||
pageY: y,
|
||||
preventDefault: jasmine.createSpy("preventDefault")
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockCompile =
|
||||
jasmine.createSpy("$compile");
|
||||
mockPopupService =
|
||||
jasmine.createSpyObj("popupService", ["display"]);
|
||||
mockPopup =
|
||||
jasmine.createSpyObj("popup", ["dismiss"]);
|
||||
mockScope =
|
||||
jasmine.createSpyObj("$scope", [ "$eval", "$apply", "$on" ]);
|
||||
mockElement =
|
||||
jasmine.createSpyObj("element", JQLITE_METHODS);
|
||||
mockBody =
|
||||
jasmine.createSpyObj("body", JQLITE_METHODS);
|
||||
mockTransclude =
|
||||
jasmine.createSpy("transclude");
|
||||
mockParentEl =
|
||||
jasmine.createSpyObj("parent", ["getBoundingClientRect"]);
|
||||
mockNewElement =
|
||||
jasmine.createSpyObj("newElement", JQLITE_METHODS);
|
||||
|
||||
testAttrs = {
|
||||
mctClickElsewhere: "some Angular expression"
|
||||
};
|
||||
testRect = {
|
||||
left: 20,
|
||||
top: 42,
|
||||
width: 60,
|
||||
height: 75
|
||||
};
|
||||
|
||||
mockCompile.andCallFake(function () {
|
||||
var mockFn = jasmine.createSpy();
|
||||
mockFn.andReturn(mockNewElement);
|
||||
return mockFn;
|
||||
});
|
||||
mockElement.parent.andReturn([mockParentEl]);
|
||||
mockParentEl.getBoundingClientRect.andReturn(testRect);
|
||||
mockPopupService.display.andReturn(mockPopup);
|
||||
|
||||
mctPopup = new MCTPopup(mockCompile, mockPopupService);
|
||||
|
||||
mctPopup.link(
|
||||
mockScope,
|
||||
mockElement,
|
||||
testAttrs,
|
||||
null,
|
||||
mockTransclude
|
||||
);
|
||||
});
|
||||
|
||||
it("is valid as an element", function () {
|
||||
expect(mctPopup.restrict).toEqual("E");
|
||||
});
|
||||
|
||||
describe("creates an element which", function () {
|
||||
it("displays as a popup", function () {
|
||||
expect(mockPopupService.display).toHaveBeenCalledWith(
|
||||
mockNewElement,
|
||||
[ testRect.left, testRect.top ]
|
||||
);
|
||||
});
|
||||
|
||||
it("displays transcluded content", function () {
|
||||
var mockClone =
|
||||
jasmine.createSpyObj('clone', JQLITE_METHODS);
|
||||
mockTransclude.mostRecentCall.args[0](mockClone);
|
||||
expect(mockNewElement.append)
|
||||
.toHaveBeenCalledWith(mockClone);
|
||||
});
|
||||
|
||||
it("is removed when its containing scope is destroyed", function () {
|
||||
expect(mockPopup.dismiss).not.toHaveBeenCalled();
|
||||
mockScope.$on.calls.forEach(function (call) {
|
||||
if (call.args[0] === '$destroy') {
|
||||
call.args[1]();
|
||||
}
|
||||
});
|
||||
expect(mockPopup.dismiss).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
98
platform/commonUI/general/test/services/PopupServiceSpec.js
Normal file
98
platform/commonUI/general/test/services/PopupServiceSpec.js
Normal file
@ -0,0 +1,98 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||
|
||||
|
||||
define(
|
||||
["../../src/services/PopupService"],
|
||||
function (PopupService) {
|
||||
'use strict';
|
||||
|
||||
describe("PopupService", function () {
|
||||
var mockDocument,
|
||||
testWindow,
|
||||
mockBody,
|
||||
mockElement,
|
||||
popupService;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDocument = jasmine.createSpyObj('$document', [ 'find' ]);
|
||||
testWindow = { innerWidth: 1000, innerHeight: 800 };
|
||||
mockBody = jasmine.createSpyObj('body', [ 'append' ]);
|
||||
mockElement = jasmine.createSpyObj('element', [
|
||||
'css',
|
||||
'remove'
|
||||
]);
|
||||
|
||||
mockDocument.find.andCallFake(function (query) {
|
||||
return query === 'body' && mockBody;
|
||||
});
|
||||
|
||||
popupService = new PopupService(mockDocument, testWindow);
|
||||
});
|
||||
|
||||
it("adds elements to the body of the document", function () {
|
||||
popupService.display(mockElement, [ 0, 0 ]);
|
||||
expect(mockBody.append).toHaveBeenCalledWith(mockElement);
|
||||
});
|
||||
|
||||
describe("when positioned in appropriate quadrants", function () {
|
||||
it("orients elements relative to the top-left", function () {
|
||||
popupService.display(mockElement, [ 25, 50 ]);
|
||||
expect(mockElement.css).toHaveBeenCalledWith({
|
||||
position: 'absolute',
|
||||
left: '25px',
|
||||
top: '50px'
|
||||
});
|
||||
});
|
||||
|
||||
it("orients elements relative to the top-right", function () {
|
||||
popupService.display(mockElement, [ 800, 50 ]);
|
||||
expect(mockElement.css).toHaveBeenCalledWith({
|
||||
position: 'absolute',
|
||||
right: '200px',
|
||||
top: '50px'
|
||||
});
|
||||
});
|
||||
|
||||
it("orients elements relative to the bottom-right", function () {
|
||||
popupService.display(mockElement, [ 800, 650 ]);
|
||||
expect(mockElement.css).toHaveBeenCalledWith({
|
||||
position: 'absolute',
|
||||
right: '200px',
|
||||
bottom: '150px'
|
||||
});
|
||||
});
|
||||
|
||||
it("orients elements relative to the bottom-left", function () {
|
||||
popupService.display(mockElement, [ 120, 650 ]);
|
||||
expect(mockElement.css).toHaveBeenCalledWith({
|
||||
position: 'absolute',
|
||||
left: '120px',
|
||||
bottom: '150px'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
74
platform/commonUI/general/test/services/PopupSpec.js
Normal file
74
platform/commonUI/general/test/services/PopupSpec.js
Normal file
@ -0,0 +1,74 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||
|
||||
|
||||
define(
|
||||
["../../src/services/Popup"],
|
||||
function (Popup) {
|
||||
'use strict';
|
||||
|
||||
describe("Popup", function () {
|
||||
var mockElement,
|
||||
testStyles,
|
||||
popup;
|
||||
|
||||
beforeEach(function () {
|
||||
mockElement =
|
||||
jasmine.createSpyObj('element', [ 'css', 'remove' ]);
|
||||
testStyles = { left: '12px', top: '14px' };
|
||||
popup = new Popup(mockElement, testStyles);
|
||||
});
|
||||
|
||||
it("applies CSS styles when instantiated", function () {
|
||||
expect(mockElement.css)
|
||||
.toHaveBeenCalledWith(testStyles);
|
||||
});
|
||||
|
||||
it("reports the orientation of the popup", function () {
|
||||
var otherStyles = {
|
||||
right: '12px',
|
||||
bottom: '14px'
|
||||
},
|
||||
otherPopup = new Popup(mockElement, otherStyles);
|
||||
|
||||
expect(popup.goesLeft()).toBeFalsy();
|
||||
expect(popup.goesRight()).toBeTruthy();
|
||||
expect(popup.goesUp()).toBeFalsy();
|
||||
expect(popup.goesDown()).toBeTruthy();
|
||||
|
||||
expect(otherPopup.goesLeft()).toBeTruthy();
|
||||
expect(otherPopup.goesRight()).toBeFalsy();
|
||||
expect(otherPopup.goesUp()).toBeTruthy();
|
||||
expect(otherPopup.goesDown()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("removes elements when dismissed", function () {
|
||||
expect(mockElement.remove).not.toHaveBeenCalled();
|
||||
popup.dismiss();
|
||||
expect(mockElement.remove).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
);
|
@ -3,16 +3,22 @@
|
||||
"controllers/BottomBarController",
|
||||
"controllers/ClickAwayController",
|
||||
"controllers/ContextMenuController",
|
||||
"controllers/DateTimePickerController",
|
||||
"controllers/GetterSetterController",
|
||||
"controllers/SelectorController",
|
||||
"controllers/SplitPaneController",
|
||||
"controllers/TimeRangeController",
|
||||
"controllers/ToggleController",
|
||||
"controllers/TreeNodeController",
|
||||
"controllers/ViewSwitcherController",
|
||||
"directives/MCTClickElsewhere",
|
||||
"directives/MCTContainer",
|
||||
"directives/MCTDrag",
|
||||
"directives/MCTPopup",
|
||||
"directives/MCTResize",
|
||||
"directives/MCTScroll",
|
||||
"services/Popup",
|
||||
"services/PopupService",
|
||||
"services/UrlService",
|
||||
"StyleSheetLoader"
|
||||
]
|
||||
|
@ -45,13 +45,12 @@
|
||||
"implementation": "services/InfoService.js",
|
||||
"depends": [
|
||||
"$compile",
|
||||
"$document",
|
||||
"$window",
|
||||
"$rootScope",
|
||||
"popupService",
|
||||
"agentService"
|
||||
]
|
||||
}
|
||||
],
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"key": "INFO_HOVER_DELAY",
|
||||
@ -66,4 +65,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,13 +31,19 @@ define({
|
||||
BUBBLE_TEMPLATE: "<mct-container key=\"bubble\" " +
|
||||
"bubble-title=\"{{bubbleTitle}}\" " +
|
||||
"bubble-layout=\"{{bubbleLayout}}\" " +
|
||||
"class=\"bubble-container\">" +
|
||||
"<mct-include key=\"bubbleTemplate\" ng-model=\"bubbleModel\">" +
|
||||
"class=\"bubble-container\">" +
|
||||
"<mct-include key=\"bubbleTemplate\" " +
|
||||
"ng-model=\"bubbleModel\">" +
|
||||
"</mct-include>" +
|
||||
"</mct-container>",
|
||||
// Pixel offset for bubble, to align arrow position
|
||||
BUBBLE_OFFSET: [ 0, -26 ],
|
||||
// Max width and margins allowed for bubbles; defined in /platform/commonUI/general/res/sass/_constants.scss
|
||||
BUBBLE_MARGIN_LR: 10,
|
||||
BUBBLE_MAX_WIDTH: 300
|
||||
// Options and classes for bubble
|
||||
BUBBLE_OPTIONS: {
|
||||
offsetX: 0,
|
||||
offsetY: -26
|
||||
},
|
||||
BUBBLE_MOBILE_POSITION: [ 0, -25 ],
|
||||
// Max width and margins allowed for bubbles;
|
||||
// defined in /platform/commonUI/general/res/sass/_constants.scss
|
||||
BUBBLE_MARGIN_LR: 10,
|
||||
BUBBLE_MAX_WIDTH: 300
|
||||
});
|
||||
|
@ -27,18 +27,18 @@ define(
|
||||
"use strict";
|
||||
|
||||
var BUBBLE_TEMPLATE = InfoConstants.BUBBLE_TEMPLATE,
|
||||
OFFSET = InfoConstants.BUBBLE_OFFSET;
|
||||
MOBILE_POSITION = InfoConstants.BUBBLE_MOBILE_POSITION,
|
||||
OPTIONS = InfoConstants.BUBBLE_OPTIONS;
|
||||
|
||||
/**
|
||||
* Displays informative content ("info bubbles") for the user.
|
||||
* @memberof platform/commonUI/inspect
|
||||
* @constructor
|
||||
*/
|
||||
function InfoService($compile, $document, $window, $rootScope, agentService) {
|
||||
function InfoService($compile, $rootScope, popupService, agentService) {
|
||||
this.$compile = $compile;
|
||||
this.$document = $document;
|
||||
this.$window = $window;
|
||||
this.$rootScope = $rootScope;
|
||||
this.popupService = popupService;
|
||||
this.agentService = agentService;
|
||||
}
|
||||
|
||||
@ -55,53 +55,47 @@ define(
|
||||
*/
|
||||
InfoService.prototype.display = function (templateKey, title, content, position) {
|
||||
var $compile = this.$compile,
|
||||
$document = this.$document,
|
||||
$window = this.$window,
|
||||
$rootScope = this.$rootScope,
|
||||
body = $document.find('body'),
|
||||
scope = $rootScope.$new(),
|
||||
winDim = [$window.innerWidth, $window.innerHeight],
|
||||
bubbleSpaceLR = InfoConstants.BUBBLE_MARGIN_LR + InfoConstants.BUBBLE_MAX_WIDTH,
|
||||
goLeft = position[0] > (winDim[0] - bubbleSpaceLR),
|
||||
goUp = position[1] > (winDim[1] / 2),
|
||||
span = $compile('<span></span>')(scope),
|
||||
bubbleSpaceLR = InfoConstants.BUBBLE_MARGIN_LR +
|
||||
InfoConstants.BUBBLE_MAX_WIDTH,
|
||||
options,
|
||||
popup,
|
||||
bubble;
|
||||
|
||||
|
||||
options = Object.create(OPTIONS);
|
||||
options.marginX = -bubbleSpaceLR;
|
||||
|
||||
// On a phone, bubble takes up more screen real estate,
|
||||
// so position it differently (toward the bottom)
|
||||
if (this.agentService.isPhone(navigator.userAgent)) {
|
||||
position = MOBILE_POSITION;
|
||||
options = {};
|
||||
}
|
||||
|
||||
popup = this.popupService.display(span, position, options);
|
||||
|
||||
// Pass model & container parameters into the scope
|
||||
scope.bubbleModel = content;
|
||||
scope.bubbleTemplate = templateKey;
|
||||
scope.bubbleLayout = (goUp ? 'arw-btm' : 'arw-top') + ' ' +
|
||||
(goLeft ? 'arw-right' : 'arw-left');
|
||||
scope.bubbleTitle = title;
|
||||
// Style the bubble according to how it was positioned
|
||||
scope.bubbleLayout = [
|
||||
popup.goesUp() ? 'arw-btm' : 'arw-top',
|
||||
popup.goesLeft() ? 'arw-right' : 'arw-left'
|
||||
].join(' ');
|
||||
scope.bubbleLayout = 'arw-top arw-left';
|
||||
|
||||
// Create the context menu
|
||||
// Create the info bubble, now that we know how to
|
||||
// point the arrow...
|
||||
bubble = $compile(BUBBLE_TEMPLATE)(scope);
|
||||
span.append(bubble);
|
||||
|
||||
// Position the bubble
|
||||
bubble.css('position', 'absolute');
|
||||
if (this.agentService.isPhone(navigator.userAgent)) {
|
||||
bubble.css('right', '0px');
|
||||
bubble.css('left', '0px');
|
||||
bubble.css('top', 'auto');
|
||||
bubble.css('bottom', '25px');
|
||||
} else {
|
||||
if (goLeft) {
|
||||
bubble.css('right', (winDim[0] - position[0] + OFFSET[0]) + 'px');
|
||||
} else {
|
||||
bubble.css('left', position[0] + OFFSET[0] + 'px');
|
||||
}
|
||||
if (goUp) {
|
||||
bubble.css('bottom', (winDim[1] - position[1] + OFFSET[1]) + 'px');
|
||||
} else {
|
||||
bubble.css('top', position[1] + OFFSET[1] + 'px');
|
||||
}
|
||||
}
|
||||
|
||||
// Add the menu to the body
|
||||
body.append(bubble);
|
||||
|
||||
// Return a function to dismiss the bubble
|
||||
return function () {
|
||||
bubble.remove();
|
||||
// Return a function to dismiss the info bubble
|
||||
return function dismiss() {
|
||||
popup.dismiss();
|
||||
scope.$destroy();
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -28,117 +28,85 @@ define(
|
||||
|
||||
describe("The info service", function () {
|
||||
var mockCompile,
|
||||
mockDocument,
|
||||
testWindow,
|
||||
mockRootScope,
|
||||
mockPopupService,
|
||||
mockAgentService,
|
||||
mockCompiledTemplate,
|
||||
testScope,
|
||||
mockBody,
|
||||
mockElement,
|
||||
mockScope,
|
||||
mockElements,
|
||||
mockPopup,
|
||||
service;
|
||||
|
||||
beforeEach(function () {
|
||||
mockCompile = jasmine.createSpy('$compile');
|
||||
mockDocument = jasmine.createSpyObj('$document', ['find']);
|
||||
testWindow = { innerWidth: 1000, innerHeight: 100 };
|
||||
mockRootScope = jasmine.createSpyObj('$rootScope', ['$new']);
|
||||
mockAgentService = jasmine.createSpyObj('agentService', ['isMobile', 'isPhone']);
|
||||
mockCompiledTemplate = jasmine.createSpy('template');
|
||||
testScope = {};
|
||||
mockBody = jasmine.createSpyObj('body', ['append']);
|
||||
mockElement = jasmine.createSpyObj('element', ['css', 'remove']);
|
||||
mockPopupService = jasmine.createSpyObj(
|
||||
'popupService',
|
||||
['display']
|
||||
);
|
||||
mockPopup = jasmine.createSpyObj('popup', [
|
||||
'dismiss',
|
||||
'goesLeft',
|
||||
'goesRight',
|
||||
'goesUp',
|
||||
'goesDown'
|
||||
]);
|
||||
|
||||
mockDocument.find.andCallFake(function (tag) {
|
||||
return tag === 'body' ? mockBody : undefined;
|
||||
mockScope = jasmine.createSpyObj("scope", ["$destroy"]);
|
||||
mockElements = [];
|
||||
|
||||
mockPopupService.display.andReturn(mockPopup);
|
||||
mockCompile.andCallFake(function () {
|
||||
var mockCompiledTemplate = jasmine.createSpy('template'),
|
||||
mockElement = jasmine.createSpyObj('element', [
|
||||
'css',
|
||||
'remove',
|
||||
'append'
|
||||
]);
|
||||
mockCompiledTemplate.andReturn(mockElement);
|
||||
mockElements.push(mockElement);
|
||||
return mockCompiledTemplate;
|
||||
});
|
||||
mockCompile.andReturn(mockCompiledTemplate);
|
||||
mockCompiledTemplate.andReturn(mockElement);
|
||||
mockRootScope.$new.andReturn(testScope);
|
||||
mockRootScope.$new.andReturn(mockScope);
|
||||
|
||||
service = new InfoService(
|
||||
mockCompile,
|
||||
mockDocument,
|
||||
testWindow,
|
||||
mockRootScope,
|
||||
mockPopupService,
|
||||
mockAgentService
|
||||
);
|
||||
});
|
||||
|
||||
it("creates elements and appends them to the body to display", function () {
|
||||
service.display('', '', {}, [0, 0]);
|
||||
expect(mockBody.append).toHaveBeenCalledWith(mockElement);
|
||||
it("creates elements and displays them as popups", function () {
|
||||
service.display('', '', {}, [123, 456]);
|
||||
expect(mockPopupService.display).toHaveBeenCalledWith(
|
||||
mockElements[0],
|
||||
[ 123, 456 ],
|
||||
jasmine.any(Object)
|
||||
);
|
||||
});
|
||||
|
||||
it("provides a function to remove displayed info bubbles", function () {
|
||||
var fn = service.display('', '', {}, [0, 0]);
|
||||
expect(mockElement.remove).not.toHaveBeenCalled();
|
||||
expect(mockPopup.dismiss).not.toHaveBeenCalled();
|
||||
fn();
|
||||
expect(mockElement.remove).toHaveBeenCalled();
|
||||
expect(mockPopup.dismiss).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("depending on mouse position", function () {
|
||||
// Positioning should vary based on quadrant in window,
|
||||
// which is 1000 x 100 in this test case.
|
||||
it("displays from the top-left in the top-left quadrant", function () {
|
||||
service.display('', '', {}, [250, 25]);
|
||||
expect(mockElement.css).toHaveBeenCalledWith(
|
||||
'left',
|
||||
(250 + InfoConstants.BUBBLE_OFFSET[0]) + 'px'
|
||||
);
|
||||
expect(mockElement.css).toHaveBeenCalledWith(
|
||||
'top',
|
||||
(25 + InfoConstants.BUBBLE_OFFSET[1]) + 'px'
|
||||
);
|
||||
});
|
||||
|
||||
it("displays from the top-right in the top-right quadrant", function () {
|
||||
service.display('', '', {}, [700, 25]);
|
||||
expect(mockElement.css).toHaveBeenCalledWith(
|
||||
'right',
|
||||
(300 + InfoConstants.BUBBLE_OFFSET[0]) + 'px'
|
||||
);
|
||||
expect(mockElement.css).toHaveBeenCalledWith(
|
||||
'top',
|
||||
(25 + InfoConstants.BUBBLE_OFFSET[1]) + 'px'
|
||||
);
|
||||
});
|
||||
|
||||
it("displays from the bottom-left in the bottom-left quadrant", function () {
|
||||
service.display('', '', {}, [250, 70]);
|
||||
expect(mockElement.css).toHaveBeenCalledWith(
|
||||
'left',
|
||||
(250 + InfoConstants.BUBBLE_OFFSET[0]) + 'px'
|
||||
);
|
||||
expect(mockElement.css).toHaveBeenCalledWith(
|
||||
'bottom',
|
||||
(30 + InfoConstants.BUBBLE_OFFSET[1]) + 'px'
|
||||
);
|
||||
});
|
||||
|
||||
it("displays from the bottom-right in the bottom-right quadrant", function () {
|
||||
service.display('', '', {}, [800, 60]);
|
||||
expect(mockElement.css).toHaveBeenCalledWith(
|
||||
'right',
|
||||
(200 + InfoConstants.BUBBLE_OFFSET[0]) + 'px'
|
||||
);
|
||||
expect(mockElement.css).toHaveBeenCalledWith(
|
||||
'bottom',
|
||||
(40 + InfoConstants.BUBBLE_OFFSET[1]) + 'px'
|
||||
);
|
||||
});
|
||||
|
||||
it("when on phone device, positioning is always on bottom", function () {
|
||||
mockAgentService.isPhone.andReturn(true);
|
||||
service = new InfoService(
|
||||
mockCompile,
|
||||
mockDocument,
|
||||
testWindow,
|
||||
mockRootScope,
|
||||
mockAgentService
|
||||
);
|
||||
service.display('', '', {}, [0, 0]);
|
||||
});
|
||||
it("when on phone device, positions at bottom", function () {
|
||||
mockAgentService.isPhone.andReturn(true);
|
||||
service = new InfoService(
|
||||
mockCompile,
|
||||
mockRootScope,
|
||||
mockPopupService,
|
||||
mockAgentService
|
||||
);
|
||||
service.display('', '', {}, [123, 456]);
|
||||
expect(mockPopupService.display).toHaveBeenCalledWith(
|
||||
mockElements[0],
|
||||
[ 0, -25 ],
|
||||
jasmine.any(Object)
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,12 +7,14 @@ $colorKey: #0099cc;
|
||||
$colorKeySelectedBg: #005177;
|
||||
$colorKeyFg: #fff;
|
||||
$colorInteriorBorder: rgba($colorBodyFg, 0.1);
|
||||
$colorA: #ccc;
|
||||
$colorAHov: #fff;
|
||||
$contrastRatioPercent: 7%;
|
||||
$basicCr: 2px;
|
||||
$controlCr: 3px;
|
||||
$smallCr: 2px;
|
||||
|
||||
// Buttons
|
||||
// Buttons and Controls
|
||||
$colorBtnBg: pullForward($colorBodyBg, $contrastRatioPercent); //
|
||||
$colorBtnFg: $colorBodyFg;
|
||||
$colorBtnMajorBg: $colorKey;
|
||||
@ -20,6 +22,18 @@ $colorBtnMajorFg: $colorKeyFg;
|
||||
$colorBtnIcon: $colorKey;
|
||||
$colorInvokeMenu: #fff;
|
||||
$contrastInvokeMenuPercent: 20%;
|
||||
$shdwBtns: rgba(black, 0.2) 0 1px 2px;
|
||||
$sliderColorBase: $colorKey;
|
||||
$sliderColorRangeHolder: rgba(black, 0.1);
|
||||
$sliderColorRange: rgba($sliderColorBase, 0.3);
|
||||
$sliderColorRangeHov: rgba($sliderColorBase, 0.5);
|
||||
$sliderColorKnob: rgba($sliderColorBase, 0.6);
|
||||
$sliderColorKnobHov: $sliderColorBase;
|
||||
$sliderColorRangeValHovBg: rgba($sliderColorBase, 0.1);
|
||||
$sliderColorRangeValHovFg: $colorKeyFg;
|
||||
$sliderKnobW: nth($ueTimeControlH,2)/2;
|
||||
$timeControllerToiLineColor: #00c2ff;
|
||||
$timeControllerToiLineColorHov: #fff;
|
||||
|
||||
// General Colors
|
||||
$colorAlt1: #ffc700;
|
||||
@ -34,6 +48,7 @@ $colorFormSectionHeader: rgba(#000, 0.2);
|
||||
$colorInvokeMenu: #fff;
|
||||
$colorObjHdrTxt: $colorBodyFg;
|
||||
$colorObjHdrIc: pullForward($colorObjHdrTxt, 20%);
|
||||
$colorTick: rgba(white, 0.2);
|
||||
|
||||
// Menu colors
|
||||
$colorMenuBg: pullForward($colorBodyBg, 23%);
|
||||
@ -99,16 +114,17 @@ $colorItemBgSelected: $colorKey;
|
||||
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
||||
$colorTabBodyBg: darken($colorBodyBg, 10%);
|
||||
$colorTabBodyFg: lighten($colorTabBodyBg, 40%);
|
||||
$colorTabHeaderBg: lighten($colorBodyBg, 10%);
|
||||
$colorTabHeaderFg: lighten($colorTabHeaderBg, 40%);
|
||||
$colorTabHeaderBg: rgba(white, 0.1); // lighten($colorBodyBg, 10%);
|
||||
$colorTabHeaderFg: $colorBodyFg; //lighten($colorTabHeaderBg, 40%);
|
||||
$colorTabHeaderBorder: $colorBodyBg;
|
||||
|
||||
// Plot
|
||||
$colorPlotBg: rgba(black, 0.1);
|
||||
$colorPlotFg: $colorBodyFg;
|
||||
$colorPlotHash: rgba(white, 0.2);
|
||||
$colorPlotHash: $colorTick;
|
||||
$stylePlotHash: dashed;
|
||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
||||
|
||||
// Tree
|
||||
$colorItemTreeIcon: $colorKey;
|
||||
@ -139,5 +155,16 @@ $colorGrippyInteriorHover: $colorKey;
|
||||
// Mobile
|
||||
$colorMobilePaneLeft: darken($colorBodyBg, 5%);
|
||||
|
||||
// Datetime Picker
|
||||
$colorCalCellHovBg: $colorKey;
|
||||
$colorCalCellHovFg: $colorKeyFg;
|
||||
$colorCalCellSelectedBg: $colorItemTreeSelectedBg;
|
||||
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
|
||||
$colorCalCellInMonthBg: pushBack($colorMenuBg, 5%);
|
||||
|
||||
// About Screen
|
||||
$colorAboutLink: #84b3ff;
|
||||
$colorAboutLink: #84b3ff;
|
||||
|
||||
// Loading
|
||||
$colorLoadingBg: rgba($colorBodyFg, 0.2);
|
||||
$colorLoadingFg: $colorAlt1;
|
@ -1,13 +1,13 @@
|
||||
@mixin containerSubtle($bg: $colorBodyBg, $fg: $colorBodyFg, $hover: false) {
|
||||
@include containerBase($bg, $fg);
|
||||
@include background-image(linear-gradient(lighten($bg, 5%), $bg));
|
||||
@include boxShdwSubtle();
|
||||
@include boxShdw($shdwBtns);
|
||||
}
|
||||
|
||||
@mixin btnSubtle($bg: $colorBodyBg, $bgHov: none, $fg: $colorBodyFg, $ic: $colorBtnIcon) {
|
||||
@mixin btnSubtle($bg: $colorBodyBg, $bgHov: none, $fg: $colorBodyFg, $ic: $colorBtnIcon) {
|
||||
@include containerSubtle($bg, $fg);
|
||||
@include btnBase($bg, linear-gradient(lighten($bg, 15%), lighten($bg, 10%)), $fg, $ic);
|
||||
@include text-shadow(rgba(black, 0.3) 0 1px 1px);
|
||||
@include text-shadow($shdwItemText);
|
||||
}
|
||||
|
||||
@function pullForward($c: $colorBodyBg, $p: 20%) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,12 +7,14 @@ $colorKey: #0099cc;
|
||||
$colorKeySelectedBg: $colorKey;
|
||||
$colorKeyFg: #fff;
|
||||
$colorInteriorBorder: rgba($colorBodyFg, 0.2);
|
||||
$colorA: #999;
|
||||
$colorAHov: $colorKey;
|
||||
$contrastRatioPercent: 40%;
|
||||
$basicCr: 4px;
|
||||
$controlCr: $basicCr;
|
||||
$smallCr: 3px;
|
||||
|
||||
// Buttons
|
||||
// Buttons and Controls
|
||||
$colorBtnBg: pullForward($colorBodyBg, $contrastRatioPercent);
|
||||
$colorBtnFg: #fff;
|
||||
$colorBtnMajorBg: $colorKey;
|
||||
@ -20,9 +22,21 @@ $colorBtnMajorFg: $colorKeyFg;
|
||||
$colorBtnIcon: #eee;
|
||||
$colorInvokeMenu: #000;
|
||||
$contrastInvokeMenuPercent: 40%;
|
||||
$shdwBtns: none;
|
||||
$sliderColorBase: $colorKey;
|
||||
$sliderColorRangeHolder: rgba(black, 0.07);
|
||||
$sliderColorRange: rgba($sliderColorBase, 0.2);
|
||||
$sliderColorRangeHov: rgba($sliderColorBase, 0.4);
|
||||
$sliderColorKnob: rgba($sliderColorBase, 0.5);
|
||||
$sliderColorKnobHov: rgba($sliderColorBase, 0.7);
|
||||
$sliderColorRangeValHovBg: $sliderColorRange; //rgba($sliderColorBase, 0.1);
|
||||
$sliderColorRangeValHovFg: $colorBodyFg;
|
||||
$sliderKnobW: nth($ueTimeControlH,2)/2;
|
||||
$timeControllerToiLineColor: $colorBodyFg;
|
||||
$timeControllerToiLineColorHov: #0052b5;
|
||||
|
||||
// General Colors
|
||||
$colorAlt1: #ff6600;
|
||||
$colorAlt1: #776ba2;
|
||||
$colorAlert: #ff3c00;
|
||||
$colorIconLink: #49dedb;
|
||||
$colorPausedBg: #ff9900;
|
||||
@ -32,6 +46,7 @@ $colorGridLines: rgba(#000, 0.05);
|
||||
$colorInvokeMenu: #fff;
|
||||
$colorObjHdrTxt: $colorBodyFg;
|
||||
$colorObjHdrIc: pushBack($colorObjHdrTxt, 30%);
|
||||
$colorTick: rgba(black, 0.2);
|
||||
|
||||
// Menu colors
|
||||
$colorMenuBg: pushBack($colorBodyBg, 10%);
|
||||
@ -104,9 +119,10 @@ $colorTabHeaderBorder: $colorBodyBg;
|
||||
// Plot
|
||||
$colorPlotBg: rgba(black, 0.05);
|
||||
$colorPlotFg: $colorBodyFg;
|
||||
$colorPlotHash: rgba(black, 0.2);
|
||||
$colorPlotHash: $colorTick;
|
||||
$stylePlotHash: dashed;
|
||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
||||
|
||||
// Tree
|
||||
$colorItemTreeIcon: $colorKey;
|
||||
@ -137,5 +153,16 @@ $colorGrippyInteriorHover: $colorBodyBg;
|
||||
// Mobile
|
||||
$colorMobilePaneLeft: darken($colorBodyBg, 2%);
|
||||
|
||||
// Datetime Picker, Calendar
|
||||
$colorCalCellHovBg: $colorKey;
|
||||
$colorCalCellHovFg: $colorKeyFg;
|
||||
$colorCalCellSelectedBg: $colorItemTreeSelectedBg;
|
||||
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
|
||||
$colorCalCellInMonthBg: pullForward($colorMenuBg, 5%);
|
||||
|
||||
// About Screen
|
||||
$colorAboutLink: #84b3ff;
|
||||
$colorAboutLink: #84b3ff;
|
||||
|
||||
// Loading
|
||||
$colorLoadingFg: $colorAlt1;
|
||||
$colorLoadingBg: rgba($colorLoadingFg, 0.1);
|
@ -1,5 +1,6 @@
|
||||
@mixin containerSubtle($bg: $colorBodyBg, $fg: $colorBodyFg) {
|
||||
@include containerBase($bg, $fg);
|
||||
@include boxShdw($shdwBtns);
|
||||
}
|
||||
|
||||
@mixin btnSubtle($bg: $colorBtnBg, $bgHov: none, $fg: $colorBtnFg, $ic: $colorBtnIcon) {
|
||||
|
@ -66,6 +66,7 @@
|
||||
"depends": [
|
||||
"persistenceService",
|
||||
"$q",
|
||||
"now",
|
||||
"PERSISTENCE_SPACE",
|
||||
"ADDITIONAL_PERSISTENCE_SPACES"
|
||||
]
|
||||
|
@ -29,7 +29,8 @@ define(
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
var TOPIC_PREFIX = "mutation:";
|
||||
var GENERAL_TOPIC = "mutation",
|
||||
TOPIC_PREFIX = "mutation:";
|
||||
|
||||
// Utility function to overwrite a destination object
|
||||
// with the contents of a source object.
|
||||
@ -78,7 +79,11 @@ define(
|
||||
* @implements {Capability}
|
||||
*/
|
||||
function MutationCapability(topic, now, domainObject) {
|
||||
this.mutationTopic = topic(TOPIC_PREFIX + domainObject.getId());
|
||||
this.generalMutationTopic =
|
||||
topic(GENERAL_TOPIC);
|
||||
this.specificMutationTopic =
|
||||
topic(TOPIC_PREFIX + domainObject.getId());
|
||||
|
||||
this.now = now;
|
||||
this.domainObject = domainObject;
|
||||
}
|
||||
@ -115,11 +120,17 @@ define(
|
||||
// mutator function has a temporary copy to work with.
|
||||
var domainObject = this.domainObject,
|
||||
now = this.now,
|
||||
t = this.mutationTopic,
|
||||
generalTopic = this.generalMutationTopic,
|
||||
specificTopic = this.specificMutationTopic,
|
||||
model = domainObject.getModel(),
|
||||
clone = JSON.parse(JSON.stringify(model)),
|
||||
useTimestamp = arguments.length > 1;
|
||||
|
||||
function notifyListeners(model) {
|
||||
generalTopic.notify(domainObject);
|
||||
specificTopic.notify(model);
|
||||
}
|
||||
|
||||
// Function to handle copying values to the actual
|
||||
function handleMutation(mutationResult) {
|
||||
// If mutation result was undefined, just use
|
||||
@ -136,7 +147,7 @@ define(
|
||||
copyValues(model, result);
|
||||
}
|
||||
model.modified = useTimestamp ? timestamp : now();
|
||||
t.notify(model);
|
||||
notifyListeners(model);
|
||||
}
|
||||
|
||||
// Report the result of the mutation
|
||||
@ -158,7 +169,7 @@ define(
|
||||
* @memberof platform/core.MutationCapability#
|
||||
*/
|
||||
MutationCapability.prototype.listen = function (listener) {
|
||||
return this.mutationTopic.listen(listener);
|
||||
return this.specificMutationTopic.listen(listener);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -39,14 +39,16 @@ define(
|
||||
* @param {PersistenceService} persistenceService the service in which
|
||||
* domain object models are persisted.
|
||||
* @param $q Angular's $q service, for working with promises
|
||||
* @param {function} now a function which provides the current time
|
||||
* @param {string} space the name of the persistence space(s)
|
||||
* from which models should be retrieved.
|
||||
* @param {string} spaces additional persistence spaces to use
|
||||
*/
|
||||
function PersistedModelProvider(persistenceService, $q, space, spaces) {
|
||||
function PersistedModelProvider(persistenceService, $q, now, space, spaces) {
|
||||
this.persistenceService = persistenceService;
|
||||
this.$q = $q;
|
||||
this.spaces = [space].concat(spaces || []);
|
||||
this.now = now;
|
||||
}
|
||||
|
||||
// Take the most recently modified model, for cases where
|
||||
@ -61,7 +63,9 @@ define(
|
||||
PersistedModelProvider.prototype.getModels = function (ids) {
|
||||
var persistenceService = this.persistenceService,
|
||||
$q = this.$q,
|
||||
spaces = this.spaces;
|
||||
spaces = this.spaces,
|
||||
space = this.space,
|
||||
now = this.now;
|
||||
|
||||
// Load a single object model from any persistence spaces
|
||||
function loadModel(id) {
|
||||
@ -72,11 +76,24 @@ define(
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure that models read from persistence have some
|
||||
// sensible timestamp indicating they've been persisted.
|
||||
function addPersistedTimestamp(model) {
|
||||
if (model && (model.persisted === undefined)) {
|
||||
model.persisted = model.modified !== undefined ?
|
||||
model.modified : now();
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
// Package the result as id->model
|
||||
function packageResult(models) {
|
||||
var result = {};
|
||||
ids.forEach(function (id, index) {
|
||||
result[id] = models[index];
|
||||
if (models[index]) {
|
||||
result[id] = addPersistedTimestamp(models[index]);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
@ -36,11 +36,16 @@ define(
|
||||
*
|
||||
* Returns a function that, when invoked, will invoke `fn` after
|
||||
* `delay` milliseconds, only if no other invocations are pending.
|
||||
* The optional argument `apply` determines whether.
|
||||
* The optional argument `apply` determines whether or not a
|
||||
* digest cycle should be triggered.
|
||||
*
|
||||
* The returned function will itself return a `Promise` which will
|
||||
* resolve to the returned value of `fn` whenever that is invoked.
|
||||
*
|
||||
* In cases where arguments are provided, only the most recent
|
||||
* set of arguments will be passed on to the throttled function
|
||||
* at the time it is executed.
|
||||
*
|
||||
* @returns {Function}
|
||||
* @memberof platform/core
|
||||
*/
|
||||
@ -56,12 +61,14 @@ define(
|
||||
* @memberof platform/core.Throttle#
|
||||
*/
|
||||
return function (fn, delay, apply) {
|
||||
var activeTimeout;
|
||||
var promise,
|
||||
args = [];
|
||||
|
||||
// Clear active timeout, so that next invocation starts
|
||||
// a new one.
|
||||
function clearActiveTimeout() {
|
||||
activeTimeout = undefined;
|
||||
function invoke() {
|
||||
// Clear the active timeout so a new one starts next time.
|
||||
promise = undefined;
|
||||
// Invoke the function with the latest supplied arguments.
|
||||
return fn.apply(null, args);
|
||||
}
|
||||
|
||||
// Defaults
|
||||
@ -69,14 +76,13 @@ define(
|
||||
apply = apply || false;
|
||||
|
||||
return function () {
|
||||
// Store arguments from this invocation
|
||||
args = Array.prototype.slice.apply(arguments, [0]);
|
||||
// Start a timeout if needed
|
||||
if (!activeTimeout) {
|
||||
activeTimeout = $timeout(fn, delay, apply);
|
||||
activeTimeout.then(clearActiveTimeout);
|
||||
}
|
||||
promise = promise || $timeout(invoke, delay, apply);
|
||||
// Return whichever timeout is active (to get
|
||||
// a promise for the results of fn)
|
||||
return activeTimeout;
|
||||
return promise;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ define(
|
||||
SPACE = "space0",
|
||||
spaces = [ "space1" ],
|
||||
modTimes,
|
||||
mockNow,
|
||||
provider;
|
||||
|
||||
function mockPromise(value) {
|
||||
@ -55,19 +56,33 @@ define(
|
||||
beforeEach(function () {
|
||||
modTimes = {};
|
||||
mockQ = { when: mockPromise, all: mockAll };
|
||||
mockPersistenceService = {
|
||||
readObject: function (space, id) {
|
||||
mockPersistenceService = jasmine.createSpyObj(
|
||||
'persistenceService',
|
||||
[
|
||||
'createObject',
|
||||
'readObject',
|
||||
'updateObject',
|
||||
'deleteObject',
|
||||
'listSpaces',
|
||||
'listObjects'
|
||||
]
|
||||
);
|
||||
mockNow = jasmine.createSpy("now");
|
||||
|
||||
mockPersistenceService.readObject
|
||||
.andCallFake(function (space, id) {
|
||||
return mockPromise({
|
||||
space: space,
|
||||
id: id,
|
||||
modified: (modTimes[space] || {})[id]
|
||||
modified: (modTimes[space] || {})[id],
|
||||
persisted: 0
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
provider = new PersistedModelProvider(
|
||||
mockPersistenceService,
|
||||
mockQ,
|
||||
mockNow,
|
||||
SPACE,
|
||||
spaces
|
||||
);
|
||||
@ -81,12 +96,13 @@ define(
|
||||
});
|
||||
|
||||
expect(models).toEqual({
|
||||
a: { space: SPACE, id: "a" },
|
||||
x: { space: SPACE, id: "x" },
|
||||
zz: { space: SPACE, id: "zz" }
|
||||
a: { space: SPACE, id: "a", persisted: 0 },
|
||||
x: { space: SPACE, id: "x", persisted: 0 },
|
||||
zz: { space: SPACE, id: "zz", persisted: 0 }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("reads object models from multiple spaces", function () {
|
||||
var models;
|
||||
|
||||
@ -99,9 +115,36 @@ define(
|
||||
});
|
||||
|
||||
expect(models).toEqual({
|
||||
a: { space: SPACE, id: "a" },
|
||||
x: { space: 'space1', id: "x", modified: 12321 },
|
||||
zz: { space: SPACE, id: "zz" }
|
||||
a: { space: SPACE, id: "a", persisted: 0 },
|
||||
x: { space: 'space1', id: "x", modified: 12321, persisted: 0 },
|
||||
zz: { space: SPACE, id: "zz", persisted: 0 }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("ensures that persisted timestamps are present", function () {
|
||||
var mockCallback = jasmine.createSpy("callback"),
|
||||
testModels = {
|
||||
a: { modified: 123, persisted: 1984, name: "A" },
|
||||
b: { persisted: 1977, name: "B" },
|
||||
c: { modified: 42, name: "C" },
|
||||
d: { name: "D" }
|
||||
};
|
||||
|
||||
mockPersistenceService.readObject.andCallFake(
|
||||
function (space, id) {
|
||||
return mockPromise(testModels[id]);
|
||||
}
|
||||
);
|
||||
mockNow.andReturn(12321);
|
||||
|
||||
provider.getModels(Object.keys(testModels)).then(mockCallback);
|
||||
|
||||
expect(mockCallback).toHaveBeenCalledWith({
|
||||
a: { modified: 123, persisted: 1984, name: "A" },
|
||||
b: { persisted: 1977, name: "B" },
|
||||
c: { modified: 42, persisted: 42, name: "C" },
|
||||
d: { persisted: 12321, name: "D" }
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -45,7 +45,9 @@ define(
|
||||
// Verify precondition: Not called at throttle-time
|
||||
expect(mockTimeout).not.toHaveBeenCalled();
|
||||
expect(throttled()).toEqual(mockPromise);
|
||||
expect(mockTimeout).toHaveBeenCalledWith(mockFn, 0, false);
|
||||
expect(mockFn).not.toHaveBeenCalled();
|
||||
expect(mockTimeout)
|
||||
.toHaveBeenCalledWith(jasmine.any(Function), 0, false);
|
||||
});
|
||||
|
||||
it("schedules only one timeout at a time", function () {
|
||||
@ -59,10 +61,11 @@ define(
|
||||
it("schedules additional invocations after resolution", function () {
|
||||
var throttled = throttle(mockFn);
|
||||
throttled();
|
||||
mockPromise.then.mostRecentCall.args[0](); // Resolve timeout
|
||||
mockTimeout.mostRecentCall.args[0](); // Resolve timeout
|
||||
throttled();
|
||||
mockPromise.then.mostRecentCall.args[0]();
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
throttled();
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
expect(mockTimeout.calls.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
9
platform/features/conductor/README.md
Normal file
9
platform/features/conductor/README.md
Normal file
@ -0,0 +1,9 @@
|
||||
Provides the time conductor, a control which appears at the
|
||||
bottom of the screen allowing telemetry start and end times
|
||||
to be modified.
|
||||
|
||||
Note that the term "time controller" is generally preferred
|
||||
outside of the code base (e.g. in UI documents, issues, etc.);
|
||||
the term "time conductor" is being used in code to avoid
|
||||
confusion with "controllers" in the Model-View-Controller
|
||||
sense.
|
46
platform/features/conductor/bundle.json
Normal file
46
platform/features/conductor/bundle.json
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"extensions": {
|
||||
"representers": [
|
||||
{
|
||||
"implementation": "ConductorRepresenter.js",
|
||||
"depends": [
|
||||
"throttle",
|
||||
"conductorService",
|
||||
"$compile",
|
||||
"views[]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"type": "decorator",
|
||||
"provides": "telemetryService",
|
||||
"implementation": "ConductorTelemetryDecorator.js",
|
||||
"depends": [ "conductorService" ]
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key": "conductorService",
|
||||
"implementation": "ConductorService.js",
|
||||
"depends": [ "now", "TIME_CONDUCTOR_DOMAINS" ]
|
||||
}
|
||||
],
|
||||
"templates": [
|
||||
{
|
||||
"key": "time-conductor",
|
||||
"templateUrl": "templates/time-conductor.html"
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"key": "TIME_CONDUCTOR_DOMAINS",
|
||||
"value": [
|
||||
{ "key": "time", "name": "Time" },
|
||||
{ "key": "yesterday", "name": "Yesterday" }
|
||||
],
|
||||
"comment": "Placeholder; to be replaced by inspection of available domains."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<mct-include key="'time-controller'"
|
||||
ng-model='ngModel.conductor'>
|
||||
</mct-include>
|
||||
<mct-control key="'select'"
|
||||
ng-model='ngModel'
|
||||
field="'domain'"
|
||||
options="ngModel.options"
|
||||
style="position: absolute; right: 0px; bottom: 46px;"
|
||||
>
|
||||
</mct-control>
|
201
platform/features/conductor/src/ConductorRepresenter.js
Normal file
201
platform/features/conductor/src/ConductorRepresenter.js
Normal file
@ -0,0 +1,201 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
var TEMPLATE = [
|
||||
"<mct-include key=\"'time-conductor'\" ng-model='ngModel' class='l-time-controller'>",
|
||||
"</mct-include>"
|
||||
].join(''),
|
||||
THROTTLE_MS = 200,
|
||||
GLOBAL_SHOWING = false;
|
||||
|
||||
/**
|
||||
* The ConductorRepresenter attaches the universal time conductor
|
||||
* to views.
|
||||
*
|
||||
* @implements {Representer}
|
||||
* @constructor
|
||||
* @memberof platform/features/conductor
|
||||
* @param {Function} throttle a function used to reduce the frequency
|
||||
* of function invocations
|
||||
* @param {platform/features/conductor.ConductorService} conductorService
|
||||
* service which provides the active time conductor
|
||||
* @param $compile Angular's $compile
|
||||
* @param {ViewDefinition[]} views all defined views
|
||||
* @param {Scope} the scope of the representation
|
||||
* @param element the jqLite-wrapped representation element
|
||||
*/
|
||||
function ConductorRepresenter(
|
||||
throttle,
|
||||
conductorService,
|
||||
$compile,
|
||||
views,
|
||||
scope,
|
||||
element
|
||||
) {
|
||||
this.throttle = throttle;
|
||||
this.scope = scope;
|
||||
this.conductorService = conductorService;
|
||||
this.element = element;
|
||||
this.views = views;
|
||||
this.$compile = $compile;
|
||||
}
|
||||
|
||||
// Update the time conductor from the scope
|
||||
ConductorRepresenter.prototype.wireScope = function () {
|
||||
var conductor = this.conductorService.getConductor(),
|
||||
conductorScope = this.conductorScope(),
|
||||
repScope = this.scope,
|
||||
lastObservedBounds,
|
||||
broadcastBounds;
|
||||
|
||||
// Combine start/end times into a single object
|
||||
function bounds(start, end) {
|
||||
return {
|
||||
start: conductor.displayStart(),
|
||||
end: conductor.displayEnd(),
|
||||
domain: conductor.domain()
|
||||
};
|
||||
}
|
||||
|
||||
function boundsAreStable(newlyObservedBounds) {
|
||||
return !lastObservedBounds ||
|
||||
(lastObservedBounds.start === newlyObservedBounds.start &&
|
||||
lastObservedBounds.end === newlyObservedBounds.end);
|
||||
}
|
||||
|
||||
function updateConductorInner() {
|
||||
var innerBounds = conductorScope.ngModel.conductor.inner;
|
||||
conductor.displayStart(innerBounds.start);
|
||||
conductor.displayEnd(innerBounds.end);
|
||||
lastObservedBounds = lastObservedBounds || bounds();
|
||||
broadcastBounds();
|
||||
}
|
||||
|
||||
function updateDomain(value) {
|
||||
conductor.domain(value);
|
||||
repScope.$broadcast('telemetry:display:bounds', bounds(
|
||||
conductor.displayStart(),
|
||||
conductor.displayEnd(),
|
||||
conductor.domain()
|
||||
));
|
||||
}
|
||||
|
||||
// telemetry domain metadata -> option for a select control
|
||||
function makeOption(domainOption) {
|
||||
return {
|
||||
name: domainOption.name,
|
||||
value: domainOption.key
|
||||
};
|
||||
}
|
||||
|
||||
broadcastBounds = this.throttle(function () {
|
||||
var newlyObservedBounds = bounds();
|
||||
|
||||
if (boundsAreStable(newlyObservedBounds)) {
|
||||
repScope.$broadcast('telemetry:display:bounds', bounds());
|
||||
lastObservedBounds = undefined;
|
||||
} else {
|
||||
lastObservedBounds = newlyObservedBounds;
|
||||
broadcastBounds();
|
||||
}
|
||||
}, THROTTLE_MS);
|
||||
|
||||
conductorScope.ngModel = {};
|
||||
conductorScope.ngModel.conductor =
|
||||
{ outer: bounds(), inner: bounds() };
|
||||
conductorScope.ngModel.options =
|
||||
conductor.domainOptions().map(makeOption);
|
||||
conductorScope.ngModel.domain = conductor.domain();
|
||||
|
||||
conductorScope
|
||||
.$watch('ngModel.conductor.inner.start', updateConductorInner);
|
||||
conductorScope
|
||||
.$watch('ngModel.conductor.inner.end', updateConductorInner);
|
||||
conductorScope
|
||||
.$watch('ngModel.domain', updateDomain);
|
||||
|
||||
repScope.$on('telemetry:view', updateConductorInner);
|
||||
};
|
||||
|
||||
ConductorRepresenter.prototype.conductorScope = function (s) {
|
||||
return (this.cScope = arguments.length > 0 ? s : this.cScope);
|
||||
};
|
||||
|
||||
// Handle a specific representation of a specific domain object
|
||||
ConductorRepresenter.prototype.represent = function represent(representation, representedObject) {
|
||||
this.destroy();
|
||||
|
||||
if (this.views.indexOf(representation) !== -1 && !GLOBAL_SHOWING) {
|
||||
// Track original states
|
||||
this.originalHeight = this.element.css('height');
|
||||
this.hadAbs = this.element.hasClass('abs');
|
||||
|
||||
// Create a new scope for the conductor
|
||||
this.conductorScope(this.scope.$new());
|
||||
this.wireScope();
|
||||
this.conductorElement =
|
||||
this.$compile(TEMPLATE)(this.conductorScope());
|
||||
this.element.after(this.conductorElement[0]);
|
||||
this.element.addClass('l-controls-visible l-time-controller-visible');
|
||||
GLOBAL_SHOWING = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Respond to the destruction of the current representation.
|
||||
ConductorRepresenter.prototype.destroy = function destroy() {
|
||||
// We may not have decided to show in the first place,
|
||||
// so circumvent any unnecessary cleanup
|
||||
if (!this.conductorElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore the original size of the mct-representation
|
||||
if (!this.hadAbs) {
|
||||
this.element.removeClass('abs');
|
||||
}
|
||||
this.element.css('height', this.originalHeight);
|
||||
|
||||
// ...and remove the conductor
|
||||
if (this.conductorElement) {
|
||||
this.conductorElement.remove();
|
||||
this.conductorElement = undefined;
|
||||
}
|
||||
|
||||
// Finally, destroy its scope
|
||||
if (this.conductorScope()) {
|
||||
this.conductorScope().$destroy();
|
||||
this.conductorScope(undefined);
|
||||
}
|
||||
|
||||
GLOBAL_SHOWING = false;
|
||||
};
|
||||
|
||||
return ConductorRepresenter;
|
||||
}
|
||||
);
|
||||
|
64
platform/features/conductor/src/ConductorService.js
Normal file
64
platform/features/conductor/src/ConductorService.js
Normal file
@ -0,0 +1,64 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
['./TimeConductor'],
|
||||
function (TimeConductor) {
|
||||
'use strict';
|
||||
|
||||
var ONE_DAY_IN_MS = 1000 * 60 * 60 * 24,
|
||||
SIX_HOURS_IN_MS = ONE_DAY_IN_MS / 4;
|
||||
|
||||
/**
|
||||
* Provides a single global instance of the time conductor, which
|
||||
* controls both query ranges and displayed ranges for telemetry
|
||||
* data.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform/features/conductor
|
||||
* @param {Function} now a function which returns the current time
|
||||
* as a UNIX timestamp, in milliseconds
|
||||
*/
|
||||
function ConductorService(now, domains) {
|
||||
var initialEnd =
|
||||
Math.ceil(now() / SIX_HOURS_IN_MS) * SIX_HOURS_IN_MS;
|
||||
|
||||
this.conductor = new TimeConductor(
|
||||
initialEnd - ONE_DAY_IN_MS,
|
||||
initialEnd,
|
||||
domains
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the global instance of the time conductor.
|
||||
* @returns {platform/features/conductor.TimeConductor} the
|
||||
* time conductor
|
||||
*/
|
||||
ConductorService.prototype.getConductor = function () {
|
||||
return this.conductor;
|
||||
};
|
||||
|
||||
return ConductorService;
|
||||
}
|
||||
);
|
@ -0,0 +1,76 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Decorates the `telemetryService` such that requests are
|
||||
* mediated by the time conductor.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof platform/features/conductor
|
||||
* @implements {TelemetryService}
|
||||
* @param {platform/features/conductor.ConductorService} conductorServe
|
||||
* the service which exposes the global time conductor
|
||||
* @param {TelemetryService} telemetryService the decorated service
|
||||
*/
|
||||
function ConductorTelemetryDecorator(conductorService, telemetryService) {
|
||||
this.conductorService = conductorService;
|
||||
this.telemetryService = telemetryService;
|
||||
}
|
||||
|
||||
ConductorTelemetryDecorator.prototype.amendRequests = function (requests) {
|
||||
var conductor = this.conductorService.getConductor(),
|
||||
start = conductor.displayStart(),
|
||||
end = conductor.displayEnd(),
|
||||
domain = conductor.domain();
|
||||
|
||||
function amendRequest(request) {
|
||||
request = request || {};
|
||||
request.start = start;
|
||||
request.end = end;
|
||||
request.domain = domain;
|
||||
return request;
|
||||
}
|
||||
|
||||
return (requests || []).map(amendRequest);
|
||||
};
|
||||
|
||||
ConductorTelemetryDecorator.prototype.requestTelemetry = function (requests) {
|
||||
var self = this;
|
||||
return this.telemetryService
|
||||
.requestTelemetry(this.amendRequests(requests));
|
||||
};
|
||||
|
||||
ConductorTelemetryDecorator.prototype.subscribe = function (callback, requests) {
|
||||
var self = this;
|
||||
|
||||
return this.telemetryService
|
||||
.subscribe(callback, this.amendRequests(requests));
|
||||
};
|
||||
|
||||
return ConductorTelemetryDecorator;
|
||||
}
|
||||
);
|
103
platform/features/conductor/src/TimeConductor.js
Normal file
103
platform/features/conductor/src/TimeConductor.js
Normal file
@ -0,0 +1,103 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
/**
|
||||
* The time conductor bundle adds a global control to the bottom of the
|
||||
* outermost viewing area. This controls both the range for time-based
|
||||
* queries and for time-based displays.
|
||||
*
|
||||
* @namespace platform/features/conductor
|
||||
*/
|
||||
define(
|
||||
function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Tracks the current state of the time conductor.
|
||||
*
|
||||
* @memberof platform/features/conductor
|
||||
* @constructor
|
||||
* @param {number} start the initial start time
|
||||
* @param {number} end the initial end time
|
||||
*/
|
||||
function TimeConductor(start, end, domains) {
|
||||
this.range = { start: start, end: end };
|
||||
this.domains = domains;
|
||||
this.activeDomain = domains[0].key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set (if called with an argument) the start time for displays.
|
||||
* @param {number} [value] the start time to set
|
||||
* @returns {number} the start time
|
||||
*/
|
||||
TimeConductor.prototype.displayStart = function (value) {
|
||||
if (arguments.length > 0) {
|
||||
this.range.start = value;
|
||||
}
|
||||
return this.range.start;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get or set (if called with an argument) the end time for displays.
|
||||
* @param {number} [value] the end time to set
|
||||
* @returns {number} the end time
|
||||
*/
|
||||
TimeConductor.prototype.displayEnd = function (value) {
|
||||
if (arguments.length > 0) {
|
||||
this.range.end = value;
|
||||
}
|
||||
return this.range.end;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get available domain options which can be used to bound time
|
||||
* selection.
|
||||
* @returns {TelemetryDomain[]} available domains
|
||||
*/
|
||||
TimeConductor.prototype.domainOptions = function () {
|
||||
return this.domains;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get or set (if called with an argument) the active domain.
|
||||
* @param {string} [key] the key identifying the domain choice
|
||||
* @returns {TelemetryDomain} the active telemetry domain
|
||||
*/
|
||||
TimeConductor.prototype.domain = function (key) {
|
||||
function matchesKey(domain) {
|
||||
return domain.key === key;
|
||||
}
|
||||
|
||||
if (arguments.length > 0) {
|
||||
if (!this.domains.some(matchesKey)) {
|
||||
throw new Error("Unknown domain " + key);
|
||||
}
|
||||
this.activeDomain = key;
|
||||
}
|
||||
return this.activeDomain;
|
||||
};
|
||||
|
||||
return TimeConductor;
|
||||
}
|
||||
);
|
259
platform/features/conductor/test/ConductorRepresenterSpec.js
Normal file
259
platform/features/conductor/test/ConductorRepresenterSpec.js
Normal file
@ -0,0 +1,259 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,afterEach,jasmine*/
|
||||
|
||||
define(
|
||||
["../src/ConductorRepresenter", "./TestTimeConductor"],
|
||||
function (ConductorRepresenter, TestTimeConductor) {
|
||||
"use strict";
|
||||
|
||||
var SCOPE_METHODS = [
|
||||
'$on',
|
||||
'$watch',
|
||||
'$broadcast',
|
||||
'$emit',
|
||||
'$new',
|
||||
'$destroy'
|
||||
],
|
||||
ELEMENT_METHODS = [
|
||||
'hasClass',
|
||||
'addClass',
|
||||
'removeClass',
|
||||
'css',
|
||||
'after',
|
||||
'remove'
|
||||
];
|
||||
|
||||
describe("ConductorRepresenter", function () {
|
||||
var mockThrottle,
|
||||
mockConductorService,
|
||||
mockCompile,
|
||||
testViews,
|
||||
mockScope,
|
||||
mockElement,
|
||||
mockConductor,
|
||||
mockCompiledTemplate,
|
||||
mockNewScope,
|
||||
mockNewElement,
|
||||
representer;
|
||||
|
||||
function fireWatch(scope, watch, value) {
|
||||
scope.$watch.calls.forEach(function (call) {
|
||||
if (call.args[0] === watch) {
|
||||
call.args[1](value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockThrottle = jasmine.createSpy('throttle');
|
||||
mockConductorService = jasmine.createSpyObj(
|
||||
'conductorService',
|
||||
['getConductor']
|
||||
);
|
||||
mockCompile = jasmine.createSpy('$compile');
|
||||
testViews = [ { someKey: "some value" } ];
|
||||
mockScope = jasmine.createSpyObj('scope', SCOPE_METHODS);
|
||||
mockElement = jasmine.createSpyObj('element', ELEMENT_METHODS);
|
||||
mockConductor = new TestTimeConductor();
|
||||
mockCompiledTemplate = jasmine.createSpy('template');
|
||||
mockNewScope = jasmine.createSpyObj('newScope', SCOPE_METHODS);
|
||||
mockNewElement = jasmine.createSpyObj('newElement', ELEMENT_METHODS);
|
||||
mockNewElement[0] = mockNewElement;
|
||||
|
||||
mockConductorService.getConductor.andReturn(mockConductor);
|
||||
mockCompile.andReturn(mockCompiledTemplate);
|
||||
mockCompiledTemplate.andReturn(mockNewElement);
|
||||
mockScope.$new.andReturn(mockNewScope);
|
||||
mockThrottle.andCallFake(function (fn) {
|
||||
return fn;
|
||||
});
|
||||
|
||||
representer = new ConductorRepresenter(
|
||||
mockThrottle,
|
||||
mockConductorService,
|
||||
mockCompile,
|
||||
testViews,
|
||||
mockScope,
|
||||
mockElement
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
representer.destroy();
|
||||
});
|
||||
|
||||
it("adds a conductor to views", function () {
|
||||
representer.represent(testViews[0], {});
|
||||
expect(mockElement.after).toHaveBeenCalledWith(mockNewElement);
|
||||
});
|
||||
|
||||
it("adds nothing to non-view representations", function () {
|
||||
representer.represent({ someKey: "something else" }, {});
|
||||
expect(mockElement.after).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("removes the conductor when destroyed", function () {
|
||||
representer.represent(testViews[0], {});
|
||||
expect(mockNewElement.remove).not.toHaveBeenCalled();
|
||||
representer.destroy();
|
||||
expect(mockNewElement.remove).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("destroys any new scope created", function () {
|
||||
representer.represent(testViews[0], {});
|
||||
representer.destroy();
|
||||
expect(mockNewScope.$destroy.calls.length)
|
||||
.toEqual(mockScope.$new.calls.length);
|
||||
});
|
||||
|
||||
it("exposes conductor state in scope", function () {
|
||||
mockConductor.displayStart.andReturn(1977);
|
||||
mockConductor.displayEnd.andReturn(1984);
|
||||
mockConductor.domain.andReturn('d');
|
||||
representer.represent(testViews[0], {});
|
||||
|
||||
expect(mockNewScope.ngModel.conductor).toEqual({
|
||||
inner: { start: 1977, end: 1984, domain: 'd' },
|
||||
outer: { start: 1977, end: 1984, domain: 'd' }
|
||||
});
|
||||
});
|
||||
|
||||
it("updates conductor state from scope", function () {
|
||||
var testState = {
|
||||
inner: { start: 42, end: 1984 },
|
||||
outer: { start: -1977, end: 12321 }
|
||||
};
|
||||
|
||||
representer.represent(testViews[0], {});
|
||||
|
||||
mockNewScope.ngModel.conductor = testState;
|
||||
|
||||
fireWatch(
|
||||
mockNewScope,
|
||||
'ngModel.conductor.inner.start',
|
||||
testState.inner.start
|
||||
);
|
||||
expect(mockConductor.displayStart).toHaveBeenCalledWith(42);
|
||||
|
||||
fireWatch(
|
||||
mockNewScope,
|
||||
'ngModel.conductor.inner.end',
|
||||
testState.inner.end
|
||||
);
|
||||
expect(mockConductor.displayEnd).toHaveBeenCalledWith(1984);
|
||||
});
|
||||
|
||||
describe("when bounds are changing", function () {
|
||||
var startWatch = "ngModel.conductor.inner.start",
|
||||
endWatch = "ngModel.conductor.inner.end",
|
||||
mockThrottledFn = jasmine.createSpy('throttledFn'),
|
||||
testBounds;
|
||||
|
||||
function fireThrottledFn() {
|
||||
mockThrottle.mostRecentCall.args[0]();
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockThrottle.andReturn(mockThrottledFn);
|
||||
representer.represent(testViews[0], {});
|
||||
testBounds = { start: 0, end: 1000 };
|
||||
mockNewScope.ngModel.conductor.inner = testBounds;
|
||||
mockConductor.displayStart.andCallFake(function () {
|
||||
return testBounds.start;
|
||||
});
|
||||
mockConductor.displayEnd.andCallFake(function () {
|
||||
return testBounds.end;
|
||||
});
|
||||
});
|
||||
|
||||
it("does not broadcast while bounds are changing", function () {
|
||||
expect(mockScope.$broadcast).not.toHaveBeenCalled();
|
||||
testBounds.start = 100;
|
||||
fireWatch(mockNewScope, startWatch, testBounds.start);
|
||||
testBounds.end = 500;
|
||||
fireWatch(mockNewScope, endWatch, testBounds.end);
|
||||
fireThrottledFn();
|
||||
testBounds.start = 200;
|
||||
fireWatch(mockNewScope, startWatch, testBounds.start);
|
||||
testBounds.end = 400;
|
||||
fireWatch(mockNewScope, endWatch, testBounds.end);
|
||||
fireThrottledFn();
|
||||
expect(mockScope.$broadcast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does broadcast when bounds have stabilized", function () {
|
||||
expect(mockScope.$broadcast).not.toHaveBeenCalled();
|
||||
testBounds.start = 100;
|
||||
fireWatch(mockNewScope, startWatch, testBounds.start);
|
||||
testBounds.end = 500;
|
||||
fireWatch(mockNewScope, endWatch, testBounds.end);
|
||||
fireThrottledFn();
|
||||
fireWatch(mockNewScope, startWatch, testBounds.start);
|
||||
fireWatch(mockNewScope, endWatch, testBounds.end);
|
||||
fireThrottledFn();
|
||||
expect(mockScope.$broadcast).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("exposes domain selection in scope", function () {
|
||||
representer.represent(testViews[0], null);
|
||||
|
||||
expect(mockNewScope.ngModel.domain)
|
||||
.toEqual(mockConductor.domain());
|
||||
});
|
||||
|
||||
it("exposes domain options in scope", function () {
|
||||
representer.represent(testViews[0], null);
|
||||
|
||||
mockConductor.domainOptions().forEach(function (option, i) {
|
||||
expect(mockNewScope.ngModel.options[i].value)
|
||||
.toEqual(option.key);
|
||||
expect(mockNewScope.ngModel.options[i].name)
|
||||
.toEqual(option.name);
|
||||
});
|
||||
});
|
||||
|
||||
it("updates domain selection from scope", function () {
|
||||
var choice;
|
||||
representer.represent(testViews[0], null);
|
||||
|
||||
// Choose a domain that isn't currently selected
|
||||
mockNewScope.ngModel.options.forEach(function (option) {
|
||||
if (option.value !== mockNewScope.ngModel.domain) {
|
||||
choice = option.value;
|
||||
}
|
||||
});
|
||||
|
||||
expect(mockConductor.domain)
|
||||
.not.toHaveBeenCalledWith(choice);
|
||||
|
||||
mockNewScope.ngModel.domain = choice;
|
||||
fireWatch(mockNewScope, "ngModel.domain", choice);
|
||||
|
||||
expect(mockConductor.domain)
|
||||
.toHaveBeenCalledWith(choice);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
58
platform/features/conductor/test/ConductorServiceSpec.js
Normal file
58
platform/features/conductor/test/ConductorServiceSpec.js
Normal file
@ -0,0 +1,58 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||
|
||||
define(
|
||||
["../src/ConductorService"],
|
||||
function (ConductorService) {
|
||||
"use strict";
|
||||
|
||||
var TEST_NOW = 1020304050;
|
||||
|
||||
describe("ConductorService", function () {
|
||||
var mockNow,
|
||||
conductorService;
|
||||
|
||||
beforeEach(function () {
|
||||
mockNow = jasmine.createSpy('now');
|
||||
mockNow.andReturn(TEST_NOW);
|
||||
conductorService = new ConductorService(mockNow, [
|
||||
{ key: "d1", name: "Domain #1" },
|
||||
{ key: "d2", name: "Domain #2" }
|
||||
]);
|
||||
});
|
||||
|
||||
it("initializes a time conductor around the current time", function () {
|
||||
var conductor = conductorService.getConductor();
|
||||
expect(conductor.displayStart() <= TEST_NOW).toBeTruthy();
|
||||
expect(conductor.displayEnd() >= TEST_NOW).toBeTruthy();
|
||||
expect(conductor.displayEnd() > conductor.displayStart())
|
||||
.toBeTruthy();
|
||||
});
|
||||
|
||||
it("provides a single shared time conductor instance", function () {
|
||||
expect(conductorService.getConductor())
|
||||
.toBe(conductorService.getConductor());
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -0,0 +1,160 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||
|
||||
|
||||
define(
|
||||
["../src/ConductorTelemetryDecorator", "./TestTimeConductor"],
|
||||
function (ConductorTelemetryDecorator, TestTimeConductor) {
|
||||
"use strict";
|
||||
|
||||
describe("ConductorTelemetryDecorator", function () {
|
||||
var mockTelemetryService,
|
||||
mockConductorService,
|
||||
mockConductor,
|
||||
mockPromise,
|
||||
mockSeries,
|
||||
decorator;
|
||||
|
||||
function seriesIsInWindow(series) {
|
||||
var i, v, inWindow = true;
|
||||
for (i = 0; i < series.getPointCount(); i += 1) {
|
||||
v = series.getDomainValue(i);
|
||||
inWindow = inWindow && (v >= mockConductor.displayStart());
|
||||
inWindow = inWindow && (v <= mockConductor.displayEnd());
|
||||
}
|
||||
return inWindow;
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockTelemetryService = jasmine.createSpyObj(
|
||||
'telemetryService',
|
||||
[ 'requestTelemetry', 'subscribe' ]
|
||||
);
|
||||
mockConductorService = jasmine.createSpyObj(
|
||||
'conductorService',
|
||||
['getConductor']
|
||||
);
|
||||
mockConductor = new TestTimeConductor();
|
||||
mockPromise = jasmine.createSpyObj(
|
||||
'promise',
|
||||
['then']
|
||||
);
|
||||
mockSeries = jasmine.createSpyObj(
|
||||
'series',
|
||||
[ 'getPointCount', 'getDomainValue', 'getRangeValue' ]
|
||||
);
|
||||
|
||||
mockTelemetryService.requestTelemetry.andReturn(mockPromise);
|
||||
mockConductorService.getConductor.andReturn(mockConductor);
|
||||
|
||||
// Prepare test series; make sure it has a broad range of
|
||||
// domain values, with at least some in the query-able range
|
||||
mockSeries.getPointCount.andReturn(1000);
|
||||
mockSeries.getDomainValue.andCallFake(function (i) {
|
||||
var j = i - 500;
|
||||
return j * j * j;
|
||||
});
|
||||
|
||||
mockConductor.displayStart.andReturn(42);
|
||||
mockConductor.displayEnd.andReturn(1977);
|
||||
mockConductor.domain.andReturn("testDomain");
|
||||
|
||||
decorator = new ConductorTelemetryDecorator(
|
||||
mockConductorService,
|
||||
mockTelemetryService
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
describe("decorates historical requests", function () {
|
||||
var request;
|
||||
|
||||
beforeEach(function () {
|
||||
decorator.requestTelemetry([{ someKey: "some value" }]);
|
||||
request = mockTelemetryService.requestTelemetry
|
||||
.mostRecentCall.args[0][0];
|
||||
});
|
||||
|
||||
it("with start times", function () {
|
||||
expect(request.start).toEqual(mockConductor.displayStart());
|
||||
});
|
||||
|
||||
it("with end times", function () {
|
||||
expect(request.end).toEqual(mockConductor.displayEnd());
|
||||
});
|
||||
|
||||
it("with domain selection", function () {
|
||||
expect(request.domain).toEqual(mockConductor.domain());
|
||||
});
|
||||
});
|
||||
|
||||
describe("decorates subscription requests", function () {
|
||||
var request;
|
||||
|
||||
beforeEach(function () {
|
||||
var mockCallback = jasmine.createSpy('callback');
|
||||
decorator.subscribe(mockCallback, [{ someKey: "some value" }]);
|
||||
request = mockTelemetryService.subscribe
|
||||
.mostRecentCall.args[1][0];
|
||||
});
|
||||
|
||||
it("with start times", function () {
|
||||
expect(request.start).toEqual(mockConductor.displayStart());
|
||||
});
|
||||
|
||||
it("with end times", function () {
|
||||
expect(request.end).toEqual(mockConductor.displayEnd());
|
||||
});
|
||||
|
||||
it("with domain selection", function () {
|
||||
expect(request.domain).toEqual(mockConductor.domain());
|
||||
});
|
||||
});
|
||||
|
||||
it("adds display start/end times & domain selection to historical requests", function () {
|
||||
decorator.requestTelemetry([{ someKey: "some value" }]);
|
||||
expect(mockTelemetryService.requestTelemetry)
|
||||
.toHaveBeenCalledWith([{
|
||||
someKey: "some value",
|
||||
start: mockConductor.displayStart(),
|
||||
end: mockConductor.displayEnd(),
|
||||
domain: jasmine.any(String)
|
||||
}]);
|
||||
});
|
||||
|
||||
it("adds display start/end times & domain selection to subscription requests", function () {
|
||||
var mockCallback = jasmine.createSpy('callback');
|
||||
decorator.subscribe(mockCallback, [{ someKey: "some value" }]);
|
||||
expect(mockTelemetryService.subscribe)
|
||||
.toHaveBeenCalledWith(jasmine.any(Function), [{
|
||||
someKey: "some value",
|
||||
start: mockConductor.displayStart(),
|
||||
end: mockConductor.displayEnd(),
|
||||
domain: jasmine.any(String)
|
||||
}]);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
);
|
50
platform/features/conductor/test/TestTimeConductor.js
Normal file
50
platform/features/conductor/test/TestTimeConductor.js
Normal file
@ -0,0 +1,50 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT Web includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine,spyOn*/
|
||||
|
||||
define(
|
||||
["../src/TimeConductor"],
|
||||
function (TimeConductor) {
|
||||
'use strict';
|
||||
|
||||
function TestTimeConductor() {
|
||||
var self = this;
|
||||
|
||||
TimeConductor.apply(this, [
|
||||
402514200000,
|
||||
444546000000,
|
||||
[
|
||||
{ key: "domain0", name: "Domain #1" },
|
||||
{ key: "domain1", name: "Domain #2" }
|
||||
]
|
||||
]);
|
||||
|
||||
Object.keys(TimeConductor.prototype).forEach(function (method) {
|
||||
spyOn(self, method).andCallThrough();
|
||||
});
|
||||
}
|
||||
|
||||
TestTimeConductor.prototype = TimeConductor.prototype;
|
||||
|
||||
return TestTimeConductor;
|
||||
}
|
||||
);
|
78
platform/features/conductor/test/TimeConductorSpec.js
Normal file
78
platform/features/conductor/test/TimeConductorSpec.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.
|
||||
*****************************************************************************/
|
||||
/*global define,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
||||
|
||||
define(
|
||||
["../src/TimeConductor"],
|
||||
function (TimeConductor) {
|
||||
"use strict";
|
||||
|
||||
describe("TimeConductor", function () {
|
||||
var testStart,
|
||||
testEnd,
|
||||
testDomains,
|
||||
conductor;
|
||||
|
||||
beforeEach(function () {
|
||||
testStart = 42;
|
||||
testEnd = 12321;
|
||||
testDomains = [
|
||||
{ key: "d1", name: "Domain #1" },
|
||||
{ key: "d2", name: "Domain #2" }
|
||||
];
|
||||
conductor = new TimeConductor(testStart, testEnd, testDomains);
|
||||
});
|
||||
|
||||
it("provides accessors for query/display start/end times", function () {
|
||||
expect(conductor.displayStart()).toEqual(testStart);
|
||||
expect(conductor.displayEnd()).toEqual(testEnd);
|
||||
});
|
||||
|
||||
it("provides setters for query/display start/end times", function () {
|
||||
expect(conductor.displayStart(3)).toEqual(3);
|
||||
expect(conductor.displayEnd(4)).toEqual(4);
|
||||
expect(conductor.displayStart()).toEqual(3);
|
||||
expect(conductor.displayEnd()).toEqual(4);
|
||||
});
|
||||
|
||||
it("exposes domain options", function () {
|
||||
expect(conductor.domainOptions()).toEqual(testDomains);
|
||||
});
|
||||
|
||||
it("exposes the current domain choice", function () {
|
||||
expect(conductor.domain()).toEqual(testDomains[0].key);
|
||||
});
|
||||
|
||||
it("allows the domain choice to be changed", function () {
|
||||
conductor.domain(testDomains[1].key);
|
||||
expect(conductor.domain()).toEqual(testDomains[1].key);
|
||||
});
|
||||
|
||||
it("throws an error on attempts to set an invalid domain", function () {
|
||||
expect(function () {
|
||||
conductor.domain("invalid-domain");
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
6
platform/features/conductor/test/suite.json
Normal file
6
platform/features/conductor/test/suite.json
Normal file
@ -0,0 +1,6 @@
|
||||
[
|
||||
"ConductorRepresenter",
|
||||
"ConductorService",
|
||||
"ConductorTelemetryDecorator",
|
||||
"TimeConductor"
|
||||
]
|
@ -167,8 +167,9 @@
|
||||
"$scope",
|
||||
"$q",
|
||||
"dialogService",
|
||||
"telemetrySubscriber",
|
||||
"telemetryFormatter"
|
||||
"telemetryHandler",
|
||||
"telemetryFormatter",
|
||||
"throttle"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
@ -19,7 +19,7 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div style="width: 100%; height: 100%;"
|
||||
<div class="l-layout"
|
||||
ng-controller="LayoutController as controller">
|
||||
|
||||
<div class='frame child-frame panel abs'
|
||||
@ -31,7 +31,6 @@
|
||||
mct-object="childObject">
|
||||
</mct-representation>
|
||||
</div>
|
||||
|
||||
<!-- Drag handles -->
|
||||
<span ng-show="domainObject.hasCapability('editor')">
|
||||
|
||||
|
@ -38,12 +38,13 @@ define(
|
||||
* @constructor
|
||||
* @param {Scope} $scope the controller's Angular scope
|
||||
*/
|
||||
function FixedController($scope, $q, dialogService, telemetrySubscriber, telemetryFormatter) {
|
||||
function FixedController($scope, $q, dialogService, telemetryHandler, telemetryFormatter, throttle) {
|
||||
var self = this,
|
||||
subscription,
|
||||
handle,
|
||||
names = {}, // Cache names by ID
|
||||
values = {}, // Cache values by ID
|
||||
elementProxiesById = {};
|
||||
elementProxiesById = {},
|
||||
maxDomainValue = Number.POSITIVE_INFINITY;
|
||||
|
||||
// Convert from element x/y/width/height to an
|
||||
// appropriate ng-style argument, to position elements.
|
||||
@ -81,25 +82,52 @@ define(
|
||||
return element.handles().map(generateDragHandle);
|
||||
}
|
||||
|
||||
// Update the displayed value for this object
|
||||
function updateValue(telemetryObject) {
|
||||
var id = telemetryObject && telemetryObject.getId(),
|
||||
// Update the value displayed in elements of this telemetry object
|
||||
function setDisplayedValue(telemetryObject, value, alarm) {
|
||||
var id = telemetryObject.getId();
|
||||
(elementProxiesById[id] || []).forEach(function (element) {
|
||||
names[id] = telemetryObject.getModel().name;
|
||||
values[id] = telemetryFormatter.formatRangeValue(value);
|
||||
element.name = names[id];
|
||||
element.value = values[id];
|
||||
element.cssClass = alarm && alarm.cssClass;
|
||||
});
|
||||
}
|
||||
|
||||
// Update the displayed value for this object, from a specific
|
||||
// telemetry series
|
||||
function updateValueFromSeries(telemetryObject, telemetrySeries) {
|
||||
var index = telemetrySeries.getPointCount() - 1,
|
||||
limit = telemetryObject &&
|
||||
telemetryObject.getCapability('limit'),
|
||||
datum = telemetryObject &&
|
||||
subscription.getDatum(telemetryObject),
|
||||
alarm = limit && datum && limit.evaluate(datum);
|
||||
datum = telemetryObject && handle.getDatum(
|
||||
telemetryObject,
|
||||
index
|
||||
);
|
||||
|
||||
if (id) {
|
||||
(elementProxiesById[id] || []).forEach(function (element) {
|
||||
names[id] = telemetryObject.getModel().name;
|
||||
values[id] = telemetryFormatter.formatRangeValue(
|
||||
subscription.getRangeValue(telemetryObject)
|
||||
);
|
||||
element.name = names[id];
|
||||
element.value = values[id];
|
||||
element.cssClass = alarm && alarm.cssClass;
|
||||
});
|
||||
if (index >= 0) {
|
||||
setDisplayedValue(
|
||||
telemetryObject,
|
||||
telemetrySeries.getRangeValue(index),
|
||||
limit && datum && limit.evaluate(datum)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the displayed value for this object
|
||||
function updateValue(telemetryObject) {
|
||||
var limit = telemetryObject &&
|
||||
telemetryObject.getCapability('limit'),
|
||||
datum = telemetryObject &&
|
||||
handle.getDatum(telemetryObject);
|
||||
|
||||
if (telemetryObject &&
|
||||
(handle.getDomainValue(telemetryObject) < maxDomainValue)) {
|
||||
setDisplayedValue(
|
||||
telemetryObject,
|
||||
handle.getRangeValue(telemetryObject),
|
||||
limit && datum && limit.evaluate(datum)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,8 +143,8 @@ define(
|
||||
|
||||
// Update telemetry values based on new data available
|
||||
function updateValues() {
|
||||
if (subscription) {
|
||||
subscription.getTelemetryObjects().forEach(updateValue);
|
||||
if (handle) {
|
||||
handle.getTelemetryObjects().forEach(updateValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,22 +206,29 @@ define(
|
||||
|
||||
// Free up subscription to telemetry
|
||||
function releaseSubscription() {
|
||||
if (subscription) {
|
||||
subscription.unsubscribe();
|
||||
subscription = undefined;
|
||||
if (handle) {
|
||||
handle.unsubscribe();
|
||||
handle = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribe to telemetry updates for this domain object
|
||||
function subscribe(domainObject) {
|
||||
// Release existing subscription (if any)
|
||||
if (subscription) {
|
||||
subscription.unsubscribe();
|
||||
if (handle) {
|
||||
handle.unsubscribe();
|
||||
}
|
||||
|
||||
// Make a new subscription
|
||||
subscription = domainObject &&
|
||||
telemetrySubscriber.subscribe(domainObject, updateValues);
|
||||
handle = domainObject && telemetryHandler.handle(
|
||||
domainObject,
|
||||
updateValues
|
||||
);
|
||||
// Request an initial historical telemetry value
|
||||
handle.request(
|
||||
{ size: 1 }, // Only need a single data point
|
||||
updateValueFromSeries
|
||||
);
|
||||
}
|
||||
|
||||
// Handle changes in the object's composition
|
||||
@ -204,6 +239,17 @@ define(
|
||||
subscribe($scope.domainObject);
|
||||
}
|
||||
|
||||
// Trigger a new query for telemetry data
|
||||
function updateDisplayBounds(event, bounds) {
|
||||
maxDomainValue = bounds.end;
|
||||
if (handle) {
|
||||
handle.request(
|
||||
{ size: 1 }, // Only need a single data point
|
||||
updateValueFromSeries
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Add an element to this view
|
||||
function addElement(element) {
|
||||
// Ensure that configuration field is populated
|
||||
@ -278,6 +324,9 @@ define(
|
||||
|
||||
// Position panes where they are dropped
|
||||
$scope.$on("mctDrop", handleDrop);
|
||||
|
||||
// Respond to external bounds changes
|
||||
$scope.$on("telemetry:display:bounds", updateDisplayBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,10 +30,10 @@ define(
|
||||
var mockScope,
|
||||
mockQ,
|
||||
mockDialogService,
|
||||
mockSubscriber,
|
||||
mockHandler,
|
||||
mockFormatter,
|
||||
mockDomainObject,
|
||||
mockSubscription,
|
||||
mockHandle,
|
||||
mockEvent,
|
||||
testGrid,
|
||||
testModel,
|
||||
@ -78,9 +78,9 @@ define(
|
||||
'$scope',
|
||||
[ "$on", "$watch", "commit" ]
|
||||
);
|
||||
mockSubscriber = jasmine.createSpyObj(
|
||||
'telemetrySubscriber',
|
||||
[ 'subscribe' ]
|
||||
mockHandler = jasmine.createSpyObj(
|
||||
'telemetryHandler',
|
||||
[ 'handle' ]
|
||||
);
|
||||
mockQ = jasmine.createSpyObj('$q', ['when']);
|
||||
mockDialogService = jasmine.createSpyObj(
|
||||
@ -95,9 +95,16 @@ define(
|
||||
'domainObject',
|
||||
[ 'getId', 'getModel', 'getCapability' ]
|
||||
);
|
||||
mockSubscription = jasmine.createSpyObj(
|
||||
mockHandle = jasmine.createSpyObj(
|
||||
'subscription',
|
||||
[ 'unsubscribe', 'getTelemetryObjects', 'getRangeValue', 'getDatum' ]
|
||||
[
|
||||
'unsubscribe',
|
||||
'getDomainValue',
|
||||
'getTelemetryObjects',
|
||||
'getRangeValue',
|
||||
'getDatum',
|
||||
'request'
|
||||
]
|
||||
);
|
||||
mockEvent = jasmine.createSpyObj(
|
||||
'event',
|
||||
@ -116,13 +123,14 @@ define(
|
||||
{ type: "fixed.telemetry", id: 'c', x: 1, y: 1 }
|
||||
]};
|
||||
|
||||
mockSubscriber.subscribe.andReturn(mockSubscription);
|
||||
mockSubscription.getTelemetryObjects.andReturn(
|
||||
mockHandler.handle.andReturn(mockHandle);
|
||||
mockHandle.getTelemetryObjects.andReturn(
|
||||
testModel.composition.map(makeMockDomainObject)
|
||||
);
|
||||
mockSubscription.getRangeValue.andCallFake(function (o) {
|
||||
mockHandle.getRangeValue.andCallFake(function (o) {
|
||||
return testValues[o.getId()];
|
||||
});
|
||||
mockHandle.getDomainValue.andReturn(12321);
|
||||
mockFormatter.formatRangeValue.andCallFake(function (v) {
|
||||
return "Formatted " + v;
|
||||
});
|
||||
@ -137,7 +145,7 @@ define(
|
||||
mockScope,
|
||||
mockQ,
|
||||
mockDialogService,
|
||||
mockSubscriber,
|
||||
mockHandler,
|
||||
mockFormatter
|
||||
);
|
||||
});
|
||||
@ -145,7 +153,7 @@ define(
|
||||
it("subscribes when a domain object is available", function () {
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
findWatch("domainObject")(mockDomainObject);
|
||||
expect(mockSubscriber.subscribe).toHaveBeenCalledWith(
|
||||
expect(mockHandler.handle).toHaveBeenCalledWith(
|
||||
mockDomainObject,
|
||||
jasmine.any(Function)
|
||||
);
|
||||
@ -156,13 +164,13 @@ define(
|
||||
|
||||
// First pass - should simply should subscribe
|
||||
findWatch("domainObject")(mockDomainObject);
|
||||
expect(mockSubscription.unsubscribe).not.toHaveBeenCalled();
|
||||
expect(mockSubscriber.subscribe.calls.length).toEqual(1);
|
||||
expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
|
||||
expect(mockHandler.handle.calls.length).toEqual(1);
|
||||
|
||||
// Object changes - should unsubscribe then resubscribe
|
||||
findWatch("domainObject")(mockDomainObject);
|
||||
expect(mockSubscription.unsubscribe).toHaveBeenCalled();
|
||||
expect(mockSubscriber.subscribe.calls.length).toEqual(2);
|
||||
expect(mockHandle.unsubscribe).toHaveBeenCalled();
|
||||
expect(mockHandler.handle.calls.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("exposes visible elements based on configuration", function () {
|
||||
@ -255,7 +263,7 @@ define(
|
||||
findWatch("model.composition")(mockScope.model.composition);
|
||||
|
||||
// Invoke the subscription callback
|
||||
mockSubscriber.subscribe.mostRecentCall.args[1]();
|
||||
mockHandler.handle.mostRecentCall.args[1]();
|
||||
|
||||
// Get elements that controller is now exposing
|
||||
elements = controller.getElements();
|
||||
@ -333,11 +341,11 @@ define(
|
||||
// Make an object available
|
||||
findWatch('domainObject')(mockDomainObject);
|
||||
// Also verify precondition
|
||||
expect(mockSubscription.unsubscribe).not.toHaveBeenCalled();
|
||||
expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
|
||||
// Destroy the scope
|
||||
findOn('$destroy')();
|
||||
// Should have unsubscribed
|
||||
expect(mockSubscription.unsubscribe).toHaveBeenCalled();
|
||||
expect(mockHandle.unsubscribe).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("exposes its grid size", function () {
|
||||
|
@ -19,7 +19,7 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="abs l-iframe">
|
||||
<div class="l-iframe abs">
|
||||
<iframe ng-controller="EmbeddedPageController as ctl"
|
||||
ng-src="{{ctl.trust(model.url)}}">
|
||||
</iframe>
|
||||
|
@ -119,7 +119,7 @@
|
||||
<span class="ui-symbol icon">I</span>
|
||||
</a>
|
||||
|
||||
<div class="menu-element s-menu menus-to-left"
|
||||
<div class="menu-element s-menu-btn menus-to-left"
|
||||
ng-if="plot.getModeOptions().length > 1"
|
||||
ng-controller="ClickAwayController as toggle">
|
||||
|
||||
|
@ -65,6 +65,7 @@ define(
|
||||
subPlotFactory = new SubPlotFactory(telemetryFormatter),
|
||||
cachedObjects = [],
|
||||
updater,
|
||||
lastBounds,
|
||||
handle;
|
||||
|
||||
// Populate the scope with axis information (specifically, options
|
||||
@ -94,6 +95,17 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
// Change the displayable bounds
|
||||
function setBasePanZoom(bounds) {
|
||||
var start = bounds.start,
|
||||
end = bounds.end;
|
||||
if (updater) {
|
||||
updater.setDomainBounds(start, end);
|
||||
self.update();
|
||||
}
|
||||
lastBounds = bounds;
|
||||
}
|
||||
|
||||
// Reinstantiate the plot updater (e.g. because we have a
|
||||
// new subscription.) This will clear the plot.
|
||||
function recreateUpdater() {
|
||||
@ -107,10 +119,15 @@ define(
|
||||
handle,
|
||||
($scope.axes[1].active || {}).key
|
||||
);
|
||||
// Keep any externally-provided bounds
|
||||
if (lastBounds) {
|
||||
setBasePanZoom(lastBounds);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle new telemetry data in this plot
|
||||
function updateValues() {
|
||||
self.pending = false;
|
||||
if (handle) {
|
||||
setupModes(handle.getTelemetryObjects());
|
||||
}
|
||||
@ -126,6 +143,7 @@ define(
|
||||
|
||||
// Display new historical data as it becomes available
|
||||
function addHistoricalData(domainObject, series) {
|
||||
self.pending = false;
|
||||
updater.addHistorical(domainObject, series);
|
||||
self.modeOptions.getModeHandler().plotTelemetry(updater);
|
||||
self.update();
|
||||
@ -165,6 +183,14 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to a display bounds change (requery for data)
|
||||
function changeDisplayBounds(event, bounds) {
|
||||
self.pending = true;
|
||||
releaseSubscription();
|
||||
subscribe($scope.domainObject);
|
||||
setBasePanZoom(bounds);
|
||||
}
|
||||
|
||||
this.modeOptions = new PlotModeOptions([], subPlotFactory);
|
||||
this.updateValues = updateValues;
|
||||
|
||||
@ -174,12 +200,19 @@ define(
|
||||
.forEach(updateSubplot);
|
||||
});
|
||||
|
||||
self.pending = true;
|
||||
|
||||
// Subscribe to telemetry when a domain object becomes available
|
||||
$scope.$watch('domainObject', subscribe);
|
||||
|
||||
// Respond to external bounds changes
|
||||
$scope.$on("telemetry:display:bounds", changeDisplayBounds);
|
||||
|
||||
// Unsubscribe when the plot is destroyed
|
||||
$scope.$on("$destroy", releaseSubscription);
|
||||
|
||||
// Notify any external observers that a new telemetry view is here
|
||||
$scope.$emit("telemetry:view");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -275,7 +308,7 @@ define(
|
||||
PlotController.prototype.isRequestPending = function () {
|
||||
// Placeholder; this should reflect request state
|
||||
// when requesting historical telemetry
|
||||
return false;
|
||||
return this.pending;
|
||||
};
|
||||
|
||||
return PlotController;
|
||||
|
@ -64,6 +64,16 @@ define(
|
||||
this.updateTicks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether this subplot has domain data to show for the current pan/zoom level. Absence of domain data
|
||||
* implies that there is no range data displayed either
|
||||
* @returns {boolean} true if domain data exists for the current pan/zoom level
|
||||
*/
|
||||
SubPlot.prototype.hasDomainData = function() {
|
||||
return this.panZoomStack
|
||||
&& this.panZoomStack.getDimensions()[0] > 0;
|
||||
};
|
||||
|
||||
// Utility function for filtering out empty strings.
|
||||
function isNonEmpty(v) {
|
||||
return typeof v === 'string' && v !== "";
|
||||
@ -253,7 +263,10 @@ define(
|
||||
this.hovering = true;
|
||||
this.subPlotBounds = $event.target.getBoundingClientRect();
|
||||
this.mousePosition = this.toMousePosition($event);
|
||||
this.updateHoverCoordinates();
|
||||
//If there is a domain to display, show hover coordinates, otherwise hover coordinates are meaningless
|
||||
if (this.hasDomainData()) {
|
||||
this.updateHoverCoordinates();
|
||||
}
|
||||
if (this.marqueeStart) {
|
||||
this.updateMarqueeBox();
|
||||
}
|
||||
|
@ -143,8 +143,7 @@ define(
|
||||
PlotPanZoomStackGroup.prototype.getDepth = function () {
|
||||
// All stacks are kept in sync, so look up depth
|
||||
// from the first one.
|
||||
return this.stacks.length > 0 ?
|
||||
this.stacks[0].getDepth() : 0;
|
||||
return this.stacks.length > 0 ? this.stacks[0].getDepth() : 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -53,7 +53,8 @@ define(
|
||||
|
||||
for (i = 0; i < count; i += 1) {
|
||||
result.push({
|
||||
label: format(i * step + start)
|
||||
//If data to show, display label for each tick line, otherwise show lines but suppress labels.
|
||||
label: span > 0 ? format(i * step + start) : ''
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -141,10 +141,10 @@ define(
|
||||
PlotUpdater.prototype.initializeDomainOffset = function (values) {
|
||||
this.domainOffset =
|
||||
((this.domainOffset === undefined) && (values.length > 0)) ?
|
||||
(values.reduce(function (a, b) {
|
||||
return (a || 0) + (b || 0);
|
||||
}, 0) / values.length) :
|
||||
this.domainOffset;
|
||||
(values.reduce(function (a, b) {
|
||||
return (a || 0) + (b || 0);
|
||||
}, 0) / values.length) :
|
||||
this.domainOffset;
|
||||
};
|
||||
|
||||
// Expand range slightly so points near edges are visible
|
||||
@ -159,7 +159,10 @@ define(
|
||||
|
||||
// Update dimensions and origin based on extrema of plots
|
||||
PlotUpdater.prototype.updateBounds = function () {
|
||||
var bufferArray = this.bufferArray;
|
||||
var bufferArray = this.bufferArray,
|
||||
priorDomainOrigin = this.origin[0],
|
||||
priorDomainDimensions = this.dimensions[0];
|
||||
|
||||
if (bufferArray.length > 0) {
|
||||
this.domainExtrema = bufferArray.map(function (lineBuffer) {
|
||||
return lineBuffer.getDomainExtrema();
|
||||
@ -178,6 +181,18 @@ define(
|
||||
// Enforce some minimum visible area
|
||||
this.expandRange();
|
||||
|
||||
// Suppress domain changes when pinned
|
||||
if (this.hasSpecificDomainBounds) {
|
||||
this.origin[0] = priorDomainOrigin;
|
||||
this.dimensions[0] = priorDomainDimensions;
|
||||
if (this.following) {
|
||||
this.origin[0] = Math.max(
|
||||
this.domainExtrema[1] - this.dimensions[0],
|
||||
this.origin[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ...then enforce a fixed duration if needed
|
||||
if (this.fixedDuration !== undefined) {
|
||||
this.origin[0] = this.origin[0] + this.dimensions[0] -
|
||||
@ -281,6 +296,21 @@ define(
|
||||
return this.bufferArray;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the start and end boundaries (usually time) for the
|
||||
* domain axis of this updater.
|
||||
*/
|
||||
PlotUpdater.prototype.setDomainBounds = function (start, end) {
|
||||
this.fixedDuration = end - start;
|
||||
this.origin[0] = start;
|
||||
this.dimensions[0] = this.fixedDuration;
|
||||
|
||||
// Suppress follow behavior if we have windowed in on the past
|
||||
this.hasSpecificDomainBounds = true;
|
||||
this.following =
|
||||
!this.domainExtrema || (end >= this.domainExtrema[1]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill in historical data.
|
||||
*/
|
||||
|
@ -45,11 +45,19 @@ define(
|
||||
};
|
||||
}
|
||||
|
||||
function fireEvent(name, args) {
|
||||
mockScope.$on.calls.forEach(function (call) {
|
||||
if (call.args[0] === name) {
|
||||
call.args[1].apply(null, args || []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
[ "$watch", "$on" ]
|
||||
[ "$watch", "$on", "$emit" ]
|
||||
);
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
"formatter",
|
||||
@ -87,6 +95,7 @@ define(
|
||||
mockHandle.getMetadata.andReturn([{}]);
|
||||
mockHandle.getDomainValue.andReturn(123);
|
||||
mockHandle.getRangeValue.andReturn(42);
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
controller = new PlotController(
|
||||
mockScope,
|
||||
@ -212,7 +221,12 @@ define(
|
||||
});
|
||||
|
||||
it("indicates if a request is pending", function () {
|
||||
// Placeholder; need to support requesting telemetry
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
expect(controller.isRequestPending()).toBeTruthy();
|
||||
mockHandle.request.mostRecentCall.args[1](
|
||||
mockDomainObject,
|
||||
mockSeries
|
||||
);
|
||||
expect(controller.isRequestPending()).toBeFalsy();
|
||||
});
|
||||
|
||||
@ -233,10 +247,20 @@ define(
|
||||
// Also verify precondition
|
||||
expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
|
||||
// Destroy the scope
|
||||
mockScope.$on.mostRecentCall.args[1]();
|
||||
fireEvent("$destroy");
|
||||
// Should have unsubscribed
|
||||
expect(mockHandle.unsubscribe).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("requeries when displayable bounds change", function () {
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
expect(mockHandle.request.calls.length).toEqual(1);
|
||||
fireEvent("telemetry:display:bounds", [
|
||||
{},
|
||||
{ start: 10, end: 100 }
|
||||
]);
|
||||
expect(mockHandle.request.calls.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user