mirror of
https://github.com/nasa/openmct.git
synced 2025-07-03 21:38:13 +00:00
Compare commits
177 Commits
context-me
...
demo-2017.
Author | SHA1 | Date | |
---|---|---|---|
e73dcd8271 | |||
ea3289b659 | |||
0b2c0f6c80 | |||
90126ccc64 | |||
9a29ed8875 | |||
ceb6c896ba | |||
b32f3b82dc | |||
3601e6b473 | |||
77a6bbdb76 | |||
a8dcd5872c | |||
0c25d47e4a | |||
ffc81f1cd0 | |||
ae1d1f0f90 | |||
7feb3fb18e | |||
cd2c04aa4d | |||
3ac1f489c5 | |||
4ec2b6eb4d | |||
1afecdc82c | |||
b9cda6985e | |||
3a7e6ab7d5 | |||
401b7e3f19 | |||
971bcd1f71 | |||
c9e9845172 | |||
abbba38eac | |||
db856251da | |||
e1566e448d | |||
d9aae0700c | |||
5c29726cc0 | |||
5c207c3fe0 | |||
eba1dd1a4e | |||
570edc0dec | |||
ad6bcd4ef8 | |||
aedbbbbf75 | |||
3caaf00483 | |||
971eda4d88 | |||
090d216517 | |||
d42d7ae68d | |||
68e6e3c121 | |||
7e7f39db2d | |||
b6e0fca828 | |||
ffc5896e5a | |||
fd6ebd152f | |||
7a5c1c0e1f | |||
2f7e1e3f1a | |||
d73746b51b | |||
2df54af019 | |||
586269f761 | |||
e536ab34d7 | |||
e15002dd72 | |||
453cf3ad6a | |||
5c46e48bde | |||
868ea9362f | |||
d69106ff2c | |||
1658b17c56 | |||
39cf0528ca | |||
3d12f7312b | |||
22481fdc31 | |||
4a9d27dc79 | |||
a5a9fefd40 | |||
dae4074934 | |||
a540a3573f | |||
4e7fe9082c | |||
568141bf81 | |||
ac3ea43fe5 | |||
e922e8d504 | |||
650a877d2a | |||
1202109c59 | |||
429d7bbd57 | |||
af749fe71b | |||
cf64c512ce | |||
14592d1c3e | |||
c5bd3da44a | |||
ff3e49e926 | |||
e244a3e431 | |||
c5d9fb6fd9 | |||
4c276ab422 | |||
bf321abae4 | |||
7336968ef9 | |||
d60956948b | |||
23d5c2e1ee | |||
2632b8891a | |||
fff4cd9d51 | |||
7f9fd5c705 | |||
be0291cf70 | |||
b3a6d7271d | |||
ebeed2f236 | |||
337c26c019 | |||
730f363f94 | |||
ae30e6110b | |||
6e4bf3e45b | |||
5465ca92f9 | |||
e55ea41b0a | |||
8cfb3cc689 | |||
2d728a1362 | |||
99333988df | |||
de783d4286 | |||
1e1a2443d5 | |||
d65e1e604e | |||
f6c1488ccd | |||
26be1ecf37 | |||
38f0f072bb | |||
e5e969665f | |||
ffbb662c99 | |||
bd7b23f896 | |||
c238def902 | |||
2d430ece7f | |||
c92644a661 | |||
41ce3c04f7 | |||
fcf77f359f | |||
40a2737915 | |||
216489d67f | |||
418a393b26 | |||
1f3d744494 | |||
ff3f2dccba | |||
e69973bd29 | |||
05b352cc36 | |||
9735548999 | |||
f9529b1362 | |||
c598cec702 | |||
e9ea1c4a0f | |||
e9238ff282 | |||
4cebd72cba | |||
f8a44d6e71 | |||
d0745b300b | |||
2a4e0a3081 | |||
1ff19f9574 | |||
7ef84cb50d | |||
cd05c70d64 | |||
568473b82f | |||
c61b074755 | |||
8ed66ab4ab | |||
b2502dd998 | |||
856eedbf9d | |||
0c0ca6e6af | |||
498b797e49 | |||
02c33388ba | |||
8a8e3cc055 | |||
36d60b16e9 | |||
de3114568b | |||
eb5835faeb | |||
ff1ddb0b79 | |||
15b127bb2e | |||
e4ed881f6d | |||
7b62cf130c | |||
72fd2e531c | |||
4a5392ef78 | |||
0150a708ca | |||
eacc181d5e | |||
405bb55881 | |||
4a35508459 | |||
98a9d71a2e | |||
a1596d0b06 | |||
4b3be4c483 | |||
0fa8472db1 | |||
e1e2dca1d8 | |||
755c013ec8 | |||
eab702b763 | |||
d15446ac91 | |||
500733afb2 | |||
2aa04b0a56 | |||
c051f342af | |||
8aeb365f5f | |||
827a28313d | |||
c83de8aad2 | |||
b55f43b8df | |||
8466723a90 | |||
a103b4dbff | |||
826ac3a947 | |||
597327f138 | |||
bef79402ca | |||
e68e0c381f | |||
c73f7259c2 | |||
4c9235ba10 | |||
55e2a77df8 | |||
cfbff02e7f | |||
54980fb296 | |||
ba98d9315c |
@ -18,7 +18,7 @@
|
||||
"node-uuid": "^1.4.7",
|
||||
"comma-separated-values": "^3.6.4",
|
||||
"FileSaver.js": "^0.0.2",
|
||||
"zepto": "^1.1.6",
|
||||
"zepto": "1.2.0",
|
||||
"eventemitter3": "^1.2.0",
|
||||
"lodash": "3.10.1",
|
||||
"almond": "~0.3.2",
|
||||
|
@ -53,13 +53,15 @@ define([
|
||||
'dataRateInHz'
|
||||
];
|
||||
|
||||
request = request || {};
|
||||
|
||||
var workerRequest = {};
|
||||
|
||||
props.forEach(function (prop) {
|
||||
if (domainObject.telemetry && domainObject.telemetry.hasOwnProperty(prop)) {
|
||||
workerRequest[prop] = domainObject.telemetry[prop];
|
||||
}
|
||||
if (request.hasOwnProperty(prop)) {
|
||||
if (request && request.hasOwnProperty(prop)) {
|
||||
workerRequest[prop] = request[prop];
|
||||
}
|
||||
if (!workerRequest[prop]) {
|
||||
|
@ -121,7 +121,7 @@
|
||||
<h2>Palettes</h2>
|
||||
<div class="cols cols1-1">
|
||||
<div class="col">
|
||||
<p>Use a palette to provide color choices. Similar to context menus and dropdowns, palettes should be dismissed when a choice is made within them, or if the user clicks outside one.</p>
|
||||
<p>Use a palette to provide color choices. Similar to context menus and dropdowns, palettes should be dismissed when a choice is made within them, or if the user clicks outside one. Selected palette choices should utilize the <code>selected</code> CSS class to visualize indicate that state.</p>
|
||||
<p>Note that while this example uses static markup for illustrative purposes, don't do this - use a front-end framework with repeaters to build the color choices.</p>
|
||||
</div>
|
||||
<mct-example><div style="height: 220px" title="Ignore me, I'm just here to provide space for this example.">
|
||||
@ -129,9 +129,9 @@
|
||||
<div class="s-button s-menu-button menu-element t-color-palette icon-paint-bucket" ng-controller="ClickAwayController as toggle">
|
||||
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
||||
<span class="color-swatch" style="background: rgb(255, 0, 0);"></span>
|
||||
<div class="menu l-color-palette" ng-show="toggle.isActive()">
|
||||
<div class="menu l-palette l-color-palette" ng-show="toggle.isActive()">
|
||||
<div class="l-palette-row l-option-row">
|
||||
<div class="l-palette-item s-palette-item " ng-click="ngModel[field] = 'transparent'"></div>
|
||||
<div class="l-palette-item s-palette-item no-selection"></div>
|
||||
<span class="l-palette-item-label">None</span>
|
||||
</div>
|
||||
<div class="l-palette-row">
|
||||
@ -147,7 +147,7 @@
|
||||
<div class="l-palette-item s-palette-item" style="background: rgb(255, 255, 255);"></div>
|
||||
</div>
|
||||
<div class="l-palette-row">
|
||||
<div class="l-palette-item s-palette-item" style="background: rgb(136, 32, 32);"></div>
|
||||
<div class="l-palette-item s-palette-item selected" style="background: rgb(255, 0, 0);"></div>
|
||||
<div class="l-palette-item s-palette-item" style="background: rgb(224, 64, 64);"></div>
|
||||
<div class="l-palette-item s-palette-item" style="background: rgb(240, 160, 72);"></div>
|
||||
<div class="l-palette-item s-palette-item" style="background: rgb(255, 248, 96);"></div>
|
||||
|
@ -25,8 +25,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<title></title>
|
||||
<script src="bower_components/requirejs/require.js">
|
||||
</script>
|
||||
<script src="bower_components/requirejs/require.js"> </script>
|
||||
<script>
|
||||
var THIRTY_MINUTES = 30 * 60 * 1000;
|
||||
|
||||
@ -44,13 +43,14 @@
|
||||
openmct.install(openmct.plugins.ExampleImagery());
|
||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||
openmct.install(openmct.plugins.ImportExport());
|
||||
openmct.install(openmct.plugins.TelemetryMean());
|
||||
openmct.install(openmct.plugins.Conductor({
|
||||
menuOptions: [
|
||||
{
|
||||
name: "Fixed",
|
||||
timeSystem: 'utc',
|
||||
bounds: {
|
||||
start: Date.now() - 30 * 60 * 1000,
|
||||
start: Date.now() - THIRTY_MINUTES,
|
||||
end: Date.now()
|
||||
}
|
||||
},
|
||||
@ -65,6 +65,7 @@
|
||||
}
|
||||
]
|
||||
}));
|
||||
openmct.install(openmct.plugins.SummaryWidget());
|
||||
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
|
||||
openmct.time.timeSystem('utc');
|
||||
openmct.start();
|
||||
|
@ -20,7 +20,7 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="abs top-bar">
|
||||
<div class="title">{{ngModel.title}}</div>
|
||||
<div class="dialog-title">{{ngModel.title}}</div>
|
||||
<div class="hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
|
||||
</div>
|
||||
<div class='abs editor'>
|
||||
|
@ -1,11 +1,10 @@
|
||||
<div class="l-message"
|
||||
ng-class="'message-severity-' + ngModel.severity">
|
||||
<div class="ui-symbol type-icon message-type"></div>
|
||||
<div class="message-contents">
|
||||
<div class="w-message-contents">
|
||||
<div class="top-bar">
|
||||
<div class="title">{{ngModel.title}}</div>
|
||||
<div class="hint" ng-hide="ngModel.hint === undefined">{{ngModel.hint}}</div>
|
||||
</div>
|
||||
<div class="hint" ng-hide="ngModel.hint === undefined">{{ngModel.hint}}</div>
|
||||
<div class="message-body">
|
||||
<div class="message-action">
|
||||
{{ngModel.actionText}}
|
||||
@ -25,8 +24,6 @@
|
||||
ng-click="ngModel.primaryOption.callback()">
|
||||
{{ngModel.primaryOption.label}}
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,17 +1,17 @@
|
||||
<mct-container key="overlay" class="t-message-list">
|
||||
<div class="message-contents">
|
||||
<div class="abs top-bar">
|
||||
<div class="title">{{ngModel.dialog.title}}</div>
|
||||
<mct-container key="overlay">
|
||||
<div class="t-message-list">
|
||||
<div class="top-bar">
|
||||
<div class="dialog-title">{{ngModel.dialog.title}}</div>
|
||||
<div class="hint">Displaying {{ngModel.dialog.messages.length}} message<span ng-show="ngModel.dialog.messages.length > 1 ||
|
||||
ngModel.dialog.messages.length == 0">s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="abs message-body">
|
||||
<div class="w-messages">
|
||||
<mct-include
|
||||
ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'"
|
||||
key="'message'" ng-model="msg.model"></mct-include>
|
||||
ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'"
|
||||
key="'message'" ng-model="msg.model"></mct-include>
|
||||
</div>
|
||||
<div class="abs bottom-bar">
|
||||
<div class="bottom-bar">
|
||||
<a ng-repeat="dialogAction in ngModel.dialog.actions"
|
||||
class="s-button major"
|
||||
ng-click="dialogAction.action()">
|
||||
|
@ -21,7 +21,7 @@
|
||||
-->
|
||||
<mct-container key="overlay">
|
||||
<div class="abs top-bar">
|
||||
<div class="title">{{ngModel.dialog.title}}</div>
|
||||
<div class="dialog-title">{{ngModel.dialog.title}}</div>
|
||||
<div class="hint">{{ngModel.dialog.hint}}</div>
|
||||
</div>
|
||||
<div class='abs editor'>
|
||||
|
@ -121,6 +121,9 @@ define([
|
||||
};
|
||||
|
||||
UTCTimeFormat.prototype.parse = function (text) {
|
||||
if (typeof text === 'number') {
|
||||
return text;
|
||||
}
|
||||
return moment.utc(text, DATE_FORMATS).valueOf();
|
||||
};
|
||||
|
||||
|
@ -137,6 +137,11 @@
|
||||
min-height: 0;
|
||||
&.holder:not(:last-child) { margin-bottom: $interiorMarginLg; }
|
||||
}
|
||||
&.l-flex-accordion .flex-accordion-holder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
//overflow: hidden !important;
|
||||
}
|
||||
.flex-container { @include flex-direction(column); }
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ $plotXBarH: 32px;
|
||||
$plotLegendH: 20px;
|
||||
$plotSwatchD: 8px;
|
||||
// 1: Top, 2: right, 3: bottom, 4: left
|
||||
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH, $plotYBarW);
|
||||
$plotDisplayArea: (0, 0, $plotXBarH, $plotYBarW);
|
||||
/* min plot height is based on user testing to find minimum useful height */
|
||||
$plotMinH: 95px;
|
||||
/*************** Bubbles */
|
||||
|
@ -40,7 +40,7 @@
|
||||
* Use https://icomoon.io/app with icomoon-project-openmct-symbols-12px.json
|
||||
* to generate font files
|
||||
*/
|
||||
font-family: 'symbolsfont 12px';
|
||||
font-family: 'symbolsfont-12px';
|
||||
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot');
|
||||
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot?#iefix') format('embedded-opentype'),
|
||||
url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.woff') format('woff'),
|
||||
@ -180,6 +180,20 @@ a.disabled {
|
||||
@include ellipsize();
|
||||
}
|
||||
|
||||
.no-selection {
|
||||
// aka selection = "None". Used in palettes and their menu buttons.
|
||||
$c: red; $s: 48%; $e: 52%;
|
||||
@include background-image(linear-gradient(-45deg,
|
||||
transparent $s - 5%,
|
||||
$c $s,
|
||||
$c $e,
|
||||
transparent $e + 5%
|
||||
));
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
|
||||
.scrolling,
|
||||
.scroll {
|
||||
overflow: auto;
|
||||
@ -234,6 +248,12 @@ a.disabled {
|
||||
color: rgba(#fff, 0.2);
|
||||
}
|
||||
|
||||
.comma-list span {
|
||||
&:not(:first-child) {
|
||||
&:before { content: ', '; }
|
||||
}
|
||||
}
|
||||
|
||||
.test-stripes {
|
||||
@include bgDiagonalStripes();
|
||||
}
|
||||
|
@ -44,6 +44,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.t-alert-unsynced {
|
||||
@extend .icon-alert-triangle;
|
||||
color: $colorPausedBg;
|
||||
}
|
||||
|
||||
.bar .ui-symbol {
|
||||
display: inline-block;
|
||||
}
|
||||
@ -81,18 +87,5 @@
|
||||
@include transform(scale(0.3));
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* .t-item-icon-glyph {
|
||||
&:after {
|
||||
color: $colorIconLink;
|
||||
content: '\e921'; //$glyph-icon-link;
|
||||
height: auto; width: auto;
|
||||
position: absolute;
|
||||
left: 0; top: 0; right: 0; bottom: 20%;
|
||||
@include transform-origin(bottom left);
|
||||
@include transform(scale(0.3));
|
||||
z-index: 2;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
@ -26,5 +26,6 @@
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,7 @@
|
||||
.l-inspector-part {
|
||||
box-sizing: border-box;
|
||||
padding-right: $interiorMargin;
|
||||
|
||||
.tree .form {
|
||||
margin-left: $treeVCW + $interiorMarginLg;
|
||||
}
|
||||
@ -78,6 +79,7 @@
|
||||
}
|
||||
}
|
||||
.form-row {
|
||||
// To be replaced with .inspector-config, see below.
|
||||
@include align-items(center);
|
||||
border: none !important;
|
||||
margin-bottom: 0 !important;
|
||||
@ -99,15 +101,12 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ul li {
|
||||
margin-bottom: $interiorMarginLg;
|
||||
}
|
||||
|
||||
em.t-inspector-part-header {
|
||||
border-radius: $basicCr;
|
||||
background-color: $colorInspectorSectionHeaderBg;
|
||||
color: $colorInspectorSectionHeaderFg;
|
||||
margin-bottom: $interiorMargin;
|
||||
margin-top: $interiorMarginLg;
|
||||
//margin-bottom: $interiorMargin;
|
||||
padding: floor($formTBPad * .75) $formLRPad;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@ -201,3 +200,102 @@ mct-representation:not(.s-status-editing) .l-inspect {
|
||||
pointer-events: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
// NEW COMPACT FORM, FOR USE IN INSPECTOR
|
||||
// ul > li > label, control
|
||||
// Make a new UL for each form section
|
||||
// Allow control-first, controls-below
|
||||
|
||||
.l-inspect .tree ul li,
|
||||
.inspector-config ul li {
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
|
||||
.inspector-config {
|
||||
$labelW: 40%;
|
||||
$minW: $labelW;
|
||||
ul {
|
||||
margin-bottom: $interiorMarginLg;
|
||||
li {
|
||||
@include display(flex);
|
||||
@include flex-wrap(wrap);
|
||||
@include align-items(center);
|
||||
label,
|
||||
.control {
|
||||
@include display(flex);
|
||||
min-width: $minW;
|
||||
}
|
||||
label {
|
||||
line-height: inherit;
|
||||
padding: $interiorMarginSm 0;
|
||||
width: $labelW;
|
||||
}
|
||||
.control {
|
||||
@include flex-grow(1);
|
||||
}
|
||||
|
||||
&:not(.section-header) {
|
||||
&:not(.connects-to-previous) {
|
||||
//border-top: 1px solid $colorFormLines;
|
||||
}
|
||||
}
|
||||
|
||||
&.connects-to-previous {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
&.section-header {
|
||||
margin-top: $interiorMarginLg;
|
||||
border-top: 1px solid $colorFormLines;
|
||||
}
|
||||
|
||||
&.controls-first {
|
||||
.control {
|
||||
@include flex-grow(0);
|
||||
margin-right: $interiorMargin;
|
||||
min-width: 0;
|
||||
order: 1;
|
||||
width: auto;
|
||||
}
|
||||
label {
|
||||
@include flex-grow(1);
|
||||
order: 2;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
&.controls-under {
|
||||
display: block;
|
||||
.control, label {
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
ul li {
|
||||
border-top: none !important;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-error {
|
||||
// Block element that visually flags an error and contains a message
|
||||
background-color: $colorFormFieldErrorBg;
|
||||
color: $colorFormFieldErrorFg;
|
||||
border-radius: $basicCr;
|
||||
display: block;
|
||||
padding: 1px 6px;
|
||||
&:before {
|
||||
content: $glyph-icon-alert-triangle;
|
||||
display: inline;
|
||||
font-family: symbolsfont;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tree .inspector-config {
|
||||
margin-left: $treeVCW + $interiorMarginLg;
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
/********************************* CONTROLS */
|
||||
@import "controls/breadcrumb";
|
||||
@import "controls/buttons";
|
||||
@import "controls/color-palette";
|
||||
@import "controls/palette";
|
||||
@import "controls/controls";
|
||||
@import "controls/lists";
|
||||
@import "controls/menus";
|
||||
@ -70,6 +70,7 @@
|
||||
@import "fixed-position";
|
||||
@import "lists/tabular";
|
||||
@import "plots/plots-main";
|
||||
@import "plots/legend";
|
||||
@import "iframe";
|
||||
@import "views";
|
||||
@import "items/item";
|
||||
@ -80,3 +81,4 @@
|
||||
@import "autoflow";
|
||||
@import "features/imagery";
|
||||
@import "features/time-display";
|
||||
@import "widgets";
|
||||
|
@ -50,7 +50,6 @@
|
||||
content:'';
|
||||
font-family: symbolsfont;
|
||||
font-size: 0.8em;
|
||||
display: inline;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
}
|
||||
|
||||
.l-view-section {
|
||||
//@include test(orange, 0.1);
|
||||
@include absPosDefault(0);
|
||||
h2 {
|
||||
color: #fff;
|
||||
|
301
platform/commonUI/general/res/sass/_widgets.scss
Normal file
301
platform/commonUI/general/res/sass/_widgets.scss
Normal file
@ -0,0 +1,301 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/************************************************************* WIDGET OBJECT */
|
||||
.l-summary-widget {
|
||||
// Widget layout classes here
|
||||
@include ellipsize();
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
.widget-label:before {
|
||||
// Widget icon
|
||||
font-size: 0.9em;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
.s-summary-widget {
|
||||
// Widget style classes here
|
||||
@include boxShdw($shdwBtns);
|
||||
border-radius: $basicCr;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
font-size: 0.8rem;
|
||||
padding: $interiorMarginLg $interiorMarginLg * 2;
|
||||
&[href] {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.widget-edit-holder {
|
||||
// Hide edit area when in browse mode
|
||||
display: none;
|
||||
}
|
||||
|
||||
.widget-rule-header {
|
||||
@extend .l-flex-row;
|
||||
@include align-items(center);
|
||||
margin-bottom: $interiorMargin;
|
||||
> .flex-elem {
|
||||
&:not(:first-child) {
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.widget-rules-wrapper,
|
||||
.widget-rule-content,
|
||||
.w-widget-test-data-content {
|
||||
@include trans-prop-nice($props: (height, min-height, opacity), $dur: 250ms);
|
||||
min-height: 0;
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.widget-rules-wrapper {
|
||||
flex: 1 1 auto !important;
|
||||
}
|
||||
|
||||
.widget-rule-content.expanded {
|
||||
overflow: visible !important;
|
||||
min-height: 50px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
pointer-events: inherit;
|
||||
}
|
||||
|
||||
.w-widget-test-data-content {
|
||||
.l-enable {
|
||||
padding: $interiorMargin 0;
|
||||
}
|
||||
|
||||
.w-widget-test-data-items {
|
||||
max-height: 20vh;
|
||||
overflow-y: scroll !important;
|
||||
padding-right: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
.l-widget-thumb-wrapper,
|
||||
.l-compact-form label {
|
||||
$ruleLabelW: 40%;
|
||||
$ruleLabelMaxW: 150px;
|
||||
@include display(flex);
|
||||
max-width: $ruleLabelMaxW;
|
||||
width: $ruleLabelW;
|
||||
}
|
||||
|
||||
.t-message-widget-no-data {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/********************************************************** EDITING A WIDGET */
|
||||
.s-status-editing > mct-view > .w-summary-widget {
|
||||
// Classes for editor layout while editing a widget
|
||||
// This selector is ugly and brittle, but needed to prevent interface from showing when widget is in a layout
|
||||
// being edited.
|
||||
@include absPosDefault();
|
||||
@extend .l-flex-col;
|
||||
|
||||
> .l-summary-widget {
|
||||
// Main view of the summary widget
|
||||
// Give some airspace and center the widget in the area
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
.widget-edit-holder {
|
||||
display: flex; // Overrides `display: none` during Browse mode
|
||||
.flex-accordion-holder {
|
||||
// Needed because otherwise accordion elements "creep" when contents expand and contract
|
||||
display: block !important;
|
||||
}
|
||||
&.expanded-widget-test-data {
|
||||
.w-widget-test-data-content {
|
||||
min-height: 50px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
pointer-events: inherit;
|
||||
}
|
||||
&:not(.expanded-widget-rules) {
|
||||
// Test data is expanded and rules are collapsed
|
||||
// Make text data take up all the vertical space
|
||||
.flex-accordion-holder { display: flex; }
|
||||
.widget-test-data {
|
||||
flex-grow: 999999;
|
||||
}
|
||||
.w-widget-test-data-items {
|
||||
max-height: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.expanded-widget-rules {
|
||||
.widget-rules-wrapper {
|
||||
min-height: 50px;
|
||||
height: auto;
|
||||
opacity: 1;
|
||||
pointer-events: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.s-status-no-data {
|
||||
.widget-edit-holder {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
.t-message-widget-no-data {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.l-compact-form {
|
||||
// Overrides on .l-compact-form
|
||||
ul {
|
||||
&:last-child { margin: 0; }
|
||||
li {
|
||||
@include align-items(flex-start);
|
||||
@include flex-wrap(nowrap);
|
||||
line-height: 230%; // Provide enough space when controls wrap
|
||||
padding: 2px 0;
|
||||
&:not(.widget-rule-header) {
|
||||
&:not(.connects-to-previous) {
|
||||
border-top: 1px solid $colorFormLines;
|
||||
}
|
||||
}
|
||||
&.connects-to-previous {
|
||||
padding: $interiorMargin 0;
|
||||
}
|
||||
|
||||
> label {
|
||||
display: block; // Needed to align text to right
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.s-widget-test-data-item {
|
||||
// Single line of ul li label span, etc.
|
||||
ul {
|
||||
li {
|
||||
border: none !important;
|
||||
> label {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.widget-edit-holder {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.widget-rules-wrapper {
|
||||
// Wrapper area that holds n rules
|
||||
box-sizing: border-box;
|
||||
overflow-y: scroll;
|
||||
padding-right: $interiorMargin;
|
||||
}
|
||||
|
||||
.l-widget-rule,
|
||||
.l-widget-test-data-item {
|
||||
box-sizing: border-box;
|
||||
margin-bottom: $interiorMarginSm;
|
||||
padding: $interiorMargin $interiorMarginLg;
|
||||
}
|
||||
|
||||
.l-widget-thumb-wrapper {
|
||||
@extend .l-flex-row;
|
||||
@include align-items(center);
|
||||
> span { display: block; }
|
||||
.grippy-holder,
|
||||
.view-control {
|
||||
margin-right: $interiorMargin;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.widget-thumb {
|
||||
@include flex(1 1 auto);
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.rule-title {
|
||||
@include flex(0 1 auto);
|
||||
color: pullForward($colorBodyFg, 50%);
|
||||
}
|
||||
|
||||
.rule-description {
|
||||
@include flex(1 1 auto);
|
||||
@include ellipsize();
|
||||
color: pushBack($colorBodyFg, 20%);
|
||||
}
|
||||
|
||||
.s-widget-rule,
|
||||
.s-widget-test-data-item {
|
||||
background-color: rgba($colorBodyFg, 0.1);
|
||||
border-radius: $basicCr;
|
||||
}
|
||||
|
||||
.widget-thumb {
|
||||
@include ellipsize();
|
||||
@extend .s-summary-widget;
|
||||
@extend .l-summary-widget;
|
||||
padding: $interiorMarginSm $interiorMargin;
|
||||
}
|
||||
|
||||
// Hide and show elements in the rule-header on hover
|
||||
.l-widget-rule,
|
||||
.l-widget-test-data-item {
|
||||
.grippy,
|
||||
.l-rule-action-buttons-wrapper,
|
||||
.l-condition-action-buttons-wrapper,
|
||||
.l-widget-test-data-item-action-buttons-wrapper {
|
||||
@include trans-prop-nice($props: opacity, $dur: 500ms);
|
||||
opacity: 0;
|
||||
}
|
||||
&:hover {
|
||||
.grippy,
|
||||
.l-rule-action-buttons-wrapper,
|
||||
.l-widget-test-data-item-action-buttons-wrapper {
|
||||
@include trans-prop-nice($props: opacity, $dur: 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.t-condition {
|
||||
&:hover {
|
||||
.l-condition-action-buttons-wrapper {
|
||||
@include trans-prop-nice($props: opacity, $dur: 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -150,6 +150,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** VIEW CONTROLS */
|
||||
// Expand/collapse > and v arrows, used in tree and plot legend
|
||||
// Moved this over from a tree-only context 5/18/17
|
||||
|
||||
.view-control {
|
||||
@extend .ui-symbol;
|
||||
cursor: pointer;
|
||||
height: 1em; width: 1em;
|
||||
line-height: inherit;
|
||||
&:before {
|
||||
position: absolute;
|
||||
@include trans-prop-nice(transform, 100ms);
|
||||
content: $glyph-icon-arrow-right;
|
||||
@include transform-origin(center);
|
||||
}
|
||||
&.expanded:before {
|
||||
@include transform(rotate(90deg));
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** CUSTOM CHECKBOXES */
|
||||
label.checkbox.custom,
|
||||
label.radio.custom {
|
||||
@ -261,7 +281,7 @@ input[type="number"] {
|
||||
input[type="text"].lg { width: 100% !important; }
|
||||
.l-input-med input[type="text"],
|
||||
input[type="text"].med { width: 200px !important; }
|
||||
input[type="text"].sm { width: 50px !important; }
|
||||
input[type="text"].sm, input[type="number"].sm { width: 50px !important; }
|
||||
.l-numeric input[type="text"],
|
||||
input[type="text"].numeric { text-align: right; }
|
||||
|
||||
@ -317,14 +337,10 @@ input[type="text"].s-input-inline,
|
||||
.select {
|
||||
@include btnSubtle($bg: $colorSelectBg);
|
||||
@extend .icon-arrow-down; // Context arrow
|
||||
@if $shdwBtns != none {
|
||||
margin: 0 0 2px 0; // Needed to avoid dropshadow from being clipped by parent containers
|
||||
}
|
||||
display: inline-block;
|
||||
padding: 0 $interiorMargin;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
line-height: $formInputH;
|
||||
select {
|
||||
@include appearance(none);
|
||||
box-sizing: border-box;
|
||||
@ -340,11 +356,13 @@ input[type="text"].s-input-inline,
|
||||
}
|
||||
}
|
||||
&:before {
|
||||
pointer-events: none;
|
||||
@include transform(translateY(-50%));
|
||||
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: $interiorMargin; top: 0;
|
||||
right: $interiorMargin;
|
||||
top: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -396,8 +414,7 @@ input[type="text"].s-input-inline,
|
||||
.l-elem-wrapper {
|
||||
mct-representation {
|
||||
// Holds the context-available item
|
||||
// Must have min-width to make flex work properly
|
||||
// in Safari
|
||||
// Must have min-width to make flex work properly in Safari
|
||||
min-width: 0.7em;
|
||||
}
|
||||
}
|
||||
@ -563,7 +580,6 @@ input[type="text"].s-input-inline,
|
||||
height: $h;
|
||||
margin-top: 1 + floor($h/2) * -1;
|
||||
@include btnSubtle(pullForward($colorBtnBg, 10%));
|
||||
//border-radius: 50% !important;
|
||||
}
|
||||
|
||||
@mixin sliderKnobRound() {
|
||||
@ -578,7 +594,6 @@ input[type="text"].s-input-inline,
|
||||
|
||||
input[type="range"] {
|
||||
// HTML5 range inputs
|
||||
|
||||
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
|
||||
background: transparent; /* Otherwise white in Chrome */
|
||||
&:focus {
|
||||
@ -736,6 +751,30 @@ textarea {
|
||||
}
|
||||
}
|
||||
|
||||
.view-switcher,
|
||||
.t-btn-view-large {
|
||||
@include trans-prop-nice-fade($controlFadeMs);
|
||||
}
|
||||
|
||||
.view-control {
|
||||
@extend .icon-arrow-right;
|
||||
cursor: pointer;
|
||||
font-size: 0.75em;
|
||||
&:before {
|
||||
position: absolute;
|
||||
@include trans-prop-nice(transform, 100ms);
|
||||
@include transform-origin(center);
|
||||
}
|
||||
&.expanded:before {
|
||||
@include transform(rotate(90deg));
|
||||
}
|
||||
}
|
||||
|
||||
.grippy {
|
||||
@extend .icon-grippy;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
/******************************************************** BROWSER ELEMENTS */
|
||||
body.desktop {
|
||||
::-webkit-scrollbar {
|
||||
|
@ -29,23 +29,27 @@
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 16px; //120%;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.title-label {
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
|
||||
.icon-swatch,
|
||||
.color-swatch {
|
||||
// Used in color menu buttons in toolbar
|
||||
$d: 10px;
|
||||
display: inline-block;
|
||||
border: 1px solid rgba($colorBtnFg, 0.2);
|
||||
height: $d;
|
||||
width: $d;
|
||||
height: $d; width: $d;
|
||||
line-height: $d;
|
||||
vertical-align: middle;
|
||||
margin-left: $interiorMarginSm;
|
||||
margin-top: -2px;
|
||||
&:not(.no-selection) {
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
|
@ -19,7 +19,7 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/******************************************************************* STATUS BLOCK ELEMS */
|
||||
@mixin statusBannerColors($bg, $fg: $colorStatusFg) {
|
||||
$bgPb: 30%;
|
||||
$bgPbD: 10%;
|
||||
@ -120,7 +120,11 @@
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
background: none !important;
|
||||
margin-right: $interiorMarginSm;
|
||||
&[class*='s-status']:before {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.count {
|
||||
@ -136,7 +140,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Styles for messages and message banners */
|
||||
/******************************************************************* MESSAGE BANNERS */
|
||||
.message {
|
||||
&.block {
|
||||
border-radius: $basicCr;
|
||||
@ -192,7 +196,6 @@
|
||||
padding: 0 $interiorMargin;
|
||||
}
|
||||
.close {
|
||||
//@include test(red, 0.7);
|
||||
cursor: pointer;
|
||||
font-size: 7px;
|
||||
width: 8px;
|
||||
@ -236,132 +239,147 @@
|
||||
}
|
||||
}
|
||||
|
||||
@mixin messageBlock($iconW: 32px) {
|
||||
.type-icon.message-type {
|
||||
/******************************************************************* MESSAGES */
|
||||
|
||||
/* Contexts:
|
||||
In .t-message-list
|
||||
In .overlay as a singleton
|
||||
Inline in the view area
|
||||
*/
|
||||
|
||||
// Archetypal message
|
||||
.l-message {
|
||||
$iconW: 32px;
|
||||
@include display(flex);
|
||||
@include flex-direction(row);
|
||||
@include align-items(stretch);
|
||||
padding: $interiorMarginLg;
|
||||
|
||||
&:before {
|
||||
// Icon
|
||||
@include flex(0 1 auto);
|
||||
@include txtShdw($shdwStatusIc);
|
||||
@extend .icon-bell;
|
||||
color: $colorStatusDefault;
|
||||
font-size: $iconW;
|
||||
padding: 1px;
|
||||
width: $iconW + 2;
|
||||
margin-right: $interiorMarginLg;
|
||||
}
|
||||
|
||||
.message-severity-info .type-icon.message-type {
|
||||
&.message-severity-info:before {
|
||||
@extend .icon-info;
|
||||
color: $colorInfo;
|
||||
}
|
||||
.message-severity-alert .type-icon.message-type {
|
||||
@extend .icon-bell;
|
||||
|
||||
&.message-severity-alert:before {
|
||||
color: $colorWarningLo;
|
||||
}
|
||||
.message-severity-error .type-icon.message-type {
|
||||
|
||||
&.message-severity-error:before {
|
||||
@extend .icon-alert-rect;
|
||||
color: $colorWarningHi;
|
||||
}
|
||||
}
|
||||
/* Paths:
|
||||
t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message >
|
||||
message-type > (icon)
|
||||
message-contents >
|
||||
top-bar >
|
||||
title
|
||||
hint
|
||||
editor >
|
||||
(if displaying list of messages)
|
||||
ul > li > l-message >
|
||||
... same as above
|
||||
bottom-bar
|
||||
*/
|
||||
|
||||
.l-message {
|
||||
|
||||
.w-message-contents {
|
||||
@include flex(1 1 auto);
|
||||
@include display(flex);
|
||||
@include flex-direction(row);
|
||||
@include align-items(stretch);
|
||||
.type-icon.message-type {
|
||||
@include flex(0 1 auto);
|
||||
position: relative;
|
||||
}
|
||||
.message-contents {
|
||||
@include flex(1 1 auto);
|
||||
margin-left: $overlayMargin;
|
||||
position: relative;
|
||||
@include flex-direction(column);
|
||||
|
||||
.top-bar,
|
||||
> div,
|
||||
> span {
|
||||
//@include test(red);
|
||||
margin-bottom: $interiorMargin;
|
||||
}
|
||||
|
||||
.message-body {
|
||||
@include flex(1 1 100%);
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton in an overlay dialog
|
||||
.t-message-single .l-message,
|
||||
.t-message-single.l-message {
|
||||
$iconW: 80px;
|
||||
@include absPosDefault();
|
||||
padding: 0;
|
||||
&:before {
|
||||
font-size: $iconW;
|
||||
width: $iconW + 2;
|
||||
}
|
||||
.title {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton inline in a view
|
||||
.t-message-inline .l-message,
|
||||
.t-message-inline.l-message {
|
||||
border-radius: $controlCr;
|
||||
&.message-severity-info { background-color: rgba($colorInfo, 0.3); }
|
||||
&.message-severity-alert { background-color: rgba($colorWarningLo, 0.3); }
|
||||
&.message-severity-error { background-color: rgba($colorWarningHi, 0.3); }
|
||||
|
||||
.w-message-contents.l-message-body-only {
|
||||
.message-body {
|
||||
margin-bottom: $interiorMarginLg * 2;
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In a list
|
||||
.t-message-list {
|
||||
@include absPosDefault();
|
||||
@include display(flex);
|
||||
@include flex-direction(column);
|
||||
|
||||
// Message as singleton
|
||||
.t-message-single {
|
||||
@include messageBlock(80px);
|
||||
}
|
||||
|
||||
body.desktop .t-message-single {
|
||||
.l-message,
|
||||
.bottom-bar {
|
||||
@include absPosDefault();
|
||||
> div,
|
||||
> span {
|
||||
margin-bottom: $interiorMargin;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
top: auto;
|
||||
height: $ovrFooterH;
|
||||
.w-messages {
|
||||
@include flex(1 1 100%);
|
||||
overflow-y: auto;
|
||||
padding-right: $interiorMargin;
|
||||
}
|
||||
// Each message
|
||||
.l-message {
|
||||
border-radius: $controlCr;
|
||||
background: rgba($colorOvrFg, 0.1);
|
||||
margin-bottom: $interiorMargin;
|
||||
.hint,
|
||||
.bottom-bar {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@include phonePortrait {
|
||||
.t-message-single {
|
||||
.l-message {
|
||||
@include flex-direction(column);
|
||||
.message-contents { margin-left: 0; }
|
||||
}
|
||||
.type-icon.message-type {
|
||||
.t-message-single .l-message,
|
||||
.t-message-single.l-message {
|
||||
@include flex-direction(column);
|
||||
&:before {
|
||||
margin-right: 0;
|
||||
margin-bottom: $interiorMarginLg;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bottom-bar {
|
||||
text-align: center !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Messages in list
|
||||
.t-message-list {
|
||||
@include messageBlock(32px);
|
||||
|
||||
.message-contents {
|
||||
.l-message {
|
||||
border-radius: $controlCr;
|
||||
background: rgba($colorOvrFg, 0.1);
|
||||
margin-bottom: $interiorMargin;
|
||||
padding: $interiorMarginLg;
|
||||
|
||||
.message-contents,
|
||||
.bottom-bar {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.message-contents {
|
||||
font-size: 0.9em;
|
||||
margin-left: $interiorMarginLg;
|
||||
.message-action { color: pushBack($colorOvrFg, 20%); }
|
||||
.bottom-bar { text-align: left; }
|
||||
}
|
||||
|
||||
.top-bar,
|
||||
.message-body {
|
||||
margin-bottom: $interiorMarginLg;
|
||||
text-align: center;
|
||||
.s-button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.desktop .t-message-list {
|
||||
.message-contents .l-message { margin-right: $interiorMarginLg; }
|
||||
.w-message-contents { padding-right: $interiorMargin; }
|
||||
}
|
||||
|
||||
// Alert elements in views
|
||||
@ -380,10 +398,6 @@ body.desktop .t-message-list {
|
||||
.object-header {
|
||||
.t-object-alert {
|
||||
display: inline;
|
||||
&.t-alert-unsynced {
|
||||
@extend .icon-alert-triangle;
|
||||
color: $colorPausedBg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,10 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
.l-color-palette {
|
||||
.l-palette {
|
||||
$d: 16px;
|
||||
$colorsPerRow: 10;
|
||||
$m: 1;
|
||||
$colorSelectedColor: #fff;
|
||||
|
||||
box-sizing: border-box;
|
||||
padding: $interiorMargin !important;
|
||||
@ -33,46 +32,41 @@
|
||||
line-height: $d;
|
||||
width: ($d * $colorsPerRow) + ($m * $colorsPerRow);
|
||||
|
||||
&.l-option-row {
|
||||
margin-bottom: $interiorMargin;
|
||||
.s-palette-item {
|
||||
border-color: $colorPaletteFg;
|
||||
}
|
||||
}
|
||||
|
||||
.l-palette-item {
|
||||
box-sizing: border-box;
|
||||
@include txtShdwSubtle(0.8);
|
||||
@include trans-prop-nice-fade(0.25s);
|
||||
border: 1px solid transparent;
|
||||
color: $colorSelectedColor;
|
||||
display: block;
|
||||
float: left;
|
||||
height: $d; width: $d;
|
||||
line-height: $d * 0.9;
|
||||
margin: 0 ($m * 1px) ($m * 1px) 0;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
&:before {
|
||||
// Check mark for selected items
|
||||
font-size: 0.8em;
|
||||
}
|
||||
}
|
||||
|
||||
.s-palette-item {
|
||||
border: 1px solid transparent;
|
||||
color: $colorPaletteFg;
|
||||
text-shadow: $shdwPaletteFg;
|
||||
@include trans-prop-nice-fade(0.25s);
|
||||
&:hover {
|
||||
@include trans-prop-nice-fade(0);
|
||||
border-color: $colorSelectedColor !important;
|
||||
border-color: $colorPaletteSelected !important;
|
||||
}
|
||||
&.selected {
|
||||
border-color: $colorPaletteSelected;
|
||||
box-shadow: $shdwPaletteSelected; //Needed to see selection rect on light colored swatches
|
||||
}
|
||||
}
|
||||
|
||||
.l-palette-item-label {
|
||||
margin-left: $interiorMargin;
|
||||
}
|
||||
|
||||
&.l-option-row {
|
||||
margin-bottom: $interiorMargin;
|
||||
.s-palette-item {
|
||||
border-color: $colorBodyFg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -20,7 +20,19 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
.section-header {
|
||||
border-radius: $basicCr;
|
||||
background: $colorFormSectionHeader;
|
||||
color: lighten($colorBodyFg, 20%);
|
||||
font-size: inherit;
|
||||
margin: $interiorMargin 0;
|
||||
padding: $formTBPad $formLRPad;
|
||||
text-transform: uppercase;
|
||||
.view-control {
|
||||
display: inline-block;
|
||||
margin-right: $interiorMargin;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.form {
|
||||
@ -41,15 +53,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.section-header {
|
||||
border-radius: $basicCr;
|
||||
background: $colorFormSectionHeader;
|
||||
$c: lighten($colorBodyFg, 20%);
|
||||
color: $c;
|
||||
font-size: 0.8em;
|
||||
padding: $formTBPad $formLRPad;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
$m: $interiorMargin;
|
||||
box-sizing: border-box;
|
||||
@ -57,9 +60,6 @@
|
||||
margin-bottom: $interiorMarginLg * 2;
|
||||
padding: $formTBPad 0;
|
||||
position: relative;
|
||||
//&ng-form {
|
||||
// display: block;
|
||||
//}
|
||||
|
||||
&.first {
|
||||
border-top: none;
|
||||
@ -171,3 +171,106 @@
|
||||
padding: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************** COMPACT FORM */
|
||||
// ul > li > label, control
|
||||
// Make a new UL for each form section
|
||||
// Allow control-first, controls-below
|
||||
// TO-DO: migrate work in branch ch-plot-styling that users .inspector-config to use classes below instead
|
||||
|
||||
.l-compact-form .tree ul li,
|
||||
.l-compact-form ul li {
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
|
||||
.l-compact-form {
|
||||
$labelW: 40%;
|
||||
$minW: $labelW;
|
||||
ul {
|
||||
margin-bottom: $interiorMarginLg;
|
||||
li {
|
||||
@include display(flex);
|
||||
@include flex-wrap(wrap);
|
||||
@include align-items(center);
|
||||
label,
|
||||
.control {
|
||||
@include display(flex);
|
||||
}
|
||||
label {
|
||||
line-height: inherit;
|
||||
width: $labelW;
|
||||
}
|
||||
.controls {
|
||||
@include flex-grow(1);
|
||||
margin-left: $interiorMargin;
|
||||
input[type="text"],
|
||||
input[type="search"],
|
||||
input[type="number"],
|
||||
.select {
|
||||
height: $btnStdH;
|
||||
line-height: $btnStdH;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.e-control {
|
||||
// Individual form controls
|
||||
&:not(:first-child) {
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.connects-to-previous {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
&.section-header {
|
||||
margin-top: $interiorMarginLg;
|
||||
border-top: 1px solid $colorFormLines;
|
||||
}
|
||||
|
||||
&.controls-first {
|
||||
.control {
|
||||
@include flex-grow(0);
|
||||
margin-right: $interiorMargin;
|
||||
min-width: 0;
|
||||
order: 1;
|
||||
width: auto;
|
||||
}
|
||||
label {
|
||||
@include flex-grow(1);
|
||||
order: 2;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
&.controls-under {
|
||||
display: block;
|
||||
.control, label {
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
ul li {
|
||||
border-top: none !important;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-error {
|
||||
// Block element that visually flags an error and contains a message
|
||||
background-color: $colorFormFieldErrorBg;
|
||||
color: $colorFormFieldErrorFg;
|
||||
border-radius: $basicCr;
|
||||
display: block;
|
||||
padding: 1px 6px;
|
||||
&:before {
|
||||
content: $glyph-icon-alert-triangle;
|
||||
display: inline;
|
||||
font-family: symbolsfont;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,18 +34,7 @@ body.touch {
|
||||
line-height: $mobileTreeItemH !important;
|
||||
.view-control {
|
||||
font-size: 1em;
|
||||
margin-right: $interiorMargin;
|
||||
width: ceil($mobileTreeItemH * 0.75);
|
||||
&.has-children {
|
||||
&:before {
|
||||
content: $glyph-icon-arrow-down;
|
||||
left: 50%;
|
||||
@include transform(translateX(-50%) rotate(-90deg));
|
||||
}
|
||||
&.expanded:before {
|
||||
@include transform(translateX(-50%) rotate(0deg));
|
||||
}
|
||||
}
|
||||
width: ceil($mobileTreeItemH * 0.5);
|
||||
}
|
||||
.t-object-label {
|
||||
line-height: inherit;
|
||||
|
@ -79,6 +79,7 @@
|
||||
|
||||
// Dialog boxes, size constrained and centered in desktop/tablet
|
||||
&.l-dialog {
|
||||
font-size: 0.8rem;
|
||||
.s-button {
|
||||
&:not(.major) {
|
||||
@include btnSubtle($bg: $colorOvrBtnBg, $bgHov: pullForward($colorOvrBtnBg, 10%), $fg: $colorOvrBtnFg, $fgHov: $colorOvrBtnFg, $ic: $colorOvrBtnFg, $icHov: $colorOvrBtnFg);
|
||||
@ -125,9 +126,9 @@
|
||||
@include containerSubtle($colorOvrBg, $colorOvrFg);
|
||||
}
|
||||
|
||||
.title {
|
||||
.dialog-title {
|
||||
@include ellipsize();
|
||||
font-size: 1.2em;
|
||||
font-size: 1.5em;
|
||||
line-height: 120%;
|
||||
margin-bottom: $interiorMargin;
|
||||
}
|
||||
|
208
platform/commonUI/general/res/sass/plots/_legend.scss
Normal file
208
platform/commonUI/general/res/sass/plots/_legend.scss
Normal file
@ -0,0 +1,208 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
.gl-plot {
|
||||
.gl-plot-legend {
|
||||
min-height: $plotLegendH;
|
||||
|
||||
.view-control {
|
||||
font-size: 1em;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
tr {
|
||||
display: table-row;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
@include ellipsize(); // Note: this won't work if table-layout uses anything other than fixed.
|
||||
display: table-cell;
|
||||
padding: 1px 3px; // Tighter than standard tabular padding
|
||||
}
|
||||
}
|
||||
|
||||
&.hover-on-plot {
|
||||
// User is hovering over the plot to get a value at a point
|
||||
.hover-value-enabled {
|
||||
background-color: $legendHoverValueBg;
|
||||
border-radius: $smallCr;
|
||||
padding: 0 $interiorMarginSm;
|
||||
&:before {
|
||||
opacity: 0.5;
|
||||
}
|
||||
&.cursor-hover,
|
||||
.value-to-display-nearestTimestamp,
|
||||
.value-to-display-nearestValue
|
||||
{
|
||||
@extend .icon-crosshair-12px;
|
||||
&:before {
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
&.value-to-display-min:before {
|
||||
content: 'MIN ';
|
||||
}
|
||||
&.value-to-display-max:before {
|
||||
content: 'MAX ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; }
|
||||
&.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; }
|
||||
|
||||
/***************** GENERAL STYLES, ALL STATES */
|
||||
.plot-legend-item {
|
||||
// General styles for legend items, both expanded and collapsed legend states
|
||||
.plot-series-color-swatch {
|
||||
border-radius: $smallCr;
|
||||
border: 1px solid $colorBodyBg;
|
||||
display: inline-block;
|
||||
height: $plotSwatchD;
|
||||
width: $plotSwatchD;
|
||||
}
|
||||
.plot-series-name {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.plot-series-value {
|
||||
@include ellipsize();
|
||||
}
|
||||
}
|
||||
|
||||
/***************** GENERAL STYLES, COLLAPSED */
|
||||
&.plot-legend-collapsed {
|
||||
// .plot-legend-item is a span of spans.
|
||||
&.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
|
||||
&.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
|
||||
&.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
|
||||
&.plot-legend-left .gl-plot-legend { margin-right: $interiorMargin; }
|
||||
|
||||
.plot-legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&:not(:first-child) {
|
||||
margin-left: $interiorMarginLg;
|
||||
}
|
||||
.plot-series-swatch-and-name,
|
||||
.plot-series-value {
|
||||
@include ellipsize();
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.plot-series-swatch-and-name {
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
.plot-series-value {
|
||||
text-align: left;
|
||||
width: 170px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************** GENERAL STYLES, EXPANDED */
|
||||
&.plot-legend-expanded {
|
||||
.gl-plot-legend {
|
||||
max-height: 70%;
|
||||
}
|
||||
|
||||
.plot-wrapper-expanded-legend {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&.plot-legend-top .gl-plot-legend {
|
||||
margin-bottom: $interiorMargin;
|
||||
}
|
||||
&.plot-legend-bottom .gl-plot-legend {
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
/***************** TOP OR BOTTOM */
|
||||
&.plot-legend-top,
|
||||
&.plot-legend-bottom {
|
||||
// General styles when legend is on the top or bottom
|
||||
@extend .l-flex-col;
|
||||
&.plot-legend-collapsed {
|
||||
// COLLAPSED ON TOP OR BOTTOM
|
||||
.plot-wrapper-collapsed-legend {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************** EITHER SIDE */
|
||||
&.plot-legend-left,
|
||||
&.plot-legend-right {
|
||||
@extend .l-flex-row;
|
||||
// If the legend is expanded, use flex-col instead so that the legend gets the width it needs.
|
||||
&.plot-legend-expanded {
|
||||
// EXPANDED, ON EITHER SIDE
|
||||
@extend .l-flex-col;
|
||||
}
|
||||
|
||||
&.plot-legend-collapsed {
|
||||
// COLLAPSED, ON EITHER SIDE
|
||||
.gl-plot-legend {
|
||||
max-height: inherit;
|
||||
width: 25%;
|
||||
}
|
||||
.plot-wrapper-collapsed-legend {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.plot-legend-item {
|
||||
margin-bottom: 1px;
|
||||
margin-left: 0;
|
||||
flex-wrap: wrap;
|
||||
.plot-series-swatch-and-name {
|
||||
flex: 0 1 auto;
|
||||
min-width: 20%;
|
||||
}
|
||||
.plot-series-value {
|
||||
flex: 0 1 auto;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************** ON BOTTOM OR RIGHT */
|
||||
&.plot-legend-right:not(.plot-legend-expanded),
|
||||
&.plot-legend-bottom {
|
||||
.gl-plot-legend {
|
||||
order: 2;
|
||||
}
|
||||
.plot-wrapper-axis-and-display-area {
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
}
|
@ -20,10 +20,42 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
.abs.holder-plot {
|
||||
// Fend off the scrollbar when less than min-height;
|
||||
right: $interiorMargin;
|
||||
right: $interiorMargin; // Fend off the scrollbar when less than min-height;
|
||||
.t-object-alert.t-alert-unsynced {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************* STACKED PLOT LAYOUT */
|
||||
.t-plot-stacked {
|
||||
.l-view-section {
|
||||
// Make this a flex container
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
.gl-plot.child-frame {
|
||||
mct-plot {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
flex: 1 1 auto;
|
||||
&:not(:first-child) {
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.s-status-timeconductor-unsynced .holder-plot {
|
||||
.t-object-alert.t-alert-unsynced {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.gl-plot {
|
||||
color: $colorPlotFg;
|
||||
font-size: 0.7rem;
|
||||
@ -32,6 +64,19 @@
|
||||
height: 100%;
|
||||
min-height: $plotMinH;
|
||||
|
||||
/********************************************* AXIS AND DISPLAY AREA */
|
||||
.plot-wrapper-axis-and-display-area {
|
||||
margin-top: $interiorMargin; // Keep the top tick label from getting clipped
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
.t-object-alert {
|
||||
position: absolute;
|
||||
display: block;
|
||||
font-size: 1.5em;
|
||||
top: $interiorMarginSm; left: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
.gl-plot-wrapper-display-area-and-x-axis {
|
||||
// Holds the plot area and the X-axis only
|
||||
position: absolute;
|
||||
@ -49,7 +94,6 @@
|
||||
}
|
||||
|
||||
.gl-plot-axis-area.gl-plot-x {
|
||||
//@include test(green);
|
||||
top: auto;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
@ -63,7 +107,7 @@
|
||||
.gl-plot-axis-area {
|
||||
position: absolute;
|
||||
&.gl-plot-y {
|
||||
top: $plotLegendH + $interiorMargin;
|
||||
top: nth($plotDisplayArea, 1);
|
||||
right: auto;
|
||||
bottom: nth($plotDisplayArea, 3);
|
||||
left: 0;
|
||||
@ -158,17 +202,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.gl-plot-legend {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: auto;
|
||||
left: 0;
|
||||
height: $plotLegendH;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/****************************** Limits and Out-of-Bounds data */
|
||||
|
||||
.l-limit-bar,
|
||||
@ -235,39 +268,6 @@
|
||||
border: 1px solid $colorPlotAreaBorder;
|
||||
}
|
||||
|
||||
.gl-plot-legend,
|
||||
.legend {
|
||||
.plot-legend-item,
|
||||
.legend-item {
|
||||
display: inline-block;
|
||||
margin-right: $interiorMarginLg;
|
||||
margin-bottom: $interiorMarginSm;
|
||||
span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.plot-color-swatch,
|
||||
.color-swatch {
|
||||
border-radius: 2px;
|
||||
display: inline-block;
|
||||
height: $plotSwatchD;
|
||||
width: $plotSwatchD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gl-plot-legend {
|
||||
.plot-legend-item {
|
||||
border-radius: $smallCr;
|
||||
line-height: 1.5em;
|
||||
padding: 0px $itemPadLR;
|
||||
.plot-color-swatch {
|
||||
border: 1px solid $colorBodyBg;
|
||||
height: $plotSwatchD + 1;
|
||||
width: $plotSwatchD + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tick {
|
||||
position: absolute;
|
||||
border: 0 $colorPlotHash solid;
|
||||
@ -277,10 +277,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.s-tick-label {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.gl-plot-tick,
|
||||
.tick-label {
|
||||
@extend .s-tick-label;
|
||||
@include reverseEllipsis();
|
||||
font-size: 0.7rem;
|
||||
position: absolute;
|
||||
&.gl-plot-x-tick-label,
|
||||
&.tick-label-x {
|
||||
@ -320,3 +324,76 @@
|
||||
right: 0; left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************* HEATMAPS */
|
||||
.l-heatmap {
|
||||
right: $interiorMargin; // Fend off from the scrollbar
|
||||
}
|
||||
|
||||
.h-heatmap-legend {
|
||||
flex: 1 1 1%; // Pertains to the width of legend area
|
||||
height: 50%;
|
||||
min-height: 100px !important;
|
||||
canvas.heatmap-legend {
|
||||
width: 10px;
|
||||
}
|
||||
.h-heatmap-legend-ticks {
|
||||
justify-content: space-between;
|
||||
margin-left: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
.h-heatmap-grid-outer {
|
||||
$ticksYW: 40px;
|
||||
flex: 1 1 100% !important;
|
||||
margin-left: $ticksYW + $interiorMarginLg;
|
||||
|
||||
.h-heatmap-grid-and-ticks {
|
||||
position: relative;
|
||||
height: 0;
|
||||
width: 100%;
|
||||
padding-bottom: 100%;
|
||||
.heatmap-grid {
|
||||
width: 100%;
|
||||
height:100%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.s-tick-label {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.l-heatmap-ticks-x,
|
||||
.l-heatmap-ticks-y {
|
||||
justify-content: stretch;
|
||||
position: absolute;
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.l-heatmap-ticks-x {
|
||||
@extend .l-flex-row;
|
||||
bottom: auto;
|
||||
top: 100%;
|
||||
transform: translateY($interiorMargin);
|
||||
.s-tick-label {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.l-heatmap-ticks-y {
|
||||
@extend .l-flex-col;
|
||||
left: auto;
|
||||
right: 100%;
|
||||
text-align: right;
|
||||
transform: translateX($interiorMargin * -1);
|
||||
.s-tick-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,21 +52,13 @@ ul.tree {
|
||||
|
||||
.view-control {
|
||||
color: $colorItemTreeVC;
|
||||
font-size: 0.75em;
|
||||
margin-right: $interiorMargin;
|
||||
height: 100%;
|
||||
line-height: inherit;
|
||||
width: $treeVCW;
|
||||
&.has-children {
|
||||
&:before {
|
||||
position: absolute;
|
||||
@include trans-prop-nice(transform, 100ms);
|
||||
content: "\e904";
|
||||
@include transform-origin(center);
|
||||
}
|
||||
&.expanded:before {
|
||||
@include transform(rotate(90deg));
|
||||
}
|
||||
&:before { display: block; }
|
||||
&.no-children {
|
||||
&:before { display: none; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,10 @@
|
||||
|
||||
&.t-object-type-timer,
|
||||
&.t-object-type-clock,
|
||||
&.t-object-type-hyperlink {
|
||||
&.t-object-type-hyperlink,
|
||||
&.t-object-type-summary-widget,
|
||||
&.no-frame .t-object-type-fixed-display,
|
||||
&.no-frame .t-object-type-layout {
|
||||
// Hide the right side buttons for objects where they don't make sense
|
||||
// Note that this will hide the view Switcher button if applied
|
||||
// to an object that has it.
|
||||
@ -103,7 +106,7 @@
|
||||
}
|
||||
&.t-frame-outer > .t-rep-frame {
|
||||
&.contents {
|
||||
$m: 2px;
|
||||
$m: 0px;
|
||||
top: $m;
|
||||
right: $m;
|
||||
bottom: $m;
|
||||
@ -125,14 +128,21 @@
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
/********************************************************** OBJECT TYPES */
|
||||
.t-object-type-hyperlink {
|
||||
/********************************************************** OBJECT TYPES */
|
||||
.t-object-type-hyperlink,
|
||||
.t-object-type-summary-widget {
|
||||
.object-holder {
|
||||
overflow: hidden;
|
||||
}
|
||||
.w-summary-widget,
|
||||
.l-summary-widget,
|
||||
.l-hyperlink.s-button {
|
||||
// When a hyperlink is a button in a frame, make it expand to fill out to the object-holder
|
||||
// Some object types expand to the full size of the object-holder.
|
||||
@extend .abs;
|
||||
}
|
||||
|
||||
.l-summary-widget,
|
||||
.l-hyperlink.s-button {
|
||||
.label {
|
||||
@include ellipsize();
|
||||
@include transform(translateY(-50%));
|
||||
|
@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
.s-hover-border {
|
||||
border: 1px dotted transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.s-status-editing {
|
||||
|
@ -83,9 +83,9 @@ define([
|
||||
this.activeObject = domainObject;
|
||||
|
||||
if (domainObject && domainObject.hasCapability('composition')) {
|
||||
$(this.toggleView.elements()).addClass('has-children');
|
||||
$(this.toggleView.elements()).removeClass('no-children');
|
||||
} else {
|
||||
$(this.toggleView.elements()).removeClass('has-children');
|
||||
$(this.toggleView.elements()).addClass('no-children');
|
||||
}
|
||||
|
||||
if (domainObject && domainObject.hasCapability('status')) {
|
||||
|
@ -181,6 +181,8 @@ $colorPlotHash: $colorTick;
|
||||
$stylePlotHash: dashed;
|
||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
||||
$legendCollapsedNameMaxW: 50%;
|
||||
$legendHoverValueBg: rgba($colorBodyFg, 0.1);
|
||||
|
||||
// Tree
|
||||
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
|
||||
@ -243,6 +245,12 @@ $colorCalCellSelectedBg: $colorItemTreeSelectedBg;
|
||||
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
|
||||
$colorCalCellInMonthBg: pushBack($colorMenuBg, 5%);
|
||||
|
||||
// Palettes
|
||||
$colorPaletteFg: pullForward($colorMenuBg, 30%);
|
||||
$colorPaletteSelected: #fff;
|
||||
$shdwPaletteFg: black 0 0 2px;
|
||||
$shdwPaletteSelected: inset 0 0 0 1px #000;
|
||||
|
||||
// About Screen
|
||||
$colorAboutLink: #84b3ff;
|
||||
|
||||
|
@ -181,6 +181,8 @@ $colorPlotHash: $colorTick;
|
||||
$stylePlotHash: dashed;
|
||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
||||
$legendCollapsedNameMaxW: 50%;
|
||||
$legendHoverValueBg: rgba($colorBodyFg, 0.2);
|
||||
|
||||
// Tree
|
||||
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
|
||||
@ -243,6 +245,12 @@ $colorCalCellSelectedBg: $colorItemTreeSelectedBg;
|
||||
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
|
||||
$colorCalCellInMonthBg: pullForward($colorMenuBg, 5%);
|
||||
|
||||
// Palettes
|
||||
$colorPaletteFg: pullForward($colorMenuBg, 30%);
|
||||
$colorPaletteSelected: #333;
|
||||
$shdwPaletteFg: none;
|
||||
$shdwPaletteSelected: inset 0 0 0 1px #fff;
|
||||
|
||||
// About Screen
|
||||
$colorAboutLink: #84b3ff;
|
||||
|
||||
|
@ -23,10 +23,13 @@
|
||||
define([
|
||||
"moment-timezone",
|
||||
"./src/indicators/ClockIndicator",
|
||||
"./src/indicators/FollowIndicator",
|
||||
"./src/services/TickerService",
|
||||
"./src/services/TimerService",
|
||||
"./src/controllers/ClockController",
|
||||
"./src/controllers/TimerController",
|
||||
"./src/controllers/RefreshingController",
|
||||
"./src/actions/FollowTimerAction",
|
||||
"./src/actions/StartTimerAction",
|
||||
"./src/actions/RestartTimerAction",
|
||||
"./src/actions/StopTimerAction",
|
||||
@ -37,10 +40,13 @@ define([
|
||||
], function (
|
||||
MomentTimezone,
|
||||
ClockIndicator,
|
||||
FollowIndicator,
|
||||
TickerService,
|
||||
TimerService,
|
||||
ClockController,
|
||||
TimerController,
|
||||
RefreshingController,
|
||||
FollowTimerAction,
|
||||
StartTimerAction,
|
||||
RestartTimerAction,
|
||||
StopTimerAction,
|
||||
@ -80,6 +86,11 @@ define([
|
||||
"CLOCK_INDICATOR_FORMAT"
|
||||
],
|
||||
"priority": "preferred"
|
||||
},
|
||||
{
|
||||
"implementation": FollowIndicator,
|
||||
"depends": ["timerService"],
|
||||
"priority": "fallback"
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
@ -90,6 +101,11 @@ define([
|
||||
"$timeout",
|
||||
"now"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "timerService",
|
||||
"implementation": TimerService,
|
||||
"depends": ["openmct"]
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
@ -134,6 +150,15 @@ define([
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"key": "timer.follow",
|
||||
"implementation": FollowTimerAction,
|
||||
"depends": ["timerService"],
|
||||
"category": "contextual",
|
||||
"name": "Follow Timer",
|
||||
"cssClass": "icon-clock",
|
||||
"priority": "optional"
|
||||
},
|
||||
{
|
||||
"key": "timer.start",
|
||||
"implementation": StartTimerAction,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
@ -21,27 +21,34 @@
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['../src/PlotOptionsForm'],
|
||||
function (PlotOptionsForm) {
|
||||
[],
|
||||
function () {
|
||||
|
||||
describe("The Plot Options form", function () {
|
||||
var plotOptionsForm;
|
||||
/**
|
||||
* Designates a specific timer for following. Timelines, for example,
|
||||
* use the actively followed timer to display a time-of-interest line
|
||||
* and interpret time conductor bounds in the Timeline's relative
|
||||
* time frame.
|
||||
*
|
||||
* @implements {Action}
|
||||
* @memberof platform/features/clock
|
||||
* @constructor
|
||||
* @param {ActionContext} context the context for this action
|
||||
*/
|
||||
function FollowTimerAction(timerService, context) {
|
||||
var domainObject = context.domainObject;
|
||||
this.perform =
|
||||
timerService.setTimer.bind(timerService, domainObject);
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
FollowTimerAction.appliesTo = function (context) {
|
||||
var model =
|
||||
(context.domainObject && context.domainObject.getModel()) ||
|
||||
{};
|
||||
|
||||
plotOptionsForm = new PlotOptionsForm();
|
||||
});
|
||||
return model.type === 'timer';
|
||||
};
|
||||
|
||||
it("defines form specs for x-axis, y-axis, and series data", function () {
|
||||
expect(plotOptionsForm.xAxisForm).toBeDefined();
|
||||
expect(plotOptionsForm.xAxisForm.sections).toBeDefined();
|
||||
expect(plotOptionsForm.xAxisForm.sections[0].rows).toBeDefined();
|
||||
expect(plotOptionsForm.xAxisForm.sections[0].rows.length).toBeGreaterThan(0);
|
||||
|
||||
expect(plotOptionsForm.yAxisForm).toBeDefined();
|
||||
expect(plotOptionsForm.plotSeriesForm).toBeDefined();
|
||||
});
|
||||
|
||||
});
|
||||
return FollowTimerAction;
|
||||
}
|
||||
);
|
@ -1,5 +1,5 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
@ -20,26 +20,38 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([], function () {
|
||||
function Region(element) {
|
||||
this.activeView = undefined;
|
||||
this.element = element;
|
||||
define(
|
||||
['moment'],
|
||||
function (moment) {
|
||||
var NO_TIMER = "No timer being followed";
|
||||
|
||||
/**
|
||||
* Indicator that displays the active timer, as well as its
|
||||
* current state.
|
||||
* @implements {Indicator}
|
||||
* @memberof platform/features/clock
|
||||
*/
|
||||
function FollowIndicator(timerService) {
|
||||
this.timerService = timerService;
|
||||
}
|
||||
|
||||
FollowIndicator.prototype.getGlyphClass = function () {
|
||||
return "";
|
||||
};
|
||||
|
||||
FollowIndicator.prototype.getCssClass = function () {
|
||||
return (this.timerService.getTimer()) ? "icon-timer s-status-ok" : "icon-timer";
|
||||
};
|
||||
|
||||
FollowIndicator.prototype.getText = function () {
|
||||
var timer = this.timerService.getTimer();
|
||||
return (timer) ? 'Following timer ' + timer.getModel().name : NO_TIMER;
|
||||
};
|
||||
|
||||
FollowIndicator.prototype.getDescription = function () {
|
||||
return "";
|
||||
};
|
||||
|
||||
return FollowIndicator;
|
||||
}
|
||||
|
||||
Region.prototype.clear = function () {
|
||||
if (this.activeView) {
|
||||
this.activeView.destroy();
|
||||
this.activeView = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
Region.prototype.show = function (view) {
|
||||
this.clear();
|
||||
this.activeView = view;
|
||||
if (this.activeView) {
|
||||
this.activeView.show(this.element);
|
||||
}
|
||||
};
|
||||
|
||||
return Region;
|
||||
});
|
||||
);
|
102
platform/features/clock/src/services/TimerService.js
Normal file
102
platform/features/clock/src/services/TimerService.js
Normal file
@ -0,0 +1,102 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['EventEmitter'], function (EventEmitter) {
|
||||
|
||||
/**
|
||||
* Tracks the currently-followed Timer object. Used by
|
||||
* timelines et al to synchronize to a particular timer.
|
||||
*
|
||||
* The TimerService emits `change` events when the active timer
|
||||
* is changed.
|
||||
*/
|
||||
function TimerService(openmct) {
|
||||
EventEmitter.apply(this);
|
||||
this.time = openmct.time;
|
||||
}
|
||||
|
||||
TimerService.prototype = Object.create(EventEmitter.prototype);
|
||||
|
||||
/**
|
||||
* Set (or clear, if `timer` is undefined) the currently active timer.
|
||||
* @param {DomainObject} timer the new active timer
|
||||
* @emits change
|
||||
*/
|
||||
TimerService.prototype.setTimer = function (timer) {
|
||||
this.timer = timer;
|
||||
this.emit('change');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the currently active timer.
|
||||
* @return {DomainObject} the active timer
|
||||
* @emits change
|
||||
*/
|
||||
TimerService.prototype.getTimer = function () {
|
||||
return this.timer;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check if there is a currently active timer.
|
||||
* @return {boolean} true if there is a timer
|
||||
*/
|
||||
TimerService.prototype.hasTimer = function () {
|
||||
return !!this.timer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the provided timestamp to milliseconds relative to
|
||||
* the active timer.
|
||||
* @return {number} milliseconds since timer start
|
||||
*/
|
||||
TimerService.prototype.convert = function (timestamp) {
|
||||
var clock = this.time.clock();
|
||||
var canConvert = this.hasTimer() &&
|
||||
!!clock &&
|
||||
this.timer.getModel().timerState !== 'stopped';
|
||||
|
||||
if (!canConvert) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var now = clock.currentValue();
|
||||
var model = this.timer.getModel();
|
||||
var delta = model.timerState === 'paused' ? now - model.pausedTime : 0;
|
||||
var epoch = model.timestamp;
|
||||
|
||||
return timestamp - epoch - delta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the value of the active clock, adjusted to be relative to the active
|
||||
* timer. If there is no clock or no active timer, this will return
|
||||
* `undefined`.
|
||||
* @return {number} milliseconds since the start of the active timer
|
||||
*/
|
||||
TimerService.prototype.now = function () {
|
||||
var clock = this.time.clock();
|
||||
return clock && this.convert(clock.currentValue());
|
||||
};
|
||||
|
||||
return TimerService;
|
||||
});
|
@ -0,0 +1,80 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
"../../src/actions/FollowTimerAction"
|
||||
], function (FollowTimerAction) {
|
||||
var TIMER_SERVICE_METHODS =
|
||||
['setTimer', 'getTimer', 'clearTimer', 'on', 'off'];
|
||||
|
||||
describe("The Follow Timer action", function () {
|
||||
var testContext;
|
||||
var testModel;
|
||||
|
||||
beforeEach(function () {
|
||||
testModel = {};
|
||||
testContext = { domainObject: { getModel: function () {
|
||||
return testModel;
|
||||
} } };
|
||||
});
|
||||
|
||||
it("is applicable to timers", function () {
|
||||
testModel.type = "timer";
|
||||
expect(FollowTimerAction.appliesTo(testContext)).toBe(true);
|
||||
});
|
||||
|
||||
it("is inapplicable to non-timers", function () {
|
||||
testModel.type = "folder";
|
||||
expect(FollowTimerAction.appliesTo(testContext)).toBe(false);
|
||||
});
|
||||
|
||||
describe("when instantiated", function () {
|
||||
var mockTimerService;
|
||||
var action;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimerService = jasmine.createSpyObj(
|
||||
'timerService',
|
||||
TIMER_SERVICE_METHODS
|
||||
);
|
||||
action = new FollowTimerAction(mockTimerService, testContext);
|
||||
});
|
||||
|
||||
it("does not interact with the timer service", function () {
|
||||
TIMER_SERVICE_METHODS.forEach(function (method) {
|
||||
expect(mockTimerService[method]).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and performed", function () {
|
||||
beforeEach(function () {
|
||||
action.perform();
|
||||
});
|
||||
|
||||
it("sets the active timer", function () {
|
||||
expect(mockTimerService.setTimer)
|
||||
.toHaveBeenCalledWith(testContext.domainObject);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,61 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(["../../src/indicators/FollowIndicator"], function (FollowIndicator) {
|
||||
var TIMER_SERVICE_METHODS =
|
||||
['setTimer', 'getTimer', 'clearTimer', 'on', 'off'];
|
||||
|
||||
describe("The timer-following indicator", function () {
|
||||
var mockTimerService;
|
||||
var indicator;
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimerService =
|
||||
jasmine.createSpyObj('timerService', TIMER_SERVICE_METHODS);
|
||||
indicator = new FollowIndicator(mockTimerService);
|
||||
});
|
||||
|
||||
it("implements the Indicator interface", function () {
|
||||
expect(indicator.getGlyphClass()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getCssClass()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getText()).toEqual(jasmine.any(String));
|
||||
expect(indicator.getDescription()).toEqual(jasmine.any(String));
|
||||
});
|
||||
|
||||
describe("when a timer is set", function () {
|
||||
var testModel;
|
||||
var mockDomainObject;
|
||||
|
||||
beforeEach(function () {
|
||||
testModel = { name: "some timer!" };
|
||||
mockDomainObject = jasmine.createSpyObj('timer', ['getModel']);
|
||||
mockDomainObject.getModel.andReturn(testModel);
|
||||
mockTimerService.getTimer.andReturn(mockDomainObject);
|
||||
});
|
||||
|
||||
it("displays the timer's name", function () {
|
||||
expect(indicator.getText().indexOf(testModel.name))
|
||||
.not.toEqual(-1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
63
platform/features/clock/test/services/TimerServiceSpec.js
Normal file
63
platform/features/clock/test/services/TimerServiceSpec.js
Normal file
@ -0,0 +1,63 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'../../src/services/TimerService'
|
||||
], function (TimerService) {
|
||||
describe("TimerService", function () {
|
||||
var callback;
|
||||
var mockmct;
|
||||
var timerService;
|
||||
|
||||
beforeEach(function () {
|
||||
callback = jasmine.createSpy('callback');
|
||||
mockmct = { time: { clock: jasmine.createSpy('clock') } };
|
||||
timerService = new TimerService(mockmct);
|
||||
timerService.on('change', callback);
|
||||
});
|
||||
|
||||
it("initially emits no change events", function () {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports no current timer", function () {
|
||||
expect(timerService.getTimer()).toBeUndefined();
|
||||
});
|
||||
|
||||
describe("setTimer", function () {
|
||||
var testTimer;
|
||||
|
||||
beforeEach(function () {
|
||||
testTimer = { name: "I am some timer; you are nobody." };
|
||||
timerService.setTimer(testTimer);
|
||||
});
|
||||
|
||||
it("emits a change event", function () {
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports the current timer", function () {
|
||||
expect(timerService.getTimer()).toBe(testTimer);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -334,46 +334,6 @@ define([
|
||||
"conversion": "number[]"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "telemetry.panel",
|
||||
"name": "Telemetry Panel",
|
||||
"cssClass": "icon-telemetry-panel",
|
||||
"description": "A panel for collecting telemetry elements.",
|
||||
"priority": 899,
|
||||
"delegates": [
|
||||
"telemetry"
|
||||
],
|
||||
"features": "creation",
|
||||
"contains": [
|
||||
{
|
||||
"has": "telemetry"
|
||||
}
|
||||
],
|
||||
"model": {
|
||||
"composition": []
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"name": "Layout Grid",
|
||||
"control": "composite",
|
||||
"items": [
|
||||
{
|
||||
"name": "Horizontal grid (px)",
|
||||
"control": "textfield",
|
||||
"cssClass": "l-input-sm l-numeric"
|
||||
},
|
||||
{
|
||||
"name": "Vertical grid (px)",
|
||||
"control": "textfield",
|
||||
"cssClass": "l-input-sm l-numeric"
|
||||
}
|
||||
],
|
||||
"pattern": "^(\\d*[1-9]\\d*)?$",
|
||||
"property": "layoutGrid",
|
||||
"conversion": "number[]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -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="frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
|
||||
<div class="frame frame-template t-frame-inner abs t-object-type-{{ domainObject.getModel().type }}">
|
||||
<div class="abs object-browse-bar l-flex-row">
|
||||
<div class="left flex-elem l-flex-row grows">
|
||||
<mct-representation
|
||||
|
@ -1,37 +0,0 @@
|
||||
# Plot README
|
||||
|
||||
## Chart
|
||||
|
||||
The `mct-chart` directive is used to support drawing of simple charts. It is
|
||||
present to support the Plot view, and its functionality is limited to the
|
||||
functionality that is relevant for that view.
|
||||
|
||||
This directive is used at the element level and takes one attribute, `draw`
|
||||
which is an Angular expression which will should evaluate to a drawing object.
|
||||
This drawing object should contain the following properties:
|
||||
|
||||
* `dimensions`: The size, in logical coordinates, of the chart area. A
|
||||
two-element array or numbers.
|
||||
* `origin`: The position, in logical coordinates, of the lower-left corner of
|
||||
the chart area. A two-element array or numbers.
|
||||
* `lines`: An array of lines (e.g. as a plot line) to draw, where each line is
|
||||
expressed as an object containing:
|
||||
* `buffer`: A Float32Array containing points in the line, in logical
|
||||
coordinates, in sequential x,y pairs.
|
||||
* `color`: The color of the line, as a four-element RGBA array, where
|
||||
each element is a number in the range of 0.0-1.0.
|
||||
* `points`: The number of points in the line.
|
||||
* `boxes`: An array of rectangles to draw in the chart area. Each is an object
|
||||
containing:
|
||||
* `start`: The first corner of the rectangle, as a two-element array of
|
||||
numbers, in logical coordinates.
|
||||
* `end`: The opposite corner of the rectangle, as a two-element array of
|
||||
numbers, in logical coordinates. color : The color of the line, as a
|
||||
four-element RGBA array, where each element is a number in the range of
|
||||
0.0-1.0.
|
||||
|
||||
While `mct-chart` is intended to support plots specifically, it does perform
|
||||
some useful management of canvas objects (e.g. choosing between WebGL and Canvas
|
||||
2D APIs for drawing based on browser support) so its usage is recommended when
|
||||
its supported drawing primitives are sufficient for other charting tasks.
|
||||
|
@ -1,157 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
"./src/MCTChart",
|
||||
"./src/PlotController",
|
||||
"./src/policies/PlotViewPolicy",
|
||||
"./src/PlotOptionsController",
|
||||
"./src/services/ExportImageService",
|
||||
"text!./res/templates/plot.html",
|
||||
"text!./res/templates/plot-options-browse.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
MCTChart,
|
||||
PlotController,
|
||||
PlotViewPolicy,
|
||||
PlotOptionsController,
|
||||
exportImageService,
|
||||
plotTemplate,
|
||||
plotOptionsBrowseTemplate,
|
||||
legacyRegistry
|
||||
) {
|
||||
|
||||
legacyRegistry.register("platform/features/plot", {
|
||||
"name": "Plot view for telemetry",
|
||||
"extensions": {
|
||||
"views": [
|
||||
{
|
||||
"name": "Plot",
|
||||
"key": "plot",
|
||||
"cssClass": "icon-sine",
|
||||
"template": plotTemplate,
|
||||
"needs": [
|
||||
"telemetry"
|
||||
],
|
||||
"priority": "preferred",
|
||||
"delegation": true
|
||||
}
|
||||
],
|
||||
"directives": [
|
||||
{
|
||||
"key": "mctChart",
|
||||
"implementation": MCTChart,
|
||||
"depends": [
|
||||
"$interval",
|
||||
"$log"
|
||||
]
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "PlotController",
|
||||
"implementation": PlotController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$element",
|
||||
"exportImageService",
|
||||
"telemetryFormatter",
|
||||
"telemetryHandler",
|
||||
"throttle",
|
||||
"PLOT_FIXED_DURATION",
|
||||
"openmct"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "PlotOptionsController",
|
||||
"implementation": PlotOptionsController,
|
||||
"depends": [
|
||||
"$scope"
|
||||
]
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key": "exportImageService",
|
||||
"implementation": exportImageService,
|
||||
"depends": [
|
||||
"$q",
|
||||
"$timeout",
|
||||
"$log",
|
||||
"EXPORT_IMAGE_TIMEOUT"
|
||||
]
|
||||
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"key": "PLOT_FIXED_DURATION",
|
||||
"value": 900000,
|
||||
"priority": "fallback",
|
||||
"comment": "Fifteen minutes."
|
||||
},
|
||||
{
|
||||
"key": "EXPORT_IMAGE_TIMEOUT",
|
||||
"value": 500,
|
||||
"priority": "fallback"
|
||||
}
|
||||
],
|
||||
"policies": [
|
||||
{
|
||||
"category": "view",
|
||||
"implementation": PlotViewPolicy,
|
||||
"depends": [
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
],
|
||||
"representations": [
|
||||
{
|
||||
"key": "plot-options-browse",
|
||||
"template": plotOptionsBrowseTemplate
|
||||
}
|
||||
],
|
||||
"licenses": [
|
||||
{
|
||||
"name": "FileSaver.js",
|
||||
"version": "0.0.2",
|
||||
"author": "Eli Grey",
|
||||
"description": "File download initiator (for file exports)",
|
||||
"website": "https://github.com/eligrey/FileSaver.js/",
|
||||
"copyright": "Copyright © 2015 Eli Grey.",
|
||||
"license": "license-mit",
|
||||
"link": "https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md"
|
||||
},
|
||||
{
|
||||
"name": "html2canvas",
|
||||
"version": "0.4.1",
|
||||
"author": "Niklas von Hertzen",
|
||||
"description": "JavaScript HTML renderer",
|
||||
"website": "https://github.com/niklasvh/html2canvas",
|
||||
"copyright": "Copyright © 2012 Niklas von Hertzen.",
|
||||
"license": "license-mit",
|
||||
"link": "https://github.com/niklasvh/html2canvas/blob/master/LICENSE"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
@ -1,70 +0,0 @@
|
||||
<!--
|
||||
Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
|
||||
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Open MCT includes source code licensed under additional open source
|
||||
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div ng-controller="PlotOptionsController" class="flex-elem grows l-inspector-part">
|
||||
<em class="t-inspector-part-header" title="Display properties for this object">Plot Options</em>
|
||||
<mct-form
|
||||
ng-model="configuration.plot.xAxis"
|
||||
structure="xAxisForm"
|
||||
name="xAxisFormState"
|
||||
class="flex-elem l-flex-row no-margin">
|
||||
</mct-form>
|
||||
<mct-form
|
||||
ng-model="configuration.plot.yAxis"
|
||||
structure="yAxisForm"
|
||||
name="yAxisFormState"
|
||||
class="flex-elem l-flex-row no-margin">
|
||||
</mct-form>
|
||||
<div class="form">
|
||||
<div class="section-header ng-binding ng-scope">
|
||||
Plot Series
|
||||
</div>
|
||||
<ul class="first flex-elem grows vscroll">
|
||||
<ul class="tree">
|
||||
<li ng-repeat="child in children">
|
||||
<span ng-controller="ToggleController as toggle">
|
||||
<span ng-controller="TreeNodeController as treeNode">
|
||||
<span class="tree-item menus-to-left">
|
||||
<span
|
||||
class='ui-symbol view-control flex-elem has-children'
|
||||
ng-class="{ expanded: toggle.isActive() }"
|
||||
ng-click="toggle.toggle(); treeNode.trackExpansion()">
|
||||
</span>
|
||||
<mct-representation
|
||||
class="rep-object-label"
|
||||
key="'label'"
|
||||
mct-object="child">
|
||||
</mct-representation>
|
||||
</span>
|
||||
</span>
|
||||
<mct-form
|
||||
ng-class="{hidden: !toggle.isActive()}"
|
||||
ng-model="configuration.plot.series[$index]"
|
||||
structure="plotSeriesForm"
|
||||
name="plotOptionsState"
|
||||
class="flex-elem l-flex-row">
|
||||
</mct-form>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
@ -1,165 +0,0 @@
|
||||
<!--
|
||||
Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
|
||||
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Open MCT includes source code licensed under additional open source
|
||||
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<span ng-controller="PlotController as plot"
|
||||
class="abs holder holder-plot has-control-bar">
|
||||
<div class="l-control-bar" ng-show="!plot.hideExportButtons">
|
||||
<span class="l-btn-set">
|
||||
<a class="s-button t-export labeled icon-download"
|
||||
ng-click="plot.exportPNG()"
|
||||
title="Export This View's Data as PNG">
|
||||
PNG
|
||||
</a>
|
||||
<a class="s-button t-export labeled"
|
||||
ng-click="plot.exportJPG()"
|
||||
title="Export This View's Data as JPG">
|
||||
JPG
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="l-view-section">
|
||||
<div class="gl-plot"
|
||||
ng-style="{ height: 100 / plot.getSubPlots().length + '%'}"
|
||||
ng-repeat="subplot in plot.getSubPlots()">
|
||||
<div class="gl-plot-legend">
|
||||
<span class='plot-legend-item'
|
||||
ng-repeat="telemetryObject in subplot.getTelemetryObjects()"
|
||||
ng-class="plot.getLegendClass(telemetryObject)">
|
||||
<span class='plot-color-swatch'
|
||||
ng-style="{ 'background-color': plot.getColor($index) }">
|
||||
</span>
|
||||
<span class='title-label'>{{telemetryObject.getModel().name}}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-axis-area gl-plot-y">
|
||||
<div class="gl-plot-label gl-plot-y-label">
|
||||
{{axes[1].active.name}}
|
||||
</div>
|
||||
<div ng-repeat="tick in subplot.getRangeTicks()"
|
||||
class="gl-plot-tick gl-plot-y-tick-label"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
</div>
|
||||
<div class="gl-plot-y-options gl-plot-local-controls"
|
||||
ng-if="axes[1].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[1].active"
|
||||
ng-options="option.name for option in axes[1].options">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-wrapper-display-area-and-x-axis">
|
||||
<mct-include key="'time-of-interest'"
|
||||
class="l-toi-holder show-val"
|
||||
ng-if="toiPerc"
|
||||
ng-class="{ 'pinned': toiPinned, 'val-to-left': toiPerc > 80 }"
|
||||
ng-style="{'left': toiPerc + '%'}"></mct-include>
|
||||
|
||||
<div class="gl-plot-coords"
|
||||
ng-if="subplot.isHovering() && subplot.getHoverCoordinates()">
|
||||
{{subplot.getHoverCoordinates()}}
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-display-area"
|
||||
ng-mouseenter="subplot.isHovering(true);"
|
||||
ng-mouseleave="subplot.isHovering(false)"
|
||||
ng-class="{ loading: plot.isRequestPending() }">
|
||||
|
||||
<!-- Out-of-bounds data indicators -->
|
||||
<!-- ng-show is temporarily hard-coded in next element -->
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-up"></div>
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-dwn"></div>
|
||||
<div class="gl-plot-hash hash-v"
|
||||
ng-repeat="tick in subplot.getDomainTicks()"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%', height: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)">
|
||||
</div>
|
||||
<div class="gl-plot-hash hash-h"
|
||||
ng-repeat="tick in subplot.getRangeTicks()"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%', width: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getRangeTicks().length - 1)">
|
||||
</div>
|
||||
<mct-chart draw="subplot.getDrawingObject()"
|
||||
ng-if="subplot.getTelemetryObjects().length > 0"
|
||||
ng-mousemove="subplot.hover($event)"
|
||||
mct-drag="subplot.continueDrag($event)"
|
||||
mct-drag-down="subplot.startDrag($event)"
|
||||
mct-drag-up="subplot.endDrag($event); plot.update()">
|
||||
</mct-chart>
|
||||
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->
|
||||
<div class="l-local-controls gl-plot-local-controls t-plot-display-controls"
|
||||
ng-if="$first">
|
||||
<a class="s-button icon-arrow-left"
|
||||
ng-click="plot.stepBackPanZoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Restore previous pan/zoom">
|
||||
</a>
|
||||
<a class="s-button icon-arrows-out"
|
||||
ng-click="plot.unzoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Reset pan/zoom">
|
||||
</a>
|
||||
<div class="menu-element s-menu-button menus-to-left {{plot.getMode().cssClass}}"
|
||||
ng-if="plot.getModeOptions().length > 1"
|
||||
ng-controller="ClickAwayController as toggle">
|
||||
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
||||
<span>{{plot.getMode().name}}</span>
|
||||
<div class="menu" ng-show="toggle.isActive()">
|
||||
<ul>
|
||||
<li ng-repeat="option in plot.getModeOptions()"
|
||||
ng-click="plot.setMode(option); toggle.setState(false)"
|
||||
class="{{option.cssClass}}">
|
||||
{{option.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="$last" class="gl-plot-axis-area gl-plot-x">
|
||||
<div ng-repeat="tick in subplot.getDomainTicks()"
|
||||
class="gl-plot-tick gl-plot-x-tick-label"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
</div>
|
||||
<div class="gl-plot-label gl-plot-x-label">
|
||||
{{axes[0].active.name}}
|
||||
</div>
|
||||
<div class="gl-plot-x-options gl-plot-local-controls"
|
||||
ng-if="axes[0].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[0].active"
|
||||
ng-options="option.name for option in axes[0].options">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
@ -1,117 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Create a new chart which uses Canvas's 2D API for rendering.
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @implements {platform/features/plot.Chart}
|
||||
* @param {CanvasElement} canvas the canvas object to render upon
|
||||
* @throws {Error} an error is thrown if Canvas's 2D API is unavailable.
|
||||
*/
|
||||
function Canvas2DChart(canvas) {
|
||||
this.canvas = canvas;
|
||||
this.c2d = canvas.getContext('2d');
|
||||
this.width = canvas.width;
|
||||
this.height = canvas.height;
|
||||
this.dimensions = [this.width, this.height];
|
||||
this.origin = [0, 0];
|
||||
|
||||
if (!this.c2d) {
|
||||
throw new Error("Canvas 2d API unavailable.");
|
||||
}
|
||||
}
|
||||
|
||||
// Convert from logical to physical x coordinates
|
||||
Canvas2DChart.prototype.x = function (v) {
|
||||
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
|
||||
};
|
||||
|
||||
// Convert from logical to physical y coordinates
|
||||
Canvas2DChart.prototype.y = function (v) {
|
||||
return this.height -
|
||||
((v - this.origin[1]) / this.dimensions[1]) * this.height;
|
||||
};
|
||||
|
||||
// Set the color to be used for drawing operations
|
||||
Canvas2DChart.prototype.setColor = function (color) {
|
||||
var mappedColor = color.map(function (c, i) {
|
||||
return i < 3 ? Math.floor(c * 255) : (c);
|
||||
}).join(',');
|
||||
this.c2d.strokeStyle = "rgba(" + mappedColor + ")";
|
||||
this.c2d.fillStyle = "rgba(" + mappedColor + ")";
|
||||
};
|
||||
|
||||
|
||||
Canvas2DChart.prototype.clear = function () {
|
||||
var canvas = this.canvas;
|
||||
this.width = canvas.width;
|
||||
this.height = canvas.height;
|
||||
this.c2d.clearRect(0, 0, this.width, this.height);
|
||||
};
|
||||
|
||||
Canvas2DChart.prototype.setDimensions = function (newDimensions, newOrigin) {
|
||||
this.dimensions = newDimensions;
|
||||
this.origin = newOrigin;
|
||||
};
|
||||
|
||||
Canvas2DChart.prototype.drawLine = function (buf, color, points) {
|
||||
var i;
|
||||
|
||||
this.setColor(color);
|
||||
|
||||
// Configure context to draw two-pixel-thick lines
|
||||
this.c2d.lineWidth = 2;
|
||||
|
||||
// Start a new path...
|
||||
if (buf.length > 1) {
|
||||
this.c2d.beginPath();
|
||||
this.c2d.moveTo(this.x(buf[0]), this.y(buf[1]));
|
||||
}
|
||||
|
||||
// ...and add points to it...
|
||||
for (i = 2; i < points * 2; i = i + 2) {
|
||||
this.c2d.lineTo(this.x(buf[i]), this.y(buf[i + 1]));
|
||||
}
|
||||
|
||||
// ...before finally drawing it.
|
||||
this.c2d.stroke();
|
||||
};
|
||||
|
||||
Canvas2DChart.prototype.drawSquare = function (min, max, color) {
|
||||
var x1 = this.x(min[0]),
|
||||
y1 = this.y(min[1]),
|
||||
w = this.x(max[0]) - x1,
|
||||
h = this.y(max[1]) - y1;
|
||||
|
||||
this.setColor(color);
|
||||
this.c2d.fillRect(x1, y1, w, h);
|
||||
};
|
||||
|
||||
return Canvas2DChart;
|
||||
}
|
||||
);
|
@ -1,160 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Module defining GLPlot. Created by vwoeltje on 11/12/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
// WebGL shader sources (for drawing plain colors)
|
||||
var FRAGMENT_SHADER = [
|
||||
"precision mediump float;",
|
||||
"uniform vec4 uColor;",
|
||||
"void main(void) {",
|
||||
"gl_FragColor = uColor;",
|
||||
"}"
|
||||
].join('\n'),
|
||||
VERTEX_SHADER = [
|
||||
"attribute vec2 aVertexPosition;",
|
||||
"uniform vec2 uDimensions;",
|
||||
"uniform vec2 uOrigin;",
|
||||
"void main(void) {",
|
||||
"gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);",
|
||||
"}"
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* Create a new chart which uses WebGL for rendering.
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @implements {platform/features/plot.Chart}
|
||||
* @param {CanvasElement} canvas the canvas object to render upon
|
||||
* @throws {Error} an error is thrown if WebGL is unavailable.
|
||||
*/
|
||||
function GLChart(canvas) {
|
||||
var gl = canvas.getContext("webgl", { preserveDrawingBuffer: true }) ||
|
||||
canvas.getContext("experimental-webgl", { preserveDrawingBuffer: true }),
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
program,
|
||||
aVertexPosition,
|
||||
uColor,
|
||||
uDimensions,
|
||||
uOrigin;
|
||||
|
||||
// Ensure a context was actually available before proceeding
|
||||
if (!gl) {
|
||||
throw new Error("WebGL unavailable.");
|
||||
}
|
||||
|
||||
// Initialize shaders
|
||||
vertexShader = gl.createShader(gl.VERTEX_SHADER);
|
||||
gl.shaderSource(vertexShader, VERTEX_SHADER);
|
||||
gl.compileShader(vertexShader);
|
||||
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
gl.shaderSource(fragmentShader, FRAGMENT_SHADER);
|
||||
gl.compileShader(fragmentShader);
|
||||
|
||||
// Assemble vertex/fragment shaders into programs
|
||||
program = gl.createProgram();
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
gl.useProgram(program);
|
||||
|
||||
// Get locations for attribs/uniforms from the
|
||||
// shader programs (to pass values into shaders at draw-time)
|
||||
aVertexPosition = gl.getAttribLocation(program, "aVertexPosition");
|
||||
uColor = gl.getUniformLocation(program, "uColor");
|
||||
uDimensions = gl.getUniformLocation(program, "uDimensions");
|
||||
uOrigin = gl.getUniformLocation(program, "uOrigin");
|
||||
gl.enableVertexAttribArray(aVertexPosition);
|
||||
|
||||
// Create a buffer to holds points which will be drawn
|
||||
this.buffer = gl.createBuffer();
|
||||
|
||||
// Use a line width of 2.0 for legibility
|
||||
gl.lineWidth(2.0);
|
||||
|
||||
// Enable blending, for smoothness
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
this.gl = gl;
|
||||
this.aVertexPosition = aVertexPosition;
|
||||
this.uColor = uColor;
|
||||
this.uDimensions = uDimensions;
|
||||
this.uOrigin = uOrigin;
|
||||
}
|
||||
|
||||
// Utility function to handle drawing of a buffer;
|
||||
// drawType will determine whether this is a box, line, etc.
|
||||
GLChart.prototype.doDraw = function (drawType, buf, color, points) {
|
||||
var gl = this.gl;
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, buf, gl.DYNAMIC_DRAW);
|
||||
gl.vertexAttribPointer(this.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.uniform4fv(this.uColor, color);
|
||||
gl.drawArrays(drawType, 0, points);
|
||||
};
|
||||
|
||||
GLChart.prototype.clear = function () {
|
||||
var gl = this.gl;
|
||||
|
||||
// Set the viewport size; note that we use the width/height
|
||||
// that our WebGL context reports, which may be lower
|
||||
// resolution than the canvas we requested.
|
||||
gl.viewport(
|
||||
0,
|
||||
0,
|
||||
gl.drawingBufferWidth,
|
||||
gl.drawingBufferHeight
|
||||
);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT);
|
||||
};
|
||||
|
||||
|
||||
GLChart.prototype.setDimensions = function (dimensions, origin) {
|
||||
var gl = this.gl;
|
||||
if (dimensions && dimensions.length > 0 &&
|
||||
origin && origin.length > 0) {
|
||||
gl.uniform2fv(this.uDimensions, dimensions);
|
||||
gl.uniform2fv(this.uOrigin, origin);
|
||||
}
|
||||
};
|
||||
|
||||
GLChart.prototype.drawLine = function (buf, color, points) {
|
||||
this.doDraw(this.gl.LINE_STRIP, buf, color, points);
|
||||
};
|
||||
|
||||
GLChart.prototype.drawSquare = function (min, max, color) {
|
||||
this.doDraw(this.gl.TRIANGLE_FAN, new Float32Array(
|
||||
min.concat([min[0], max[1]]).concat(max).concat([max[0], min[1]])
|
||||
), color, 4);
|
||||
};
|
||||
|
||||
return GLChart;
|
||||
}
|
||||
);
|
@ -1,250 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Module defining MCTChart. Created by vwoeltje on 11/12/14.
|
||||
*/
|
||||
define(
|
||||
["./GLChart", "./Canvas2DChart"],
|
||||
function (GLChart, Canvas2DChart) {
|
||||
|
||||
var TEMPLATE = "<canvas style='position: absolute; background: none; width: 100%; height: 100%;'></canvas>";
|
||||
|
||||
/**
|
||||
* The mct-chart directive provides a canvas element which can be
|
||||
* drawn upon, to support Plot view and similar visualizations.
|
||||
*
|
||||
* This directive takes one attribute, "draw", which is an Angular
|
||||
* expression which will be two-way bound to a drawing object. This
|
||||
* drawing object should contain:
|
||||
*
|
||||
* * `dimensions`: An object describing the logical bounds of the
|
||||
* drawable area, containing two fields:
|
||||
* * `origin`: The position, in logical coordinates, of the
|
||||
* lower-left corner of the chart area. A two-element array.
|
||||
* * `dimensions`: A two-element array containing the width
|
||||
* and height of the chart area, in logical coordinates.
|
||||
* * `lines`: An array of lines to be drawn, where each line is
|
||||
* expressed as an object containing:
|
||||
* * `buffer`: A Float32Array containing points in the line,
|
||||
* in logical coordinate, in sequential x/y pairs.
|
||||
* * `color`: The color of the line, as a four-element RGBA
|
||||
* array, where each element is in the range of 0.0-1.0
|
||||
* * `points`: The number of points in the line.
|
||||
* * `boxes`: An array of rectangles to draw in the chart area
|
||||
* (used for marquee zoom). Each is an object containing:
|
||||
* * `start`: The first corner of the rectangle (as a two-element
|
||||
* array, logical coordinates)
|
||||
* * `end`: The opposite corner of the rectangle (again, as a
|
||||
* two-element array)
|
||||
* * `color`: The color of the box, as a four-element RGBA
|
||||
* array, where each element is in the range of 0.0-1.0
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
*/
|
||||
function MCTChart($interval, $log) {
|
||||
// Get an underlying chart implementation
|
||||
function getChart(Charts, canvas) {
|
||||
// Try the first available option...
|
||||
var Chart = Charts[0];
|
||||
|
||||
// This function recursively try-catches all options;
|
||||
// if these all fail, issue a warning.
|
||||
if (!Chart) {
|
||||
$log.warn("Cannot initialize mct-chart.");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Try first option; if it fails, try remaining options
|
||||
try {
|
||||
return new Chart(canvas);
|
||||
} catch (e) {
|
||||
$log.warn([
|
||||
"Could not instantiate chart",
|
||||
Chart.name,
|
||||
";",
|
||||
e.message
|
||||
].join(" "));
|
||||
|
||||
return getChart(Charts.slice(1), canvas);
|
||||
}
|
||||
}
|
||||
|
||||
function linkChart(scope, element) {
|
||||
var canvas = element.find("canvas")[0],
|
||||
activeInterval,
|
||||
chart;
|
||||
|
||||
// Handle drawing, based on contents of the "draw" object
|
||||
// in scope
|
||||
function doDraw(draw) {
|
||||
// Ensure canvas context has same resolution
|
||||
// as canvas element
|
||||
canvas.width = canvas.offsetWidth;
|
||||
canvas.height = canvas.offsetHeight;
|
||||
|
||||
// Clear previous contents
|
||||
chart.clear();
|
||||
|
||||
// Nothing to draw if no draw object defined
|
||||
if (!draw) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set logical boundaries for the chart
|
||||
chart.setDimensions(
|
||||
draw.dimensions || [1, 1],
|
||||
draw.origin || [0, 0]
|
||||
);
|
||||
|
||||
// Draw line segments
|
||||
(draw.lines || []).forEach(function (line) {
|
||||
chart.drawLine(
|
||||
line.buffer,
|
||||
line.color,
|
||||
line.points
|
||||
);
|
||||
});
|
||||
|
||||
// Draw boxes (e.g. marquee zoom rect)
|
||||
(draw.boxes || []).forEach(function (box) {
|
||||
chart.drawSquare(
|
||||
box.start,
|
||||
box.end,
|
||||
box.color
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Issue a drawing call, if-and-only-if canvas size
|
||||
// has changed. This will be called on a timer, since
|
||||
// there is no event to depend on.
|
||||
function drawIfResized() {
|
||||
if (canvas.width !== canvas.offsetWidth ||
|
||||
canvas.height !== canvas.offsetHeight) {
|
||||
doDraw(scope.draw);
|
||||
scope.$apply();
|
||||
}
|
||||
}
|
||||
|
||||
// Stop watching for changes to size (scope destroyed)
|
||||
function releaseInterval() {
|
||||
if (activeInterval) {
|
||||
$interval.cancel(activeInterval);
|
||||
}
|
||||
}
|
||||
|
||||
// Switch from WebGL to plain 2D if context is lost
|
||||
function fallbackFromWebGL() {
|
||||
element.html(TEMPLATE);
|
||||
canvas = element.find("canvas")[0];
|
||||
chart = getChart([Canvas2DChart], canvas);
|
||||
if (chart) {
|
||||
doDraw(scope.draw);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to initialize a chart.
|
||||
chart = getChart([GLChart, Canvas2DChart], canvas);
|
||||
|
||||
// If that failed, there's nothing more we can do here.
|
||||
// (A warning will already have been issued)
|
||||
if (!chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
// WebGL is a bit of a special case; it may work, then fail
|
||||
// later for various reasons, so we need to listen for this
|
||||
// and fall back to plain canvas drawing when it occurs.
|
||||
canvas.addEventListener("webglcontextlost", fallbackFromWebGL);
|
||||
|
||||
// Check for resize, on a timer
|
||||
activeInterval = $interval(drawIfResized, 1000, 0, false);
|
||||
|
||||
// Watch "draw" for external changes to the set of
|
||||
// things to be drawn.
|
||||
scope.$watchCollection("draw", doDraw);
|
||||
|
||||
// Stop checking for resize when scope is destroyed
|
||||
scope.$on("$destroy", releaseInterval);
|
||||
}
|
||||
|
||||
return {
|
||||
// Apply directive only to elements
|
||||
restrict: "E",
|
||||
|
||||
// Template to use (a canvas element)
|
||||
template: TEMPLATE,
|
||||
|
||||
// Link function; set up scope
|
||||
link: linkChart,
|
||||
|
||||
// Initial, isolate scope for the directive
|
||||
scope: { draw: "=" }
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface platform/features/plot.Chart
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Clear the chart.
|
||||
* @method platform/features/plot.Chart#clear
|
||||
*/
|
||||
/**
|
||||
* Set the logical boundaries of the chart.
|
||||
* @param {number[]} dimensions the horizontal and
|
||||
* vertical dimensions of the chart
|
||||
* @param {number[]} origin the horizontal/vertical
|
||||
* origin of the chart
|
||||
* @memberof platform/features/plot.Chart#setDimensions
|
||||
*/
|
||||
/**
|
||||
* Draw the supplied buffer as a line strip (a sequence
|
||||
* of line segments), in the chosen color.
|
||||
* @param {Float32Array} buf the line strip to draw,
|
||||
* in alternating x/y positions
|
||||
* @param {number[]} color the color to use when drawing
|
||||
* the line, as an RGBA color where each element
|
||||
* is in the range of 0.0-1.0
|
||||
* @param {number} points the number of points to draw
|
||||
* @memberof platform/features/plot.Chart#drawLine
|
||||
*/
|
||||
/**
|
||||
* Draw a rectangle extending from one corner to another,
|
||||
* in the chosen color.
|
||||
* @param {number[]} min the first corner of the rectangle
|
||||
* @param {number[]} max the opposite corner
|
||||
* @param {number[]} color the color to use when drawing
|
||||
* the rectangle, as an RGBA color where each element
|
||||
* is in the range of 0.0-1.0
|
||||
* @memberof platform/features/plot.Chart#drawSquare
|
||||
*/
|
||||
|
||||
return MCTChart;
|
||||
}
|
||||
);
|
||||
|
@ -1,437 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* This bundle adds a "Plot" view for numeric telemetry data.
|
||||
* @namespace platform/features/plot
|
||||
*/
|
||||
define(
|
||||
[
|
||||
"./elements/PlotUpdater",
|
||||
"./elements/PlotPalette",
|
||||
"./elements/PlotAxis",
|
||||
"./elements/PlotLimitTracker",
|
||||
"./elements/PlotTelemetryFormatter",
|
||||
"./modes/PlotModeOptions",
|
||||
"./SubPlotFactory"
|
||||
],
|
||||
function (
|
||||
PlotUpdater,
|
||||
PlotPalette,
|
||||
PlotAxis,
|
||||
PlotLimitTracker,
|
||||
PlotTelemetryFormatter,
|
||||
PlotModeOptions,
|
||||
SubPlotFactory
|
||||
) {
|
||||
|
||||
var AXIS_DEFAULTS = [
|
||||
{ "name": "Time" },
|
||||
{ "name": "Value" }
|
||||
];
|
||||
|
||||
/**
|
||||
* The PlotController is responsible for any computation/logic
|
||||
* associated with displaying the plot view. Specifically, these
|
||||
* responsibilities include:
|
||||
*
|
||||
* * Describing axes and labeling.
|
||||
* * Handling user interactions.
|
||||
* * Deciding what needs to be drawn in the chart area.
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
*/
|
||||
function PlotController(
|
||||
$scope,
|
||||
$element,
|
||||
exportImageService,
|
||||
telemetryFormatter,
|
||||
telemetryHandler,
|
||||
throttle,
|
||||
PLOT_FIXED_DURATION,
|
||||
openmct
|
||||
) {
|
||||
var self = this,
|
||||
plotTelemetryFormatter =
|
||||
new PlotTelemetryFormatter(telemetryFormatter),
|
||||
subPlotFactory =
|
||||
new SubPlotFactory(plotTelemetryFormatter),
|
||||
cachedObjects = [],
|
||||
updater,
|
||||
lastBounds,
|
||||
lastRange,
|
||||
lastDomain,
|
||||
handle;
|
||||
var timeAPI = openmct.time;
|
||||
|
||||
// Populate the scope with axis information (specifically, options
|
||||
// available for each axis.)
|
||||
function setupAxes(metadatas) {
|
||||
$scope.axes.forEach(function (axis) {
|
||||
axis.updateMetadata(metadatas);
|
||||
});
|
||||
}
|
||||
|
||||
// Trigger an update of a specific subplot;
|
||||
// used in a loop to update all subplots.
|
||||
function updateSubplot(subplot) {
|
||||
subplot.update();
|
||||
}
|
||||
|
||||
// Set up available modes (stacked/overlaid), based on the
|
||||
// set of telemetry objects in this plot view.
|
||||
function setupModes(telemetryObjects) {
|
||||
if (cachedObjects !== telemetryObjects) {
|
||||
cachedObjects = telemetryObjects;
|
||||
self.modeOptions = new PlotModeOptions(
|
||||
telemetryObjects || [],
|
||||
subPlotFactory
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 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() {
|
||||
var domain = $scope.axes[0].active.key,
|
||||
range = $scope.axes[1].active.key,
|
||||
duration = PLOT_FIXED_DURATION;
|
||||
|
||||
updater = new PlotUpdater(handle, domain, range, duration);
|
||||
lastDomain = domain;
|
||||
lastRange = range;
|
||||
|
||||
self.limitTracker = new PlotLimitTracker(handle, range);
|
||||
|
||||
// Keep any externally-provided bounds
|
||||
if (lastBounds) {
|
||||
setBasePanZoom(lastBounds);
|
||||
}
|
||||
}
|
||||
|
||||
function getUpdater() {
|
||||
if (!updater) {
|
||||
recreateUpdater();
|
||||
}
|
||||
return updater;
|
||||
}
|
||||
|
||||
// Handle new telemetry data in this plot
|
||||
function updateValues() {
|
||||
self.pending = false;
|
||||
if (handle) {
|
||||
setupModes(handle.getTelemetryObjects());
|
||||
setupAxes(handle.getMetadata());
|
||||
getUpdater().update();
|
||||
self.modeOptions.getModeHandler().plotTelemetry(updater);
|
||||
self.limitTracker.update();
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
|
||||
// Display new historical data as it becomes available
|
||||
function addHistoricalData(domainObject, series) {
|
||||
self.pending = false;
|
||||
getUpdater().addHistorical(domainObject, series);
|
||||
self.modeOptions.getModeHandler().plotTelemetry(updater);
|
||||
self.update();
|
||||
}
|
||||
|
||||
// Issue a new request for historical telemetry
|
||||
function requestTelemetry() {
|
||||
if (handle) {
|
||||
handle.request({}, addHistoricalData);
|
||||
}
|
||||
}
|
||||
|
||||
// Requery for data entirely
|
||||
function replot() {
|
||||
if (handle) {
|
||||
updater = undefined;
|
||||
requestTelemetry();
|
||||
}
|
||||
}
|
||||
|
||||
function changeTimeOfInterest(timeOfInterest) {
|
||||
if (timeOfInterest !== undefined) {
|
||||
var bounds = timeAPI.bounds();
|
||||
var range = bounds.end - bounds.start;
|
||||
$scope.toiPerc = ((timeOfInterest - bounds.start) / range) * 100;
|
||||
$scope.toiPinned = true;
|
||||
} else {
|
||||
$scope.toiPerc = undefined;
|
||||
$scope.toiPinned = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new subscription; telemetrySubscriber gets
|
||||
// to do the meaningful work here.
|
||||
function subscribe(domainObject) {
|
||||
if (handle) {
|
||||
handle.unsubscribe();
|
||||
}
|
||||
handle = domainObject && telemetryHandler.handle(
|
||||
domainObject,
|
||||
updateValues,
|
||||
true // Lossless
|
||||
);
|
||||
replot();
|
||||
|
||||
changeTimeOfInterest(timeAPI.timeOfInterest());
|
||||
timeAPI.on("timeOfInterest", changeTimeOfInterest);
|
||||
}
|
||||
|
||||
// Release the current subscription (called when scope is destroyed)
|
||||
function releaseSubscription() {
|
||||
if (handle) {
|
||||
handle.unsubscribe();
|
||||
handle = undefined;
|
||||
}
|
||||
timeAPI.off("timeOfInterest", changeTimeOfInterest);
|
||||
}
|
||||
|
||||
function requery() {
|
||||
self.pending = true;
|
||||
releaseSubscription();
|
||||
subscribe($scope.domainObject);
|
||||
}
|
||||
|
||||
function updateDomainFormat() {
|
||||
var domainAxis = $scope.axes[0];
|
||||
plotTelemetryFormatter
|
||||
.setDomainFormat(domainAxis.active.format);
|
||||
}
|
||||
|
||||
function domainRequery(newDomain) {
|
||||
if (newDomain !== lastDomain) {
|
||||
updateDomainFormat();
|
||||
requery();
|
||||
}
|
||||
}
|
||||
|
||||
function rangeRequery(newRange) {
|
||||
if (newRange !== lastRange) {
|
||||
requery();
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to a display bounds change (requery for data)
|
||||
function changeDisplayBounds(event, bounds, follow) {
|
||||
//'hack' for follow mode
|
||||
if (follow === true) {
|
||||
setBasePanZoom(bounds);
|
||||
} else {
|
||||
var domainAxis = $scope.axes[0];
|
||||
|
||||
if (bounds.domain) {
|
||||
domainAxis.chooseOption(bounds.domain);
|
||||
}
|
||||
updateDomainFormat();
|
||||
setBasePanZoom(bounds);
|
||||
requery();
|
||||
}
|
||||
self.setUnsynchedStatus($scope.domainObject, follow && self.isZoomed());
|
||||
changeTimeOfInterest(timeAPI.timeOfInterest());
|
||||
}
|
||||
|
||||
this.modeOptions = new PlotModeOptions([], subPlotFactory);
|
||||
this.updateValues = updateValues;
|
||||
|
||||
// Create a throttled update function
|
||||
this.scheduleUpdate = throttle(function () {
|
||||
self.modeOptions.getModeHandler().getSubPlots()
|
||||
.forEach(updateSubplot);
|
||||
});
|
||||
|
||||
self.pending = true;
|
||||
self.$element = $element;
|
||||
self.exportImageService = exportImageService;
|
||||
|
||||
// Initialize axes; will get repopulated when telemetry
|
||||
// metadata becomes available.
|
||||
$scope.axes = [
|
||||
new PlotAxis("domains", [], AXIS_DEFAULTS[0]),
|
||||
new PlotAxis("ranges", [], AXIS_DEFAULTS[1])
|
||||
];
|
||||
|
||||
//Are some initialized bounds defined?
|
||||
var bounds = timeAPI.bounds();
|
||||
if (bounds &&
|
||||
bounds.start !== undefined &&
|
||||
bounds.end !== undefined) {
|
||||
changeDisplayBounds(undefined, timeAPI.bounds(), timeAPI.clock() !== undefined);
|
||||
}
|
||||
|
||||
// Watch for changes to the selected axis
|
||||
$scope.$watch("axes[0].active.key", domainRequery);
|
||||
$scope.$watch("axes[1].active.key", rangeRequery);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color (as a style-friendly string) to use
|
||||
* for plotting the trace at the specified index.
|
||||
* @param {number} index the index of the trace
|
||||
* @returns {string} the color, in #RRGGBB form
|
||||
*/
|
||||
PlotController.prototype.getColor = function (index) {
|
||||
return PlotPalette.getStringColor(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the plot is zoomed or panned out
|
||||
* of its default state (to determine whether back/unzoom
|
||||
* controls should be shown)
|
||||
* @returns {boolean} true if not in default state
|
||||
*/
|
||||
PlotController.prototype.isZoomed = function () {
|
||||
return this.modeOptions.getModeHandler().isZoomed();
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo the most recent pan/zoom change and restore
|
||||
* the prior state.
|
||||
*/
|
||||
PlotController.prototype.stepBackPanZoom = function () {
|
||||
return this.modeOptions.getModeHandler().stepBackPanZoom();
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo all pan/zoom changes and restore the initial state.
|
||||
*/
|
||||
PlotController.prototype.unzoom = function () {
|
||||
return this.modeOptions.getModeHandler().unzoom();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the mode options (Stacked/Overlaid) that are applicable
|
||||
* for this plot.
|
||||
*/
|
||||
PlotController.prototype.getModeOptions = function () {
|
||||
return this.modeOptions.getModeOptions();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current mode that is applicable to this plot. This
|
||||
* will include key, name, and cssClass fields.
|
||||
*/
|
||||
PlotController.prototype.getMode = function () {
|
||||
return this.modeOptions.getMode();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the mode which should be active in this plot.
|
||||
* @param mode one of the mode options returned from
|
||||
* getModeOptions()
|
||||
*/
|
||||
PlotController.prototype.setMode = function (mode) {
|
||||
this.modeOptions.setMode(mode);
|
||||
this.updateValues();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all individual plots contained within this Plot view.
|
||||
* (Multiple may be contained when in Stacked mode).
|
||||
* @returns {SubPlot[]} all subplots in this Plot view
|
||||
*/
|
||||
PlotController.prototype.getSubPlots = function () {
|
||||
return this.modeOptions.getModeHandler().getSubPlots();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the CSS class to apply to the legend for this domain
|
||||
* object; this will reflect limit state.
|
||||
* @returns {string} the CSS class
|
||||
*/
|
||||
PlotController.prototype.getLegendClass = function (telemetryObject) {
|
||||
return this.limitTracker &&
|
||||
this.limitTracker.getLegendClass(telemetryObject);
|
||||
};
|
||||
|
||||
/**
|
||||
* Explicitly update all plots.
|
||||
*/
|
||||
PlotController.prototype.update = function () {
|
||||
this.scheduleUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a request is pending (to show the wait spinner)
|
||||
*/
|
||||
PlotController.prototype.isRequestPending = function () {
|
||||
// Placeholder; this should reflect request state
|
||||
// when requesting historical telemetry
|
||||
return this.pending;
|
||||
};
|
||||
|
||||
PlotController.prototype.setUnsynchedStatus = function (domainObject, status) {
|
||||
if (domainObject.hasCapability('status')) {
|
||||
domainObject.getCapability('status').set('timeconductor-unsynced', status);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the plot to PNG
|
||||
*/
|
||||
PlotController.prototype.exportPNG = function () {
|
||||
var self = this;
|
||||
self.hideExportButtons = true;
|
||||
self.exportImageService.exportPNG(self.$element[0], "plot.png").finally(function () {
|
||||
self.hideExportButtons = false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the plot to JPG
|
||||
*/
|
||||
PlotController.prototype.exportJPG = function () {
|
||||
var self = this;
|
||||
self.hideExportButtons = true;
|
||||
self.exportImageService.exportJPG(self.$element[0], "plot.jpg").finally(function () {
|
||||
self.hideExportButtons = false;
|
||||
});
|
||||
};
|
||||
|
||||
return PlotController;
|
||||
}
|
||||
);
|
||||
|
@ -1,195 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['./PlotOptionsForm'],
|
||||
function (PlotOptionsForm) {
|
||||
|
||||
/**
|
||||
* Notes on implementation of plot options
|
||||
*
|
||||
* Multiple y-axes will have to be handled with multiple forms as
|
||||
* they will need to be stored on distinct model object
|
||||
*
|
||||
* Likewise plot series options per-child will need to be separate
|
||||
* forms.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The LayoutController is responsible for supporting the
|
||||
* Layout view. It arranges frames according to saved configuration
|
||||
* and provides methods for updating these based on mouse
|
||||
* movement.
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {Scope} $scope the controller's Angular scope
|
||||
*/
|
||||
function PlotOptionsController($scope) {
|
||||
|
||||
var self = this;
|
||||
this.$scope = $scope;
|
||||
this.domainObject = $scope.domainObject;
|
||||
this.configuration = this.domainObject.getModel().configuration || {};
|
||||
this.plotOptionsForm = new PlotOptionsForm();
|
||||
this.composition = [];
|
||||
this.watches = [];
|
||||
|
||||
/*
|
||||
Listen for changes to the domain object and update the object's
|
||||
children.
|
||||
*/
|
||||
this.mutationListener = this.domainObject.getCapability('mutation').listen(function (model) {
|
||||
if (self.hasCompositionChanged(self.composition, model.composition)) {
|
||||
self.updateChildren();
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Set form structures on scope
|
||||
*/
|
||||
$scope.plotSeriesForm = this.plotOptionsForm.plotSeriesForm;
|
||||
$scope.xAxisForm = this.plotOptionsForm.xAxisForm;
|
||||
$scope.yAxisForm = this.plotOptionsForm.yAxisForm;
|
||||
|
||||
$scope.$on("$destroy", function () {
|
||||
//Clean up any listeners on destruction of controller
|
||||
self.mutationListener();
|
||||
});
|
||||
|
||||
this.defaultConfiguration();
|
||||
this.updateChildren();
|
||||
|
||||
/*
|
||||
* Setup a number of watches for changes to form values. On
|
||||
* change, update the model configuration via mutation
|
||||
*/
|
||||
$scope.$watchCollection('configuration.plot.yAxis', function (newValue, oldValue) {
|
||||
self.updateConfiguration(newValue, oldValue);
|
||||
});
|
||||
$scope.$watchCollection('configuration.plot.xAxis', function (newValue, oldValue) {
|
||||
self.updateConfiguration(newValue, oldValue);
|
||||
});
|
||||
|
||||
this.watchSeries();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all watches for series data (ie. the configuration for
|
||||
* child objects)
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.clearSeriesWatches = function () {
|
||||
this.watches.forEach(function (watch) {
|
||||
watch();
|
||||
});
|
||||
this.watches = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Attach watches for each object in the plot's composition
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.watchSeries = function () {
|
||||
var self = this;
|
||||
|
||||
this.clearSeriesWatches();
|
||||
|
||||
(self.$scope.children || []).forEach(function (child, index) {
|
||||
self.watches.push(
|
||||
self.$scope.$watchCollection(
|
||||
'configuration.plot.series[' + index + ']',
|
||||
function (newValue, oldValue) {
|
||||
self.updateConfiguration(newValue, oldValue);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine whether the changes to the model that triggered a
|
||||
* mutation event were purely compositional.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.hasCompositionChanged = function (oldComposition, newComposition) {
|
||||
// Framed slightly strangely, but the boolean logic is
|
||||
// easier to follow for the unchanged case.
|
||||
var isUnchanged = oldComposition === newComposition ||
|
||||
(
|
||||
oldComposition.length === newComposition.length &&
|
||||
oldComposition.every(function (currentValue, index) {
|
||||
return newComposition[index] && currentValue === newComposition[index];
|
||||
})
|
||||
);
|
||||
return !isUnchanged;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default the plot options model
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.defaultConfiguration = function () {
|
||||
this.configuration.plot = this.configuration.plot || {};
|
||||
this.configuration.plot.xAxis = this.configuration.plot.xAxis || {};
|
||||
this.configuration.plot.yAxis = this.configuration.plot.yAxis || {}; // y-axes will be associative array keyed on axis key
|
||||
this.configuration.plot.series = this.configuration.plot.series || []; // series will be associative array keyed on sub-object id
|
||||
this.$scope.configuration = this.configuration;
|
||||
};
|
||||
|
||||
/**
|
||||
* When a child is added to, or removed from a plot, update the
|
||||
* plot options model
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.updateChildren = function () {
|
||||
var self = this;
|
||||
this.domainObject.useCapability('composition').then(function (children) {
|
||||
self.$scope.children = children;
|
||||
self.composition = self.domainObject.getModel().composition;
|
||||
children.forEach(function (child, index) {
|
||||
self.configuration.plot.series[index] =
|
||||
self.configuration.plot.series[index] || {'id': child.getId()};
|
||||
});
|
||||
self.watchSeries();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* On changes to the form, update the configuration on the domain
|
||||
* object
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.updateConfiguration = function () {
|
||||
var self = this;
|
||||
this.domainObject.useCapability('mutation', function (model) {
|
||||
model.configuration = model.configuration || {};
|
||||
model.configuration.plot = self.configuration.plot;
|
||||
});
|
||||
};
|
||||
|
||||
return PlotOptionsController;
|
||||
}
|
||||
);
|
||||
|
@ -1,150 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* A class for encapsulating structure and behaviour of the plot
|
||||
* options form
|
||||
* @memberOf platform/features/plot
|
||||
* @param topic
|
||||
* @constructor
|
||||
*/
|
||||
function PlotOptionsForm() {
|
||||
|
||||
/*
|
||||
Defined below are the form structures for the plot options.
|
||||
*/
|
||||
this.xAxisForm = {
|
||||
'name': 'x-axis',
|
||||
'sections': [{
|
||||
'name': 'x-axis',
|
||||
'rows': [
|
||||
{
|
||||
'name': 'Domain',
|
||||
'control': 'select',
|
||||
'key': 'key',
|
||||
'options': [
|
||||
{'name': 'SCET', 'value': 'scet'},
|
||||
{'name': 'SCLK', 'value': 'sclk'},
|
||||
{'name': 'LST', 'value': 'lst'}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]};
|
||||
|
||||
this.yAxisForm = {
|
||||
'name': 'y-axis',
|
||||
'sections': [{
|
||||
// Will need to be repeated for each y-axis, with a
|
||||
// distinct name for each. Ideally the name of the axis
|
||||
// itself.
|
||||
'name': 'y-axis',
|
||||
'rows': [
|
||||
{
|
||||
'name': 'Range',
|
||||
'control': 'select',
|
||||
'key': 'key',
|
||||
'options': [
|
||||
{'name': 'EU', 'value': 'eu'},
|
||||
{'name': 'DN', 'value': 'dn'},
|
||||
{'name': 'Status', 'value': 'status'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'name': 'Autoscale',
|
||||
'control': 'checkbox',
|
||||
'key': 'autoscale'
|
||||
},
|
||||
{
|
||||
'name': 'Min',
|
||||
'control': 'textfield',
|
||||
'key': 'min',
|
||||
'pattern': '[0-9]',
|
||||
'inputsize' : 'sm'
|
||||
},
|
||||
{
|
||||
'name': 'Max',
|
||||
'control': 'textfield',
|
||||
'key': 'max',
|
||||
'pattern': '[0-9]',
|
||||
'inputsize' : 'sm'
|
||||
}
|
||||
]
|
||||
}]
|
||||
};
|
||||
this.plotSeriesForm = {
|
||||
'name': 'Series Options',
|
||||
'sections': [
|
||||
{
|
||||
rows: [
|
||||
{
|
||||
'name': 'Color',
|
||||
'control': 'color',
|
||||
'key': 'color'
|
||||
}]
|
||||
},
|
||||
{
|
||||
'rows': [
|
||||
{
|
||||
'name': 'Markers',
|
||||
'control': 'checkbox',
|
||||
'key': 'markers',
|
||||
'layout': 'control-first'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'rows': [
|
||||
{
|
||||
'name': 'No Line',
|
||||
'control': 'radio',
|
||||
'key': 'lineType',
|
||||
'value': 'noLine',
|
||||
'layout': 'control-first'
|
||||
},
|
||||
{
|
||||
'name': 'Step Line',
|
||||
'control': 'radio',
|
||||
'key': 'lineType',
|
||||
'value': 'stepLine',
|
||||
'layout': 'control-first'
|
||||
},
|
||||
{
|
||||
'name': 'Linear Line',
|
||||
'control': 'radio',
|
||||
'key': 'lineType',
|
||||
'value': 'linearLine',
|
||||
'layout': 'control-first'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
return PlotOptionsForm;
|
||||
}
|
||||
);
|
||||
|
@ -1,415 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[
|
||||
'./elements/PlotPosition',
|
||||
'./elements/PlotTickGenerator'
|
||||
],
|
||||
function (PlotPosition, PlotTickGenerator) {
|
||||
|
||||
var DOMAIN_TICKS = 5,
|
||||
RANGE_TICKS = 7;
|
||||
|
||||
/**
|
||||
* A SubPlot is an individual plot within a Plot View (which
|
||||
* may contain multiple plots, specifically when in Stacked
|
||||
* plot mode.)
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {DomainObject[]} telemetryObjects the domain objects
|
||||
* which will be plotted in this sub-plot
|
||||
* @param {PlotPanZoomStack} panZoomStack the stack of pan-zoom
|
||||
* states which is applicable to this sub-plot
|
||||
* @param {TelemetryFormatter} telemetryFormatter the telemetry
|
||||
* formatting service; used to convert domain/range values
|
||||
* from telemetry data sets to a human-readable form.
|
||||
*/
|
||||
function SubPlot(telemetryObjects, panZoomStack, telemetryFormatter) {
|
||||
// We are used from a template often, so maintain
|
||||
// state in local variables to allow for fast look-up,
|
||||
// as is normal for controllers.
|
||||
this.telemetryObjects = telemetryObjects;
|
||||
this.domainTicks = [];
|
||||
this.rangeTicks = [];
|
||||
this.formatter = telemetryFormatter;
|
||||
this.draw = {};
|
||||
this.hovering = false;
|
||||
this.panZoomStack = panZoomStack;
|
||||
|
||||
// Start with the right initial drawing bounds,
|
||||
// tick marks
|
||||
this.updateDrawingBounds();
|
||||
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 !== "";
|
||||
}
|
||||
|
||||
// Converts from pixel coordinates to domain-range,
|
||||
// to interpret mouse gestures.
|
||||
SubPlot.prototype.mousePositionToDomainRange = function (mousePosition) {
|
||||
return new PlotPosition(
|
||||
mousePosition.x,
|
||||
mousePosition.y,
|
||||
mousePosition.width,
|
||||
mousePosition.height,
|
||||
this.panZoomStack
|
||||
).getPosition();
|
||||
};
|
||||
|
||||
// Utility function to get the mouse position (in x,y
|
||||
// pixel coordinates in the canvas area) from a mouse
|
||||
// event object.
|
||||
SubPlot.prototype.toMousePosition = function ($event) {
|
||||
var bounds = this.subPlotBounds;
|
||||
|
||||
return {
|
||||
x: $event.clientX - bounds.left,
|
||||
y: $event.clientY - bounds.top,
|
||||
width: bounds.width,
|
||||
height: bounds.height
|
||||
};
|
||||
};
|
||||
|
||||
// Convert a domain-range position to a displayable
|
||||
// position. This will subtract the domain offset, which
|
||||
// is used to bias domain values to minimize loss-of-precision
|
||||
// associated with conversion to a 32-bit floating point
|
||||
// format (which is needed in the chart area itself, by WebGL.)
|
||||
SubPlot.prototype.toDisplayable = function (position) {
|
||||
return [position[0] - this.domainOffset, position[1]];
|
||||
};
|
||||
|
||||
// Update the current hover coordinates
|
||||
SubPlot.prototype.updateHoverCoordinates = function () {
|
||||
var formatter = this.formatter;
|
||||
|
||||
// Utility, for map/forEach loops. Index 0 is domain,
|
||||
// index 1 is range.
|
||||
function formatValue(v, i) {
|
||||
return i ?
|
||||
formatter.formatRangeValue(v) :
|
||||
formatter.formatDomainValue(v);
|
||||
}
|
||||
|
||||
this.hoverCoordinates = this.mousePosition &&
|
||||
this.mousePositionToDomainRange(this.mousePosition)
|
||||
.map(formatValue)
|
||||
.filter(isNonEmpty)
|
||||
.join(", ");
|
||||
};
|
||||
|
||||
// Update the drawable marquee area to reflect current
|
||||
// mouse position (or don't show it at all, if no marquee
|
||||
// zoom is in progress)
|
||||
SubPlot.prototype.updateMarqueeBox = function () {
|
||||
// Express this as a box in the draw object, which
|
||||
// is passed to an mct-chart in the template for rendering.
|
||||
this.draw.boxes = this.marqueeStart ?
|
||||
[{
|
||||
start: this.toDisplayable(
|
||||
this.mousePositionToDomainRange(this.marqueeStart)
|
||||
),
|
||||
end: this.toDisplayable(
|
||||
this.mousePositionToDomainRange(this.mousePosition)
|
||||
),
|
||||
color: [1, 1, 1, 0.5]
|
||||
}] : undefined;
|
||||
};
|
||||
|
||||
// Update the bounds (origin and dimensions) of the drawing area.
|
||||
SubPlot.prototype.updateDrawingBounds = function () {
|
||||
var panZoom = this.panZoomStack.getPanZoom();
|
||||
|
||||
// Communicate pan-zoom state from stack to the draw object
|
||||
// which is passed to mct-chart in the template.
|
||||
this.draw.dimensions = panZoom.dimensions;
|
||||
this.draw.origin = [
|
||||
panZoom.origin[0] - this.domainOffset,
|
||||
panZoom.origin[1]
|
||||
];
|
||||
};
|
||||
|
||||
// Update tick marks in scope.
|
||||
SubPlot.prototype.updateTicks = function () {
|
||||
var tickGenerator =
|
||||
new PlotTickGenerator(this.panZoomStack, this.formatter);
|
||||
|
||||
this.domainTicks =
|
||||
tickGenerator.generateDomainTicks(DOMAIN_TICKS);
|
||||
this.rangeTicks =
|
||||
tickGenerator.generateRangeTicks(RANGE_TICKS);
|
||||
};
|
||||
|
||||
SubPlot.prototype.updatePan = function () {
|
||||
var start, current, delta, nextOrigin;
|
||||
|
||||
// Clear the previous panning pan-zoom state
|
||||
this.panZoomStack.popPanZoom();
|
||||
|
||||
// Calculate what the new resulting pan-zoom should be
|
||||
start = this.mousePositionToDomainRange(
|
||||
this.panStart,
|
||||
this.panZoomStack
|
||||
);
|
||||
current = this.mousePositionToDomainRange(
|
||||
this.mousePosition,
|
||||
this.panZoomStack
|
||||
);
|
||||
|
||||
delta = [current[0] - start[0], current[1] - start[1]];
|
||||
nextOrigin = [
|
||||
this.panStartBounds.origin[0] - delta[0],
|
||||
this.panStartBounds.origin[1] - delta[1]
|
||||
];
|
||||
|
||||
// ...and push a new one at the current mouse position
|
||||
this.panZoomStack
|
||||
.pushPanZoom(nextOrigin, this.panStartBounds.dimensions);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the set of domain objects which are being
|
||||
* represented in this sub-plot.
|
||||
* @returns {DomainObject[]} the domain objects which
|
||||
* will have data plotted in this sub-plot
|
||||
*/
|
||||
SubPlot.prototype.getTelemetryObjects = function () {
|
||||
return this.telemetryObjects;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get ticks mark information appropriate for using in the
|
||||
* template for this sub-plot's domain axis, as prepared
|
||||
* by the PlotTickGenerator.
|
||||
* @returns {Array} tick marks for the domain axis
|
||||
*/
|
||||
SubPlot.prototype.getDomainTicks = function () {
|
||||
return this.domainTicks;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get ticks mark information appropriate for using in the
|
||||
* template for this sub-plot's range axis, as prepared
|
||||
* by the PlotTickGenerator.
|
||||
* @returns {Array} tick marks for the range axis
|
||||
*/
|
||||
SubPlot.prototype.getRangeTicks = function () {
|
||||
return this.rangeTicks;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the drawing object associated with this sub-plot;
|
||||
* this object will be passed to the mct-chart in which
|
||||
* this sub-plot's lines will be plotted, as its "draw"
|
||||
* attribute, and should have the same internal format
|
||||
* expected by that directive.
|
||||
* @return {object} the drawing object
|
||||
*/
|
||||
SubPlot.prototype.getDrawingObject = function () {
|
||||
return this.draw;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the coordinates (as displayable text) for the
|
||||
* current mouse position.
|
||||
* @returns {string[]} the displayable domain and range
|
||||
* coordinates over which the mouse is hovered
|
||||
*/
|
||||
SubPlot.prototype.getHoverCoordinates = function () {
|
||||
return this.hoverCoordinates;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle mouse movement over the chart area.
|
||||
* @param $event the mouse event
|
||||
* @memberof platform/features/plot.SubPlot#
|
||||
*/
|
||||
SubPlot.prototype.hover = function ($event) {
|
||||
this.hovering = true;
|
||||
this.subPlotBounds = $event.target.getBoundingClientRect();
|
||||
this.mousePosition = this.toMousePosition($event);
|
||||
//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();
|
||||
}
|
||||
if (this.panStart) {
|
||||
this.updatePan();
|
||||
this.updateDrawingBounds();
|
||||
this.updateTicks();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Continue a previously-start pan or zoom gesture.
|
||||
* @param $event the mouse event
|
||||
* @memberof platform/features/plot.SubPlot#
|
||||
*/
|
||||
SubPlot.prototype.continueDrag = function ($event) {
|
||||
this.mousePosition = this.toMousePosition($event);
|
||||
if (this.marqueeStart) {
|
||||
this.updateMarqueeBox();
|
||||
}
|
||||
if (this.panStart) {
|
||||
this.updatePan();
|
||||
this.updateDrawingBounds();
|
||||
this.updateTicks();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initiate a marquee zoom action.
|
||||
* @param $event the mouse event
|
||||
*/
|
||||
SubPlot.prototype.startDrag = function ($event) {
|
||||
this.subPlotBounds = $event.target.getBoundingClientRect();
|
||||
this.mousePosition = this.toMousePosition($event);
|
||||
// Treat any modifier key as a pan
|
||||
if ($event.altKey || $event.shiftKey || $event.ctrlKey) {
|
||||
// Start panning
|
||||
this.panStart = this.mousePosition;
|
||||
this.panStartBounds = this.panZoomStack.getPanZoom();
|
||||
// We're starting a pan, so add this back as a
|
||||
// state on the stack; it will get replaced
|
||||
// during the pan.
|
||||
this.panZoomStack.pushPanZoom(
|
||||
this.panStartBounds.origin,
|
||||
this.panStartBounds.dimensions
|
||||
);
|
||||
$event.preventDefault();
|
||||
} else {
|
||||
// Start marquee zooming
|
||||
this.marqueeStart = this.mousePosition;
|
||||
this.updateMarqueeBox();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Complete a marquee zoom action.
|
||||
* @param $event the mouse event
|
||||
*/
|
||||
SubPlot.prototype.endDrag = function ($event) {
|
||||
var self = this;
|
||||
|
||||
// Perform a marquee zoom.
|
||||
function marqueeZoom(start, end) {
|
||||
// Determine what boundary is described by the marquee,
|
||||
// in domain-range values. Use the minima for origin, so that
|
||||
// it doesn't matter what direction the user marqueed in.
|
||||
var a = self.mousePositionToDomainRange(start),
|
||||
b = self.mousePositionToDomainRange(end),
|
||||
origin = [
|
||||
Math.min(a[0], b[0]),
|
||||
Math.min(a[1], b[1])
|
||||
],
|
||||
dimensions = [
|
||||
Math.max(a[0], b[0]) - origin[0],
|
||||
Math.max(a[1], b[1]) - origin[1]
|
||||
];
|
||||
|
||||
// Proceed with zoom if zoom dimensions are non zeros
|
||||
if (!(dimensions[0] === 0 && dimensions[1] === 0)) {
|
||||
// Push the new state onto the pan-zoom stack
|
||||
self.panZoomStack.pushPanZoom(origin, dimensions);
|
||||
|
||||
// Make sure tick marks reflect new bounds
|
||||
self.updateTicks();
|
||||
}
|
||||
}
|
||||
|
||||
this.mousePosition = this.toMousePosition($event);
|
||||
this.subPlotBounds = undefined;
|
||||
if (this.marqueeStart) {
|
||||
marqueeZoom(this.marqueeStart, this.mousePosition);
|
||||
this.marqueeStart = undefined;
|
||||
this.updateMarqueeBox();
|
||||
this.updateDrawingBounds();
|
||||
this.updateTicks();
|
||||
}
|
||||
if (this.panStart) {
|
||||
// End panning
|
||||
this.panStart = undefined;
|
||||
this.panStartBounds = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the drawing bounds, marquee box, and
|
||||
* tick marks for this subplot.
|
||||
*/
|
||||
SubPlot.prototype.update = function () {
|
||||
this.updateDrawingBounds();
|
||||
this.updateMarqueeBox();
|
||||
this.updateTicks();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the domain offset associated with this sub-plot.
|
||||
* A domain offset is subtracted from all domain
|
||||
* before lines are drawn to avoid artifacts associated
|
||||
* with the use of 32-bit floats when domain values
|
||||
* are often timestamps (due to insufficient precision.)
|
||||
* A SubPlot will be drawing boxes (for marquee zoom) in
|
||||
* the same offset coordinate space, so it needs to know
|
||||
* the value of this to position that marquee box
|
||||
* correctly.
|
||||
* @param {number} value the domain offset
|
||||
*/
|
||||
SubPlot.prototype.setDomainOffset = function (value) {
|
||||
this.domainOffset = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* When used with no argument, check whether or not the user
|
||||
* is currently hovering over this subplot. When used with
|
||||
* an argument, set that state.
|
||||
* @param {boolean} [state] the new hovering state
|
||||
* @returns {boolean} the hovering state
|
||||
*/
|
||||
SubPlot.prototype.isHovering = function (state) {
|
||||
if (state !== undefined) {
|
||||
this.hovering = state;
|
||||
}
|
||||
return this.hovering;
|
||||
};
|
||||
|
||||
return SubPlot;
|
||||
|
||||
}
|
||||
);
|
||||
|
@ -1,134 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* A PlotAxis provides a template-ready set of options
|
||||
* for the domain or range axis, sufficient to populate
|
||||
* selectors.
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {string} axisType the field in metadatas to
|
||||
* look at for axis options; usually one of
|
||||
* "domains" or "ranges"
|
||||
* @param {object[]} metadatas metadata objects, as
|
||||
* returned by the `getMetadata()` method of
|
||||
* a `telemetry` capability.
|
||||
* @param {object} defaultValue the value to use for the
|
||||
* active state in the event that no options are
|
||||
* found; should contain "name" and "key" at
|
||||
* minimum.
|
||||
*
|
||||
*/
|
||||
function PlotAxis(axisType, metadatas, defaultValue) {
|
||||
this.axisType = axisType;
|
||||
this.defaultValue = defaultValue;
|
||||
this.optionKeys = {};
|
||||
|
||||
/**
|
||||
* The currently chosen option for this axis. An
|
||||
* initial value is provided; this will be updated
|
||||
* directly form the plot template.
|
||||
* @memberof platform/features/plot.PlotAxis#
|
||||
*/
|
||||
this.active = defaultValue;
|
||||
|
||||
/**
|
||||
* The set of options applicable for this axis;
|
||||
* an array of objects, where each object contains a
|
||||
* "key" field and a "name" field (for machine- and
|
||||
* human-readable names respectively)
|
||||
* @memberof platform/features/plot.PlotAxis#
|
||||
*/
|
||||
this.options = [];
|
||||
|
||||
// Initialize options from metadata objects
|
||||
this.updateMetadata(metadatas);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update axis options to reflect current metadata.
|
||||
* @param {TelemetryMetadata[]} metadata objects describing
|
||||
* applicable telemetry
|
||||
*/
|
||||
PlotAxis.prototype.updateMetadata = function (metadatas) {
|
||||
var axisType = this.axisType,
|
||||
optionKeys = this.optionKeys,
|
||||
newOptions = {},
|
||||
toAdd = [];
|
||||
|
||||
function isValid(option) {
|
||||
return option && optionKeys[option.key];
|
||||
}
|
||||
|
||||
metadatas.forEach(function (m) {
|
||||
(m[axisType] || []).forEach(function (option) {
|
||||
var key = option.key;
|
||||
if (!optionKeys[key] && !newOptions[key]) {
|
||||
toAdd.push(option);
|
||||
}
|
||||
newOptions[key] = true;
|
||||
});
|
||||
});
|
||||
|
||||
optionKeys = this.optionKeys = newOptions;
|
||||
|
||||
// General approach here is to avoid changing object
|
||||
// instances unless something has really changed, since
|
||||
// Angular is watching; don't want to trigger extra digests.
|
||||
if (!this.options.every(isValid)) {
|
||||
this.options = this.options.filter(isValid);
|
||||
}
|
||||
|
||||
if (toAdd.length > 0) {
|
||||
this.options = this.options.concat(toAdd);
|
||||
}
|
||||
|
||||
if (!isValid(this.active)) {
|
||||
this.active = this.options[0] || this.defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the domain/range selection for this axis. If the
|
||||
* provided `key` is not recognized as an option, no change
|
||||
* will occur.
|
||||
* @param {string} key the identifier for the domain/range
|
||||
*/
|
||||
PlotAxis.prototype.chooseOption = function (key) {
|
||||
var self = this;
|
||||
this.options.forEach(function (option) {
|
||||
if (option.key === key) {
|
||||
self.active = option;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return PlotAxis;
|
||||
|
||||
}
|
||||
);
|
@ -1,78 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Tracks the limit state of telemetry objects being plotted.
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {platform/telemetry.TelemetryHandle} handle the handle
|
||||
* to telemetry access
|
||||
* @param {string} range the key to use when looking up range values
|
||||
*/
|
||||
function PlotLimitTracker(handle, range) {
|
||||
this.handle = handle;
|
||||
this.range = range;
|
||||
this.legendClasses = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update limit states to reflect the latest data.
|
||||
*/
|
||||
PlotLimitTracker.prototype.update = function () {
|
||||
var legendClasses = {},
|
||||
range = this.range,
|
||||
handle = this.handle;
|
||||
|
||||
function updateLimit(telemetryObject) {
|
||||
var limit = telemetryObject.getCapability('limit'),
|
||||
datum = handle.getDatum(telemetryObject);
|
||||
|
||||
if (limit && datum) {
|
||||
legendClasses[telemetryObject.getId()] =
|
||||
(limit.evaluate(datum, range) || {}).cssClass;
|
||||
}
|
||||
}
|
||||
|
||||
handle.getTelemetryObjects().forEach(updateLimit);
|
||||
|
||||
this.legendClasses = legendClasses;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the CSS class associated with any limit violations for this
|
||||
* telemetry object.
|
||||
* @param {DomainObject} domainObject the telemetry object to check
|
||||
* @returns {string} the CSS class name, if any
|
||||
*/
|
||||
PlotLimitTracker.prototype.getLegendClass = function (domainObject) {
|
||||
var id = domainObject && domainObject.getId();
|
||||
return id && this.legendClasses[id];
|
||||
};
|
||||
|
||||
return PlotLimitTracker;
|
||||
|
||||
}
|
||||
);
|
@ -1,118 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['./PlotSeriesWindow'],
|
||||
function (PlotSeriesWindow) {
|
||||
|
||||
|
||||
/**
|
||||
* Represents a single line or trace of a plot.
|
||||
* @param {{PlotLineBuffer}} buffer the plot buffer
|
||||
* @constructor
|
||||
*/
|
||||
function PlotLine(buffer) {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a point to this plot line.
|
||||
* @param {number} domainValue the domain value
|
||||
* @param {number} rangeValue the range value
|
||||
*/
|
||||
PlotLine.prototype.addPoint = function (domainValue, rangeValue) {
|
||||
var buffer = this.buffer,
|
||||
index;
|
||||
|
||||
// Make sure we got real/useful values here...
|
||||
if (domainValue !== undefined && rangeValue !== undefined) {
|
||||
index = buffer.findInsertionIndex(domainValue);
|
||||
|
||||
// Already in the buffer? Skip insertion
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert the point
|
||||
if (!buffer.insertPoint(domainValue, rangeValue, index)) {
|
||||
// If insertion failed, trim from the beginning...
|
||||
buffer.trim(1);
|
||||
// ...and try again.
|
||||
buffer.insertPoint(domainValue, rangeValue, index);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a series of telemetry data to this plot line.
|
||||
* @param {TelemetrySeries} series the data series
|
||||
* @param {string} [domain] the key indicating which domain
|
||||
* to use when looking up data from this series
|
||||
* @param {string} [range] the key indicating which range
|
||||
* to use when looking up data from this series
|
||||
*/
|
||||
PlotLine.prototype.addSeries = function (series, domain, range) {
|
||||
var buffer = this.buffer;
|
||||
|
||||
// Insert a time-windowed data series into the buffer
|
||||
function insertSeriesWindow(seriesWindow) {
|
||||
var count = seriesWindow.getPointCount();
|
||||
|
||||
function doInsert() {
|
||||
var firstTimestamp = seriesWindow.getDomainValue(0),
|
||||
lastTimestamp = seriesWindow.getDomainValue(count - 1),
|
||||
startIndex = buffer.findInsertionIndex(firstTimestamp),
|
||||
endIndex = buffer.findInsertionIndex(lastTimestamp);
|
||||
|
||||
// Does the whole series fit in between two adjacent indexes?
|
||||
if ((startIndex === endIndex) && startIndex > -1) {
|
||||
// Insert it in between
|
||||
buffer.insert(seriesWindow, startIndex);
|
||||
} else {
|
||||
// Split it up, and add the two halves
|
||||
seriesWindow.split().forEach(insertSeriesWindow);
|
||||
}
|
||||
}
|
||||
|
||||
// Only insert if there are points to insert
|
||||
if (count > 0) {
|
||||
doInsert();
|
||||
}
|
||||
}
|
||||
|
||||
// Should try to add via insertion if a
|
||||
// clear insertion point is available;
|
||||
// if not, should split and add each half.
|
||||
// Insertion operation also needs to factor out
|
||||
// redundant timestamps, for overlapping data
|
||||
insertSeriesWindow(new PlotSeriesWindow(
|
||||
series,
|
||||
domain,
|
||||
range,
|
||||
0,
|
||||
series.getPointCount()
|
||||
));
|
||||
};
|
||||
|
||||
return PlotLine;
|
||||
}
|
||||
);
|
@ -1,268 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Contains the buffer used to draw a plot.
|
||||
* @param {number} domainOffset number to subtract from domain values
|
||||
* @param {number} initialSize initial buffer size
|
||||
* @param {number} maxSize maximum buffer size
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
*/
|
||||
function PlotLineBuffer(domainOffset, initialSize, maxSize) {
|
||||
this.buffer = new Float32Array(initialSize * 2);
|
||||
this.rangeExtrema =
|
||||
[Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY];
|
||||
this.length = 0;
|
||||
this.domainOffset = domainOffset;
|
||||
this.initialSize = initialSize;
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
// Binary search for an insertion index
|
||||
PlotLineBuffer.prototype.binSearch = function (value, min, max) {
|
||||
var mid = Math.floor((min + max) / 2),
|
||||
found = this.buffer[mid * 2];
|
||||
|
||||
// On collisions, insert at same index
|
||||
if (found === value) {
|
||||
return mid;
|
||||
}
|
||||
|
||||
// Otherwise, if we're down to a single index,
|
||||
// we've found our insertion point
|
||||
if (min >= max) {
|
||||
// Compare the found timestamp with the search
|
||||
// value to decide if we'll insert after or before.
|
||||
return min + ((found < value) ? 1 : 0);
|
||||
}
|
||||
|
||||
// Finally, do the recursive step
|
||||
if (found < value) {
|
||||
return this.binSearch(value, mid + 1, max);
|
||||
} else {
|
||||
return this.binSearch(value, min, mid - 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Increase the size of the buffer
|
||||
PlotLineBuffer.prototype.doubleBufferSize = function () {
|
||||
var sz = Math.min(this.maxSize * 2, this.buffer.length * 2),
|
||||
canDouble = sz > this.buffer.length,
|
||||
doubled = canDouble && new Float32Array(sz);
|
||||
|
||||
if (canDouble) {
|
||||
doubled.set(this.buffer); // Copy contents of original
|
||||
this.buffer = doubled;
|
||||
}
|
||||
|
||||
return canDouble;
|
||||
};
|
||||
|
||||
// Decrease the size of the buffer
|
||||
PlotLineBuffer.prototype.halveBufferSize = function () {
|
||||
var sz = Math.max(this.initialSize * 2, this.buffer.length / 2),
|
||||
canHalve = sz < this.buffer.length;
|
||||
|
||||
if (canHalve) {
|
||||
this.buffer = new Float32Array(this.buffer.subarray(0, sz));
|
||||
}
|
||||
|
||||
return canHalve;
|
||||
};
|
||||
|
||||
// Set a value in the buffer
|
||||
PlotLineBuffer.prototype.setValue = function (index, domainValue, rangeValue) {
|
||||
this.buffer[index * 2] = domainValue - this.domainOffset;
|
||||
this.buffer[index * 2 + 1] = rangeValue;
|
||||
// Track min/max of range values (min/max for
|
||||
// domain values can be read directly from buffer)
|
||||
this.rangeExtrema[0] = Math.min(this.rangeExtrema[0], rangeValue);
|
||||
this.rangeExtrema[1] = Math.max(this.rangeExtrema[1], rangeValue);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the WebGL-displayable buffer of points to plot.
|
||||
* @returns {Float32Array} displayable buffer for this line
|
||||
*/
|
||||
PlotLineBuffer.prototype.getBuffer = function () {
|
||||
return this.buffer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of points stored in this buffer.
|
||||
* @returns {number} the number of points stored
|
||||
*/
|
||||
PlotLineBuffer.prototype.getLength = function () {
|
||||
return this.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the min/max range values that are currently in this
|
||||
* buffer. Unlike range extrema, these will change as the
|
||||
* buffer gets trimmed.
|
||||
* @returns {number[]} min, max domain values
|
||||
*/
|
||||
PlotLineBuffer.prototype.getDomainExtrema = function () {
|
||||
// Since these are ordered in the buffer, assume
|
||||
// these are the values at the first and last index
|
||||
return [
|
||||
this.buffer[0] + this.domainOffset,
|
||||
this.buffer[this.length * 2 - 2] + this.domainOffset
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the min/max range values that have been observed for this
|
||||
* buffer. Note that these values may have been trimmed out at
|
||||
* some point.
|
||||
* @returns {number[]} min, max range values
|
||||
*/
|
||||
PlotLineBuffer.prototype.getRangeExtrema = function () {
|
||||
return this.rangeExtrema;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove values from this buffer.
|
||||
* Normally, values are removed from the start
|
||||
* of the buffer; a truthy value in the second argument
|
||||
* will cause values to be removed from the end.
|
||||
* @param {number} count number of values to remove
|
||||
* @param {boolean} [fromEnd] true if the most recent
|
||||
* values should be removed
|
||||
*/
|
||||
PlotLineBuffer.prototype.trim = function (count, fromEnd) {
|
||||
// If we're removing values from the start...
|
||||
if (!fromEnd) {
|
||||
// ...do so by shifting buffer contents over
|
||||
this.buffer.set(this.buffer.subarray(2 * count));
|
||||
}
|
||||
// Reduce used buffer size accordingly
|
||||
this.length -= count;
|
||||
// Finally, if less than half of the buffer is being
|
||||
// used, free up some memory.
|
||||
if (this.length < this.buffer.length / 4) {
|
||||
this.halveBufferSize();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Insert data from the provided series at the specified
|
||||
* index. If this would exceed the buffer's maximum capacity,
|
||||
* this operation fails and the buffer is unchanged.
|
||||
* @param {TelemetrySeries} series the series to insert
|
||||
* @param {number} index the index at which to insert this
|
||||
* series
|
||||
* @returns {boolean} true if insertion succeeded; otherwise
|
||||
* false
|
||||
*/
|
||||
PlotLineBuffer.prototype.insert = function (series, index) {
|
||||
var sz = series.getPointCount(),
|
||||
i;
|
||||
|
||||
// Don't allow append after the end; that doesn't make sense
|
||||
index = Math.min(index, this.length);
|
||||
|
||||
// Resize if necessary
|
||||
while (sz > ((this.buffer.length / 2) - this.length)) {
|
||||
if (!this.doubleBufferSize()) {
|
||||
// Can't make room for this, insertion fails
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Shift data over if necessary
|
||||
if (index < this.length) {
|
||||
this.buffer.set(
|
||||
this.buffer.subarray(index * 2, this.length * 2),
|
||||
(index + sz) * 2
|
||||
);
|
||||
}
|
||||
|
||||
// Insert data into the set
|
||||
for (i = 0; i < sz; i += 1) {
|
||||
this.setValue(
|
||||
i + index,
|
||||
series.getDomainValue(i),
|
||||
series.getRangeValue(i)
|
||||
);
|
||||
}
|
||||
|
||||
// Increase the length
|
||||
this.length += sz;
|
||||
|
||||
// Indicate that insertion was successful
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Append a single data point.
|
||||
* @memberof platform/features/plot.PlotLineBuffer#
|
||||
*/
|
||||
PlotLineBuffer.prototype.insertPoint = function (domainValue, rangeValue) {
|
||||
// Ensure there is space for this point
|
||||
if (this.length >= (this.buffer.length / 2)) {
|
||||
if (!this.doubleBufferSize()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Put the data in the buffer
|
||||
this.setValue(this.length, domainValue, rangeValue);
|
||||
|
||||
// Update length
|
||||
this.length += 1;
|
||||
|
||||
// Indicate that this was successful
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find an index for inserting data with this
|
||||
* timestamp. The second argument indicates whether
|
||||
* we are searching for insert-before or insert-after
|
||||
* positions.
|
||||
* Timestamps are meant to be unique, so if a collision
|
||||
* occurs, this will return -1.
|
||||
* @param {number} timestamp timestamp to insert
|
||||
* @returns {number} the index for insertion (or -1)
|
||||
*/
|
||||
PlotLineBuffer.prototype.findInsertionIndex = function (timestamp) {
|
||||
var value = timestamp - this.domainOffset;
|
||||
|
||||
// Handle empty buffer case and check for an
|
||||
// append opportunity (which is most common case for
|
||||
// real-time data so is optimized-for) before falling
|
||||
// back to a binary search for the insertion point.
|
||||
return (this.length < 1) ? 0 :
|
||||
(value > this.buffer[this.length * 2 - 2]) ? this.length :
|
||||
this.binSearch(value, 0, this.length - 1);
|
||||
};
|
||||
|
||||
return PlotLineBuffer;
|
||||
}
|
||||
);
|
||||
|
@ -1,133 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Plot palette. Defines colors for various plot lines.
|
||||
*/
|
||||
define(
|
||||
function () {
|
||||
|
||||
// Prepare different forms of the palette, since we wish to
|
||||
// describe colors in several ways (as RGB 0-255, as
|
||||
// RGB 0.0-1.0, or as stylesheet-appropriate #-prefixed colors).
|
||||
var integerPalette = [
|
||||
[0x20, 0xB2, 0xAA],
|
||||
[0x9A, 0xCD, 0x32],
|
||||
[0xFF, 0x8C, 0x00],
|
||||
[0xD2, 0xB4, 0x8C],
|
||||
[0x40, 0xE0, 0xD0],
|
||||
[0x41, 0x69, 0xFF],
|
||||
[0xFF, 0xD7, 0x00],
|
||||
[0x6A, 0x5A, 0xCD],
|
||||
[0xEE, 0x82, 0xEE],
|
||||
[0xCC, 0x99, 0x66],
|
||||
[0x99, 0xCC, 0xCC],
|
||||
[0x66, 0xCC, 0x33],
|
||||
[0xFF, 0xCC, 0x00],
|
||||
[0xFF, 0x66, 0x33],
|
||||
[0xCC, 0x66, 0xFF],
|
||||
[0xFF, 0x00, 0x66],
|
||||
[0xFF, 0xFF, 0x00],
|
||||
[0x80, 0x00, 0x80],
|
||||
[0x00, 0x86, 0x8B],
|
||||
[0x00, 0x8A, 0x00],
|
||||
[0xFF, 0x00, 0x00],
|
||||
[0x00, 0x00, 0xFF],
|
||||
[0xF5, 0xDE, 0xB3],
|
||||
[0xBC, 0x8F, 0x8F],
|
||||
[0x46, 0x82, 0xB4],
|
||||
[0xFF, 0xAF, 0xAF],
|
||||
[0x43, 0xCD, 0x80],
|
||||
[0xCD, 0xC1, 0xC5],
|
||||
[0xA0, 0x52, 0x2D],
|
||||
[0x64, 0x95, 0xED]
|
||||
], stringPalette = integerPalette.map(function (arr) {
|
||||
// Convert to # notation for use in styles
|
||||
return '#' + arr.map(function (c) {
|
||||
return (c < 16 ? '0' : '') + c.toString(16);
|
||||
}).join('');
|
||||
}), floatPalette = integerPalette.map(function (arr) {
|
||||
return arr.map(function (c) {
|
||||
return c / 255.0;
|
||||
}).concat([1]); // RGBA
|
||||
});
|
||||
|
||||
/**
|
||||
* PlotPalette allows a consistent set of colors to be retrieved
|
||||
* by index, in various color formats. All PlotPalette methods are
|
||||
* static, so there is no need for a constructor call; using
|
||||
* this will simply return PlotPalette itself.
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
*/
|
||||
function PlotPalette() {
|
||||
return PlotPalette;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a color in the plot's palette, by index.
|
||||
* This will be returned as a three element array of RGB
|
||||
* values, as integers in the range of 0-255.
|
||||
* @param {number} i the index of the color to look up
|
||||
* @return {number[]} the color, as integer RGB values
|
||||
*/
|
||||
PlotPalette.getIntegerColor = function (i) {
|
||||
return integerPalette[Math.floor(i) % integerPalette.length];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Look up a color in the plot's palette, by index.
|
||||
* This will be returned as a three element array of RGB
|
||||
* values, in the range of 0.0-1.0.
|
||||
*
|
||||
* This format is present specifically to support use with
|
||||
* WebGL, which expects colors of that form.
|
||||
*
|
||||
* @param {number} i the index of the color to look up
|
||||
* @return {number[]} the color, as floating-point RGB values
|
||||
*/
|
||||
PlotPalette.getFloatColor = function (i) {
|
||||
return floatPalette[Math.floor(i) % floatPalette.length];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Look up a color in the plot's palette, by index.
|
||||
* This will be returned as a string using #-prefixed
|
||||
* six-digit RGB hex notation (e.g. #FF0000)
|
||||
* See http://www.w3.org/TR/css3-color/#rgb-color.
|
||||
*
|
||||
* This format is useful for representing colors in in-line
|
||||
* styles.
|
||||
*
|
||||
* @param {number} i the index of the color to look up
|
||||
* @return {string} the color, as a style-friendly string
|
||||
*/
|
||||
PlotPalette.getStringColor = function (i) {
|
||||
return stringPalette[Math.floor(i) % stringPalette.length];
|
||||
};
|
||||
|
||||
return PlotPalette;
|
||||
|
||||
}
|
||||
);
|
@ -1,141 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* The PlotPanZoomStack is responsible for maintaining the
|
||||
* pan-zoom state of a plot (expressed as a boundary starting
|
||||
* at an origin and extending to certain dimensions) in a
|
||||
* stack, to support the back and unzoom buttons in plot controls.
|
||||
*
|
||||
* Dimensions and origins are here described each by two-element
|
||||
* arrays, where the first element describes a value or quantity
|
||||
* along the domain axis, and the second element describes the same
|
||||
* along the range axis.
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {number[]} origin the plot's origin, initially
|
||||
* @param {number[]} dimensions the plot's dimensions, initially
|
||||
*/
|
||||
function PlotPanZoomStack(origin, dimensions) {
|
||||
// Use constructor parameters as the stack's initial state
|
||||
this.stack = [{ origin: origin, dimensions: dimensions }];
|
||||
}
|
||||
|
||||
// Various functions which follow are simply wrappers for
|
||||
// normal stack-like array methods, with the exception that
|
||||
// they prevent undesired modification and enforce that this
|
||||
// stack must remain non-empty.
|
||||
// See JSDoc for specific methods below for more detail.
|
||||
|
||||
/**
|
||||
* Get the current stack depth; that is, the number
|
||||
* of items on the stack. A depth of one means that no
|
||||
* panning or zooming relative to the base value has
|
||||
* been applied.
|
||||
* @returns {number} the depth of the stack
|
||||
*/
|
||||
PlotPanZoomStack.prototype.getDepth = function getDepth() {
|
||||
return this.stack.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Push a new pan-zoom state onto the stack; this will
|
||||
* become the active pan-zoom state.
|
||||
* @param {number[]} origin the new origin
|
||||
* @param {number[]} dimensions the new dimensions
|
||||
*/
|
||||
PlotPanZoomStack.prototype.pushPanZoom = function (origin, dimensions) {
|
||||
this.stack.push({ origin: origin, dimensions: dimensions });
|
||||
};
|
||||
|
||||
/**
|
||||
* Pop a pan-zoom state from the stack. Whatever pan-zoom
|
||||
* state was previously present will become current.
|
||||
* If called when there is only one pan-zoom state on the
|
||||
* stack, this acts as a no-op (that is, the lowest
|
||||
* pan-zoom state on the stack cannot be popped, to ensure
|
||||
* that some pan-zoom state is always available.)
|
||||
*/
|
||||
PlotPanZoomStack.prototype.popPanZoom = function popPanZoom() {
|
||||
if (this.stack.length > 1) {
|
||||
this.stack.pop();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the base pan-zoom state; that is, the state at the
|
||||
* bottom of the stack. This allows the "unzoomed" state of
|
||||
* a plot to be updated (e.g. as new data comes in) without
|
||||
* interfering with the user's chosen zoom level.
|
||||
* @param {number[]} origin the base origin
|
||||
* @param {number[]} dimensions the base dimensions
|
||||
* @memberof platform/features/plot.PlotPanZoomStack#
|
||||
*/
|
||||
PlotPanZoomStack.prototype.setBasePanZoom = function (origin, dimensions) {
|
||||
this.stack[0] = { origin: origin, dimensions: dimensions };
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the pan-zoom stack down to its bottom element;
|
||||
* in effect, pop all elements but the last, e.g. to remove
|
||||
* any temporary user modifications to pan-zoom state.
|
||||
*/
|
||||
PlotPanZoomStack.prototype.clearPanZoom = function clearPanZoom() {
|
||||
this.stack = [this.stack[0]];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current pan-zoom state (the state at the top
|
||||
* of the stack), expressed as an object with "origin" and
|
||||
* "dimensions" fields.
|
||||
* @returns {object} the current pan-zoom state
|
||||
*/
|
||||
PlotPanZoomStack.prototype.getPanZoom = function getPanZoom() {
|
||||
return this.stack[this.stack.length - 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current origin, as represented on the top of the
|
||||
* stack.
|
||||
* @returns {number[]} the current plot origin
|
||||
*/
|
||||
PlotPanZoomStack.prototype.getOrigin = function getOrigin() {
|
||||
return this.getPanZoom().origin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current dimensions, as represented on the top of
|
||||
* the stack.
|
||||
* @returns {number[]} the current plot dimensions
|
||||
*/
|
||||
PlotPanZoomStack.prototype.getDimensions = function getDimensions() {
|
||||
return this.getPanZoom().dimensions;
|
||||
};
|
||||
|
||||
return PlotPanZoomStack;
|
||||
}
|
||||
);
|
@ -1,167 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['./PlotPanZoomStack'],
|
||||
function (PlotPanZoomStack) {
|
||||
|
||||
/**
|
||||
* A plot pan zoom stack group provides a collection of individual
|
||||
* pan-zoom stacks that synchronize upon the domain axis, but
|
||||
* remain independent upon the range axis. This supports panning
|
||||
* and zooming in stacked-plot mode (and, importantly,
|
||||
* stepping back through those states.)
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {number} count the number of stacks to include in this
|
||||
* group
|
||||
*/
|
||||
function PlotPanZoomStackGroup(count) {
|
||||
var self = this;
|
||||
|
||||
// Push a pan-zoom state; the index argument identifies
|
||||
// which stack originated the request (all other stacks
|
||||
// will ignore the range part of the change.)
|
||||
function pushPanZoom(origin, dimensions, index) {
|
||||
self.stacks.forEach(function (stack, i) {
|
||||
if (i === index) {
|
||||
// Do a normal push for the specified stack
|
||||
stack.pushPanZoom(origin, dimensions);
|
||||
} else {
|
||||
// For other stacks, do a push, but repeat
|
||||
// their current range axis bounds.
|
||||
stack.pushPanZoom(
|
||||
[origin[0], stack.getOrigin()[1]],
|
||||
[dimensions[0], stack.getDimensions()[1]]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Decorate a pan-zoom stack; returns an object with
|
||||
// the same interface, but whose stack-mutation methods
|
||||
// effect all items in the group.
|
||||
function decorateStack(stack, index) {
|
||||
var result = Object.create(stack);
|
||||
|
||||
// Use the methods defined above
|
||||
result.pushPanZoom = function (origin, dimensions) {
|
||||
pushPanZoom(origin, dimensions, index);
|
||||
};
|
||||
result.setBasePanZoom = function () {
|
||||
self.setBasePanZoom.apply(self, arguments);
|
||||
};
|
||||
result.popPanZoom = function () {
|
||||
self.popPanZoom.apply(self, arguments);
|
||||
};
|
||||
result.clearPanZoom = function () {
|
||||
self.clearPanZoom.apply(self, arguments);
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create the stacks in this group ...
|
||||
this.stacks = [];
|
||||
while (this.stacks.length < count) {
|
||||
this.stacks.push(new PlotPanZoomStack([], []));
|
||||
}
|
||||
// ... and their decorated-to-synchronize versions.
|
||||
this.decoratedStacks = this.stacks.map(decorateStack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop a pan-zoom state from all stacks in the group.
|
||||
* If called when there is only one pan-zoom state on each
|
||||
* stack, this acts as a no-op (that is, the lowest
|
||||
* pan-zoom state on the stack cannot be popped, to ensure
|
||||
* that some pan-zoom state is always available.)
|
||||
*/
|
||||
PlotPanZoomStackGroup.prototype.popPanZoom = function () {
|
||||
this.stacks.forEach(function (stack) {
|
||||
stack.popPanZoom();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the base pan-zoom state for all stacks in this group.
|
||||
* This changes the state at the bottom of each stack.
|
||||
* This allows the "unzoomed" state of plots to be updated
|
||||
* (e.g. as new data comes in) without
|
||||
* interfering with the user's chosen pan/zoom states.
|
||||
* @param {number[]} origin the base origin
|
||||
* @param {number[]} dimensions the base dimensions
|
||||
*/
|
||||
PlotPanZoomStackGroup.prototype.setBasePanZoom = function (origin, dimensions) {
|
||||
this.stacks.forEach(function (stack) {
|
||||
stack.setBasePanZoom(origin, dimensions);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear all pan-zoom stacks in this group down to
|
||||
* their bottom element; in effect, pop all elements
|
||||
* but the last, e.g. to remove any temporary user
|
||||
* modifications to pan-zoom state.
|
||||
*/
|
||||
PlotPanZoomStackGroup.prototype.clearPanZoom = function () {
|
||||
this.stacks.forEach(function (stack) {
|
||||
stack.clearPanZoom();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current stack depth; that is, the number
|
||||
* of items on each stack in the group.
|
||||
* A depth of one means that no
|
||||
* panning or zooming relative to the base value has
|
||||
* been applied.
|
||||
* @returns {number} the depth of the stacks in this group
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a specific pan-zoom stack in this group.
|
||||
* Stacks are specified by index; this index must be less
|
||||
* than the count provided at construction time, and must
|
||||
* not be less than zero.
|
||||
* The stack returned by this function will be synchronized
|
||||
* to other stacks in this group; that is, mutating that
|
||||
* stack directly will result in other stacks in this group
|
||||
* undergoing similar updates to ensure that domain bounds
|
||||
* remain the same.
|
||||
* @param {number} index the index of the stack to get
|
||||
* @returns {PlotPanZoomStack} the pan-zoom stack in the
|
||||
* group identified by that index
|
||||
*/
|
||||
PlotPanZoomStackGroup.prototype.getPanZoomStack = function (index) {
|
||||
return this.decoratedStacks[index];
|
||||
};
|
||||
|
||||
return PlotPanZoomStackGroup;
|
||||
}
|
||||
);
|
@ -1,95 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* A PlotPosition converts from pixel coordinates to domain-range
|
||||
* coordinates, based on the current plot boundary as described on
|
||||
* the pan-zoom stack.
|
||||
*
|
||||
* These coordinates are not updated after construction; that is,
|
||||
* they represent the result of the conversion at the time the
|
||||
* PlotPosition was instantiated. Care should be taken when retaining
|
||||
* PlotPosition objects across changes to the pan-zoom stack.
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {number} x the horizontal pixel position in the plot area
|
||||
* @param {number} y the vertical pixel position in the plot area
|
||||
* @param {number} width the width of the plot area
|
||||
* @param {number} height the height of the plot area
|
||||
* @param {PanZoomStack} panZoomStack the applicable pan-zoom stack,
|
||||
* used to determine the plot's domain-range boundaries.
|
||||
*/
|
||||
function PlotPosition(x, y, width, height, panZoomStack) {
|
||||
var panZoom = panZoomStack.getPanZoom(),
|
||||
origin = panZoom.origin,
|
||||
dimensions = panZoom.dimensions;
|
||||
|
||||
function convert(v, i) {
|
||||
return v * dimensions[i] + origin[i];
|
||||
}
|
||||
|
||||
if (!dimensions || !origin) {
|
||||
// We need both dimensions and origin to compute a position
|
||||
this.position = [];
|
||||
} else {
|
||||
// Convert from pixel to domain-range space.
|
||||
// Note that range is reversed from the y-axis in pixel space
|
||||
//(positive range points up, positive pixel-y points down)
|
||||
this.position =
|
||||
[x / width, (height - y) / height].map(convert);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the domain value corresponding to this pixel position.
|
||||
* @returns {number} the domain value
|
||||
*/
|
||||
PlotPosition.prototype.getDomain = function () {
|
||||
return this.position[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the range value corresponding to this pixel position.
|
||||
* @returns {number} the range value
|
||||
*/
|
||||
PlotPosition.prototype.getRange = function () {
|
||||
return this.position[1];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the domain and values corresponding to this
|
||||
* pixel position.
|
||||
* @returns {number[]} an array containing the domain and
|
||||
* the range value, in that order
|
||||
*/
|
||||
PlotPosition.prototype.getPosition = function () {
|
||||
return this.position;
|
||||
};
|
||||
|
||||
return PlotPosition;
|
||||
}
|
||||
);
|
@ -1,153 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Prepares data to be rendered in a GL Plot. Handles
|
||||
* the conversion from data API to displayable buffers.
|
||||
*/
|
||||
define(
|
||||
function () {
|
||||
|
||||
function identity(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* The PlotPreparer is responsible for handling data sets and
|
||||
* preparing them to be rendered. It creates a WebGL-plottable
|
||||
* Float32Array for each trace, and tracks the boundaries of the
|
||||
* data sets (since this is convenient to do during the same pass).
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {Telemetry[]} datas telemetry data objects
|
||||
* @param {string} domain the key to use when looking up domain values
|
||||
* @param {string} range the key to use when looking up range values
|
||||
*/
|
||||
function PlotPreparer(datas, domain, range) {
|
||||
var index,
|
||||
vertices = [],
|
||||
max = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
|
||||
min = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
|
||||
x,
|
||||
y,
|
||||
domainOffset = Number.POSITIVE_INFINITY;
|
||||
|
||||
// Remove any undefined data sets
|
||||
datas = (datas || []).filter(identity);
|
||||
|
||||
// Do a first pass to determine the domain offset.
|
||||
// This will be use to reduce the magnitude of domain values
|
||||
// in the buffer, to minimize loss-of-precision when
|
||||
// converting to a 32-bit float.
|
||||
datas.forEach(function (data) {
|
||||
domainOffset = Math.min(data.getDomainValue(0, domain), domainOffset);
|
||||
});
|
||||
|
||||
// Assemble buffers, and track bounds of the data present
|
||||
datas.forEach(function (data, i) {
|
||||
vertices.push([]);
|
||||
for (index = 0; index < data.getPointCount(); index += 1) {
|
||||
x = data.getDomainValue(index, domain);
|
||||
y = data.getRangeValue(index, range);
|
||||
vertices[i].push(x - domainOffset);
|
||||
vertices[i].push(y);
|
||||
min[0] = Math.min(min[0], x);
|
||||
min[1] = Math.min(min[1], y);
|
||||
max[0] = Math.max(max[0], x);
|
||||
max[1] = Math.max(max[1], y);
|
||||
}
|
||||
});
|
||||
|
||||
// If range is empty, add some padding
|
||||
if (max[1] === min[1]) {
|
||||
max[1] = max[1] + 1.0;
|
||||
min[1] = min[1] - 1.0;
|
||||
}
|
||||
|
||||
// Convert to Float32Array
|
||||
this.buffers = vertices.map(function (v) {
|
||||
return new Float32Array(v);
|
||||
});
|
||||
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.domainOffset = domainOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dimensions which bound all data in the provided
|
||||
* data sets. This is given as a two-element array where the
|
||||
* first element is domain, and second is range.
|
||||
* @returns {number[]} the dimensions which bound this data set
|
||||
*/
|
||||
PlotPreparer.prototype.getDimensions = function () {
|
||||
var max = this.max, min = this.min;
|
||||
return [max[0] - min[0], max[1] - min[1]];
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the origin of this data set's boundary.
|
||||
* This is given as a two-element array where the
|
||||
* first element is domain, and second is range.
|
||||
* The domain value here is not adjusted by the domain offset.
|
||||
* @returns {number[]} the origin of this data set's boundary
|
||||
*/
|
||||
PlotPreparer.prototype.getOrigin = function () {
|
||||
return this.min;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the domain offset; this offset will have been subtracted
|
||||
* from all domain values in all buffers returned by this
|
||||
* preparer, in order to minimize loss-of-precision due to
|
||||
* conversion to the 32-bit float format needed by WebGL.
|
||||
* @returns {number} the domain offset
|
||||
*/
|
||||
PlotPreparer.prototype.getDomainOffset = function () {
|
||||
return this.domainOffset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all renderable buffers for this data set. This will
|
||||
* be returned as an array which can be correlated back to
|
||||
* the provided telemetry data objects (from the constructor
|
||||
* call) by index.
|
||||
*
|
||||
* Internally, these are flattened; each buffer contains a
|
||||
* sequence of alternating domain and range values.
|
||||
*
|
||||
* All domain values in all buffers will have been adjusted
|
||||
* from their original values by subtraction of the domain
|
||||
* offset; this minimizes loss-of-precision resulting from
|
||||
* the conversion to 32-bit floats, which may otherwise
|
||||
* cause aliasing artifacts (particularly for timestamps)
|
||||
*
|
||||
* @returns {Float32Array[]} the buffers for these traces
|
||||
*/
|
||||
PlotPreparer.prototype.getBuffers = function () {
|
||||
return this.buffers;
|
||||
};
|
||||
|
||||
return PlotPreparer;
|
||||
|
||||
}
|
||||
);
|
@ -1,80 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Provides a window on a telemetry data series, to support
|
||||
* insertion into a plot line.
|
||||
* @constructor
|
||||
* @memberof platform/features/plot
|
||||
* @implements {TelemetrySeries}
|
||||
*/
|
||||
function PlotSeriesWindow(series, domain, range, start, end) {
|
||||
this.series = series;
|
||||
this.domain = domain;
|
||||
this.range = range;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
PlotSeriesWindow.prototype.getPointCount = function () {
|
||||
return this.end - this.start;
|
||||
};
|
||||
|
||||
PlotSeriesWindow.prototype.getDomainValue = function (index) {
|
||||
return this.series.getDomainValue(index + this.start, this.domain);
|
||||
};
|
||||
|
||||
PlotSeriesWindow.prototype.getRangeValue = function (index) {
|
||||
return this.series.getRangeValue(index + this.start, this.range);
|
||||
};
|
||||
|
||||
/**
|
||||
* Split this series into two series of equal (or nearly-equal) size.
|
||||
* @returns {PlotSeriesWindow[]} two series
|
||||
*/
|
||||
PlotSeriesWindow.prototype.split = function () {
|
||||
var mid = Math.floor((this.end + this.start) / 2);
|
||||
return ((this.end - this.start) > 1) ?
|
||||
[
|
||||
new PlotSeriesWindow(
|
||||
this.series,
|
||||
this.domain,
|
||||
this.range,
|
||||
this.start,
|
||||
mid
|
||||
),
|
||||
new PlotSeriesWindow(
|
||||
this.series,
|
||||
this.domain,
|
||||
this.range,
|
||||
mid,
|
||||
this.end
|
||||
)
|
||||
] : [];
|
||||
};
|
||||
|
||||
return PlotSeriesWindow;
|
||||
}
|
||||
);
|
@ -1,76 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
var DIGITS = 3;
|
||||
|
||||
/**
|
||||
* Wraps a `TelemetryFormatter` to provide formats for domain and
|
||||
* range values; provides a single place to track domain/range
|
||||
* formats within a plot, allowing other plot elements to simply
|
||||
* request that values be formatted.
|
||||
* @constructor
|
||||
* @memberof platform/features/plot
|
||||
* @implements {platform/telemetry.TelemetryFormatter}
|
||||
* @param {TelemetryFormatter} telemetryFormatter the formatter
|
||||
* to wrap.
|
||||
*/
|
||||
function PlotTelemetryFormatter(telemetryFormatter) {
|
||||
this.telemetryFormatter = telemetryFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the format to use for domain values.
|
||||
* @param {string} key the format's identifier
|
||||
*/
|
||||
PlotTelemetryFormatter.prototype.setDomainFormat = function (key) {
|
||||
this.domainFormat = key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specify the format to use for range values.
|
||||
* @param {string} key the format's identifier
|
||||
*/
|
||||
PlotTelemetryFormatter.prototype.setRangeFormat = function (key) {
|
||||
this.rangeFormat = key;
|
||||
};
|
||||
|
||||
PlotTelemetryFormatter.prototype.formatDomainValue = function (value) {
|
||||
return this.telemetryFormatter
|
||||
.formatDomainValue(value, this.domainFormat);
|
||||
};
|
||||
|
||||
PlotTelemetryFormatter.prototype.formatRangeValue = function (value) {
|
||||
if (typeof value === 'number') {
|
||||
return value.toFixed(DIGITS);
|
||||
}
|
||||
|
||||
return this.telemetryFormatter
|
||||
.formatRangeValue(value, this.rangeFormat);
|
||||
};
|
||||
|
||||
return PlotTelemetryFormatter;
|
||||
}
|
||||
);
|
@ -1,102 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* The PlotTickGenerator provides labels for ticks along the
|
||||
* domain and range axes of the plot, to support the plot
|
||||
* template.
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {PlotPanZoomStack} panZoomStack the pan-zoom stack for
|
||||
* this plot, used to determine plot boundaries
|
||||
* @param {TelemetryFormatter} formatter used to format (for display)
|
||||
* domain and range values.
|
||||
*/
|
||||
function PlotTickGenerator(panZoomStack, formatter) {
|
||||
this.panZoomStack = panZoomStack;
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
// For phantomjs compatibility, for headless testing
|
||||
// (Function.prototype.bind unsupported)
|
||||
function bind(fn, thisObj) {
|
||||
return fn.bind ? fn.bind(thisObj) : function () {
|
||||
return fn.apply(thisObj, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
// Generate ticks; interpolate from start up to
|
||||
// start + span in count steps, using the provided
|
||||
// formatter to represent each value.
|
||||
PlotTickGenerator.prototype.generateTicks = function (start, span, count, format) {
|
||||
var step = span / (count - 1),
|
||||
result = [],
|
||||
i;
|
||||
|
||||
for (i = 0; i < count; i += 1) {
|
||||
result.push({
|
||||
//If data to show, display label for each tick line, otherwise show lines but suppress labels.
|
||||
label: span > 0 ? format(i * step + start) : ''
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate tick marks for the domain axis.
|
||||
* @param {number} count the number of ticks
|
||||
* @returns {string[]} labels for those ticks
|
||||
*/
|
||||
PlotTickGenerator.prototype.generateDomainTicks = function (count) {
|
||||
var panZoom = this.panZoomStack.getPanZoom();
|
||||
return this.generateTicks(
|
||||
panZoom.origin[0],
|
||||
panZoom.dimensions[0],
|
||||
count,
|
||||
bind(this.formatter.formatDomainValue, this.formatter)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate tick marks for the range axis.
|
||||
* @param {number} count the number of ticks
|
||||
* @returns {string[]} labels for those ticks
|
||||
*/
|
||||
PlotTickGenerator.prototype.generateRangeTicks = function (count) {
|
||||
var panZoom = this.panZoomStack.getPanZoom();
|
||||
return this.generateTicks(
|
||||
panZoom.origin[1],
|
||||
panZoom.dimensions[1],
|
||||
count,
|
||||
bind(this.formatter.formatRangeValue, this.formatter)
|
||||
);
|
||||
};
|
||||
|
||||
return PlotTickGenerator;
|
||||
}
|
||||
);
|
@ -1,353 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['./PlotLine', './PlotLineBuffer'],
|
||||
function (PlotLine, PlotLineBuffer) {
|
||||
|
||||
var MAX_POINTS = 86400,
|
||||
PADDING_RATIO = 0.10, // Padding percentage for top & bottom
|
||||
INITIAL_SIZE = 675; // 1/128 of MAX_POINTS
|
||||
|
||||
/**
|
||||
* The PlotPreparer is responsible for handling data sets and
|
||||
* preparing them to be rendered. It creates a WebGL-plottable
|
||||
* Float32Array for each trace, and tracks the boundaries of the
|
||||
* data sets (since this is convenient to do during the same pass).
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {TelemetryHandle} handle the handle to telemetry access
|
||||
* @param {string} domain the key to use when looking up domain values
|
||||
* @param {string} range the key to use when looking up range values
|
||||
* @param {number} fixedDuration maximum plot duration to display
|
||||
* @param {number} maxPoints maximum number of points to display
|
||||
*/
|
||||
function PlotUpdater(handle, domain, range, fixedDuration, maxPoints) {
|
||||
this.handle = handle;
|
||||
this.domain = domain;
|
||||
this.range = range;
|
||||
this.fixedDuration = fixedDuration;
|
||||
this.maxPoints = maxPoints;
|
||||
|
||||
this.ids = [];
|
||||
this.lines = {};
|
||||
this.buffers = {};
|
||||
this.bufferArray = [];
|
||||
|
||||
// Use a default MAX_POINTS if none is provided
|
||||
this.maxPoints = maxPoints !== undefined ? maxPoints : MAX_POINTS;
|
||||
this.dimensions = [0, 0];
|
||||
this.origin = [0, 0];
|
||||
|
||||
// Initially prepare state for these objects.
|
||||
// Note that this may be an empty array at this time,
|
||||
// so we also need to check during update cycles.
|
||||
this.update();
|
||||
}
|
||||
|
||||
// Look up a domain object's id (for mapping, below)
|
||||
function getId(domainObject) {
|
||||
return domainObject.getId();
|
||||
}
|
||||
|
||||
// Used in the reduce step of updateExtrema
|
||||
function reduceExtrema(a, b) {
|
||||
return [Math.min(a[0], b[0]), Math.max(a[1], b[1])];
|
||||
}
|
||||
|
||||
// Convert a domain/range extrema to plot dimensions
|
||||
function dimensionsOf(extrema) {
|
||||
return extrema[1] - extrema[0];
|
||||
}
|
||||
|
||||
// Convert a domain/range extrema to a plot origin
|
||||
function originOf(extrema) {
|
||||
return extrema[0];
|
||||
}
|
||||
|
||||
// Check if this set of ids matches the current set of ids
|
||||
// (used to detect if line preparation can be skipped)
|
||||
PlotUpdater.prototype.idsMatch = function (nextIds) {
|
||||
var ids = this.ids;
|
||||
return ids.length === nextIds.length &&
|
||||
nextIds.every(function (id, index) {
|
||||
return ids[index] === id;
|
||||
});
|
||||
};
|
||||
|
||||
// Prepare plot lines for this group of telemetry objects
|
||||
PlotUpdater.prototype.prepareLines = function (telemetryObjects) {
|
||||
var nextIds = telemetryObjects.map(getId),
|
||||
next = {},
|
||||
self = this;
|
||||
|
||||
// Detect if we already have everything we need prepared
|
||||
if (this.idsMatch(nextIds)) {
|
||||
// Nothing to prepare, move on
|
||||
return;
|
||||
}
|
||||
|
||||
// Built up a set of ids. Note that we can only
|
||||
// create plot lines after our domain offset has
|
||||
// been determined.
|
||||
if (this.domainOffset !== undefined) {
|
||||
// Update list of ids in use
|
||||
this.ids = nextIds;
|
||||
|
||||
// Create buffers for these objects
|
||||
this.bufferArray = this.ids.map(function (id) {
|
||||
self.buffers[id] = self.buffers[id] || new PlotLineBuffer(
|
||||
self.domainOffset,
|
||||
INITIAL_SIZE,
|
||||
self.maxPoints
|
||||
);
|
||||
next[id] =
|
||||
self.lines[id] || new PlotLine(self.buffers[id]);
|
||||
return self.buffers[id];
|
||||
});
|
||||
}
|
||||
|
||||
// If there are no more lines, clear the domain offset
|
||||
if (Object.keys(next).length < 1) {
|
||||
this.domainOffset = undefined;
|
||||
}
|
||||
|
||||
// Update to the current set of lines
|
||||
this.lines = next;
|
||||
};
|
||||
|
||||
// Initialize the domain offset, based on these observed values
|
||||
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;
|
||||
};
|
||||
|
||||
// Expand range slightly so points near edges are visible
|
||||
PlotUpdater.prototype.expandRange = function () {
|
||||
var padding = PADDING_RATIO * this.dimensions[1],
|
||||
top;
|
||||
padding = Math.max(padding, 1.0);
|
||||
top = Math.ceil(this.origin[1] + this.dimensions[1] + padding / 2);
|
||||
this.origin[1] = Math.floor(this.origin[1] - padding / 2);
|
||||
this.dimensions[1] = top - this.origin[1];
|
||||
};
|
||||
|
||||
// Update dimensions and origin based on extrema of plots
|
||||
PlotUpdater.prototype.updateBounds = function () {
|
||||
var bufferArray = this.bufferArray.filter(function (lineBuffer) {
|
||||
return lineBuffer.getLength() > 0; // Ignore empty lines
|
||||
}),
|
||||
priorDomainOrigin = this.origin[0],
|
||||
priorDomainDimensions = this.dimensions[0];
|
||||
|
||||
if (bufferArray.length > 0) {
|
||||
this.domainExtrema = bufferArray.map(function (lineBuffer) {
|
||||
return lineBuffer.getDomainExtrema();
|
||||
}).reduce(reduceExtrema);
|
||||
|
||||
this.rangeExtrema = bufferArray.map(function (lineBuffer) {
|
||||
return lineBuffer.getRangeExtrema();
|
||||
}).reduce(reduceExtrema);
|
||||
|
||||
// Calculate best-fit dimensions
|
||||
this.dimensions = [this.domainExtrema, this.rangeExtrema]
|
||||
.map(dimensionsOf);
|
||||
this.origin = [this.domainExtrema, this.rangeExtrema]
|
||||
.map(originOf);
|
||||
|
||||
// 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] -
|
||||
this.fixedDuration;
|
||||
this.dimensions[0] = this.fixedDuration;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add latest data for this domain object
|
||||
PlotUpdater.prototype.addPointFor = function (domainObject) {
|
||||
var line = this.lines[domainObject.getId()];
|
||||
if (line) {
|
||||
line.addPoint(
|
||||
this.handle.getDomainValue(domainObject, this.domain),
|
||||
this.handle.getRangeValue(domainObject, this.range)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update with latest data.
|
||||
*/
|
||||
PlotUpdater.prototype.update = function update() {
|
||||
var objects = this.handle.getTelemetryObjects(),
|
||||
self = this;
|
||||
|
||||
// Initialize domain offset if necessary
|
||||
if (this.domainOffset === undefined) {
|
||||
this.initializeDomainOffset(objects.map(function (obj) {
|
||||
return self.handle.getDomainValue(obj, self.domain);
|
||||
}).filter(function (value) {
|
||||
return typeof value === 'number';
|
||||
}));
|
||||
}
|
||||
|
||||
// Make sure lines are available
|
||||
this.prepareLines(objects);
|
||||
|
||||
// Add new data
|
||||
objects.forEach(function (domainObject, index) {
|
||||
self.addPointFor(domainObject, index);
|
||||
});
|
||||
|
||||
// Then, update extrema
|
||||
this.updateBounds();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the dimensions which bound all data in the provided
|
||||
* data sets. This is given as a two-element array where the
|
||||
* first element is domain, and second is range.
|
||||
* @returns {number[]} the dimensions which bound this data set
|
||||
*/
|
||||
PlotUpdater.prototype.getDimensions = function () {
|
||||
return this.dimensions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the origin of this data set's boundary.
|
||||
* This is given as a two-element array where the
|
||||
* first element is domain, and second is range.
|
||||
* The domain value here is not adjusted by the domain offset.
|
||||
* @returns {number[]} the origin of this data set's boundary
|
||||
*/
|
||||
PlotUpdater.prototype.getOrigin = function () {
|
||||
return this.origin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the domain offset; this offset will have been subtracted
|
||||
* from all domain values in all buffers returned by this
|
||||
* preparer, in order to minimize loss-of-precision due to
|
||||
* conversion to the 32-bit float format needed by WebGL.
|
||||
* @returns {number} the domain offset
|
||||
* @memberof platform/features/plot.PlotUpdater#
|
||||
*/
|
||||
PlotUpdater.prototype.getDomainOffset = function () {
|
||||
return this.domainOffset;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all renderable buffers for this data set. This will
|
||||
* be returned as an array which can be correlated back to
|
||||
* the provided telemetry data objects (from the constructor
|
||||
* call) by index.
|
||||
*
|
||||
* Internally, these are flattened; each buffer contains a
|
||||
* sequence of alternating domain and range values.
|
||||
*
|
||||
* All domain values in all buffers will have been adjusted
|
||||
* from their original values by subtraction of the domain
|
||||
* offset; this minimizes loss-of-precision resulting from
|
||||
* the conversion to 32-bit floats, which may otherwise
|
||||
* cause aliasing artifacts (particularly for timestamps)
|
||||
*
|
||||
* @returns {Float32Array[]} the buffers for these traces
|
||||
* @memberof platform/features/plot.PlotUpdater#
|
||||
*/
|
||||
PlotUpdater.prototype.getLineBuffers = function () {
|
||||
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.
|
||||
*/
|
||||
PlotUpdater.prototype.addHistorical = function (domainObject, series) {
|
||||
var count = series ? series.getPointCount() : 0,
|
||||
line;
|
||||
|
||||
// Nothing to do if it's an empty series
|
||||
if (count < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize domain offset if necessary
|
||||
if (this.domainOffset === undefined) {
|
||||
this.initializeDomainOffset([
|
||||
series.getDomainValue(0, this.domain),
|
||||
series.getDomainValue(count - 1, this.domain)
|
||||
]);
|
||||
}
|
||||
|
||||
// Make sure lines are available
|
||||
this.prepareLines(this.handle.getTelemetryObjects());
|
||||
|
||||
// Look up the line for this domain object
|
||||
line = this.lines[domainObject.getId()];
|
||||
|
||||
// ...and put the data into it.
|
||||
if (line) {
|
||||
line.addSeries(series, this.domain, this.range);
|
||||
}
|
||||
|
||||
// Update extrema
|
||||
this.updateBounds();
|
||||
};
|
||||
|
||||
return PlotUpdater;
|
||||
|
||||
}
|
||||
);
|
||||
|
@ -1,155 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["./PlotOverlayMode", "./PlotStackMode"],
|
||||
function (PlotOverlayMode, PlotStackMode) {
|
||||
|
||||
var STACKED = {
|
||||
key: "stacked",
|
||||
name: "Stacked",
|
||||
cssClass: "icon-plot-stacked",
|
||||
Constructor: PlotStackMode
|
||||
},
|
||||
OVERLAID = {
|
||||
key: "overlaid",
|
||||
name: "Overlaid",
|
||||
cssClass: "icon-plot-overlay",
|
||||
Constructor: PlotOverlayMode
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles distinct behavior associated with different
|
||||
* plot modes.
|
||||
*
|
||||
* @interface platform/features/plot.PlotModeHandler
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Plot telemetry to the sub-plot(s) managed by this mode.
|
||||
* @param {platform/features/plot.PlotUpdater} updater a source
|
||||
* of data that is ready to plot
|
||||
* @method platform/features/plot.PlotModeHandler#plotTelemetry
|
||||
*/
|
||||
/**
|
||||
* Get all sub-plots to be displayed in this mode; used
|
||||
* to populate the plot template.
|
||||
* @return {platform/features/plot.SubPlot[]} all sub-plots to
|
||||
* display in this mode
|
||||
* @method platform/features/plot.PlotModeHandler#getSubPlots
|
||||
*/
|
||||
/**
|
||||
* Check if we are not in our base pan-zoom state (that is,
|
||||
* there are some temporary user modifications to the
|
||||
* current pan-zoom state.)
|
||||
* @returns {boolean} true if not in the base pan-zoom state
|
||||
* @method platform/features/plot.PlotModeHandler#isZoomed
|
||||
*/
|
||||
/**
|
||||
* Undo the most recent pan/zoom change and restore
|
||||
* the prior state.
|
||||
* @method platform/features/plot.PlotModeHandler#stepBackPanZoom
|
||||
*/
|
||||
/**
|
||||
* Undo all pan/zoom change and restore the base state.
|
||||
* @method platform/features/plot.PlotModeHandler#unzoom
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determines which plotting modes (stacked/overlaid)
|
||||
* are applicable in a given plot view, maintains current
|
||||
* selection state thereof, and provides handlers for the
|
||||
* different behaviors associated with these modes.
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {DomainObject[]} telemetryObjects the telemetry objects being
|
||||
* represented in this plot view
|
||||
* @param {platform/features/plot.SubPlotFactory} subPlotFactory a
|
||||
* factory for creating sub-plots
|
||||
*/
|
||||
function PlotModeOptions(telemetryObjects, subPlotFactory) {
|
||||
this.options = telemetryObjects.length > 1 ?
|
||||
[OVERLAID, STACKED] : [OVERLAID];
|
||||
this.mode = this.options[0]; // Initial selection (overlaid)
|
||||
this.telemetryObjects = telemetryObjects;
|
||||
this.subPlotFactory = subPlotFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a handler for the current mode. This will handle
|
||||
* plotting telemetry, providing subplots for the template,
|
||||
* and view-level interactions with pan-zoom state.
|
||||
* @returns {PlotOverlayMode|PlotStackMode} a handler
|
||||
* for the current mode
|
||||
*/
|
||||
PlotModeOptions.prototype.getModeHandler = function () {
|
||||
// Lazily initialize
|
||||
if (!this.modeHandler) {
|
||||
this.modeHandler = new this.mode.Constructor(
|
||||
this.telemetryObjects,
|
||||
this.subPlotFactory
|
||||
);
|
||||
}
|
||||
return this.modeHandler;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all mode options available for each plot. Each
|
||||
* mode contains a `name` and `cssClass` field suitable
|
||||
* for display in a template.
|
||||
* @return {Array} the available modes
|
||||
*/
|
||||
PlotModeOptions.prototype.getModeOptions = function () {
|
||||
return this.options;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the plotting mode option currently in use.
|
||||
* This will be one of the elements returned from
|
||||
* `getModeOptions`.
|
||||
* @return {*} the current mode
|
||||
*/
|
||||
PlotModeOptions.prototype.getMode = function () {
|
||||
return this.mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the plotting mode option to use.
|
||||
* The passed argument must be one of the options
|
||||
* returned by `getModeOptions`.
|
||||
* @param {object} option one of the plot mode options
|
||||
* from `getModeOptions`
|
||||
*/
|
||||
PlotModeOptions.prototype.setMode = function (option) {
|
||||
if (this.mode !== option) {
|
||||
this.mode = option;
|
||||
// Clear the existing mode handler, so it
|
||||
// can be instantiated next time it's needed.
|
||||
this.modeHandler = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return PlotModeOptions;
|
||||
}
|
||||
);
|
@ -1,88 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../SubPlot", "../elements/PlotPalette", "../elements/PlotPanZoomStack"],
|
||||
function (SubPlot, PlotPalette, PlotPanZoomStack) {
|
||||
|
||||
/**
|
||||
* Handles plotting in Overlaid mode. In overlaid mode, there
|
||||
* is one sub-plot which contains all plotted objects.
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @implements {platform/features/plot.PlotModeHandler}
|
||||
* @param {DomainObject[]} the domain objects to be plotted
|
||||
*/
|
||||
function PlotOverlayMode(telemetryObjects, subPlotFactory) {
|
||||
this.panZoomStack = new PlotPanZoomStack([], []);
|
||||
this.subplot = subPlotFactory.createSubPlot(
|
||||
telemetryObjects,
|
||||
this.panZoomStack
|
||||
);
|
||||
this.subplots = [this.subplot];
|
||||
}
|
||||
|
||||
PlotOverlayMode.prototype.plotTelemetry = function (updater) {
|
||||
// Fit to the boundaries of the data, but don't
|
||||
// override any user-initiated pan-zoom changes.
|
||||
this.panZoomStack.setBasePanZoom(
|
||||
updater.getOrigin(),
|
||||
updater.getDimensions()
|
||||
);
|
||||
|
||||
// Track the domain offset, used to bias domain values
|
||||
// to minimize loss of precision when converted to 32-bit
|
||||
// floating point values for display.
|
||||
this.subplot.setDomainOffset(updater.getDomainOffset());
|
||||
|
||||
// Draw the buffers. Select color by index.
|
||||
this.subplot.getDrawingObject().lines =
|
||||
updater.getLineBuffers().map(function (buf, i) {
|
||||
return {
|
||||
buffer: buf.getBuffer(),
|
||||
color: PlotPalette.getFloatColor(i),
|
||||
points: buf.getLength()
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
PlotOverlayMode.prototype.getSubPlots = function () {
|
||||
return this.subplots;
|
||||
};
|
||||
|
||||
PlotOverlayMode.prototype.isZoomed = function () {
|
||||
return this.panZoomStack.getDepth() > 1;
|
||||
};
|
||||
|
||||
PlotOverlayMode.prototype.stepBackPanZoom = function () {
|
||||
this.panZoomStack.popPanZoom();
|
||||
this.subplot.update();
|
||||
};
|
||||
|
||||
PlotOverlayMode.prototype.unzoom = function () {
|
||||
this.panZoomStack.clearPanZoom();
|
||||
this.subplot.update();
|
||||
};
|
||||
|
||||
return PlotOverlayMode;
|
||||
}
|
||||
);
|
@ -1,104 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../SubPlot", "../elements/PlotPalette", "../elements/PlotPanZoomStackGroup"],
|
||||
function (SubPlot, PlotPalette, PlotPanZoomStackGroup) {
|
||||
|
||||
/**
|
||||
* Handles plotting in Stacked mode. In stacked mode, there
|
||||
* is one sub-plot for each plotted object.
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @implements {platform/features/plot.PlotModeHandler}
|
||||
* @param {DomainObject[]} the domain objects to be plotted
|
||||
*/
|
||||
function PlotStackMode(telemetryObjects, subPlotFactory) {
|
||||
var self = this;
|
||||
|
||||
this.panZoomStackGroup =
|
||||
new PlotPanZoomStackGroup(telemetryObjects.length);
|
||||
|
||||
this.subplots = telemetryObjects.map(function (telemetryObject, i) {
|
||||
return subPlotFactory.createSubPlot(
|
||||
[telemetryObject],
|
||||
self.panZoomStackGroup.getPanZoomStack(i)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
PlotStackMode.prototype.plotTelemetryTo = function (subplot, prepared, index) {
|
||||
var buffer = prepared.getLineBuffers()[index];
|
||||
|
||||
// Track the domain offset, used to bias domain values
|
||||
// to minimize loss of precision when converted to 32-bit
|
||||
// floating point values for display.
|
||||
subplot.setDomainOffset(prepared.getDomainOffset());
|
||||
|
||||
// Draw the buffers. Always use the 0th color, because there
|
||||
// is one line per plot.
|
||||
subplot.getDrawingObject().lines = [{
|
||||
buffer: buffer.getBuffer(),
|
||||
color: PlotPalette.getFloatColor(0),
|
||||
points: buffer.getLength()
|
||||
}];
|
||||
};
|
||||
|
||||
PlotStackMode.prototype.plotTelemetry = function (prepared) {
|
||||
var self = this;
|
||||
// Fit to the boundaries of the data, but don't
|
||||
// override any user-initiated pan-zoom changes.
|
||||
this.panZoomStackGroup.setBasePanZoom(
|
||||
prepared.getOrigin(),
|
||||
prepared.getDimensions()
|
||||
);
|
||||
|
||||
this.subplots.forEach(function (subplot, index) {
|
||||
self.plotTelemetryTo(subplot, prepared, index);
|
||||
});
|
||||
};
|
||||
|
||||
PlotStackMode.prototype.getSubPlots = function () {
|
||||
return this.subplots;
|
||||
};
|
||||
|
||||
PlotStackMode.prototype.isZoomed = function () {
|
||||
return this.panZoomStackGroup.getDepth() > 1;
|
||||
};
|
||||
|
||||
PlotStackMode.prototype.stepBackPanZoom = function () {
|
||||
this.panZoomStackGroup.popPanZoom();
|
||||
this.subplots.forEach(function (subplot) {
|
||||
subplot.update();
|
||||
});
|
||||
};
|
||||
|
||||
PlotStackMode.prototype.unzoom = function () {
|
||||
this.panZoomStackGroup.clearPanZoom();
|
||||
this.subplots.forEach(function (subplot) {
|
||||
subplot.update();
|
||||
});
|
||||
};
|
||||
|
||||
return PlotStackMode;
|
||||
}
|
||||
);
|
@ -1,95 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../src/Canvas2DChart"],
|
||||
function (Canvas2DChart) {
|
||||
|
||||
describe("A canvas 2d chart", function () {
|
||||
var mockCanvas,
|
||||
mock2d,
|
||||
chart;
|
||||
|
||||
beforeEach(function () {
|
||||
mockCanvas = jasmine.createSpyObj("canvas", ["getContext"]);
|
||||
mock2d = jasmine.createSpyObj(
|
||||
"2d",
|
||||
[
|
||||
"clearRect",
|
||||
"beginPath",
|
||||
"moveTo",
|
||||
"lineTo",
|
||||
"stroke",
|
||||
"fillRect"
|
||||
]
|
||||
);
|
||||
mockCanvas.getContext.andReturn(mock2d);
|
||||
|
||||
chart = new Canvas2DChart(mockCanvas);
|
||||
});
|
||||
|
||||
// Note that tests below are less specific than they
|
||||
// could be, esp. w.r.t. arguments to drawing calls;
|
||||
// this is a fallback option so is a lower test priority.
|
||||
|
||||
it("allows the canvas to be cleared", function () {
|
||||
chart.clear();
|
||||
expect(mock2d.clearRect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not construct if 2D is unavailable", function () {
|
||||
mockCanvas.getContext.andReturn(undefined);
|
||||
expect(function () {
|
||||
return new Canvas2DChart(mockCanvas);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it("allows dimensions to be set", function () {
|
||||
// No return value, just verify API is present
|
||||
chart.setDimensions([120, 120], [0, 10]);
|
||||
});
|
||||
|
||||
it("allows lines to be drawn", function () {
|
||||
var testBuffer = [0, 1, 3, 8],
|
||||
testColor = [0.25, 0.33, 0.66, 1.0],
|
||||
testPoints = 2;
|
||||
chart.drawLine(testBuffer, testColor, testPoints);
|
||||
expect(mock2d.beginPath).toHaveBeenCalled();
|
||||
expect(mock2d.lineTo.calls.length).toEqual(1);
|
||||
expect(mock2d.stroke).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("allows squares to be drawn", function () {
|
||||
var testMin = [0, 1],
|
||||
testMax = [10, 10],
|
||||
testColor = [0.25, 0.33, 0.66, 1.0];
|
||||
|
||||
chart.drawSquare(testMin, testMax, testColor);
|
||||
expect(mock2d.fillRect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,143 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../src/GLChart"],
|
||||
function (GLChart) {
|
||||
|
||||
describe("A WebGL chart", function () {
|
||||
var mockCanvas,
|
||||
mockGL,
|
||||
glChart;
|
||||
|
||||
beforeEach(function () {
|
||||
mockCanvas = jasmine.createSpyObj("canvas", ["getContext"]);
|
||||
mockGL = jasmine.createSpyObj(
|
||||
"gl",
|
||||
[
|
||||
"createShader",
|
||||
"compileShader",
|
||||
"shaderSource",
|
||||
"attachShader",
|
||||
"createProgram",
|
||||
"linkProgram",
|
||||
"useProgram",
|
||||
"enableVertexAttribArray",
|
||||
"getAttribLocation",
|
||||
"getUniformLocation",
|
||||
"createBuffer",
|
||||
"lineWidth",
|
||||
"enable",
|
||||
"blendFunc",
|
||||
"viewport",
|
||||
"clear",
|
||||
"uniform2fv",
|
||||
"uniform4fv",
|
||||
"bufferData",
|
||||
"bindBuffer",
|
||||
"vertexAttribPointer",
|
||||
"drawArrays"
|
||||
]
|
||||
);
|
||||
mockGL.ARRAY_BUFFER = "ARRAY_BUFFER";
|
||||
mockGL.DYNAMIC_DRAW = "DYNAMIC_DRAW";
|
||||
mockGL.TRIANGLE_FAN = "TRIANGLE_FAN";
|
||||
mockGL.LINE_STRIP = "LINE_STRIP";
|
||||
|
||||
// Echo back names for uniform locations, so we can
|
||||
// test which of these are set for certain operations.
|
||||
mockGL.getUniformLocation.andCallFake(function (a, name) {
|
||||
return name;
|
||||
});
|
||||
|
||||
mockCanvas.getContext.andReturn(mockGL);
|
||||
|
||||
glChart = new GLChart(mockCanvas);
|
||||
});
|
||||
|
||||
it("allows the canvas to be cleared", function () {
|
||||
glChart.clear();
|
||||
expect(mockGL.clear).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not construct if WebGL is unavailable", function () {
|
||||
mockCanvas.getContext.andReturn(undefined);
|
||||
expect(function () {
|
||||
return new GLChart(mockCanvas);
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it("allows dimensions to be set", function () {
|
||||
glChart.setDimensions([120, 120], [0, 10]);
|
||||
expect(mockGL.uniform2fv)
|
||||
.toHaveBeenCalledWith("uDimensions", [120, 120]);
|
||||
expect(mockGL.uniform2fv)
|
||||
.toHaveBeenCalledWith("uOrigin", [0, 10]);
|
||||
});
|
||||
|
||||
it("allows lines to be drawn", function () {
|
||||
var testBuffer = [0, 1, 3, 8],
|
||||
testColor = [0.25, 0.33, 0.66, 1.0],
|
||||
testPoints = 2;
|
||||
glChart.drawLine(testBuffer, testColor, testPoints);
|
||||
expect(mockGL.bufferData).toHaveBeenCalledWith(
|
||||
mockGL.ARRAY_BUFFER,
|
||||
testBuffer,
|
||||
mockGL.DYNAMIC_DRAW
|
||||
);
|
||||
expect(mockGL.uniform4fv)
|
||||
.toHaveBeenCalledWith("uColor", testColor);
|
||||
expect(mockGL.drawArrays)
|
||||
.toHaveBeenCalledWith("LINE_STRIP", 0, testPoints);
|
||||
});
|
||||
|
||||
it("allows squares to be drawn", function () {
|
||||
var testMin = [0, 1],
|
||||
testMax = [10, 10],
|
||||
testColor = [0.25, 0.33, 0.66, 1.0];
|
||||
|
||||
glChart.drawSquare(testMin, testMax, testColor);
|
||||
|
||||
expect(mockGL.uniform4fv)
|
||||
.toHaveBeenCalledWith("uColor", testColor);
|
||||
expect(mockGL.drawArrays)
|
||||
.toHaveBeenCalledWith("TRIANGLE_FAN", 0, 4);
|
||||
});
|
||||
|
||||
it("uses buffer sizes reported by WebGL", function () {
|
||||
// Make sure that GLChart uses the GL buffer size, which may
|
||||
// differ from what canvas requested. WTD-852
|
||||
mockCanvas.width = 300;
|
||||
mockCanvas.height = 150;
|
||||
mockGL.drawingBufferWidth = 200;
|
||||
mockGL.drawingBufferHeight = 175;
|
||||
|
||||
glChart.clear();
|
||||
|
||||
expect(mockGL.viewport).toHaveBeenCalledWith(0, 0, 200, 175);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,216 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../src/MCTChart"],
|
||||
function (MCTChart) {
|
||||
|
||||
describe("The mct-chart directive", function () {
|
||||
var mockInterval,
|
||||
mockLog,
|
||||
mockScope,
|
||||
mockElement,
|
||||
mockCanvas,
|
||||
mockGL,
|
||||
mockC2d,
|
||||
mockPromise,
|
||||
mctChart;
|
||||
|
||||
beforeEach(function () {
|
||||
mockInterval =
|
||||
jasmine.createSpy("$interval");
|
||||
mockLog =
|
||||
jasmine.createSpyObj("$log", ["warn", "info", "debug"]);
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
["$watchCollection", "$on", "$apply"]
|
||||
);
|
||||
mockElement =
|
||||
jasmine.createSpyObj("element", ["find", "html"]);
|
||||
mockInterval.cancel = jasmine.createSpy("cancelInterval");
|
||||
mockPromise = jasmine.createSpyObj("promise", ["then"]);
|
||||
|
||||
|
||||
// mct-chart uses GLChart, so it needs WebGL API
|
||||
mockCanvas =
|
||||
jasmine.createSpyObj("canvas", ["getContext", "addEventListener"]);
|
||||
mockGL = jasmine.createSpyObj(
|
||||
"gl",
|
||||
[
|
||||
"createShader",
|
||||
"compileShader",
|
||||
"shaderSource",
|
||||
"attachShader",
|
||||
"createProgram",
|
||||
"linkProgram",
|
||||
"useProgram",
|
||||
"enableVertexAttribArray",
|
||||
"getAttribLocation",
|
||||
"getUniformLocation",
|
||||
"createBuffer",
|
||||
"lineWidth",
|
||||
"enable",
|
||||
"blendFunc",
|
||||
"viewport",
|
||||
"clear",
|
||||
"uniform2fv",
|
||||
"uniform4fv",
|
||||
"bufferData",
|
||||
"bindBuffer",
|
||||
"vertexAttribPointer",
|
||||
"drawArrays"
|
||||
]
|
||||
);
|
||||
mockC2d = jasmine.createSpyObj('c2d', ['clearRect']);
|
||||
mockGL.ARRAY_BUFFER = "ARRAY_BUFFER";
|
||||
mockGL.DYNAMIC_DRAW = "DYNAMIC_DRAW";
|
||||
mockGL.TRIANGLE_FAN = "TRIANGLE_FAN";
|
||||
mockGL.LINE_STRIP = "LINE_STRIP";
|
||||
|
||||
// Echo back names for uniform locations, so we can
|
||||
// test which of these are set for certain operations.
|
||||
mockGL.getUniformLocation.andCallFake(function (a, name) {
|
||||
return name;
|
||||
});
|
||||
|
||||
mockElement.find.andReturn([mockCanvas]);
|
||||
mockCanvas.getContext.andCallFake(function (type) {
|
||||
return { webgl: mockGL, '2d': mockC2d }[type];
|
||||
});
|
||||
mockInterval.andReturn(mockPromise);
|
||||
|
||||
mctChart = new MCTChart(mockInterval, mockLog);
|
||||
});
|
||||
|
||||
it("is applicable at the element level", function () {
|
||||
expect(mctChart.restrict).toEqual("E");
|
||||
});
|
||||
|
||||
it("places a 'draw' attribute in-scope", function () {
|
||||
// Should ask Angular for the draw attribute
|
||||
expect(mctChart.scope.draw).toEqual("=");
|
||||
});
|
||||
|
||||
it("watches for changes in the drawn object", function () {
|
||||
mctChart.link(mockScope, mockElement);
|
||||
expect(mockScope.$watchCollection)
|
||||
.toHaveBeenCalledWith("draw", jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("issues one draw call per line", function () {
|
||||
mctChart.link(mockScope, mockElement);
|
||||
mockScope.$watchCollection.mostRecentCall.args[1]({
|
||||
lines: [{}, {}, {}]
|
||||
});
|
||||
expect(mockGL.drawArrays.calls.length).toEqual(3);
|
||||
});
|
||||
|
||||
it("issues one draw call per box", function () {
|
||||
mctChart.link(mockScope, mockElement);
|
||||
mockScope.$watchCollection.mostRecentCall.args[1]({
|
||||
boxes: [
|
||||
{ start: [0, 0], end: [1, 1] },
|
||||
{ start: [0, 0], end: [1, 1] },
|
||||
{ start: [0, 0], end: [1, 1] },
|
||||
{ start: [0, 0], end: [1, 1] }
|
||||
]
|
||||
});
|
||||
expect(mockGL.drawArrays.calls.length).toEqual(4);
|
||||
});
|
||||
|
||||
it("does not fail if no draw object is in scope", function () {
|
||||
mctChart.link(mockScope, mockElement);
|
||||
expect(mockScope.$watchCollection.mostRecentCall.args[1])
|
||||
.not.toThrow();
|
||||
});
|
||||
|
||||
it("draws on canvas resize", function () {
|
||||
mctChart.link(mockScope, mockElement);
|
||||
|
||||
// Should track canvas size in an interval
|
||||
expect(mockInterval).toHaveBeenCalledWith(
|
||||
jasmine.any(Function),
|
||||
jasmine.any(Number),
|
||||
0,
|
||||
false
|
||||
);
|
||||
|
||||
// Verify pre-condition
|
||||
expect(mockGL.clear).not.toHaveBeenCalled();
|
||||
|
||||
mockCanvas.width = 100;
|
||||
mockCanvas.offsetWidth = 150;
|
||||
mockCanvas.height = 200;
|
||||
mockCanvas.offsetHeight = 200;
|
||||
mockInterval.mostRecentCall.args[0]();
|
||||
|
||||
// Use clear as an indication that drawing has occurred
|
||||
expect(mockGL.clear).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("warns if no WebGL context is available", function () {
|
||||
mockCanvas.getContext.andReturn(undefined);
|
||||
mctChart.link(mockScope, mockElement);
|
||||
expect(mockLog.warn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("falls back to Canvas 2d API if WebGL context is lost", function () {
|
||||
mctChart.link(mockScope, mockElement);
|
||||
expect(mockCanvas.addEventListener)
|
||||
.toHaveBeenCalledWith("webglcontextlost", jasmine.any(Function));
|
||||
expect(mockCanvas.getContext).not.toHaveBeenCalledWith('2d');
|
||||
mockCanvas.addEventListener.mostRecentCall.args[1]();
|
||||
expect(mockCanvas.getContext).toHaveBeenCalledWith('2d');
|
||||
});
|
||||
|
||||
it("logs nothing in nominal situations (WebGL available)", function () {
|
||||
// Complement the previous test
|
||||
mctChart.link(mockScope, mockElement);
|
||||
expect(mockLog.warn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Avoid resource leaks
|
||||
it("stops polling for size changes on destroy", function () {
|
||||
mctChart.link(mockScope, mockElement);
|
||||
|
||||
// Should be listening for a destroy event
|
||||
expect(mockScope.$on).toHaveBeenCalledWith(
|
||||
"$destroy",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
|
||||
// Precondition - interval still active
|
||||
expect(mockInterval.cancel).not.toHaveBeenCalled();
|
||||
|
||||
// Broadcast a $destroy
|
||||
mockScope.$on.mostRecentCall.args[1]();
|
||||
|
||||
// Should have stopped the interval
|
||||
expect(mockInterval.cancel).toHaveBeenCalledWith(mockPromise);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,403 +0,0 @@
|
||||
/*global angular*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../src/PlotController"],
|
||||
function (PlotController) {
|
||||
|
||||
describe("The plot controller", function () {
|
||||
var mockScope,
|
||||
mockElement,
|
||||
mockExportImageService,
|
||||
mockFormatter,
|
||||
mockHandler,
|
||||
mockThrottle,
|
||||
mockHandle,
|
||||
mockDomainObject,
|
||||
mockSeries,
|
||||
mockStatusCapability,
|
||||
controller,
|
||||
mockConductor;
|
||||
|
||||
function bind(method, thisObj) {
|
||||
return function () {
|
||||
return method.apply(thisObj, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
function fireEvent(name, args) {
|
||||
mockScope.$on.calls.forEach(function (call) {
|
||||
if (call.args[0] === name) {
|
||||
call.args[1].apply(null, args || []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fireWatch(expr, value) {
|
||||
mockScope.$watch.calls.forEach(function (call) {
|
||||
if (call.args[0] === expr) {
|
||||
call.args[1].apply(null, [value]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
beforeEach(function () {
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
["$watch", "$on", "$emit"]
|
||||
);
|
||||
mockElement = angular.element('<div />');
|
||||
mockExportImageService = jasmine.createSpyObj(
|
||||
"ExportImageService",
|
||||
["exportJPG", "exportPNG"]
|
||||
);
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
"formatter",
|
||||
["formatDomainValue", "formatRangeValue"]
|
||||
);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getId", "getModel", "getCapability", "hasCapability"]
|
||||
);
|
||||
mockHandler = jasmine.createSpyObj(
|
||||
"telemetrySubscriber",
|
||||
["handle"]
|
||||
);
|
||||
mockThrottle = jasmine.createSpy("throttle");
|
||||
mockHandle = jasmine.createSpyObj(
|
||||
"subscription",
|
||||
[
|
||||
"unsubscribe",
|
||||
"getTelemetryObjects",
|
||||
"getMetadata",
|
||||
"getDomainValue",
|
||||
"getRangeValue",
|
||||
"getDatum",
|
||||
"request"
|
||||
]
|
||||
);
|
||||
mockSeries = jasmine.createSpyObj(
|
||||
'series',
|
||||
['getPointCount', 'getDomainValue', 'getRangeValue']
|
||||
);
|
||||
|
||||
mockStatusCapability = jasmine.createSpyObj(
|
||||
"statusCapability",
|
||||
["set"]
|
||||
);
|
||||
|
||||
mockHandler.handle.andReturn(mockHandle);
|
||||
mockThrottle.andCallFake(function (fn) {
|
||||
return fn;
|
||||
});
|
||||
mockHandle.getTelemetryObjects.andReturn([mockDomainObject]);
|
||||
mockHandle.getMetadata.andReturn([{}]);
|
||||
mockHandle.getDomainValue.andReturn(123);
|
||||
mockHandle.getRangeValue.andReturn(42);
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
mockConductor = jasmine.createSpyObj('conductor', [
|
||||
'on',
|
||||
'off',
|
||||
'bounds',
|
||||
'timeSystem',
|
||||
'timeOfInterest',
|
||||
'follow'
|
||||
]);
|
||||
|
||||
mockConductor.bounds.andReturn({});
|
||||
|
||||
controller = new PlotController(
|
||||
mockScope,
|
||||
mockElement,
|
||||
mockExportImageService,
|
||||
mockFormatter,
|
||||
mockHandler,
|
||||
mockThrottle,
|
||||
undefined,
|
||||
{time: mockConductor}
|
||||
);
|
||||
});
|
||||
|
||||
it("provides plot colors", function () {
|
||||
// PlotPalette will have its own tests
|
||||
expect(controller.getColor(0))
|
||||
.toEqual(jasmine.any(String));
|
||||
|
||||
// Colors should be unique
|
||||
expect(controller.getColor(0))
|
||||
.not.toEqual(controller.getColor(1));
|
||||
});
|
||||
|
||||
it("subscribes to telemetry when a domain object appears in scope", function () {
|
||||
// Make sure we're using the right watch here
|
||||
expect(mockScope.$watch.mostRecentCall.args[0])
|
||||
.toEqual("domainObject");
|
||||
// Make an object available
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
// Should have subscribed
|
||||
expect(mockHandler.handle).toHaveBeenCalledWith(
|
||||
mockDomainObject,
|
||||
jasmine.any(Function),
|
||||
true // Lossless
|
||||
);
|
||||
});
|
||||
|
||||
it("draws lines when data becomes available", function () {
|
||||
// Make an object available
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
|
||||
// Verify precondition
|
||||
controller.getSubPlots().forEach(function (subplot) {
|
||||
expect(subplot.getDrawingObject().lines)
|
||||
.not.toBeDefined();
|
||||
});
|
||||
|
||||
// Make sure there actually are subplots being verified
|
||||
expect(controller.getSubPlots().length > 0).toBeTruthy();
|
||||
|
||||
// Broadcast data
|
||||
mockHandler.handle.mostRecentCall.args[1]();
|
||||
|
||||
controller.getSubPlots().forEach(function (subplot) {
|
||||
expect(subplot.getDrawingObject().lines)
|
||||
.toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("unsubscribes when domain object changes", function () {
|
||||
// Make an object available
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
// Verify precondition - shouldn't unsubscribe yet
|
||||
expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
|
||||
// Remove the domain object
|
||||
mockScope.$watch.mostRecentCall.args[1](undefined);
|
||||
// Should have unsubscribed
|
||||
expect(mockHandle.unsubscribe).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it("changes modes depending on number of objects", function () {
|
||||
// Act like one object is available
|
||||
mockHandle.getTelemetryObjects.andReturn([
|
||||
mockDomainObject
|
||||
]);
|
||||
|
||||
// Make an object available; invoke handler's callback
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
mockHandler.handle.mostRecentCall.args[1]();
|
||||
|
||||
expect(controller.getModeOptions().length).toEqual(1);
|
||||
|
||||
// Act like one object is available
|
||||
mockHandle.getTelemetryObjects.andReturn([
|
||||
mockDomainObject,
|
||||
mockDomainObject,
|
||||
mockDomainObject
|
||||
]);
|
||||
|
||||
// Make an object available; invoke handler's callback
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
mockHandler.handle.mostRecentCall.args[1]();
|
||||
|
||||
expect(controller.getModeOptions().length).toEqual(2);
|
||||
});
|
||||
|
||||
// Interface tests follow; these will be delegated (mostly
|
||||
// to PlotModeOptions, which is tested separately).
|
||||
it("provides access to available plot mode options", function () {
|
||||
expect(Array.isArray(controller.getModeOptions()))
|
||||
.toBeTruthy();
|
||||
});
|
||||
|
||||
it("provides a current plot mode", function () {
|
||||
expect(controller.getMode().name)
|
||||
.toEqual(jasmine.any(String));
|
||||
});
|
||||
|
||||
it("allows plot mode to be changed", function () {
|
||||
expect(function () {
|
||||
controller.setMode(controller.getMode());
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
it("provides an array of sub-plots", function () {
|
||||
expect(Array.isArray(controller.getSubPlots()))
|
||||
.toBeTruthy();
|
||||
});
|
||||
|
||||
it("allows plots to be updated", function () {
|
||||
expect(bind(controller.update, controller)).not.toThrow();
|
||||
});
|
||||
|
||||
it("allows changing pan-zoom state", function () {
|
||||
expect(bind(controller.isZoomed, controller)).not.toThrow();
|
||||
expect(bind(controller.stepBackPanZoom, controller)).not.toThrow();
|
||||
expect(bind(controller.unzoom, controller)).not.toThrow();
|
||||
});
|
||||
|
||||
it("sets status when plot becomes detached from time conductor", function () {
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
|
||||
function boundsEvent() {
|
||||
fireEvent("telemetry:display:bounds", [
|
||||
{},
|
||||
{ start: 10, end: 100 },
|
||||
true
|
||||
]);
|
||||
}
|
||||
|
||||
mockDomainObject.hasCapability.andCallFake(function (name) {
|
||||
return name === "status";
|
||||
});
|
||||
mockDomainObject.getCapability.andReturn(mockStatusCapability);
|
||||
spyOn(controller, "isZoomed");
|
||||
|
||||
//Mock zoomed in state
|
||||
controller.isZoomed.andReturn(true);
|
||||
boundsEvent();
|
||||
expect(mockStatusCapability.set).toHaveBeenCalledWith("timeconductor-unsynced", true);
|
||||
|
||||
//"Reset" zoom
|
||||
controller.isZoomed.andReturn(false);
|
||||
boundsEvent();
|
||||
expect(mockStatusCapability.set).toHaveBeenCalledWith("timeconductor-unsynced", false);
|
||||
});
|
||||
|
||||
it("indicates if a request is pending", function () {
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
expect(controller.isRequestPending()).toBeTruthy();
|
||||
mockHandle.request.mostRecentCall.args[1](
|
||||
mockDomainObject,
|
||||
mockSeries
|
||||
);
|
||||
expect(controller.isRequestPending()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("requests historical telemetry", function () {
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
expect(mockHandle.request).toHaveBeenCalled();
|
||||
mockHandle.request.mostRecentCall.args[1](
|
||||
mockDomainObject,
|
||||
mockSeries
|
||||
);
|
||||
});
|
||||
|
||||
it("unsubscribes when destroyed", function () {
|
||||
// Make an object available
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
// Make sure $destroy is what's listened for
|
||||
expect(mockScope.$on.mostRecentCall.args[0]).toEqual('$destroy');
|
||||
// Also verify precondition
|
||||
expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
|
||||
// Destroy the scope
|
||||
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);
|
||||
});
|
||||
|
||||
it("requeries when user changes domain selection", function () {
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
expect(mockHandle.request.calls.length).toEqual(1);
|
||||
fireWatch("axes[0].active.key", 'someNewKey');
|
||||
expect(mockHandle.request.calls.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("requeries when user changes range selection", function () {
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
expect(mockHandle.request.calls.length).toEqual(1);
|
||||
fireWatch("axes[1].active.key", 'someNewKey');
|
||||
expect(mockHandle.request.calls.length).toEqual(2);
|
||||
});
|
||||
|
||||
it("maintains externally-provided domain axis bounds after data is received", function () {
|
||||
mockSeries.getPointCount.andReturn(3);
|
||||
mockSeries.getRangeValue.andReturn(42);
|
||||
mockSeries.getDomainValue.andCallFake(function (i) {
|
||||
return 2500 + i * 2500;
|
||||
});
|
||||
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
fireEvent("telemetry:display:bounds", [
|
||||
{},
|
||||
{start: 0, end: 10000}
|
||||
]);
|
||||
mockHandle.request.mostRecentCall.args[1](
|
||||
mockDomainObject,
|
||||
mockSeries
|
||||
);
|
||||
|
||||
// Pan-zoom state should reflect bounds set externally;
|
||||
// domain axis should not have shrunk to fit data.
|
||||
expect(
|
||||
controller.getSubPlots()[0].panZoomStack.getOrigin()[0]
|
||||
).toEqual(0);
|
||||
expect(
|
||||
controller.getSubPlots()[0].panZoomStack.getDimensions()[0]
|
||||
).toEqual(10000);
|
||||
});
|
||||
|
||||
it("provides classes for legends based on limit state", function () {
|
||||
var mockTelemetryObjects = mockHandle.getTelemetryObjects();
|
||||
|
||||
mockHandle.getDatum.andReturn({});
|
||||
mockTelemetryObjects.forEach(function (mockObject, i) {
|
||||
var id = 'object-' + i,
|
||||
mockLimitCapability =
|
||||
jasmine.createSpyObj('limit-' + id, ['evaluate']);
|
||||
|
||||
mockObject.getId.andReturn(id);
|
||||
mockObject.getCapability.andCallFake(function (key) {
|
||||
return (key === 'limit') && mockLimitCapability;
|
||||
});
|
||||
|
||||
mockLimitCapability.evaluate
|
||||
.andReturn({ cssClass: 'alarm-' + id });
|
||||
});
|
||||
|
||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
||||
mockHandler.handle.mostRecentCall.args[1]();
|
||||
|
||||
mockTelemetryObjects.forEach(function (mockTelemetryObject) {
|
||||
expect(controller.getLegendClass(mockTelemetryObject))
|
||||
.toEqual('alarm-' + mockTelemetryObject.getId());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,147 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['../src/PlotOptionsController'],
|
||||
function (PlotOptionsController) {
|
||||
|
||||
describe("The Plot Options controller", function () {
|
||||
var plotOptionsController,
|
||||
mockDomainObject,
|
||||
mockMutationCapability,
|
||||
mockUseCapabilities,
|
||||
mockCompositionCapability,
|
||||
mockComposition,
|
||||
mockUnlisten,
|
||||
mockChildOne,
|
||||
mockChildTwo,
|
||||
model,
|
||||
mockScope;
|
||||
|
||||
beforeEach(function () {
|
||||
model = {
|
||||
composition: ['childOne']
|
||||
};
|
||||
|
||||
mockChildOne = jasmine.createSpyObj('domainObject', [
|
||||
'getId'
|
||||
]);
|
||||
mockChildOne.getId.andReturn('childOne');
|
||||
|
||||
mockChildTwo = jasmine.createSpyObj('childTwo', [
|
||||
'getId'
|
||||
]);
|
||||
mockChildOne.getId.andReturn('childTwo');
|
||||
|
||||
mockCompositionCapability = jasmine.createSpyObj('compositionCapability', [
|
||||
'then'
|
||||
]);
|
||||
mockComposition = [
|
||||
mockChildOne
|
||||
];
|
||||
mockCompositionCapability.then.andCallFake(function (callback) {
|
||||
callback(mockComposition);
|
||||
});
|
||||
|
||||
mockUseCapabilities = jasmine.createSpyObj('useCapabilities', [
|
||||
'composition',
|
||||
'mutation'
|
||||
]);
|
||||
mockUseCapabilities.composition.andReturn(mockCompositionCapability);
|
||||
|
||||
mockMutationCapability = jasmine.createSpyObj('mutationCapability', [
|
||||
'listen'
|
||||
]);
|
||||
mockUnlisten = jasmine.createSpy('unlisten');
|
||||
mockMutationCapability.listen.andReturn(mockUnlisten);
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
||||
'getModel',
|
||||
'useCapability',
|
||||
'getCapability'
|
||||
]);
|
||||
mockDomainObject.useCapability.andCallFake(function (capability) {
|
||||
return mockUseCapabilities[capability]();
|
||||
});
|
||||
mockDomainObject.getCapability.andReturn(mockMutationCapability);
|
||||
mockDomainObject.getModel.andReturn(model);
|
||||
|
||||
mockScope = jasmine.createSpyObj('scope', [
|
||||
'$on',
|
||||
'$watchCollection'
|
||||
]);
|
||||
mockScope.domainObject = mockDomainObject;
|
||||
|
||||
function noop() {}
|
||||
mockScope.$watchCollection.andReturn(noop);
|
||||
|
||||
plotOptionsController = new PlotOptionsController(mockScope);
|
||||
});
|
||||
|
||||
it("sets form definitions on scope", function () {
|
||||
expect(mockScope.xAxisForm).toBeDefined();
|
||||
expect(mockScope.yAxisForm).toBeDefined();
|
||||
expect(mockScope.plotSeriesForm).toBeDefined();
|
||||
});
|
||||
|
||||
it("sets object children on scope", function () {
|
||||
expect(mockScope.children).toBe(mockComposition);
|
||||
});
|
||||
|
||||
it("on changes in object composition, updates the form", function () {
|
||||
expect(mockMutationCapability.listen).toHaveBeenCalled();
|
||||
expect(mockScope.children).toBe(mockComposition);
|
||||
expect(mockScope.children.length).toBe(1);
|
||||
mockComposition.push(mockChildTwo);
|
||||
model.composition.push('childTwo');
|
||||
mockMutationCapability.listen.mostRecentCall.args[0](model);
|
||||
expect(mockScope.children).toBe(mockComposition);
|
||||
expect(mockScope.children.length).toBe(2);
|
||||
});
|
||||
|
||||
it("on changes in form values, updates the object model", function () {
|
||||
var scopeConfiguration = mockScope.configuration,
|
||||
objModel = mockDomainObject.getModel();
|
||||
|
||||
scopeConfiguration.plot.yAxis.autoScale = true;
|
||||
scopeConfiguration.plot.yAxis.key = 'eu';
|
||||
scopeConfiguration.plot.xAxis.key = 'lst';
|
||||
|
||||
expect(mockScope.$watchCollection).toHaveBeenCalled();
|
||||
mockScope.$watchCollection.calls[0].args[1]();
|
||||
expect(mockDomainObject.useCapability).toHaveBeenCalledWith('mutation', jasmine.any(Function));
|
||||
|
||||
mockDomainObject.useCapability.mostRecentCall.args[1](objModel);
|
||||
expect(objModel.configuration.plot.yAxis.autoScale).toBe(true);
|
||||
expect(objModel.configuration.plot.yAxis.key).toBe('eu');
|
||||
expect(objModel.configuration.plot.xAxis.key).toBe('lst');
|
||||
|
||||
});
|
||||
|
||||
it("cleans up listeners on destruction of the controller", function () {
|
||||
mockScope.$on.mostRecentCall.args[1]();
|
||||
expect(mockUnlisten).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,66 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../src/SubPlotFactory"],
|
||||
function (SubPlotFactory) {
|
||||
|
||||
describe("The sub-plot factory", function () {
|
||||
var mockDomainObject,
|
||||
mockPanZoomStack,
|
||||
mockFormatter,
|
||||
factory;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getId", "getModel", "getCapability"]
|
||||
);
|
||||
mockPanZoomStack = jasmine.createSpyObj(
|
||||
"panZoomStack",
|
||||
["getPanZoom"]
|
||||
);
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
"formatter",
|
||||
["formatDomainValue", "formatRangeValue"]
|
||||
);
|
||||
|
||||
mockPanZoomStack.getPanZoom.andReturn({
|
||||
origin: [0, 0],
|
||||
dimensions: [100, 100]
|
||||
});
|
||||
|
||||
factory = new SubPlotFactory(mockFormatter);
|
||||
});
|
||||
|
||||
it("creates sub-plots", function () {
|
||||
expect(factory.createSubPlot(
|
||||
[mockDomainObject],
|
||||
mockPanZoomStack
|
||||
).getTelemetryObjects()).toEqual([mockDomainObject]);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,208 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../src/SubPlot"],
|
||||
function (SubPlot) {
|
||||
|
||||
describe("A sub-plot", function () {
|
||||
var mockDomainObject,
|
||||
mockPanZoomStack,
|
||||
mockFormatter,
|
||||
mockElement,
|
||||
testDomainObjects,
|
||||
testOrigin,
|
||||
testDimensions,
|
||||
subplot;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getId", "getModel", "getCapability"]
|
||||
);
|
||||
mockPanZoomStack = jasmine.createSpyObj(
|
||||
"panZoomStack",
|
||||
[
|
||||
"getDepth",
|
||||
"pushPanZoom",
|
||||
"popPanZoom",
|
||||
"setBasePanZoom",
|
||||
"clearPanZoom",
|
||||
"getPanZoom",
|
||||
"getOrigin",
|
||||
"getDimensions"
|
||||
]
|
||||
);
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
"formatter",
|
||||
["formatDomainValue", "formatRangeValue"]
|
||||
);
|
||||
mockElement = jasmine.createSpyObj(
|
||||
"element",
|
||||
["getBoundingClientRect"]
|
||||
);
|
||||
|
||||
testOrigin = [5, 10];
|
||||
testDimensions = [3000, 1000];
|
||||
testDomainObjects = [mockDomainObject, mockDomainObject];
|
||||
|
||||
mockPanZoomStack.getOrigin.andReturn(testOrigin);
|
||||
mockPanZoomStack.getDimensions.andReturn(testDimensions);
|
||||
mockPanZoomStack.getPanZoom.andReturn(
|
||||
{ origin: testOrigin, dimensions: testDimensions }
|
||||
);
|
||||
mockElement.getBoundingClientRect.andReturn(
|
||||
{ left: 10, top: 20, width: 100, height: 100 }
|
||||
);
|
||||
|
||||
subplot = new SubPlot(
|
||||
testDomainObjects,
|
||||
mockPanZoomStack,
|
||||
mockFormatter
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it("provides a getter for its plotted objects", function () {
|
||||
expect(subplot.getTelemetryObjects())
|
||||
.toEqual(testDomainObjects);
|
||||
});
|
||||
|
||||
it("exposes tick marks", function () {
|
||||
// Just test availability; details are tested
|
||||
// in PlotTickFormatter
|
||||
expect(Array.isArray(subplot.getDomainTicks()))
|
||||
.toBeTruthy();
|
||||
expect(Array.isArray(subplot.getRangeTicks()))
|
||||
.toBeTruthy();
|
||||
});
|
||||
|
||||
it("allows hovering state to be tracked", function () {
|
||||
expect(subplot.isHovering()).toBeFalsy();
|
||||
expect(subplot.isHovering(true)).toBeTruthy();
|
||||
expect(subplot.isHovering()).toBeTruthy();
|
||||
expect(subplot.isHovering(false)).toBeFalsy();
|
||||
expect(subplot.isHovering()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("provides hovering coordinates", function () {
|
||||
// Should be empty when not hovering
|
||||
expect(subplot.getHoverCoordinates())
|
||||
.toBeUndefined();
|
||||
|
||||
// Start hovering
|
||||
subplot.hover({ target: mockElement });
|
||||
|
||||
// Should now have coordinates to display
|
||||
expect(subplot.getHoverCoordinates())
|
||||
.toEqual(jasmine.any(String));
|
||||
});
|
||||
|
||||
it("supports marquee zoom", function () {
|
||||
expect(mockPanZoomStack.pushPanZoom).not.toHaveBeenCalled();
|
||||
|
||||
// Simulate a marquee zoom. Note that the mockElement
|
||||
// is 100 by 100 and starts at 10,20
|
||||
subplot.startDrag({
|
||||
target: mockElement,
|
||||
clientX: 60,
|
||||
clientY: 45
|
||||
});
|
||||
subplot.hover({
|
||||
target: mockElement,
|
||||
clientX: 75,
|
||||
clientY: 85
|
||||
});
|
||||
subplot.endDrag({
|
||||
target: mockElement,
|
||||
clientX: 80,
|
||||
clientY: 95
|
||||
});
|
||||
// ... so the origin should be 50%,25% into current dimensions,
|
||||
// and new dimensions should be 20%,50% thereof
|
||||
|
||||
expect(mockPanZoomStack.pushPanZoom).toHaveBeenCalledWith(
|
||||
[
|
||||
testOrigin[0] + testDimensions[0] * 0.50,
|
||||
testOrigin[1] + testDimensions[1] * 0.25
|
||||
],
|
||||
[
|
||||
testDimensions[0] * 0.20,
|
||||
testDimensions[1] * 0.50
|
||||
]
|
||||
);
|
||||
});
|
||||
|
||||
it ("indicates when there is domain data shown", function () {
|
||||
expect(subplot.hasDomainData()).toEqual(true);
|
||||
});
|
||||
|
||||
it ("indicates when there is no domain data shown", function () {
|
||||
mockPanZoomStack.getDimensions.andReturn([0,0]);
|
||||
expect(subplot.hasDomainData()).toEqual(false);
|
||||
});
|
||||
|
||||
it("disallows marquee zoom when start and end Marquee is at the same position", function () {
|
||||
expect(mockPanZoomStack.pushPanZoom).not.toHaveBeenCalled();
|
||||
|
||||
// Simulate a marquee zoom. Note that the mockElement
|
||||
// is 100 by 100 and starts at 10,20
|
||||
subplot.startDrag({
|
||||
target: mockElement,
|
||||
clientX: 60,
|
||||
clientY: 45
|
||||
});
|
||||
subplot.hover({
|
||||
target: mockElement,
|
||||
clientX: 75,
|
||||
clientY: 85
|
||||
});
|
||||
subplot.endDrag({
|
||||
target: mockElement,
|
||||
clientX: 60,
|
||||
clientY: 45
|
||||
});
|
||||
|
||||
expect(mockPanZoomStack.pushPanZoom).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("provides access to a drawable object", function () {
|
||||
expect(typeof subplot.getDrawingObject()).toEqual('object');
|
||||
});
|
||||
|
||||
it("allows a domain offset to be provided", function () {
|
||||
// Domain object is needed to adjust canvas coordinates
|
||||
// to avoid loss-of-precision associated with converting
|
||||
// to 32 bit floats.
|
||||
subplot.setDomainOffset(3);
|
||||
subplot.update();
|
||||
// Should have adjusted the origin accordingly
|
||||
expect(subplot.getDrawingObject().origin[0])
|
||||
.toEqual(2);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,107 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/elements/PlotAxis"],
|
||||
function (PlotAxis) {
|
||||
|
||||
describe("A plot axis", function () {
|
||||
var testMetadatas,
|
||||
testDefault,
|
||||
axis;
|
||||
|
||||
beforeEach(function () {
|
||||
testMetadatas = [
|
||||
{
|
||||
tests: [
|
||||
{ key: "t0", name: "T0" },
|
||||
{ key: "t1", name: "T1" }
|
||||
],
|
||||
someKey: "some value"
|
||||
},
|
||||
{
|
||||
tests: [
|
||||
{ key: "t0", name: "T0" },
|
||||
{ key: "t2", name: "T2" }
|
||||
]
|
||||
},
|
||||
{
|
||||
tests: [
|
||||
{ key: "t3", name: "T3" },
|
||||
{ key: "t4", name: "T4" },
|
||||
{ key: "t5", name: "T5" },
|
||||
{ key: "t6", name: "T6" }
|
||||
]
|
||||
}
|
||||
];
|
||||
testDefault = { key: "test", name: "Test" };
|
||||
axis = new PlotAxis("tests", testMetadatas, testDefault);
|
||||
});
|
||||
|
||||
it("pulls out a list of domain or range options", function () {
|
||||
// Should have filtered out duplicates, etc
|
||||
expect(axis.options).toEqual([
|
||||
{ key: "t0", name: "T0" },
|
||||
{ key: "t1", name: "T1" },
|
||||
{ key: "t2", name: "T2" },
|
||||
{ key: "t3", name: "T3" },
|
||||
{ key: "t4", name: "T4" },
|
||||
{ key: "t5", name: "T5" },
|
||||
{ key: "t6", name: "T6" }
|
||||
]);
|
||||
});
|
||||
|
||||
it("chooses the first option as a default", function () {
|
||||
expect(axis.active).toEqual({ key: "t0", name: "T0" });
|
||||
});
|
||||
|
||||
it("falls back to a provided default if no options are present", function () {
|
||||
expect(new PlotAxis("tests", [{}], testDefault).active)
|
||||
.toEqual(testDefault);
|
||||
});
|
||||
|
||||
it("allows options to be chosen by key", function () {
|
||||
axis.chooseOption("t3");
|
||||
expect(axis.active).toEqual({ key: "t3", name: "T3" });
|
||||
});
|
||||
|
||||
it("reflects changes to applicable metadata", function () {
|
||||
axis.updateMetadata([testMetadatas[1]]);
|
||||
expect(axis.options).toEqual([
|
||||
{ key: "t0", name: "T0" },
|
||||
{ key: "t2", name: "T2" }
|
||||
]);
|
||||
});
|
||||
|
||||
it("returns the same array instance for unchanged metadata", function () {
|
||||
// ...to avoid triggering extra digest cycles.
|
||||
var oldInstance = axis.options;
|
||||
axis.updateMetadata(testMetadatas);
|
||||
expect(axis.options).toBe(oldInstance);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,100 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/elements/PlotLimitTracker"],
|
||||
function (PlotLimitTracker) {
|
||||
|
||||
describe("A plot's limit tracker", function () {
|
||||
var mockHandle,
|
||||
testRange,
|
||||
mockTelemetryObjects,
|
||||
testData,
|
||||
tracker;
|
||||
|
||||
beforeEach(function () {
|
||||
testRange = "some-range";
|
||||
testData = {};
|
||||
mockHandle = jasmine.createSpyObj(
|
||||
'handle',
|
||||
['getTelemetryObjects', 'getDatum']
|
||||
);
|
||||
mockTelemetryObjects = ['a', 'b', 'c'].map(function (id, i) {
|
||||
var mockTelemetryObject = jasmine.createSpyObj(
|
||||
'object-' + id,
|
||||
['getId', 'getCapability', 'getModel']
|
||||
),
|
||||
mockLimitCapability = jasmine.createSpyObj(
|
||||
'limit-' + id,
|
||||
['evaluate']
|
||||
);
|
||||
testData[id] = { id: id, value: i };
|
||||
mockTelemetryObject.getId.andReturn(id);
|
||||
mockTelemetryObject.getCapability.andCallFake(function (key) {
|
||||
return key === 'limit' && mockLimitCapability;
|
||||
});
|
||||
mockLimitCapability.evaluate
|
||||
.andReturn({ cssClass: 'alarm-' + id});
|
||||
return mockTelemetryObject;
|
||||
});
|
||||
mockHandle.getTelemetryObjects.andReturn(mockTelemetryObjects);
|
||||
mockHandle.getDatum.andCallFake(function (telemetryObject) {
|
||||
return testData[telemetryObject.getId()];
|
||||
});
|
||||
|
||||
tracker = new PlotLimitTracker(mockHandle, testRange);
|
||||
});
|
||||
|
||||
it("initially provides no limit state", function () {
|
||||
mockTelemetryObjects.forEach(function (mockTelemetryObject) {
|
||||
expect(tracker.getLegendClass(mockTelemetryObject))
|
||||
.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when asked to update", function () {
|
||||
beforeEach(function () {
|
||||
tracker.update();
|
||||
});
|
||||
|
||||
it("evaluates limits using the limit capability", function () {
|
||||
mockTelemetryObjects.forEach(function (mockTelemetryObject) {
|
||||
var id = mockTelemetryObject.getId(),
|
||||
mockLimit =
|
||||
mockTelemetryObject.getCapability('limit');
|
||||
expect(mockLimit.evaluate)
|
||||
.toHaveBeenCalledWith(testData[id], testRange);
|
||||
});
|
||||
});
|
||||
|
||||
it("exposes legend classes returned by the limit capability", function () {
|
||||
mockTelemetryObjects.forEach(function (mockTelemetryObject) {
|
||||
var id = mockTelemetryObject.getId();
|
||||
expect(tracker.getLegendClass(mockTelemetryObject))
|
||||
.toEqual('alarm-' + id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,167 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/elements/PlotLineBuffer"],
|
||||
function (PlotLineBuffer) {
|
||||
|
||||
var TEST_INITIAL_SIZE = 10,
|
||||
TEST_MAX_SIZE = 40,
|
||||
TEST_DOMAIN_OFFSET = 42;
|
||||
|
||||
describe("A plot line buffer", function () {
|
||||
var mockSeries,
|
||||
testDomainValues,
|
||||
testRangeValues,
|
||||
buffer;
|
||||
|
||||
beforeEach(function () {
|
||||
testDomainValues = [1, 3, 7, 9, 14, 15];
|
||||
testRangeValues = [8, 0, 3, 9, 8, 11];
|
||||
mockSeries = jasmine.createSpyObj(
|
||||
"series",
|
||||
['getPointCount', 'getDomainValue', 'getRangeValue']
|
||||
);
|
||||
mockSeries.getPointCount.andCallFake(function () {
|
||||
return testDomainValues.length;
|
||||
});
|
||||
mockSeries.getDomainValue.andCallFake(function (i) {
|
||||
return testDomainValues[i];
|
||||
});
|
||||
mockSeries.getRangeValue.andCallFake(function (i) {
|
||||
return testRangeValues[i];
|
||||
});
|
||||
|
||||
buffer = new PlotLineBuffer(
|
||||
TEST_DOMAIN_OFFSET,
|
||||
TEST_INITIAL_SIZE,
|
||||
TEST_MAX_SIZE
|
||||
);
|
||||
|
||||
// Start with some data in there
|
||||
buffer.insert(mockSeries, 0);
|
||||
});
|
||||
|
||||
it("allows insertion of series data", function () {
|
||||
// Convert to a regular array for checking.
|
||||
// Verify that domain/ranges were interleaved and
|
||||
// that domain offset was adjusted for.
|
||||
expect(
|
||||
Array.prototype.slice.call(buffer.getBuffer()).slice(0, 12)
|
||||
).toEqual([-41, 8, -39, 0, -35, 3, -33, 9, -28, 8, -27, 11]);
|
||||
expect(buffer.getLength()).toEqual(6);
|
||||
});
|
||||
|
||||
it("finds insertion indexes", function () {
|
||||
expect(buffer.findInsertionIndex(0)).toEqual(0);
|
||||
expect(buffer.findInsertionIndex(2)).toEqual(1);
|
||||
expect(buffer.findInsertionIndex(5)).toEqual(2);
|
||||
expect(buffer.findInsertionIndex(10)).toEqual(4);
|
||||
expect(buffer.findInsertionIndex(14.5)).toEqual(5);
|
||||
expect(buffer.findInsertionIndex(20)).toEqual(6);
|
||||
});
|
||||
|
||||
it("allows insertion in the middle", function () {
|
||||
var head = [-41, 8, -39, 0, -35, 3],
|
||||
tail = [-33, 9, -28, 8, -27, 11];
|
||||
buffer.insert(mockSeries, 3);
|
||||
expect(
|
||||
Array.prototype.slice.call(buffer.getBuffer()).slice(0, 24)
|
||||
).toEqual(head.concat(head).concat(tail).concat(tail));
|
||||
expect(buffer.getLength()).toEqual(12);
|
||||
});
|
||||
|
||||
it("allows values to be trimmed from the start", function () {
|
||||
buffer.trim(2);
|
||||
expect(buffer.getLength()).toEqual(4);
|
||||
expect(
|
||||
Array.prototype.slice.call(buffer.getBuffer()).slice(0, 8)
|
||||
).toEqual([-35, 3, -33, 9, -28, 8, -27, 11]);
|
||||
});
|
||||
|
||||
it("expands buffer when needed to accommodate more data", function () {
|
||||
var i;
|
||||
|
||||
// Initial underlying buffer should be twice initial size...
|
||||
// (Since each pair will take up two elements)
|
||||
expect(buffer.getBuffer().length).toEqual(20);
|
||||
|
||||
// Should be able to insert 6 series of 6 points each
|
||||
// (After that, we'll hit the test max of 40)
|
||||
for (i = 1; i < 15; i += 1) {
|
||||
expect(buffer.insertPoint(i * 10, Math.sin(i), i))
|
||||
.toBeTruthy();
|
||||
}
|
||||
|
||||
// Buffer should have expanded in the process
|
||||
expect(buffer.getBuffer().length).toEqual(40);
|
||||
|
||||
// Push to maximum size just to make sure...
|
||||
for (i = 1; i < 150; i += 1) {
|
||||
buffer.insertPoint(i * 10, Math.sin(i), i);
|
||||
}
|
||||
|
||||
expect(buffer.getBuffer().length).toEqual(80);
|
||||
});
|
||||
|
||||
it("ensures a maximum size", function () {
|
||||
var i;
|
||||
|
||||
// Should be able to insert 6 series of 6 points each
|
||||
// (After that, we'll hit the test max of 40)
|
||||
for (i = 1; i < 6; i += 1) {
|
||||
expect(buffer.getLength()).toEqual(6 * i);
|
||||
expect(buffer.insert(mockSeries, Number.POSITIVE_INFINITY))
|
||||
.toBeTruthy();
|
||||
}
|
||||
|
||||
// Should be maxed out now
|
||||
expect(buffer.getLength()).toEqual(36);
|
||||
expect(buffer.insert(mockSeries, Number.POSITIVE_INFINITY))
|
||||
.toBeFalsy();
|
||||
expect(buffer.getLength()).toEqual(36);
|
||||
|
||||
});
|
||||
|
||||
it("reduces buffer size when space is no longer needed", function () {
|
||||
// Check that actual buffer is sized to the initial size
|
||||
// (double TEST_INITIAL_SIZE, since two elements are needed per
|
||||
// point; one for domain, one for range)
|
||||
expect(buffer.getBuffer().length).toEqual(20);
|
||||
// Should have 6 elements now... grow to 24
|
||||
buffer.insert(mockSeries, Number.POSITIVE_INFINITY);
|
||||
buffer.insert(mockSeries, Number.POSITIVE_INFINITY);
|
||||
buffer.insert(mockSeries, Number.POSITIVE_INFINITY);
|
||||
// This should have doubled the actual buffer size
|
||||
expect(buffer.getBuffer().length).toEqual(80);
|
||||
// Remove some values
|
||||
buffer.trim(20);
|
||||
// Actual buffer size should have been reduced accordingly
|
||||
expect(buffer.getBuffer().length).toBeLessThan(80);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,133 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/elements/PlotLine"],
|
||||
function (PlotLine) {
|
||||
|
||||
describe("A plot line", function () {
|
||||
var mockBuffer,
|
||||
mockSeries,
|
||||
testDomainBuffer,
|
||||
testRangeBuffer,
|
||||
testSeries,
|
||||
line;
|
||||
|
||||
beforeEach(function () {
|
||||
testDomainBuffer = [];
|
||||
testRangeBuffer = [];
|
||||
testSeries = [];
|
||||
|
||||
mockBuffer = jasmine.createSpyObj(
|
||||
'buffer',
|
||||
['findInsertionIndex', 'insert', 'insertPoint', 'trim']
|
||||
);
|
||||
mockSeries = jasmine.createSpyObj(
|
||||
'series',
|
||||
['getPointCount', 'getDomainValue', 'getRangeValue']
|
||||
);
|
||||
|
||||
mockSeries.getPointCount.andCallFake(function () {
|
||||
return testSeries.length;
|
||||
});
|
||||
mockSeries.getDomainValue.andCallFake(function (i) {
|
||||
return (testSeries[i] || [])[0];
|
||||
});
|
||||
mockSeries.getRangeValue.andCallFake(function (i) {
|
||||
return (testSeries[i] || [])[1];
|
||||
});
|
||||
|
||||
// Function like PlotLineBuffer, to aid in testability
|
||||
mockBuffer.findInsertionIndex.andCallFake(function (v) {
|
||||
var index = 0;
|
||||
if (testDomainBuffer.indexOf(v) !== -1) {
|
||||
return -1;
|
||||
}
|
||||
while ((index < testDomainBuffer.length) &&
|
||||
(testDomainBuffer[index] < v)) {
|
||||
index += 1;
|
||||
}
|
||||
return index;
|
||||
});
|
||||
mockBuffer.insert.andCallFake(function (series, index) {
|
||||
var domains = [], ranges = [], i;
|
||||
for (i = 0; i < series.getPointCount(); i += 1) {
|
||||
domains.push(series.getDomainValue(i));
|
||||
ranges.push(series.getRangeValue(i));
|
||||
}
|
||||
testDomainBuffer = testDomainBuffer.slice(0, index)
|
||||
.concat(domains)
|
||||
.concat(testDomainBuffer.slice(index));
|
||||
testRangeBuffer = testRangeBuffer.slice(0, index)
|
||||
.concat(ranges)
|
||||
.concat(testRangeBuffer.slice(index));
|
||||
return true;
|
||||
});
|
||||
mockBuffer.insertPoint.andCallFake(function (dv, rv, index) {
|
||||
testDomainBuffer.splice(index, 0, dv);
|
||||
testRangeBuffer.splice(index, 0, rv);
|
||||
return true;
|
||||
});
|
||||
|
||||
line = new PlotLine(mockBuffer);
|
||||
});
|
||||
|
||||
it("allows single point insertion", function () {
|
||||
line.addPoint(100, 200);
|
||||
line.addPoint(50, 42);
|
||||
line.addPoint(150, 12321);
|
||||
// Should have managed insertion index choices to get to...
|
||||
expect(testDomainBuffer).toEqual([50, 100, 150]);
|
||||
expect(testRangeBuffer).toEqual([42, 200, 12321]);
|
||||
});
|
||||
|
||||
it("allows series insertion", function () {
|
||||
testSeries = [[50, 42], [100, 200], [150, 12321]];
|
||||
line.addSeries(mockSeries);
|
||||
// Should have managed insertion index choices to get to...
|
||||
expect(testDomainBuffer).toEqual([50, 100, 150]);
|
||||
expect(testRangeBuffer).toEqual([42, 200, 12321]);
|
||||
});
|
||||
|
||||
it("splits series insertion when necessary", function () {
|
||||
testSeries = [[50, 42], [100, 200], [150, 12321]];
|
||||
line.addPoint(75, 1);
|
||||
line.addSeries(mockSeries);
|
||||
// Should have managed insertion index choices to get to...
|
||||
expect(testDomainBuffer).toEqual([50, 75, 100, 150]);
|
||||
expect(testRangeBuffer).toEqual([42, 1, 200, 12321]);
|
||||
});
|
||||
|
||||
it("attempts to remove points when insertion fails", function () {
|
||||
// Verify precondition - normally doesn't try to trim
|
||||
line.addPoint(1, 2);
|
||||
expect(mockBuffer.trim).not.toHaveBeenCalled();
|
||||
|
||||
// But if insertPoint fails, it should trim
|
||||
mockBuffer.insertPoint.andReturn(false);
|
||||
line.addPoint(2, 3);
|
||||
expect(mockBuffer.trim).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,123 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/elements/PlotPalette"],
|
||||
function (PlotPalette) {
|
||||
|
||||
describe("The plot palette", function () {
|
||||
it("can be used as a constructor", function () {
|
||||
// PlotPalette has all static methods, so make
|
||||
// sure it returns itself if used as a constructor.
|
||||
expect(new PlotPalette()).toBe(PlotPalette);
|
||||
});
|
||||
|
||||
it("has 30 unique colors in an integer format", function () {
|
||||
// Integer format may be useful internal to the application.
|
||||
// RGB 0-255
|
||||
var i, j;
|
||||
|
||||
// Used to verify one of R, G, B in loop below
|
||||
function verifyChannel(c) {
|
||||
expect(typeof c).toEqual("number");
|
||||
expect(c <= 255).toBeTruthy();
|
||||
expect(c >= 0).toBeTruthy();
|
||||
}
|
||||
|
||||
for (i = 0; i < 30; i += 1) {
|
||||
// Verify that we got an array of numbers
|
||||
expect(Array.isArray(PlotPalette.getIntegerColor(i)))
|
||||
.toBeTruthy();
|
||||
expect(PlotPalette.getIntegerColor(i).length).toEqual(3);
|
||||
|
||||
// Verify all three channels for type and range
|
||||
PlotPalette.getIntegerColor(i).forEach(verifyChannel);
|
||||
|
||||
// Verify uniqueness
|
||||
for (j = i + 1; j < 30; j += 1) {
|
||||
expect(PlotPalette.getIntegerColor(i)).not.toEqual(
|
||||
PlotPalette.getIntegerColor(j)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
it("has 30 unique colors in a floating-point format", function () {
|
||||
// Float format is useful to WebGL.
|
||||
// RGB 0.0-1.1
|
||||
var i, j;
|
||||
|
||||
// Used to verify one of R, G, B in loop below
|
||||
function verifyChannel(c) {
|
||||
expect(typeof c).toEqual("number");
|
||||
expect(c <= 1.0).toBeTruthy();
|
||||
expect(c >= 0.0).toBeTruthy();
|
||||
}
|
||||
|
||||
for (i = 0; i < 30; i += 1) {
|
||||
// Verify that we got an array of numbers
|
||||
expect(Array.isArray(PlotPalette.getFloatColor(i)))
|
||||
.toBeTruthy();
|
||||
expect(PlotPalette.getFloatColor(i).length).toEqual(4);
|
||||
|
||||
// Verify all three channels for type and range
|
||||
PlotPalette.getFloatColor(i).forEach(verifyChannel);
|
||||
|
||||
// Verify uniqueness
|
||||
for (j = i + 1; j < 30; j += 1) {
|
||||
expect(PlotPalette.getFloatColor(i)).not.toEqual(
|
||||
PlotPalette.getFloatColor(j)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
it("has 30 unique colors in a string format", function () {
|
||||
// String format is useful in stylesheets
|
||||
// #RRGGBB in hex
|
||||
var i, j, c;
|
||||
|
||||
|
||||
for (i = 0; i < 30; i += 1) {
|
||||
c = PlotPalette.getStringColor(i);
|
||||
|
||||
// Verify that we #-style color strings
|
||||
expect(typeof c).toEqual('string');
|
||||
expect(c.length).toEqual(7);
|
||||
expect(/^#[0-9a-fA-F]+$/.test(c)).toBeTruthy();
|
||||
|
||||
// Verify uniqueness
|
||||
for (j = i + 1; j < 30; j += 1) {
|
||||
expect(PlotPalette.getStringColor(i)).not.toEqual(
|
||||
PlotPalette.getStringColor(j)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,126 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/elements/PlotPanZoomStackGroup"],
|
||||
function (PlotPanZoomStackGroup) {
|
||||
|
||||
var COUNT = 8;
|
||||
|
||||
describe("A plot pan-zoom stack group", function () {
|
||||
var stacks,
|
||||
group;
|
||||
|
||||
beforeEach(function () {
|
||||
group = new PlotPanZoomStackGroup(COUNT);
|
||||
stacks = [];
|
||||
while (stacks.length < COUNT) {
|
||||
stacks.push(group.getPanZoomStack(stacks.length));
|
||||
}
|
||||
});
|
||||
|
||||
it("creates a number of separate stacks", function () {
|
||||
expect(group.getPanZoomStack(0)).toBeDefined();
|
||||
expect(group.getPanZoomStack(COUNT - 1)).toBeDefined();
|
||||
expect(group.getPanZoomStack(COUNT)).toBeUndefined();
|
||||
});
|
||||
|
||||
it("synchronizes pan-zoom stack depth", function () {
|
||||
expect(group.getDepth()).toEqual(1);
|
||||
group.getPanZoomStack(1).pushPanZoom([10, 20], [30, 40]);
|
||||
stacks.forEach(function (stack) {
|
||||
expect(stack.getDepth()).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
it("synchronizes domain but not range", function () {
|
||||
// Set up different initial states
|
||||
stacks.forEach(function (stack, i) {
|
||||
stack.pushPanZoom([i, i], [i, i]);
|
||||
});
|
||||
|
||||
// Push a new pan-zoom state onto one of the stacks
|
||||
group.getPanZoomStack(1).pushPanZoom([99, 99], [42, 42]);
|
||||
|
||||
// Should changed domain values for all stacks, but
|
||||
// only changed range values for stack 1
|
||||
stacks.forEach(function (stack, i) {
|
||||
expect(stack.getOrigin())
|
||||
.toEqual([99, i === 1 ? 99 : i]);
|
||||
expect(stack.getDimensions())
|
||||
.toEqual([42, i === 1 ? 42 : i]);
|
||||
});
|
||||
});
|
||||
|
||||
it("synchronizes base pan-zoom", function () {
|
||||
group.setBasePanZoom([10, 9], [8, 7]);
|
||||
stacks.forEach(function (stack) {
|
||||
expect(stack.getOrigin()).toEqual([10, 9]);
|
||||
expect(stack.getDimensions()).toEqual([8, 7]);
|
||||
});
|
||||
});
|
||||
|
||||
it("clears pan-zoom on request", function () {
|
||||
// Set up different initial states
|
||||
stacks.forEach(function (stack, i) {
|
||||
stack.pushPanZoom([i, i], [i, i]);
|
||||
});
|
||||
|
||||
// Verify that we have a greater depth
|
||||
expect(group.getDepth() > 1).toBeTruthy();
|
||||
|
||||
// Clear the pan-zoom state
|
||||
group.clearPanZoom();
|
||||
|
||||
// Should be back down to our initial state
|
||||
expect(group.getDepth()).toEqual(1);
|
||||
stacks.forEach(function (stack) {
|
||||
expect(stack.getDepth()).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
it("pops pan-zoom on request", function () {
|
||||
// Set up different initial states
|
||||
stacks.forEach(function (stack, i) {
|
||||
stack.pushPanZoom([i, i], [i, i]);
|
||||
});
|
||||
|
||||
// Verify that we have a greater depth
|
||||
expect(group.getDepth()).toEqual(COUNT + 1);
|
||||
|
||||
// Clear the pan-zoom state
|
||||
group.popPanZoom();
|
||||
|
||||
// Should be back down to our initial state
|
||||
expect(group.getDepth()).toEqual(COUNT);
|
||||
stacks.forEach(function (stack) {
|
||||
expect(stack.getDepth()).toEqual(COUNT);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,99 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/elements/PlotPanZoomStack"],
|
||||
function (PlotPanZoomStack) {
|
||||
|
||||
describe("A plot pan-zoom stack", function () {
|
||||
var panZoomStack,
|
||||
initialOrigin,
|
||||
initialDimensions,
|
||||
otherOrigins,
|
||||
otherDimensions;
|
||||
|
||||
// Shorthand for verifying getOrigin, getDimensions, and getPanZoom,
|
||||
// which should always agree.
|
||||
function verifyPanZoom(origin, dimensions) {
|
||||
expect(panZoomStack.getOrigin()).toEqual(origin);
|
||||
expect(panZoomStack.getDimensions()).toEqual(dimensions);
|
||||
expect(panZoomStack.getPanZoom()).toEqual({
|
||||
origin: origin,
|
||||
dimensions: dimensions
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
initialOrigin = [4, 2];
|
||||
initialDimensions = [600, 400];
|
||||
otherOrigins = [[8, 6], [12, 9]];
|
||||
otherDimensions = [[400, 300], [200, 300]];
|
||||
panZoomStack =
|
||||
new PlotPanZoomStack(initialOrigin, initialDimensions);
|
||||
});
|
||||
|
||||
it("starts off reporting its initial values", function () {
|
||||
verifyPanZoom(initialOrigin, initialDimensions);
|
||||
});
|
||||
|
||||
it("allows origin/dimensions pairs to be pushed/popped", function () {
|
||||
panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
|
||||
verifyPanZoom(otherOrigins[0], otherDimensions[0]);
|
||||
panZoomStack.pushPanZoom(otherOrigins[1], otherDimensions[1]);
|
||||
verifyPanZoom(otherOrigins[1], otherDimensions[1]);
|
||||
panZoomStack.popPanZoom();
|
||||
verifyPanZoom(otherOrigins[0], otherDimensions[0]);
|
||||
panZoomStack.popPanZoom();
|
||||
verifyPanZoom(initialOrigin, initialDimensions);
|
||||
});
|
||||
|
||||
it("reports current stack depth", function () {
|
||||
expect(panZoomStack.getDepth()).toEqual(1);
|
||||
panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
|
||||
expect(panZoomStack.getDepth()).toEqual(2);
|
||||
panZoomStack.pushPanZoom(otherOrigins[1], otherDimensions[1]);
|
||||
expect(panZoomStack.getDepth()).toEqual(3);
|
||||
});
|
||||
|
||||
it("allows base pan zoom to be restored", function () {
|
||||
panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
|
||||
panZoomStack.pushPanZoom(otherOrigins[1], otherDimensions[1]);
|
||||
panZoomStack.clearPanZoom();
|
||||
verifyPanZoom(initialOrigin, initialDimensions);
|
||||
});
|
||||
|
||||
it("allows base pan zoom to be changed", function () {
|
||||
panZoomStack.pushPanZoom(otherOrigins[0], otherDimensions[0]);
|
||||
panZoomStack.setBasePanZoom(otherOrigins[1], otherDimensions[1]);
|
||||
// Should not have changed current top-of-stack
|
||||
verifyPanZoom(otherOrigins[0], otherDimensions[0]);
|
||||
|
||||
// Clear the stack - should be at our new base pan-zoom state
|
||||
panZoomStack.clearPanZoom();
|
||||
verifyPanZoom(otherOrigins[1], otherDimensions[1]);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,67 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/elements/PlotPosition"],
|
||||
function (PlotPosition) {
|
||||
|
||||
describe("A plot position", function () {
|
||||
var mockPanZoom,
|
||||
testOrigin = [10, 20],
|
||||
testDimensions = [800, 10];
|
||||
|
||||
beforeEach(function () {
|
||||
mockPanZoom = jasmine.createSpyObj(
|
||||
"panZoomStack",
|
||||
["getPanZoom"]
|
||||
);
|
||||
mockPanZoom.getPanZoom.andReturn({
|
||||
origin: testOrigin,
|
||||
dimensions: testDimensions
|
||||
});
|
||||
});
|
||||
|
||||
it("transforms pixel coordinates to domain-range", function () {
|
||||
var position = new PlotPosition(42, 450, 100, 1000, mockPanZoom);
|
||||
// Domain: .42 * 800 + 10 = 346
|
||||
// Range: .55 * 10 + 20 = 25.5
|
||||
// Notably, y-axis is reversed between pixel space and range
|
||||
expect(position.getPosition()).toEqual([346, 25.5]);
|
||||
expect(position.getDomain()).toEqual(346);
|
||||
expect(position.getRange()).toEqual(25.5);
|
||||
});
|
||||
|
||||
it("treats a position as undefined if no pan-zoom state is present", function () {
|
||||
var position;
|
||||
|
||||
mockPanZoom.getPanZoom.andReturn({});
|
||||
position = new PlotPosition(1, 2, 100, 100, mockPanZoom);
|
||||
expect(position.getDomain()).toBeUndefined();
|
||||
expect(position.getRange()).toBeUndefined();
|
||||
expect(position.getPosition()).toEqual([]);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,93 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/elements/PlotPreparer"],
|
||||
function (PlotPreparer) {
|
||||
|
||||
var START = 123456;
|
||||
|
||||
describe("A plot preparer", function () {
|
||||
|
||||
function makeMockData(scale) {
|
||||
var mockData = jasmine.createSpyObj(
|
||||
"data" + scale,
|
||||
["getPointCount", "getDomainValue", "getRangeValue"]
|
||||
);
|
||||
mockData.getPointCount.andReturn(1000);
|
||||
mockData.getDomainValue.andCallFake(function (i) {
|
||||
return START + i * 1000;
|
||||
});
|
||||
mockData.getRangeValue.andCallFake(function (i) {
|
||||
return Math.sin(i / 100) * scale;
|
||||
});
|
||||
return mockData;
|
||||
}
|
||||
|
||||
it("fits to provided data sets", function () {
|
||||
var datas = [1, 2, 3].map(makeMockData),
|
||||
preparer = new PlotPreparer(datas);
|
||||
|
||||
expect(preparer.getDomainOffset()).toEqual(START);
|
||||
expect(preparer.getOrigin()[0]).toBeCloseTo(START, 3);
|
||||
expect(preparer.getOrigin()[1]).toBeCloseTo(-3, 3);
|
||||
expect(preparer.getDimensions()[0]).toBeCloseTo(999000, 3);
|
||||
expect(preparer.getDimensions()[1]).toBeCloseTo(6, 3);
|
||||
});
|
||||
|
||||
it("looks up values using a specified domain and range", function () {
|
||||
var datas = [makeMockData(1)],
|
||||
preparer = new PlotPreparer(datas, "testDomain", "testRange");
|
||||
|
||||
expect(preparer).toBeDefined();
|
||||
|
||||
expect(datas[0].getDomainValue).toHaveBeenCalledWith(
|
||||
jasmine.any(Number),
|
||||
"testDomain"
|
||||
);
|
||||
|
||||
expect(datas[0].getRangeValue).toHaveBeenCalledWith(
|
||||
jasmine.any(Number),
|
||||
"testRange"
|
||||
);
|
||||
});
|
||||
|
||||
it("provides a default range if data set is flat", function () {
|
||||
var datas = [makeMockData(0)],
|
||||
preparer = new PlotPreparer(datas);
|
||||
|
||||
expect(preparer.getDimensions[1]).not.toEqual(0);
|
||||
});
|
||||
|
||||
it("provides buffers", function () {
|
||||
var datas = [makeMockData(0)],
|
||||
preparer = new PlotPreparer(datas);
|
||||
expect(preparer.getBuffers()[0] instanceof Float32Array)
|
||||
.toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,93 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/elements/PlotSeriesWindow"],
|
||||
function (PlotSeriesWindow) {
|
||||
|
||||
describe("A plot's window on a telemetry series", function () {
|
||||
var mockSeries,
|
||||
testSeries,
|
||||
window;
|
||||
|
||||
beforeEach(function () {
|
||||
testSeries = [
|
||||
[0, 42],
|
||||
[10, 1],
|
||||
[20, 4],
|
||||
[30, 9],
|
||||
[40, 3]
|
||||
];
|
||||
|
||||
mockSeries = jasmine.createSpyObj(
|
||||
'series',
|
||||
['getPointCount', 'getDomainValue', 'getRangeValue']
|
||||
);
|
||||
|
||||
mockSeries.getPointCount.andCallFake(function () {
|
||||
return testSeries.length;
|
||||
});
|
||||
mockSeries.getDomainValue.andCallFake(function (i) {
|
||||
return testSeries[i][0];
|
||||
});
|
||||
mockSeries.getRangeValue.andCallFake(function (i) {
|
||||
return testSeries[i][1];
|
||||
});
|
||||
|
||||
window = new PlotSeriesWindow(
|
||||
mockSeries,
|
||||
"testDomain",
|
||||
"testRange",
|
||||
1,
|
||||
testSeries.length
|
||||
);
|
||||
});
|
||||
|
||||
it("provides a window upon a data series", function () {
|
||||
expect(window.getPointCount()).toEqual(4);
|
||||
expect(window.getDomainValue(0)).toEqual(10);
|
||||
expect(window.getRangeValue(0)).toEqual(1);
|
||||
});
|
||||
|
||||
it("looks up using specific domain/range keys", function () {
|
||||
window.getDomainValue(0);
|
||||
window.getRangeValue(0);
|
||||
expect(mockSeries.getDomainValue)
|
||||
.toHaveBeenCalledWith(1, 'testDomain');
|
||||
expect(mockSeries.getRangeValue)
|
||||
.toHaveBeenCalledWith(1, 'testRange');
|
||||
});
|
||||
|
||||
it("can be split into smaller windows", function () {
|
||||
var windows = window.split();
|
||||
expect(windows.length).toEqual(2);
|
||||
expect(windows[0].getPointCount()).toEqual(2);
|
||||
expect(windows[1].getPointCount()).toEqual(2);
|
||||
expect(windows[0].getDomainValue(0)).toEqual(10);
|
||||
expect(windows[1].getDomainValue(0)).toEqual(30);
|
||||
expect(windows[0].getRangeValue(0)).toEqual(1);
|
||||
expect(windows[1].getRangeValue(0)).toEqual(9);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,71 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/elements/PlotTelemetryFormatter"],
|
||||
function (PlotTelemetryFormatter) {
|
||||
|
||||
describe("The PlotTelemetryFormatter", function () {
|
||||
var mockFormatter,
|
||||
formatter;
|
||||
|
||||
beforeEach(function () {
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
'telemetryFormatter',
|
||||
['formatDomainValue', 'formatRangeValue']
|
||||
);
|
||||
formatter = new PlotTelemetryFormatter(mockFormatter);
|
||||
});
|
||||
|
||||
describe("using domain & range format keys", function () {
|
||||
var rangeFormat = "someRangeFormat",
|
||||
domainFormat = "someDomainFormat";
|
||||
|
||||
beforeEach(function () {
|
||||
formatter.setRangeFormat(rangeFormat);
|
||||
formatter.setDomainFormat(domainFormat);
|
||||
});
|
||||
|
||||
it("includes format in formatDomainValue calls", function () {
|
||||
mockFormatter.formatDomainValue.andReturn("formatted!");
|
||||
expect(formatter.formatDomainValue(12321))
|
||||
.toEqual("formatted!");
|
||||
expect(mockFormatter.formatDomainValue)
|
||||
.toHaveBeenCalledWith(12321, domainFormat);
|
||||
});
|
||||
|
||||
it("includes format in formatRangeValue calls for strings", function () {
|
||||
mockFormatter.formatRangeValue.andReturn("formatted!");
|
||||
expect(formatter.formatRangeValue('foo'))
|
||||
.toEqual("formatted!");
|
||||
expect(mockFormatter.formatRangeValue)
|
||||
.toHaveBeenCalledWith('foo', rangeFormat);
|
||||
});
|
||||
|
||||
it("formats numeric values with three fixed digits", function () {
|
||||
expect(formatter.formatRangeValue(10)).toEqual("10.000");
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,73 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/elements/PlotTickGenerator"],
|
||||
function (PlotTickGenerator) {
|
||||
|
||||
describe("A plot tick generator", function () {
|
||||
var mockPanZoomStack,
|
||||
mockFormatter,
|
||||
generator;
|
||||
|
||||
beforeEach(function () {
|
||||
mockPanZoomStack = jasmine.createSpyObj(
|
||||
"panZoomStack",
|
||||
["getPanZoom"]
|
||||
);
|
||||
mockFormatter = jasmine.createSpyObj(
|
||||
"formatter",
|
||||
["formatDomainValue", "formatRangeValue"]
|
||||
);
|
||||
|
||||
mockPanZoomStack.getPanZoom.andReturn({
|
||||
origin: [0, 0],
|
||||
dimensions: [100, 100]
|
||||
});
|
||||
|
||||
generator =
|
||||
new PlotTickGenerator(mockPanZoomStack, mockFormatter);
|
||||
});
|
||||
|
||||
it("provides tick marks for range", function () {
|
||||
expect(generator.generateRangeTicks(11).length).toEqual(11);
|
||||
|
||||
// Should have used range formatter
|
||||
expect(mockFormatter.formatRangeValue).toHaveBeenCalled();
|
||||
expect(mockFormatter.formatDomainValue).not.toHaveBeenCalled();
|
||||
|
||||
});
|
||||
|
||||
it("provides tick marks for domain", function () {
|
||||
expect(generator.generateDomainTicks(11).length).toEqual(11);
|
||||
|
||||
// Should have used domain formatter
|
||||
expect(mockFormatter.formatRangeValue).not.toHaveBeenCalled();
|
||||
expect(mockFormatter.formatDomainValue).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,237 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/elements/PlotUpdater"],
|
||||
function (PlotUpdater) {
|
||||
|
||||
describe("A plot updater", function () {
|
||||
var mockSubscription,
|
||||
testDomain,
|
||||
testRange,
|
||||
testDomainValues,
|
||||
testRangeValues,
|
||||
mockSeries,
|
||||
updater;
|
||||
|
||||
function makeMockDomainObject(id) {
|
||||
var mockDomainObject = jasmine.createSpyObj(
|
||||
"object-" + id,
|
||||
["getId", "getCapability", "getModel"]
|
||||
);
|
||||
mockDomainObject.getId.andReturn(id);
|
||||
return mockDomainObject;
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
var ids = ['a', 'b', 'c'],
|
||||
mockObjects = ids.map(makeMockDomainObject);
|
||||
|
||||
mockSubscription = jasmine.createSpyObj(
|
||||
"subscription",
|
||||
["getDomainValue", "getRangeValue", "getTelemetryObjects"]
|
||||
);
|
||||
mockSeries = jasmine.createSpyObj(
|
||||
'series',
|
||||
['getPointCount', 'getDomainValue', 'getRangeValue']
|
||||
);
|
||||
testDomain = "testDomain";
|
||||
testRange = "testRange";
|
||||
testDomainValues = { a: 3, b: 7, c: 13 };
|
||||
testRangeValues = { a: 123, b: 456, c: 789 };
|
||||
|
||||
mockSubscription.getTelemetryObjects.andReturn(mockObjects);
|
||||
mockSubscription.getDomainValue.andCallFake(function (mockObject) {
|
||||
return testDomainValues[mockObject.getId()];
|
||||
});
|
||||
mockSubscription.getRangeValue.andCallFake(function (mockObject) {
|
||||
return testRangeValues[mockObject.getId()];
|
||||
});
|
||||
|
||||
updater = new PlotUpdater(
|
||||
mockSubscription,
|
||||
testDomain,
|
||||
testRange,
|
||||
1350 // Smaller max size for easier testing
|
||||
);
|
||||
});
|
||||
|
||||
it("provides one buffer per telemetry object", function () {
|
||||
expect(updater.getLineBuffers().length).toEqual(3);
|
||||
});
|
||||
|
||||
it("changes buffer count if telemetry object counts change", function () {
|
||||
mockSubscription.getTelemetryObjects
|
||||
.andReturn([makeMockDomainObject('a')]);
|
||||
updater.update();
|
||||
expect(updater.getLineBuffers().length).toEqual(1);
|
||||
});
|
||||
|
||||
it("can handle delayed telemetry object availability", function () {
|
||||
// The case can occur where getTelemetryObjects() returns an
|
||||
// empty array - specifically, while objects are still being
|
||||
// loaded. The updater needs to be able to cope with that
|
||||
// case.
|
||||
var tmp = mockSubscription.getTelemetryObjects();
|
||||
mockSubscription.getTelemetryObjects.andReturn([]);
|
||||
|
||||
// Reinstantiate with the empty subscription
|
||||
updater = new PlotUpdater(
|
||||
mockSubscription,
|
||||
testDomain,
|
||||
testRange
|
||||
);
|
||||
|
||||
// Should have 0 buffers for 0 objects
|
||||
expect(updater.getLineBuffers().length).toEqual(0);
|
||||
|
||||
// Restore the three objects the test subscription would
|
||||
// normally have.
|
||||
mockSubscription.getTelemetryObjects.andReturn(tmp);
|
||||
updater.update();
|
||||
|
||||
// Should have 3 buffers for 3 objects
|
||||
expect(updater.getLineBuffers().length).toEqual(3);
|
||||
});
|
||||
|
||||
it("accepts historical telemetry updates", function () {
|
||||
var mockObject = mockSubscription.getTelemetryObjects()[0];
|
||||
|
||||
mockSeries.getPointCount.andReturn(3);
|
||||
mockSeries.getDomainValue.andCallFake(function (i) {
|
||||
return 1000 + i * 1000;
|
||||
});
|
||||
mockSeries.getRangeValue.andReturn(10);
|
||||
|
||||
// PlotLine & PlotLineBuffer are tested for most of the
|
||||
// details here, so just check for some expected side
|
||||
// effect; in this case, should see more points in the buffer
|
||||
expect(updater.getLineBuffers()[0].getLength()).toEqual(1);
|
||||
updater.addHistorical(mockObject, mockSeries);
|
||||
expect(updater.getLineBuffers()[0].getLength()).toEqual(4);
|
||||
});
|
||||
|
||||
it("clears the domain offset if no objects are present", function () {
|
||||
mockSubscription.getTelemetryObjects.andReturn([]);
|
||||
updater.update();
|
||||
expect(updater.getDomainOffset()).toBeUndefined();
|
||||
});
|
||||
|
||||
it("handles empty historical telemetry updates", function () {
|
||||
// General robustness check for when a series is empty
|
||||
var mockObject = mockSubscription.getTelemetryObjects()[0];
|
||||
|
||||
mockSeries.getPointCount.andReturn(0);
|
||||
mockSeries.getDomainValue.andCallFake(function (i) {
|
||||
return 1000 + i * 1000;
|
||||
});
|
||||
mockSeries.getRangeValue.andReturn(10);
|
||||
|
||||
// PlotLine & PlotLineBuffer are tested for most of the
|
||||
// details here, so just check for some expected side
|
||||
// effect; in this case, should see more points in the buffer
|
||||
expect(updater.getLineBuffers()[0].getLength()).toEqual(1);
|
||||
updater.addHistorical(mockObject, mockSeries);
|
||||
expect(updater.getLineBuffers()[0].getLength()).toEqual(1);
|
||||
});
|
||||
|
||||
it("can initialize domain offset from historical telemetry", function () {
|
||||
var tmp = mockSubscription.getTelemetryObjects();
|
||||
|
||||
mockSubscription.getTelemetryObjects.andReturn([]);
|
||||
|
||||
// Reinstantiate with the empty subscription
|
||||
updater = new PlotUpdater(
|
||||
mockSubscription,
|
||||
testDomain,
|
||||
testRange
|
||||
);
|
||||
|
||||
// Restore subscription, provide some historical data
|
||||
mockSubscription.getTelemetryObjects.andReturn(tmp);
|
||||
mockSeries.getPointCount.andReturn(3);
|
||||
mockSeries.getDomainValue.andCallFake(function (i) {
|
||||
return 1000 + i * 1000;
|
||||
});
|
||||
mockSeries.getRangeValue.andReturn(10);
|
||||
|
||||
// PlotLine & PlotLineBuffer are tested for most of the
|
||||
// details here, so just check for some expected side
|
||||
// effect; in this case, should see more points in the buffer
|
||||
expect(updater.getDomainOffset()).toBeUndefined();
|
||||
updater.addHistorical(tmp[0], mockSeries);
|
||||
expect(updater.getDomainOffset()).toBeDefined();
|
||||
});
|
||||
|
||||
it("provides some margin for the range", function () {
|
||||
var mockObject = mockSubscription.getTelemetryObjects()[0];
|
||||
|
||||
mockSeries.getPointCount.andReturn(3);
|
||||
mockSeries.getDomainValue.andCallFake(function (i) {
|
||||
return 1000 + i * 1000;
|
||||
});
|
||||
mockSeries.getRangeValue.andCallFake(function (i) {
|
||||
return 10 + i; // 10, 20, 30
|
||||
});
|
||||
updater.addHistorical(mockObject, mockSeries);
|
||||
expect(updater.getOrigin()[1]).toBeLessThan(10);
|
||||
expect(updater.getDimensions()[1]).toBeGreaterThan(20);
|
||||
});
|
||||
|
||||
describe("when no data is initially available", function () {
|
||||
beforeEach(function () {
|
||||
testDomainValues = {};
|
||||
testRangeValues = {};
|
||||
updater = new PlotUpdater(
|
||||
mockSubscription,
|
||||
testDomain,
|
||||
testRange,
|
||||
1350 // Smaller max size for easier testing
|
||||
);
|
||||
});
|
||||
|
||||
it("has no line data", function () {
|
||||
// Either no lines, or empty lines are fine
|
||||
expect(updater.getLineBuffers().map(function (lineBuffer) {
|
||||
return lineBuffer.getLength();
|
||||
}).reduce(function (a, b) {
|
||||
return a + b;
|
||||
}, 0)).toEqual(0);
|
||||
});
|
||||
|
||||
it("determines initial domain bounds from first available data", function () {
|
||||
testDomainValues.a = 123;
|
||||
testRangeValues.a = 456;
|
||||
updater.update();
|
||||
expect(updater.getOrigin()[0]).toEqual(jasmine.any(Number));
|
||||
expect(updater.getOrigin()[1]).toEqual(jasmine.any(Number));
|
||||
expect(isNaN(updater.getOrigin()[0])).toBeFalsy();
|
||||
expect(isNaN(updater.getOrigin()[1])).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,87 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/modes/PlotModeOptions"],
|
||||
function (PlotModeOptions) {
|
||||
|
||||
describe("Plot mode options", function () {
|
||||
var mockDomainObject,
|
||||
mockSubPlotFactory;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getId", "getModel", "getCapability"]
|
||||
);
|
||||
mockSubPlotFactory = jasmine.createSpyObj(
|
||||
"subPlotFactory",
|
||||
["createSubPlot"]
|
||||
);
|
||||
});
|
||||
|
||||
it("offers only one option when one object is present", function () {
|
||||
expect(
|
||||
new PlotModeOptions([mockDomainObject], mockSubPlotFactory)
|
||||
.getModeOptions().length
|
||||
).toEqual(1);
|
||||
});
|
||||
|
||||
it("offers two options when multiple objects are present", function () {
|
||||
var objects = [
|
||||
mockDomainObject,
|
||||
mockDomainObject,
|
||||
mockDomainObject,
|
||||
mockDomainObject
|
||||
];
|
||||
expect(
|
||||
new PlotModeOptions(objects, mockSubPlotFactory)
|
||||
.getModeOptions().length
|
||||
).toEqual(2);
|
||||
});
|
||||
|
||||
it("allows modes to be changed", function () {
|
||||
var plotModeOptions = new PlotModeOptions([
|
||||
mockDomainObject,
|
||||
mockDomainObject,
|
||||
mockDomainObject,
|
||||
mockDomainObject
|
||||
], mockSubPlotFactory),
|
||||
initialHandler = plotModeOptions.getModeHandler();
|
||||
|
||||
// Change the mode
|
||||
plotModeOptions.getModeOptions().forEach(function (option) {
|
||||
if (option !== plotModeOptions.getMode()) {
|
||||
plotModeOptions.setMode(option);
|
||||
}
|
||||
});
|
||||
|
||||
// Mode should be different now
|
||||
expect(plotModeOptions.getModeHandler())
|
||||
.not.toBe(initialHandler);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,184 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/modes/PlotOverlayMode"],
|
||||
function (PlotOverlayMode) {
|
||||
|
||||
describe("Overlaid plot mode", function () {
|
||||
var mockDomainObject,
|
||||
mockSubPlotFactory,
|
||||
mockPrepared,
|
||||
testBuffers,
|
||||
testDrawingObjects,
|
||||
mode;
|
||||
|
||||
function createMockSubPlot() {
|
||||
var mockSubPlot = jasmine.createSpyObj(
|
||||
"subPlot",
|
||||
[
|
||||
"setDomainOffset",
|
||||
"hover",
|
||||
"startMarquee",
|
||||
"endMarquee",
|
||||
"getDrawingObject",
|
||||
"update"
|
||||
]
|
||||
),
|
||||
testDrawingObject = {};
|
||||
|
||||
// Track drawing objects in order of creation
|
||||
testDrawingObjects.push(testDrawingObject);
|
||||
mockSubPlot.getDrawingObject.andReturn(testDrawingObject);
|
||||
return mockSubPlot;
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getId", "getModel", "getCapability"]
|
||||
);
|
||||
mockSubPlotFactory = jasmine.createSpyObj(
|
||||
"subPlotFactory",
|
||||
["createSubPlot"]
|
||||
);
|
||||
// Prepared telemetry data
|
||||
mockPrepared = jasmine.createSpyObj(
|
||||
"prepared",
|
||||
[
|
||||
"getDomainOffset",
|
||||
"getOrigin",
|
||||
"getDimensions",
|
||||
"getLineBuffers"
|
||||
]
|
||||
);
|
||||
|
||||
mockSubPlotFactory.createSubPlot.andCallFake(createMockSubPlot);
|
||||
|
||||
// Act as if we have three buffers full of data
|
||||
testBuffers = ['a', 'b', 'c'].map(function (id) {
|
||||
var mockBuffer = jasmine.createSpyObj(
|
||||
'buffer-' + id,
|
||||
['getBuffer', 'getLength']
|
||||
);
|
||||
mockBuffer.getBuffer.andReturn([id]);
|
||||
mockBuffer.getLength.andReturn(3);
|
||||
return mockBuffer;
|
||||
});
|
||||
mockPrepared.getLineBuffers.andReturn(testBuffers);
|
||||
mockPrepared.getDomainOffset.andReturn(1234);
|
||||
mockPrepared.getOrigin.andReturn([10, 10]);
|
||||
mockPrepared.getDimensions.andReturn([500, 500]);
|
||||
|
||||
// Clear out drawing objects
|
||||
testDrawingObjects = [];
|
||||
|
||||
mode = new PlotOverlayMode([
|
||||
mockDomainObject,
|
||||
mockDomainObject,
|
||||
mockDomainObject
|
||||
], mockSubPlotFactory);
|
||||
});
|
||||
|
||||
it("creates one sub-plot for all domain objects", function () {
|
||||
expect(mode.getSubPlots().length).toEqual(1);
|
||||
});
|
||||
|
||||
it("draws telemetry to subplots", function () {
|
||||
// Verify precondition
|
||||
mode.getSubPlots().forEach(function (subplot) {
|
||||
// Either empty list or undefined is fine;
|
||||
// just want to make sure there are no lines.
|
||||
expect(subplot.getDrawingObject().lines || [])
|
||||
.toEqual([]);
|
||||
});
|
||||
|
||||
mode.plotTelemetry(mockPrepared);
|
||||
|
||||
// Should have one sub-plot with three lines
|
||||
testDrawingObjects.forEach(function (testDrawingObject) {
|
||||
// Either empty list or undefined is fine;
|
||||
// just want to make sure there are no lines.
|
||||
expect(testDrawingObject.lines.length)
|
||||
.toEqual(3);
|
||||
// Make sure the right buffer was drawn to the
|
||||
// right subplot.
|
||||
testDrawingObject.lines.forEach(function (line, j) {
|
||||
expect(line.buffer).toEqual(testBuffers[j].getBuffer());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("tracks zoomed state of subplots", function () {
|
||||
// Should start out unzoomed
|
||||
expect(mode.isZoomed()).toBeFalsy();
|
||||
|
||||
// Trigger some zoom changes
|
||||
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
|
||||
// Second argument to the factory was pan-zoom stack
|
||||
c.args[1].pushPanZoom([1, 2], [3, 4]);
|
||||
});
|
||||
|
||||
// Should start out unzoomed
|
||||
expect(mode.isZoomed()).toBeTruthy();
|
||||
});
|
||||
|
||||
it("supports unzooming", function () {
|
||||
// Trigger some zoom changes
|
||||
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
|
||||
// Second argument to the factory was pan-zoom stack
|
||||
c.args[1].pushPanZoom([1, 2], [3, 4]);
|
||||
});
|
||||
// Verify that we are indeed zoomed now
|
||||
expect(mode.isZoomed()).toBeTruthy();
|
||||
|
||||
// Unzoom
|
||||
mode.unzoom();
|
||||
|
||||
// Should no longer be zoomed
|
||||
expect(mode.isZoomed()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("supports stepping back through zoom states", function () {
|
||||
// Trigger some zoom changes
|
||||
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
|
||||
// Second argument to the factory was pan-zoom stack
|
||||
c.args[1].pushPanZoom([1, 2], [3, 4]);
|
||||
});
|
||||
|
||||
// Step back the same number of zoom changes
|
||||
mockSubPlotFactory.createSubPlot.calls.forEach(function () {
|
||||
// Should still be zoomed at start of each iteration
|
||||
expect(mode.isZoomed()).toBeTruthy();
|
||||
// Step back one of the zoom changes.
|
||||
mode.stepBackPanZoom();
|
||||
});
|
||||
|
||||
// Should no longer be zoomed
|
||||
expect(mode.isZoomed()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,179 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* MergeModelsSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/modes/PlotStackMode"],
|
||||
function (PlotStackMode) {
|
||||
|
||||
describe("Stacked plot mode", function () {
|
||||
var mockDomainObject,
|
||||
mockSubPlotFactory,
|
||||
mockPrepared,
|
||||
testBuffers,
|
||||
testDrawingObjects,
|
||||
mode;
|
||||
|
||||
function createMockSubPlot() {
|
||||
var mockSubPlot = jasmine.createSpyObj(
|
||||
"subPlot",
|
||||
[
|
||||
"setDomainOffset",
|
||||
"hover",
|
||||
"startMarquee",
|
||||
"endMarquee",
|
||||
"getDrawingObject",
|
||||
"update"
|
||||
]
|
||||
),
|
||||
testDrawingObject = {};
|
||||
|
||||
// Track drawing objects in order of creation
|
||||
testDrawingObjects.push(testDrawingObject);
|
||||
mockSubPlot.getDrawingObject.andReturn(testDrawingObject);
|
||||
return mockSubPlot;
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getId", "getModel", "getCapability"]
|
||||
);
|
||||
mockSubPlotFactory = jasmine.createSpyObj(
|
||||
"subPlotFactory",
|
||||
["createSubPlot"]
|
||||
);
|
||||
// Prepared telemetry data
|
||||
mockPrepared = jasmine.createSpyObj(
|
||||
"prepared",
|
||||
["getDomainOffset", "getOrigin", "getDimensions", "getLineBuffers"]
|
||||
);
|
||||
|
||||
mockSubPlotFactory.createSubPlot.andCallFake(createMockSubPlot);
|
||||
|
||||
// Act as if we have three buffers full of data
|
||||
testBuffers = ['a', 'b', 'c'].map(function (id) {
|
||||
var mockBuffer = jasmine.createSpyObj(
|
||||
'buffer-' + id,
|
||||
['getBuffer', 'getLength']
|
||||
);
|
||||
mockBuffer.getBuffer.andReturn([id]);
|
||||
mockBuffer.getLength.andReturn(3);
|
||||
return mockBuffer;
|
||||
});
|
||||
mockPrepared.getLineBuffers.andReturn(testBuffers);
|
||||
mockPrepared.getDomainOffset.andReturn(1234);
|
||||
mockPrepared.getOrigin.andReturn([10, 10]);
|
||||
mockPrepared.getDimensions.andReturn([500, 500]);
|
||||
|
||||
// Objects that will be drawn to in sub-plots
|
||||
testDrawingObjects = [];
|
||||
|
||||
mode = new PlotStackMode([
|
||||
mockDomainObject,
|
||||
mockDomainObject,
|
||||
mockDomainObject
|
||||
], mockSubPlotFactory);
|
||||
});
|
||||
|
||||
it("creates one sub-plot per domain object", function () {
|
||||
expect(mode.getSubPlots().length).toEqual(3);
|
||||
});
|
||||
|
||||
it("draws telemetry to subplots", function () {
|
||||
// Verify precondition
|
||||
mode.getSubPlots().forEach(function (subplot) {
|
||||
// Either empty list or undefined is fine;
|
||||
// just want to make sure there are no lines.
|
||||
expect(subplot.getDrawingObject().lines || [])
|
||||
.toEqual([]);
|
||||
});
|
||||
|
||||
mode.plotTelemetry(mockPrepared);
|
||||
|
||||
// Should all each have one line
|
||||
testDrawingObjects.forEach(function (testDrawingObject, i) {
|
||||
// Either empty list or undefined is fine;
|
||||
// just want to make sure there are no lines.
|
||||
expect(testDrawingObject.lines.length)
|
||||
.toEqual(1);
|
||||
// Make sure the right buffer was drawn to the
|
||||
// right subplot.
|
||||
expect(testDrawingObject.lines[0].buffer)
|
||||
.toEqual(testBuffers[i].getBuffer());
|
||||
});
|
||||
});
|
||||
|
||||
it("tracks zoomed state of subplots", function () {
|
||||
// Should start out unzoomed
|
||||
expect(mode.isZoomed()).toBeFalsy();
|
||||
|
||||
// Trigger some zoom changes
|
||||
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
|
||||
// Second argument to the factory was pan-zoom stack
|
||||
c.args[1].pushPanZoom([1, 2], [3, 4]);
|
||||
});
|
||||
|
||||
// Should start out unzoomed
|
||||
expect(mode.isZoomed()).toBeTruthy();
|
||||
});
|
||||
|
||||
it("supports unzooming", function () {
|
||||
// Trigger some zoom changes
|
||||
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
|
||||
// Second argument to the factory was pan-zoom stack
|
||||
c.args[1].pushPanZoom([1, 2], [3, 4]);
|
||||
});
|
||||
// Verify that we are indeed zoomed now
|
||||
expect(mode.isZoomed()).toBeTruthy();
|
||||
|
||||
// Unzoom
|
||||
mode.unzoom();
|
||||
|
||||
// Should no longer be zoomed
|
||||
expect(mode.isZoomed()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("supports stepping back through zoom states", function () {
|
||||
// Trigger some zoom changes
|
||||
mockSubPlotFactory.createSubPlot.calls.forEach(function (c) {
|
||||
// Second argument to the factory was pan-zoom stack
|
||||
c.args[1].pushPanZoom([1, 2], [3, 4]);
|
||||
});
|
||||
|
||||
// Step back the same number of zoom changes
|
||||
mockSubPlotFactory.createSubPlot.calls.forEach(function () {
|
||||
// Should still be zoomed at start of each iteration
|
||||
expect(mode.isZoomed()).toBeTruthy();
|
||||
// Step back
|
||||
mode.stepBackPanZoom();
|
||||
});
|
||||
|
||||
// Should no longer be zoomed
|
||||
expect(mode.isZoomed()).toBeFalsy();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,123 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/policies/PlotViewPolicy"],
|
||||
function (PlotViewPolicy) {
|
||||
|
||||
describe("Plot view policy", function () {
|
||||
var testView,
|
||||
mockDomainObject,
|
||||
testAdaptedObject,
|
||||
openmct,
|
||||
telemetryMetadata,
|
||||
policy;
|
||||
|
||||
beforeEach(function () {
|
||||
testView = { key: "plot" };
|
||||
testAdaptedObject = { telemetry: {} };
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
'domainObject',
|
||||
['useCapability', 'hasCapability', 'getCapability']
|
||||
);
|
||||
mockDomainObject.useCapability.andReturn(testAdaptedObject);
|
||||
openmct = {
|
||||
telemetry: jasmine.createSpyObj('telemetryAPI', [
|
||||
'getMetadata'
|
||||
])
|
||||
};
|
||||
telemetryMetadata = jasmine.createSpyObj('telemetryMetadata', [
|
||||
'valuesForHints'
|
||||
]);
|
||||
telemetryMetadata.valuesForHints.andReturn([]);
|
||||
openmct.telemetry.getMetadata.andReturn(telemetryMetadata);
|
||||
policy = new PlotViewPolicy(openmct);
|
||||
});
|
||||
|
||||
it('fetches metadata from telem api', function () {
|
||||
policy.allow(testView, mockDomainObject);
|
||||
expect(mockDomainObject.useCapability)
|
||||
.toHaveBeenCalledWith('adapter');
|
||||
expect(openmct.telemetry.getMetadata)
|
||||
.toHaveBeenCalledWith(testAdaptedObject);
|
||||
expect(telemetryMetadata.valuesForHints)
|
||||
.toHaveBeenCalledWith(['range']);
|
||||
});
|
||||
|
||||
it('returns false if no ranges exist', function () {
|
||||
telemetryMetadata.valuesForHints.andReturn([]);
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true if any ranges exist', function () {
|
||||
telemetryMetadata.valuesForHints.andReturn([{}]);
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false if all ranges are strings', function () {
|
||||
telemetryMetadata.valuesForHints.andReturn([{
|
||||
format: 'string'
|
||||
}, {
|
||||
format: 'string'
|
||||
}]);
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true if only some ranges are strings', function () {
|
||||
telemetryMetadata.valuesForHints.andReturn([{
|
||||
format: 'string'
|
||||
}, {}]);
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for telemetry delegators', function () {
|
||||
delete testAdaptedObject.telemetry;
|
||||
mockDomainObject.hasCapability.andCallFake(function (c) {
|
||||
return c === 'delegation';
|
||||
});
|
||||
mockDomainObject.getCapability.andReturn(
|
||||
jasmine.createSpyObj('delegation', [
|
||||
'doesDelegateCapability'
|
||||
])
|
||||
);
|
||||
mockDomainObject.getCapability('delegation')
|
||||
.doesDelegateCapability.andCallFake(function (c) {
|
||||
return c === 'telemetry';
|
||||
});
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(true);
|
||||
expect(openmct.telemetry.getMetadata).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns true for non-telemetry non-delegators', function () {
|
||||
delete testAdaptedObject.telemetry;
|
||||
mockDomainObject.hasCapability.andReturn(false);
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(false);
|
||||
});
|
||||
|
||||
it("allows other views", function () {
|
||||
testView.key = "somethingElse";
|
||||
expect(policy.allow(testView, mockDomainObject)).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
@ -1,120 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* ExportImageServiceSpec. Created by hudsonfoo on 09/03/16.
|
||||
*/
|
||||
define(
|
||||
["../../src/services/ExportImageService"],
|
||||
function (ExportImageService) {
|
||||
var mockQ,
|
||||
mockDeferred,
|
||||
mockPromise,
|
||||
mockTimeout,
|
||||
mockLog,
|
||||
mockHtml2Canvas,
|
||||
mockCanvas,
|
||||
mockSaveAs,
|
||||
mockFileReader,
|
||||
mockExportTimeoutConstant,
|
||||
testElement,
|
||||
exportImageService;
|
||||
|
||||
describe("ExportImageService", function () {
|
||||
beforeEach(function () {
|
||||
mockDeferred = jasmine.createSpyObj(
|
||||
"deferred",
|
||||
["reject", "resolve"]
|
||||
);
|
||||
mockPromise = jasmine.createSpyObj(
|
||||
"promise",
|
||||
["then", "finally"]
|
||||
);
|
||||
mockPromise.then = function (callback) {
|
||||
callback();
|
||||
};
|
||||
mockQ = {
|
||||
"defer": function () {
|
||||
return {
|
||||
"resolve": mockDeferred.resolve,
|
||||
"reject": mockDeferred.reject,
|
||||
"promise": mockPromise
|
||||
};
|
||||
}
|
||||
};
|
||||
mockTimeout = function (fn, time) {
|
||||
return {
|
||||
"cancel": function () {}
|
||||
};
|
||||
};
|
||||
mockLog = jasmine.createSpyObj(
|
||||
"$log",
|
||||
["warn"]
|
||||
);
|
||||
mockHtml2Canvas = jasmine.createSpy("html2canvas").andCallFake(function (element, opts) {
|
||||
opts.onrendered(mockCanvas);
|
||||
});
|
||||
mockCanvas = jasmine.createSpyObj(
|
||||
"canvas",
|
||||
["toBlob"]
|
||||
);
|
||||
mockSaveAs = jasmine.createSpy("saveAs");
|
||||
mockFileReader = jasmine.createSpyObj(
|
||||
"FileReader",
|
||||
["readAsDataURL", "onloadend"]
|
||||
);
|
||||
mockExportTimeoutConstant = 0;
|
||||
testElement = {};
|
||||
|
||||
exportImageService = new ExportImageService(
|
||||
mockQ,
|
||||
mockTimeout,
|
||||
mockLog,
|
||||
mockExportTimeoutConstant,
|
||||
mockHtml2Canvas,
|
||||
mockSaveAs,
|
||||
mockFileReader
|
||||
);
|
||||
});
|
||||
|
||||
it("runs html2canvas and tries to save a png", function () {
|
||||
exportImageService.exportPNG(testElement, "plot.png");
|
||||
|
||||
expect(mockHtml2Canvas).toHaveBeenCalledWith(testElement, { onrendered: jasmine.any(Function) });
|
||||
expect(mockCanvas.toBlob).toHaveBeenCalledWith(mockDeferred.resolve, "image/png");
|
||||
expect(mockDeferred.reject).not.toHaveBeenCalled();
|
||||
expect(mockSaveAs).toHaveBeenCalled();
|
||||
expect(mockPromise.finally).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("runs html2canvas and tries to save a jpg", function () {
|
||||
exportImageService.exportJPG(testElement, "plot.png");
|
||||
|
||||
expect(mockHtml2Canvas).toHaveBeenCalledWith(testElement, { onrendered: jasmine.any(Function) });
|
||||
expect(mockCanvas.toBlob).toHaveBeenCalledWith(mockDeferred.resolve, "image/jpeg");
|
||||
expect(mockDeferred.reject).not.toHaveBeenCalled();
|
||||
expect(mockSaveAs).toHaveBeenCalled();
|
||||
expect(mockPromise.finally).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -29,6 +29,7 @@ define([
|
||||
"./src/controllers/TimelineTickController",
|
||||
"./src/controllers/TimelineTableController",
|
||||
"./src/controllers/TimelineGanttController",
|
||||
"./src/controllers/TimelineTOIController",
|
||||
"./src/controllers/ActivityModeValuesController",
|
||||
"./src/capabilities/ActivityTimespanCapability",
|
||||
"./src/capabilities/TimelineTimespanCapability",
|
||||
@ -59,6 +60,7 @@ define([
|
||||
TimelineTickController,
|
||||
TimelineTableController,
|
||||
TimelineGanttController,
|
||||
TimelineTOIController,
|
||||
ActivityModeValuesController,
|
||||
ActivityTimespanCapability,
|
||||
TimelineTimespanCapability,
|
||||
@ -502,6 +504,15 @@ define([
|
||||
"TIMELINE_MAXIMUM_OFFSCREEN"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "TimelineTOIController",
|
||||
"implementation": TimelineTOIController,
|
||||
"depends": [
|
||||
"openmct",
|
||||
"timerService",
|
||||
"$scope"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "ActivityModeValuesController",
|
||||
"implementation": ActivityModeValuesController,
|
||||
|
@ -29,6 +29,44 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Follow Line
|
||||
.l-follow-line {
|
||||
// TODO: move before and after into l-timeline-gantt so those only render in that pane
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
width: 1px;
|
||||
z-index: 9; // Just below .l-hover-btns-holder
|
||||
}
|
||||
}
|
||||
|
||||
.l-timeline-gantt {
|
||||
.l-follow-line {
|
||||
$d: 0.8rem;
|
||||
top: $interiorMargin;
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
height: $d;
|
||||
width: $d;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@include transform(translateX(-50%));
|
||||
}
|
||||
&:before {
|
||||
// Icon blocker
|
||||
width: 2 * $d;
|
||||
}
|
||||
&:after {
|
||||
// Icon
|
||||
font-size: $d;
|
||||
line-height: $d;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.s-timeline-gantt {
|
||||
@ -108,10 +146,9 @@
|
||||
}
|
||||
.s-hover-btns-holder {
|
||||
$bg: $timelineHeaderColorBg;
|
||||
$bga: 1;
|
||||
$l: 5%;
|
||||
@include user-select(none);
|
||||
@include background-image(linear-gradient(-90deg, rgba($bg, $bga), rgba($bg, $bga) 70%, rgba($bg, 0) 100%));
|
||||
@include background-image(linear-gradient(-90deg, rgba($bg, 1), rgba($bg, 1) 70%, rgba($bg, 0) 100%));
|
||||
.s-button {
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
@ -129,4 +166,27 @@
|
||||
color: $timelineResourceGraphFg;
|
||||
}
|
||||
}
|
||||
|
||||
.s-follow-line {
|
||||
background: rgba($timeControllerToiLineColor, 0.5);
|
||||
}
|
||||
|
||||
.s-timeline-gantt {
|
||||
.s-follow-line {
|
||||
&:after {
|
||||
// Icon
|
||||
color: $timeControllerToiLineColor;
|
||||
content: $glyph-icon-timer;
|
||||
font-family: symbolsfont;
|
||||
text-shadow: $shdwItemText;
|
||||
}
|
||||
&:before {
|
||||
// Blocker
|
||||
$bg: $timelineHeaderColorBg;
|
||||
$l: 30%;
|
||||
@include background-image(linear-gradient(90deg, rgba($bg, 0), rgba($bg, 1) $l, rgba($bg, 1) 100% - $l, rgba($bg, 0)));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,10 @@
|
||||
}
|
||||
}
|
||||
&.l-timeline-gantt {
|
||||
.abs.l-timeline-gantt-header-w {
|
||||
overflow: hidden;
|
||||
height: $timelineTopPaneHeaderH;
|
||||
}
|
||||
.l-swimlanes-holder {
|
||||
@include scrollV(scroll);
|
||||
bottom: $scrollbarTrackSize;
|
||||
|
@ -24,6 +24,7 @@ $output-bourbon-deprecation-warnings: false;
|
||||
|
||||
@import "../../../../commonUI/general/res/sass/constants";
|
||||
@import "../../../../commonUI/general/res/sass/mixins";
|
||||
@import "../../../../commonUI/general/res/sass/glyphs";
|
||||
@import "../../../../commonUI/themes/espresso/res/sass/constants";
|
||||
@import "../../../../commonUI/themes/espresso/res/sass/mixins";
|
||||
@import "constants";
|
||||
|
@ -24,6 +24,7 @@ $output-bourbon-deprecation-warnings: false;
|
||||
|
||||
@import "../../../../commonUI/general/res/sass/constants";
|
||||
@import "../../../../commonUI/general/res/sass/mixins";
|
||||
@import "../../../../commonUI/general/res/sass/glyphs";
|
||||
@import "../../../../commonUI/themes/snow/res/sass/constants";
|
||||
@import "../../../../commonUI/themes/snow/res/sass/mixins";
|
||||
@import "constants";
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user