mirror of
https://github.com/nasa/openmct.git
synced 2025-06-26 11:09:22 +00:00
Compare commits
51 Commits
plot-telem
...
plot-impor
Author | SHA1 | Date | |
---|---|---|---|
3ead4375c8 | |||
386cb1c3af | |||
553989638d | |||
088bd565d9 | |||
c0df73265c | |||
de971772e3 | |||
d4785446f1 | |||
54ac81b98e | |||
08d31cc493 | |||
d749a6693b | |||
e6f4098ed0 | |||
fb1b765002 | |||
bfec434369 | |||
14df350994 | |||
7442768ced | |||
77c7bdfdec | |||
07d9769966 | |||
385b6177b2 | |||
7f68d26433 | |||
d7b44f8d09 | |||
c4cd36e15b | |||
618a6e7e8d | |||
63a8c91f71 | |||
e59020fec7 | |||
a2e424203a | |||
16853644cb | |||
2272766c57 | |||
1d9cdea2d4 | |||
715219c44d | |||
00dc2875bf | |||
8703f363b8 | |||
26210eaa50 | |||
a4a1cb5e05 | |||
9570f2f7a1 | |||
eb4ded39b3 | |||
7deb3cd025 | |||
06779e6cd9 | |||
7f43c0bf1a | |||
bfa3bbcdc7 | |||
2baf3f8bb0 | |||
10ac13ac5c | |||
138cb199bb | |||
374c363a78 | |||
e9cb5cd639 | |||
bc7d92ee0d | |||
78f49784a0 | |||
8754c438cc | |||
1419c75503 | |||
ca8cad0a74 | |||
a3a55d3b48 | |||
e66f818996 |
@ -4,12 +4,6 @@ deployment:
|
|||||||
commands:
|
commands:
|
||||||
- npm install canvas nomnoml
|
- npm install canvas nomnoml
|
||||||
- ./build-docs.sh
|
- ./build-docs.sh
|
||||||
- git fetch --unshallow
|
|
||||||
- git push git@heroku.com:openmctweb-demo.git $CIRCLE_SHA1:refs/heads/master
|
|
||||||
openmct-demo:
|
|
||||||
branch: live_demo
|
|
||||||
heroku:
|
|
||||||
appname: openmct-demo
|
|
||||||
openmctweb-staging-deux:
|
openmctweb-staging-deux:
|
||||||
branch: mobile
|
branch: mobile
|
||||||
heroku:
|
heroku:
|
||||||
|
@ -30,7 +30,8 @@ define([
|
|||||||
amplitude: 1,
|
amplitude: 1,
|
||||||
period: 10,
|
period: 10,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
dataRateInHz: 1
|
dataRateInHz: 1,
|
||||||
|
phase: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
function GeneratorProvider() {
|
function GeneratorProvider() {
|
||||||
@ -50,9 +51,12 @@ define([
|
|||||||
'amplitude',
|
'amplitude',
|
||||||
'period',
|
'period',
|
||||||
'offset',
|
'offset',
|
||||||
'dataRateInHz'
|
'dataRateInHz',
|
||||||
|
'phase',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
request = request || {};
|
||||||
|
|
||||||
var workerRequest = {};
|
var workerRequest = {};
|
||||||
|
|
||||||
props.forEach(function (prop) {
|
props.forEach(function (prop) {
|
||||||
@ -67,7 +71,7 @@ define([
|
|||||||
}
|
}
|
||||||
workerRequest[prop] = Number(workerRequest[prop]);
|
workerRequest[prop] = Number(workerRequest[prop]);
|
||||||
});
|
});
|
||||||
|
workerRequest.name = domainObject.name;
|
||||||
return workerRequest;
|
return workerRequest;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
80
example/generator/StateGeneratorProvider.js
Normal file
80
example/generator/StateGeneratorProvider.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 (
|
||||||
|
|
||||||
|
) {
|
||||||
|
|
||||||
|
function StateGeneratorProvider() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointForTimestamp(timestamp, duration, name) {
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
utc: Math.floor(timestamp / duration) * duration,
|
||||||
|
value: Math.floor(timestamp / duration) % 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
StateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) {
|
||||||
|
return domainObject.type === 'example.state-generator';
|
||||||
|
};
|
||||||
|
|
||||||
|
StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
|
||||||
|
var duration = domainObject.telemetry.duration * 1000;
|
||||||
|
|
||||||
|
var interval = setInterval(function () {
|
||||||
|
var now = Date.now();
|
||||||
|
callback(pointForTimestamp(now, duration, domainObject.name));
|
||||||
|
}, duration);
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
StateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
|
||||||
|
return domainObject.type === 'example.state-generator';
|
||||||
|
};
|
||||||
|
|
||||||
|
StateGeneratorProvider.prototype.request = function (domainObject, options) {
|
||||||
|
var start = options.start;
|
||||||
|
var end = options.end;
|
||||||
|
var duration = domainObject.telemetry.duration * 1000;
|
||||||
|
if (options.strategy === 'latest' || options.size === 1) {
|
||||||
|
start = end;
|
||||||
|
}
|
||||||
|
var data = [];
|
||||||
|
while (start <= end && data.length < 5000) {
|
||||||
|
data.push(pointForTimestamp(start, duration, domainObject.name));
|
||||||
|
start += 5000;
|
||||||
|
}
|
||||||
|
return Promise.resolve(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
return StateGeneratorProvider;
|
||||||
|
|
||||||
|
});
|
@ -62,10 +62,11 @@
|
|||||||
self.postMessage({
|
self.postMessage({
|
||||||
id: message.id,
|
id: message.id,
|
||||||
data: {
|
data: {
|
||||||
|
name: data.name,
|
||||||
utc: nextStep,
|
utc: nextStep,
|
||||||
yesterday: nextStep - 60*60*24*1000,
|
yesterday: nextStep - 60*60*24*1000,
|
||||||
sin: sin(nextStep, data.period, data.amplitude, data.offset),
|
sin: sin(nextStep, data.period, data.amplitude, data.offset, data.phase),
|
||||||
cos: cos(nextStep, data.period, data.amplitude, data.offset)
|
cos: cos(nextStep, data.period, data.amplitude, data.offset, data.phase)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
nextStep += step;
|
nextStep += step;
|
||||||
@ -82,21 +83,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onRequest(message) {
|
function onRequest(message) {
|
||||||
var data = message.data;
|
var request = message.data;
|
||||||
if (data.end == undefined) {
|
if (request.end == undefined) {
|
||||||
data.end = Date.now();
|
request.end = Date.now();
|
||||||
}
|
}
|
||||||
if (data.start == undefined){
|
if (request.start == undefined){
|
||||||
data.start = data.end - FIFTEEN_MINUTES;
|
request.start = request.end - FIFTEEN_MINUTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
var start = data.start;
|
var start = request.start;
|
||||||
var end = data.end > now ? now : data.end;
|
var end = request.end > now ? now : request.end;
|
||||||
var amplitude = data.amplitude;
|
var amplitude = request.amplitude;
|
||||||
var period = data.period;
|
var period = request.period;
|
||||||
var offset = data.offset;
|
var offset = request.offset;
|
||||||
var dataRateInHz = data.dataRateInHz;
|
var dataRateInHz = request.dataRateInHz;
|
||||||
|
var phase = request.phase;
|
||||||
|
|
||||||
var step = 1000 / dataRateInHz;
|
var step = 1000 / dataRateInHz;
|
||||||
var nextStep = start - (start % step) + step;
|
var nextStep = start - (start % step) + step;
|
||||||
@ -105,10 +107,11 @@
|
|||||||
|
|
||||||
for (; nextStep < end && data.length < 5000; nextStep += step) {
|
for (; nextStep < end && data.length < 5000; nextStep += step) {
|
||||||
data.push({
|
data.push({
|
||||||
|
name: request.name,
|
||||||
utc: nextStep,
|
utc: nextStep,
|
||||||
yesterday: nextStep - 60*60*24*1000,
|
yesterday: nextStep - 60*60*24*1000,
|
||||||
sin: sin(nextStep, period, amplitude, offset),
|
sin: sin(nextStep, period, amplitude, offset, phase),
|
||||||
cos: cos(nextStep, period, amplitude, offset)
|
cos: cos(nextStep, period, amplitude, offset, phase)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
@ -117,14 +120,14 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function cos(timestamp, period, amplitude, offset) {
|
function cos(timestamp, period, amplitude, offset, phase) {
|
||||||
return amplitude *
|
return amplitude *
|
||||||
Math.cos(timestamp / period / 1000 * Math.PI * 2) + offset;
|
Math.cos(phase + (timestamp / period / 1000 * Math.PI * 2)) + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sin(timestamp, period, amplitude, offset) {
|
function sin(timestamp, period, amplitude, offset, phase) {
|
||||||
return amplitude *
|
return amplitude *
|
||||||
Math.sin(timestamp / period / 1000 * Math.PI * 2) + offset;
|
Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendError(error, message) {
|
function sendError(error, message) {
|
||||||
|
@ -23,10 +23,12 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
"./GeneratorProvider",
|
"./GeneratorProvider",
|
||||||
"./SinewaveLimitCapability"
|
"./SinewaveLimitCapability",
|
||||||
|
"./StateGeneratorProvider"
|
||||||
], function (
|
], function (
|
||||||
GeneratorProvider,
|
GeneratorProvider,
|
||||||
SinewaveLimitCapability
|
SinewaveLimitCapability,
|
||||||
|
StateGeneratorProvider
|
||||||
) {
|
) {
|
||||||
|
|
||||||
var legacyExtensions = {
|
var legacyExtensions = {
|
||||||
@ -46,6 +48,75 @@ define([
|
|||||||
openmct.legacyExtension(type, extension)
|
openmct.legacyExtension(type, extension)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
openmct.types.addType("example.state-generator", {
|
||||||
|
name: "State Generator",
|
||||||
|
description: "For development use. Generates test enumerated telemetry by cycling through a given set of states",
|
||||||
|
cssClass: "icon-telemetry",
|
||||||
|
creatable: true,
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
name: "State Duration (seconds)",
|
||||||
|
control: "textfield",
|
||||||
|
cssClass: "l-input-sm l-numeric",
|
||||||
|
key: "duration",
|
||||||
|
required: true,
|
||||||
|
property: [
|
||||||
|
"telemetry",
|
||||||
|
"duration"
|
||||||
|
],
|
||||||
|
pattern: "^\\d*(\\.\\d*)?$"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
initialize: function (object) {
|
||||||
|
object.telemetry = {
|
||||||
|
duration: 5,
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
key: "name",
|
||||||
|
name: "Name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "utc",
|
||||||
|
name: "Time",
|
||||||
|
format: "utc",
|
||||||
|
hints: {
|
||||||
|
domain: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "state",
|
||||||
|
source: "value",
|
||||||
|
name: "State",
|
||||||
|
format: "enum",
|
||||||
|
enumerations: [
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
string: "OFF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 1,
|
||||||
|
string: "ON"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
hints: {
|
||||||
|
range: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "value",
|
||||||
|
name: "Value",
|
||||||
|
hints: {
|
||||||
|
range: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
openmct.telemetry.addProvider(new StateGeneratorProvider());
|
||||||
|
|
||||||
openmct.types.addType("generator", {
|
openmct.types.addType("generator", {
|
||||||
name: "Sine Wave Generator",
|
name: "Sine Wave Generator",
|
||||||
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
|
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
|
||||||
@ -99,6 +170,18 @@ define([
|
|||||||
"dataRateInHz"
|
"dataRateInHz"
|
||||||
],
|
],
|
||||||
pattern: "^\\d*(\\.\\d*)?$"
|
pattern: "^\\d*(\\.\\d*)?$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Phase (radians)",
|
||||||
|
control: "textfield",
|
||||||
|
cssClass: "l-input-sm l-numeric",
|
||||||
|
key: "phase",
|
||||||
|
required: true,
|
||||||
|
property: [
|
||||||
|
"telemetry",
|
||||||
|
"phase"
|
||||||
|
],
|
||||||
|
pattern: "^\\d*(\\.\\d*)?$"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
initialize: function (object) {
|
initialize: function (object) {
|
||||||
@ -107,7 +190,12 @@ define([
|
|||||||
amplitude: 1,
|
amplitude: 1,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
dataRateInHz: 1,
|
dataRateInHz: 1,
|
||||||
|
phase: 0,
|
||||||
values: [
|
values: [
|
||||||
|
{
|
||||||
|
key: "name",
|
||||||
|
name: "Name"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "utc",
|
key: "utc",
|
||||||
name: "Time",
|
name: "Time",
|
||||||
@ -142,6 +230,7 @@ define([
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
openmct.telemetry.addProvider(new GeneratorProvider());
|
openmct.telemetry.addProvider(new GeneratorProvider());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,8 +48,9 @@ define([
|
|||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg"
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg"
|
||||||
];
|
];
|
||||||
|
|
||||||
function pointForTimestamp(timestamp) {
|
function pointForTimestamp(timestamp, name) {
|
||||||
return {
|
return {
|
||||||
|
name: name,
|
||||||
utc: Math.floor(timestamp / 5000) * 5000,
|
utc: Math.floor(timestamp / 5000) * 5000,
|
||||||
url: IMAGE_SAMPLES[Math.floor(timestamp / 5000) % IMAGE_SAMPLES.length]
|
url: IMAGE_SAMPLES[Math.floor(timestamp / 5000) % IMAGE_SAMPLES.length]
|
||||||
};
|
};
|
||||||
@ -61,7 +62,7 @@ define([
|
|||||||
},
|
},
|
||||||
subscribe: function (domainObject, callback) {
|
subscribe: function (domainObject, callback) {
|
||||||
var interval = setInterval(function () {
|
var interval = setInterval(function () {
|
||||||
callback(pointForTimestamp(Date.now()));
|
callback(pointForTimestamp(Date.now(), domainObject.name));
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
return function (interval) {
|
return function (interval) {
|
||||||
@ -79,8 +80,8 @@ define([
|
|||||||
var start = options.start;
|
var start = options.start;
|
||||||
var end = options.end;
|
var end = options.end;
|
||||||
var data = [];
|
var data = [];
|
||||||
while (start < end && data.length < 5000) {
|
while (start <= end && data.length < 5000) {
|
||||||
data.push(pointForTimestamp(start));
|
data.push(pointForTimestamp(start, domainObject.name));
|
||||||
start += 5000;
|
start += 5000;
|
||||||
}
|
}
|
||||||
return Promise.resolve(data);
|
return Promise.resolve(data);
|
||||||
@ -93,7 +94,7 @@ define([
|
|||||||
options.strategy === 'latest';
|
options.strategy === 'latest';
|
||||||
},
|
},
|
||||||
request: function (domainObject, options) {
|
request: function (domainObject, options) {
|
||||||
return Promise.resolve([pointForTimestamp(Date.now())]);
|
return Promise.resolve([pointForTimestamp(Date.now(), domainObject.name)]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -109,6 +110,10 @@ define([
|
|||||||
initialize: function (object) {
|
initialize: function (object) {
|
||||||
object.telemetry = {
|
object.telemetry = {
|
||||||
values: [
|
values: [
|
||||||
|
{
|
||||||
|
name: 'Name',
|
||||||
|
key: 'name'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Time',
|
name: 'Time',
|
||||||
key: 'utc',
|
key: 'utc',
|
||||||
|
@ -127,7 +127,8 @@
|
|||||||
{ 'meaning': 'Timer object', 'cssClass': 'icon-timer', 'cssContent': 'e1127', 'htmlEntity': '&#xe1127' },
|
{ 'meaning': 'Timer object', 'cssClass': 'icon-timer', 'cssContent': 'e1127', 'htmlEntity': '&#xe1127' },
|
||||||
{ 'meaning': 'Data Topic', 'cssClass': 'icon-topic', 'cssContent': 'e1128', 'htmlEntity': '&#xe1128' },
|
{ 'meaning': 'Data Topic', 'cssClass': 'icon-topic', 'cssContent': 'e1128', 'htmlEntity': '&#xe1128' },
|
||||||
{ 'meaning': 'Fixed Position object', 'cssClass': 'icon-box-with-dashed-lines', 'cssContent': 'e1129', 'htmlEntity': '&#xe1129' },
|
{ 'meaning': 'Fixed Position object', 'cssClass': 'icon-box-with-dashed-lines', 'cssContent': 'e1129', 'htmlEntity': '&#xe1129' },
|
||||||
{ 'meaning': 'Summary Widget', 'cssClass': 'icon-summary-widget', 'cssContent': 'e1130', 'htmlEntity': '&#xe1130' }
|
{ 'meaning': 'Summary Widget', 'cssClass': 'icon-summary-widget', 'cssContent': 'e1130', 'htmlEntity': '&#xe1130' },
|
||||||
|
{ 'meaning': 'Notebook object', 'cssClass': 'icon-notebook', 'cssContent': 'e1131', 'htmlEntity': '&#xe1131' }
|
||||||
];
|
];
|
||||||
"></div>
|
"></div>
|
||||||
|
|
||||||
|
28
gulpfile.js
28
gulpfile.js
@ -46,9 +46,22 @@ var gulp = require('gulp'),
|
|||||||
name: 'bower_components/almond/almond.js',
|
name: 'bower_components/almond/almond.js',
|
||||||
include: paths.main.replace('.js', ''),
|
include: paths.main.replace('.js', ''),
|
||||||
wrap: {
|
wrap: {
|
||||||
startFile: "src/start.frag",
|
start: (function () {
|
||||||
|
var buildVariables = {
|
||||||
|
version: project.version,
|
||||||
|
timestamp: moment.utc(Date.now()).format(),
|
||||||
|
revision: fs.existsSync('.git') ? git.long() : 'Unknown',
|
||||||
|
branch: fs.existsSync('.git') ? git.branch() : 'Unknown'
|
||||||
|
};
|
||||||
|
return fs.readFileSync("src/start.frag", 'utf-8')
|
||||||
|
.replace(/@@(\w+)/g, function (match, key) {
|
||||||
|
return buildVariables[key];
|
||||||
|
});;
|
||||||
|
}()),
|
||||||
endFile: "src/end.frag"
|
endFile: "src/end.frag"
|
||||||
},
|
},
|
||||||
|
optimize: 'uglify2',
|
||||||
|
uglify2: { output: { comments: /@preserve/ } },
|
||||||
mainConfigFile: paths.main,
|
mainConfigFile: paths.main,
|
||||||
wrapShim: true
|
wrapShim: true
|
||||||
},
|
},
|
||||||
@ -58,14 +71,6 @@ var gulp = require('gulp'),
|
|||||||
},
|
},
|
||||||
sass: {
|
sass: {
|
||||||
sourceComments: true
|
sourceComments: true
|
||||||
},
|
|
||||||
replace: {
|
|
||||||
variables: {
|
|
||||||
version: project.version,
|
|
||||||
timestamp: moment.utc(Date.now()).format(),
|
|
||||||
revision: fs.existsSync('.git') ? git.long() : 'Unknown',
|
|
||||||
branch: fs.existsSync('.git') ? git.branch() : 'Unknown'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,16 +81,11 @@ if (process.env.NODE_ENV === 'development') {
|
|||||||
|
|
||||||
gulp.task('scripts', function () {
|
gulp.task('scripts', function () {
|
||||||
var requirejsOptimize = require('gulp-requirejs-optimize');
|
var requirejsOptimize = require('gulp-requirejs-optimize');
|
||||||
var replace = require('gulp-replace-task');
|
|
||||||
var header = require('gulp-header');
|
|
||||||
var comment = fs.readFileSync('src/about.frag');
|
|
||||||
|
|
||||||
return gulp.src(paths.main)
|
return gulp.src(paths.main)
|
||||||
.pipe(sourcemaps.init())
|
.pipe(sourcemaps.init())
|
||||||
.pipe(requirejsOptimize(options.requirejsOptimize))
|
.pipe(requirejsOptimize(options.requirejsOptimize))
|
||||||
.pipe(sourcemaps.write('.'))
|
.pipe(sourcemaps.write('.'))
|
||||||
.pipe(replace(options.replace))
|
|
||||||
.pipe(header(comment, options.replace.variables))
|
|
||||||
.pipe(gulp.dest(paths.dist));
|
.pipe(gulp.dest(paths.dist));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
);
|
);
|
||||||
openmct.install(openmct.plugins.MyItems());
|
openmct.install(openmct.plugins.MyItems());
|
||||||
openmct.install(openmct.plugins.LocalStorage());
|
openmct.install(openmct.plugins.LocalStorage());
|
||||||
openmct.install(openmct.plugins.Espresso());
|
openmct.install(openmct.plugins.Snow());
|
||||||
openmct.install(openmct.plugins.Generator());
|
openmct.install(openmct.plugins.Generator());
|
||||||
openmct.install(openmct.plugins.ExampleImagery());
|
openmct.install(openmct.plugins.ExampleImagery());
|
||||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||||
|
11
openmct.js
11
openmct.js
@ -19,7 +19,7 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/*global requirejs*/
|
/*global requirejs,BUILD_CONSTANTS*/
|
||||||
|
|
||||||
requirejs.config({
|
requirejs.config({
|
||||||
"paths": {
|
"paths": {
|
||||||
@ -91,12 +91,17 @@ requirejs.config({
|
|||||||
define([
|
define([
|
||||||
'./platform/framework/src/Main',
|
'./platform/framework/src/Main',
|
||||||
'./src/defaultRegistry',
|
'./src/defaultRegistry',
|
||||||
'./src/MCT'
|
'./src/MCT',
|
||||||
], function (Main, defaultRegistry, MCT) {
|
'./src/plugins/buildInfo/plugin'
|
||||||
|
], function (Main, defaultRegistry, MCT, buildInfo) {
|
||||||
var openmct = new MCT();
|
var openmct = new MCT();
|
||||||
|
|
||||||
openmct.legacyRegistry = defaultRegistry;
|
openmct.legacyRegistry = defaultRegistry;
|
||||||
|
|
||||||
|
if (typeof BUILD_CONSTANTS !== 'undefined') {
|
||||||
|
openmct.install(buildInfo(BUILD_CONSTANTS));
|
||||||
|
}
|
||||||
|
|
||||||
openmct.on('start', function () {
|
openmct.on('start', function () {
|
||||||
return new Main().run(defaultRegistry);
|
return new Main().run(defaultRegistry);
|
||||||
});
|
});
|
||||||
|
@ -22,12 +22,10 @@
|
|||||||
"git-rev-sync": "^1.4.0",
|
"git-rev-sync": "^1.4.0",
|
||||||
"glob": ">= 3.0.0",
|
"glob": ">= 3.0.0",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"gulp-header": "^1.8.8",
|
|
||||||
"gulp-jscs": "^3.0.2",
|
"gulp-jscs": "^3.0.2",
|
||||||
"gulp-jshint": "^2.0.0",
|
"gulp-jshint": "^2.0.0",
|
||||||
"gulp-jshint-html-reporter": "^0.1.3",
|
"gulp-jshint-html-reporter": "^0.1.3",
|
||||||
"gulp-rename": "^1.2.2",
|
"gulp-rename": "^1.2.2",
|
||||||
"gulp-replace-task": "^0.11.0",
|
|
||||||
"gulp-requirejs-optimize": "^0.3.1",
|
"gulp-requirejs-optimize": "^0.3.1",
|
||||||
"gulp-sass": "^2.2.0",
|
"gulp-sass": "^2.2.0",
|
||||||
"gulp-sourcemaps": "^1.6.0",
|
"gulp-sourcemaps": "^1.6.0",
|
||||||
|
@ -26,6 +26,7 @@ define([
|
|||||||
"./src/InspectorPaneController",
|
"./src/InspectorPaneController",
|
||||||
"./src/BrowseObjectController",
|
"./src/BrowseObjectController",
|
||||||
"./src/MenuArrowController",
|
"./src/MenuArrowController",
|
||||||
|
"./src/ObjectHeaderController",
|
||||||
"./src/navigation/NavigationService",
|
"./src/navigation/NavigationService",
|
||||||
"./src/navigation/NavigateAction",
|
"./src/navigation/NavigateAction",
|
||||||
"./src/navigation/OrphanNavigationHandler",
|
"./src/navigation/OrphanNavigationHandler",
|
||||||
@ -36,6 +37,7 @@ define([
|
|||||||
"text!./res/templates/browse-object.html",
|
"text!./res/templates/browse-object.html",
|
||||||
"text!./res/templates/items/grid-item.html",
|
"text!./res/templates/items/grid-item.html",
|
||||||
"text!./res/templates/browse/object-header.html",
|
"text!./res/templates/browse/object-header.html",
|
||||||
|
"text!./res/templates/browse/object-header-frame.html",
|
||||||
"text!./res/templates/menu-arrow.html",
|
"text!./res/templates/menu-arrow.html",
|
||||||
"text!./res/templates/back-arrow.html",
|
"text!./res/templates/back-arrow.html",
|
||||||
"text!./res/templates/items/items.html",
|
"text!./res/templates/items/items.html",
|
||||||
@ -48,6 +50,7 @@ define([
|
|||||||
InspectorPaneController,
|
InspectorPaneController,
|
||||||
BrowseObjectController,
|
BrowseObjectController,
|
||||||
MenuArrowController,
|
MenuArrowController,
|
||||||
|
ObjectHeaderController,
|
||||||
NavigationService,
|
NavigationService,
|
||||||
NavigateAction,
|
NavigateAction,
|
||||||
OrphanNavigationHandler,
|
OrphanNavigationHandler,
|
||||||
@ -58,6 +61,7 @@ define([
|
|||||||
browseObjectTemplate,
|
browseObjectTemplate,
|
||||||
gridItemTemplate,
|
gridItemTemplate,
|
||||||
objectHeaderTemplate,
|
objectHeaderTemplate,
|
||||||
|
objectHeaderFrameTemplate,
|
||||||
menuArrowTemplate,
|
menuArrowTemplate,
|
||||||
backArrowTemplate,
|
backArrowTemplate,
|
||||||
itemsTemplate,
|
itemsTemplate,
|
||||||
@ -140,6 +144,13 @@ define([
|
|||||||
"$location",
|
"$location",
|
||||||
"$attrs"
|
"$attrs"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ObjectHeaderController",
|
||||||
|
"implementation": ObjectHeaderController,
|
||||||
|
"depends": [
|
||||||
|
"$scope"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"representations": [
|
"representations": [
|
||||||
@ -173,6 +184,13 @@ define([
|
|||||||
"type"
|
"type"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "object-header-frame",
|
||||||
|
"template": objectHeaderFrameTemplate,
|
||||||
|
"uses": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "menu-arrow",
|
"key": "menu-arrow",
|
||||||
"template": menuArrowTemplate,
|
"template": menuArrowTemplate,
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
<!--
|
||||||
|
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 class='type-icon flex-elem {{type.getCssClass()}}'></span>
|
||||||
|
<span class="l-elem-wrapper l-flex-row flex-elem grows" ng-controller="ObjectHeaderController as controller">
|
||||||
|
<span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
|
||||||
|
<span class='title-label flex-elem holder flex-can-shrink s-input-inline'>{{model.name}}</span>
|
||||||
|
<span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span>
|
||||||
|
<mct-representation
|
||||||
|
key="'menu-arrow'"
|
||||||
|
mct-object='domainObject'
|
||||||
|
class="flex-elem context-available-w"></mct-representation>
|
||||||
|
</span>
|
@ -20,9 +20,13 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<span class='type-icon flex-elem {{type.getCssClass()}}'></span>
|
<span class='type-icon flex-elem {{type.getCssClass()}}'></span>
|
||||||
<span class="l-elem-wrapper l-flex-row flex-elem grows">
|
<span class="l-elem-wrapper l-flex-row flex-elem grows" ng-controller="ObjectHeaderController as controller">
|
||||||
<span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
|
<span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
|
||||||
<span class='title-label flex-elem holder flex-can-shrink'>{{model.name}}</span>
|
<span ng-attr-contenteditable="{{ controller.editable ? true : undefined }}"
|
||||||
|
class='title-label flex-elem holder flex-can-shrink s-input-inline'
|
||||||
|
ng-click="controller.edit()"
|
||||||
|
ng-blur="controller.updateName($event)"
|
||||||
|
ng-keypress="controller.updateName($event)">{{model.name}}</span>
|
||||||
<span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span>
|
<span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span>
|
||||||
<mct-representation
|
<mct-representation
|
||||||
key="'menu-arrow'"
|
key="'menu-arrow'"
|
||||||
|
92
platform/commonUI/browse/src/ObjectHeaderController.js
Normal file
92
platform/commonUI/browse/src/ObjectHeaderController.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller to provide the ability to inline edit an object name.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @memberof platform/commonUI/browse
|
||||||
|
*/
|
||||||
|
function ObjectHeaderController($scope) {
|
||||||
|
this.$scope = $scope;
|
||||||
|
this.domainObject = $scope.domainObject;
|
||||||
|
this.editable = this.allowEdit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the object name on blur and enter keypress events.
|
||||||
|
*
|
||||||
|
* @param event the mouse event
|
||||||
|
*/
|
||||||
|
ObjectHeaderController.prototype.updateName = function (event) {
|
||||||
|
if (!event || !event.currentTarget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'blur') {
|
||||||
|
this.updateModel(event);
|
||||||
|
} else if (event.which === 13) {
|
||||||
|
this.updateModel(event);
|
||||||
|
event.currentTarget.blur();
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the model.
|
||||||
|
*
|
||||||
|
* @param event the mouse event
|
||||||
|
* @param private
|
||||||
|
*/
|
||||||
|
ObjectHeaderController.prototype.updateModel = function (event) {
|
||||||
|
var name = event.currentTarget.textContent.replace(/\n/g, ' ');
|
||||||
|
|
||||||
|
if (name.length === 0) {
|
||||||
|
name = "Unnamed " + this.domainObject.getCapability("type").typeDef.name;
|
||||||
|
event.currentTarget.textContent = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name !== this.domainObject.getModel().name) {
|
||||||
|
this.domainObject.getCapability('mutation').mutate(function (model) {
|
||||||
|
model.name = name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the domain object is editable.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @return true if object is editable
|
||||||
|
*/
|
||||||
|
ObjectHeaderController.prototype.allowEdit = function () {
|
||||||
|
var type = this.domainObject && this.domainObject.getCapability('type');
|
||||||
|
return !!(type && type.hasFeature('creation'));
|
||||||
|
};
|
||||||
|
|
||||||
|
return ObjectHeaderController;
|
||||||
|
}
|
||||||
|
);
|
137
platform/commonUI/browse/test/ObjectHeaderControllerSpec.js
Normal file
137
platform/commonUI/browse/test/ObjectHeaderControllerSpec.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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/ObjectHeaderController"],
|
||||||
|
function (ObjectHeaderController) {
|
||||||
|
|
||||||
|
describe("The object header controller", function () {
|
||||||
|
var mockScope,
|
||||||
|
mockDomainObject,
|
||||||
|
mockCapabilities,
|
||||||
|
mockMutationCapability,
|
||||||
|
mockTypeCapability,
|
||||||
|
mockEvent,
|
||||||
|
mockCurrentTarget,
|
||||||
|
model,
|
||||||
|
controller;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockMutationCapability = jasmine.createSpyObj("mutation", ["mutate"]);
|
||||||
|
mockTypeCapability = jasmine.createSpyObj("type", ["typeDef", "hasFeature"]);
|
||||||
|
mockTypeCapability.typeDef = { name: ""};
|
||||||
|
mockTypeCapability.hasFeature.andCallFake(function (feature) {
|
||||||
|
return feature === 'creation';
|
||||||
|
});
|
||||||
|
|
||||||
|
mockCapabilities = {
|
||||||
|
mutation: mockMutationCapability,
|
||||||
|
type: mockTypeCapability
|
||||||
|
};
|
||||||
|
|
||||||
|
model = {
|
||||||
|
name: "Test name"
|
||||||
|
};
|
||||||
|
mockDomainObject = jasmine.createSpyObj("domainObject", ["getCapability", "getModel"]);
|
||||||
|
mockDomainObject.getModel.andReturn(model);
|
||||||
|
mockDomainObject.getCapability.andCallFake(function (key) {
|
||||||
|
return mockCapabilities[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
mockScope = {
|
||||||
|
domainObject: mockDomainObject
|
||||||
|
};
|
||||||
|
|
||||||
|
mockCurrentTarget = jasmine.createSpyObj("currentTarget", ["blur", "textContent"]);
|
||||||
|
mockCurrentTarget.blur.andReturn(mockCurrentTarget);
|
||||||
|
|
||||||
|
mockEvent = {
|
||||||
|
which: {},
|
||||||
|
type: {},
|
||||||
|
currentTarget: mockCurrentTarget
|
||||||
|
};
|
||||||
|
|
||||||
|
controller = new ObjectHeaderController(mockScope);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the model with new name on blur", function () {
|
||||||
|
mockEvent.type = "blur";
|
||||||
|
mockCurrentTarget.textContent = "New name";
|
||||||
|
controller.updateName(mockEvent);
|
||||||
|
|
||||||
|
expect(mockMutationCapability.mutate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the model with a default for blank names", function () {
|
||||||
|
mockEvent.type = "blur";
|
||||||
|
mockCurrentTarget.textContent = "";
|
||||||
|
controller.updateName(mockEvent);
|
||||||
|
|
||||||
|
expect(mockCurrentTarget.textContent.length).not.toEqual(0);
|
||||||
|
expect(mockMutationCapability.mutate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not update the model if the same name", function () {
|
||||||
|
mockEvent.type = "blur";
|
||||||
|
mockCurrentTarget.textContent = mockDomainObject.getModel().name;
|
||||||
|
controller.updateName(mockEvent);
|
||||||
|
|
||||||
|
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the model on enter keypress event only", function () {
|
||||||
|
mockCurrentTarget.textContent = "New name";
|
||||||
|
controller.updateName(mockEvent);
|
||||||
|
|
||||||
|
expect(mockMutationCapability.mutate).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
mockEvent.which = 13;
|
||||||
|
controller.updateName(mockEvent);
|
||||||
|
|
||||||
|
expect(mockMutationCapability.mutate).toHaveBeenCalledWith(jasmine.any(Function));
|
||||||
|
|
||||||
|
mockMutationCapability.mutate.mostRecentCall.args[0](model);
|
||||||
|
|
||||||
|
expect(mockDomainObject.getModel().name).toBe("New name");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("blurs the field on enter key press", function () {
|
||||||
|
mockCurrentTarget.textContent = "New name";
|
||||||
|
mockEvent.which = 13;
|
||||||
|
controller.updateName(mockEvent);
|
||||||
|
|
||||||
|
expect(mockEvent.currentTarget.blur).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows editting name when object is creatable", function () {
|
||||||
|
expect(controller.allowEdit()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("disallows editting name when object is non-creatable", function () {
|
||||||
|
mockTypeCapability.hasFeature.andReturn(false);
|
||||||
|
|
||||||
|
expect(controller.allowEdit()).toBe(false);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -23,7 +23,7 @@
|
|||||||
<div class="s-menu-button major create-button" ng-click="createController.toggle()">
|
<div class="s-menu-button major create-button" ng-click="createController.toggle()">
|
||||||
<span class="title-label">Create</span>
|
<span class="title-label">Create</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu super-menu" ng-show="createController.isActive()">
|
<div class="menu super-menu l-create-menu" ng-show="createController.isActive()">
|
||||||
<mct-representation mct-object="domainObject" key="'create-menu'">
|
<mct-representation mct-object="domainObject" key="'create-menu'">
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="contents" ng-controller="CreateMenuController">
|
<div class="w-menu" ng-controller="CreateMenuController">
|
||||||
<div class="pane left menu-items">
|
<div class="col menu-items">
|
||||||
<ul>
|
<ul>
|
||||||
<li ng-repeat="createAction in createActions" ng-click="createAction.perform()">
|
<li ng-repeat="createAction in createActions" ng-click="createAction.perform()">
|
||||||
<a ng-mouseover="representation.activeMetadata = createAction.getMetadata()"
|
<a ng-mouseover="representation.activeMetadata = createAction.getMetadata()"
|
||||||
@ -31,13 +31,15 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="pane right menu-item-description">
|
<div class="col menu-item-description">
|
||||||
<div class="desc-area icon {{ representation.activeMetadata.cssClass }}"></div>
|
<div class="desc-area icon {{ representation.activeMetadata.cssClass }}"></div>
|
||||||
<div class="desc-area title">
|
<div class="w-title-desc">
|
||||||
{{representation.activeMetadata.name}}
|
<div class="desc-area title">
|
||||||
</div>
|
{{representation.activeMetadata.name}}
|
||||||
<div class="desc-area description">
|
</div>
|
||||||
{{representation.activeMetadata.description}}
|
<div class="desc-area description">
|
||||||
|
{{representation.activeMetadata.description}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -121,6 +121,9 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
UTCTimeFormat.prototype.parse = function (text) {
|
UTCTimeFormat.prototype.parse = function (text) {
|
||||||
|
if (typeof text === 'number') {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
return moment.utc(text, DATE_FORMATS).valueOf();
|
return moment.utc(text, DATE_FORMATS).valueOf();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "openmct-symbols-16px",
|
"name": "openmct-symbols-16px",
|
||||||
"lastOpened": 0,
|
"lastOpened": 0,
|
||||||
"created": 1505151140023
|
"created": 1506973656040
|
||||||
},
|
},
|
||||||
"iconSets": [
|
"iconSets": [
|
||||||
{
|
{
|
||||||
@ -899,6 +899,14 @@
|
|||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"code": 921904,
|
"code": 921904,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"order": 139,
|
||||||
|
"id": 117,
|
||||||
|
"name": "icon-notebook",
|
||||||
|
"prevSize": 24,
|
||||||
|
"code": 921905,
|
||||||
|
"tempChar": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
@ -3524,6 +3532,29 @@
|
|||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 117,
|
||||||
|
"paths": [
|
||||||
|
"M896 110.8c0-79.8-55.4-127.4-123-105.4l-773 250.6h896v-145.2z",
|
||||||
|
"M896 320h-896v576c0 70.4 57.6 128 128 128h768c70.4 0 128-57.6 128-128v-448c0-70.4-57.6-128-128-128zM832 832h-384v-320h384v320z"
|
||||||
|
],
|
||||||
|
"attrs": [
|
||||||
|
{},
|
||||||
|
{}
|
||||||
|
],
|
||||||
|
"isMulticolor": false,
|
||||||
|
"isMulticolor2": false,
|
||||||
|
"grid": 16,
|
||||||
|
"tags": [
|
||||||
|
"icon-notebook"
|
||||||
|
],
|
||||||
|
"colorPermutations": {
|
||||||
|
"1161751207457516161751": [
|
||||||
|
{},
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"colorThemes": [
|
"colorThemes": [
|
||||||
|
Binary file not shown.
@ -118,4 +118,5 @@
|
|||||||
<glyph unicode="󡄨" glyph-name="icon-topic" d="M454.36 483.36l86.3 86.3c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c19.328-19.358 42.832-34.541 69.047-44.082l1.313 171.722-57.64 57.64c-34.407 34.33-81.9 55.558-134.35 55.558s-99.943-21.228-134.354-55.562l-86.296-86.297c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-28.674 28.654v-172.14c19.045-7.022 41.040-11.084 63.984-11.084 52.463 0 99.966 21.239 134.379 55.587zM505.64 412.64l-86.3-86.3c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-86.294 86.294c-2 2-4.2 4-6.36 6v-197.36c33.664-30.72 78.65-49.537 128.031-49.537 52.44 0 99.923 21.22 134.333 55.541l86.296 86.296c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c2-2 4.2-4 6.36-6v197.36c-33.664 30.72-78.65 49.537-128.031 49.537-52.44 0-99.923-21.22-134.333-55.541zM832 960h-128v-192h127.66l0.34-0.34v-639.32l-0.34-0.34h-127.66v-192h128c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM320 128h-127.66l-0.34 0.34v639.32l0.34 0.34h127.66v192h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192z" />
|
<glyph unicode="󡄨" glyph-name="icon-topic" d="M454.36 483.36l86.3 86.3c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c19.328-19.358 42.832-34.541 69.047-44.082l1.313 171.722-57.64 57.64c-34.407 34.33-81.9 55.558-134.35 55.558s-99.943-21.228-134.354-55.562l-86.296-86.297c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-28.674 28.654v-172.14c19.045-7.022 41.040-11.084 63.984-11.084 52.463 0 99.966 21.239 134.379 55.587zM505.64 412.64l-86.3-86.3c-9.088-8.965-21.577-14.502-35.36-14.502s-26.272 5.537-35.366 14.507l-86.294 86.294c-2 2-4.2 4-6.36 6v-197.36c33.664-30.72 78.65-49.537 128.031-49.537 52.44 0 99.923 21.22 134.333 55.541l86.296 86.296c9.088 8.965 21.577 14.502 35.36 14.502s26.272-5.537 35.366-14.507l86.294-86.294c2-2 4.2-4 6.36-6v197.36c-33.664 30.72-78.65 49.537-128.031 49.537-52.44 0-99.923-21.22-134.333-55.541zM832 960h-128v-192h127.66l0.34-0.34v-639.32l-0.34-0.34h-127.66v-192h128c105.6 0 192 86.4 192 192v640c0 105.6-86.4 192-192 192zM320 128h-127.66l-0.34 0.34v639.32l0.34 0.34h127.66v192h-128c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h128v192z" />
|
||||||
<glyph unicode="󡄩" glyph-name="icon-box-with-dashed-lines" d="M0 576h128v-256h-128v256zM128 831.78l0.22 0.22h191.78v128h-192c-70.606-0.215-127.785-57.394-128-127.979v-192.021h128v191.78zM128 64.22v191.78h-128v-192c0.215-70.606 57.394-127.785 127.979-128h192.021v128h-191.78zM384 960h256v-128h-256v128zM896 64.22l-0.22-0.22h-191.78v-128h192c70.606 0.215 127.785 57.394 128 127.979v192.021h-128v-191.78zM896 960h-192v-128h191.78l0.22-0.22v-191.78h128v192c-0.215 70.606-57.394 127.785-127.979 128zM896 576h128v-256h-128v256zM384 64h256v-128h-256v128zM256 704h512v-512h-512v512z" />
|
<glyph unicode="󡄩" glyph-name="icon-box-with-dashed-lines" d="M0 576h128v-256h-128v256zM128 831.78l0.22 0.22h191.78v128h-192c-70.606-0.215-127.785-57.394-128-127.979v-192.021h128v191.78zM128 64.22v191.78h-128v-192c0.215-70.606 57.394-127.785 127.979-128h192.021v128h-191.78zM384 960h256v-128h-256v128zM896 64.22l-0.22-0.22h-191.78v-128h192c70.606 0.215 127.785 57.394 128 127.979v192.021h-128v-191.78zM896 960h-192v-128h191.78l0.22-0.22v-191.78h128v192c-0.215 70.606-57.394 127.785-127.979 128zM896 576h128v-256h-128v256zM384 64h256v-128h-256v128zM256 704h512v-512h-512v512z" />
|
||||||
<glyph unicode="󡄰" glyph-name="icon-summary-widget" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM847.8 349.6l-82.6-143.2-189.6 131.6 19.2-230h-165.4l19.2 230-189.6-131.6-82.6 143.2 208.6 98.4-208.8 98.4 82.6 143.2 189.6-131.6-19.2 230h165.4l-19.2-230 189.6 131.6 82.6-143.2-208.6-98.4 208.8-98.4z" />
|
<glyph unicode="󡄰" glyph-name="icon-summary-widget" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM847.8 349.6l-82.6-143.2-189.6 131.6 19.2-230h-165.4l19.2 230-189.6-131.6-82.6 143.2 208.6 98.4-208.8 98.4 82.6 143.2 189.6-131.6-19.2 230h165.4l-19.2-230 189.6 131.6 82.6-143.2-208.6-98.4 208.8-98.4z" />
|
||||||
|
<glyph unicode="󡄱" glyph-name="icon-notebook" d="M896 849.2c0 79.8-55.4 127.4-123 105.4l-773-250.6h896v145.2zM896 640h-896v-576c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v448c0 70.4-57.6 128-128 128zM832 128h-384v320h384v-320z" />
|
||||||
</font></defs></svg>
|
</font></defs></svg>
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Binary file not shown.
Binary file not shown.
@ -99,7 +99,7 @@ $plotXBarH: 32px;
|
|||||||
$plotLegendH: 20px;
|
$plotLegendH: 20px;
|
||||||
$plotSwatchD: 8px;
|
$plotSwatchD: 8px;
|
||||||
// 1: Top, 2: right, 3: bottom, 4: left
|
// 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 */
|
/* min plot height is based on user testing to find minimum useful height */
|
||||||
$plotMinH: 95px;
|
$plotMinH: 95px;
|
||||||
/*************** Bubbles */
|
/*************** Bubbles */
|
||||||
@ -111,7 +111,9 @@ $bubbleMaxW: 300px;
|
|||||||
$reqSymbolW: 15px;
|
$reqSymbolW: 15px;
|
||||||
$reqSymbolM: $interiorMargin * 2;
|
$reqSymbolM: $interiorMargin * 2;
|
||||||
$reqSymbolFontSize: 0.75em;
|
$reqSymbolFontSize: 0.75em;
|
||||||
$inputTextP: 3px 5px;
|
$inputTextPTopBtm: 3px;
|
||||||
|
$inputTextPLeftRight: 5px;
|
||||||
|
$inputTextP: $inputTextPTopBtm $inputTextPLeftRight;
|
||||||
/*************** Wait Spinner Defaults */
|
/*************** Wait Spinner Defaults */
|
||||||
$waitSpinnerD: 32px;
|
$waitSpinnerD: 32px;
|
||||||
$waitSpinnerTreeD: 20px;
|
$waitSpinnerTreeD: 20px;
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
* Use https://icomoon.io/app with icomoon-project-openmct-symbols-12px.json
|
* Use https://icomoon.io/app with icomoon-project-openmct-symbols-12px.json
|
||||||
* to generate font files
|
* 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');
|
||||||
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot?#iefix') format('embedded-opentype'),
|
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot?#iefix') format('embedded-opentype'),
|
||||||
url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.woff') format('woff'),
|
url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.woff') format('woff'),
|
||||||
@ -234,6 +234,12 @@ a.disabled {
|
|||||||
color: rgba(#fff, 0.2);
|
color: rgba(#fff, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comma-list span {
|
||||||
|
&:not(:first-child) {
|
||||||
|
&:before { content: ', '; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.test-stripes {
|
.test-stripes {
|
||||||
@include bgDiagonalStripes();
|
@include bgDiagonalStripes();
|
||||||
}
|
}
|
||||||
|
@ -146,6 +146,7 @@ $glyph-icon-timer: '\e1127';
|
|||||||
$glyph-icon-topic: '\e1128';
|
$glyph-icon-topic: '\e1128';
|
||||||
$glyph-icon-box-with-dashed-lines: '\e1129';
|
$glyph-icon-box-with-dashed-lines: '\e1129';
|
||||||
$glyph-icon-summary-widget: '\e1130';
|
$glyph-icon-summary-widget: '\e1130';
|
||||||
|
$glyph-icon-notebook: '\e1131';
|
||||||
|
|
||||||
/************************** 16 PX CLASSES */
|
/************************** 16 PX CLASSES */
|
||||||
|
|
||||||
@ -260,6 +261,7 @@ $glyph-icon-summary-widget: '\e1130';
|
|||||||
.icon-topic { @include glyphBefore($glyph-icon-topic); }
|
.icon-topic { @include glyphBefore($glyph-icon-topic); }
|
||||||
.icon-box-with-dashed-lines { @include glyphBefore($glyph-icon-box-with-dashed-lines); }
|
.icon-box-with-dashed-lines { @include glyphBefore($glyph-icon-box-with-dashed-lines); }
|
||||||
.icon-summary-widget { @include glyphBefore($glyph-icon-summary-widget); }
|
.icon-summary-widget { @include glyphBefore($glyph-icon-summary-widget); }
|
||||||
|
.icon-notebook { @include glyphBefore($glyph-icon-notebook); }
|
||||||
|
|
||||||
/************************** 12 PX CLASSES */
|
/************************** 12 PX CLASSES */
|
||||||
.icon-crosshair-12px { @include glyphBefore($glyph-icon-crosshair,'symbolsfont-12px'); }
|
.icon-crosshair-12px { @include glyphBefore($glyph-icon-crosshair,'symbolsfont-12px'); }
|
||||||
|
@ -44,6 +44,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.t-alert-unsynced {
|
||||||
|
@extend .icon-alert-triangle;
|
||||||
|
color: $colorPausedBg;
|
||||||
|
}
|
||||||
|
|
||||||
.bar .ui-symbol {
|
.bar .ui-symbol {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
@ -81,18 +87,5 @@
|
|||||||
@include transform(scale(0.3));
|
@include transform(scale(0.3));
|
||||||
z-index: 2;
|
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;
|
display: block;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
.l-inspector-part {
|
.l-inspector-part {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding-right: $interiorMargin;
|
padding-right: $interiorMargin;
|
||||||
|
|
||||||
.tree .form {
|
.tree .form {
|
||||||
margin-left: $treeVCW + $interiorMarginLg;
|
margin-left: $treeVCW + $interiorMarginLg;
|
||||||
}
|
}
|
||||||
@ -78,6 +79,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.form-row {
|
.form-row {
|
||||||
|
// To be replaced with .inspector-config, see below.
|
||||||
@include align-items(center);
|
@include align-items(center);
|
||||||
border: none !important;
|
border: none !important;
|
||||||
margin-bottom: 0 !important;
|
margin-bottom: 0 !important;
|
||||||
@ -99,15 +101,12 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul li {
|
|
||||||
margin-bottom: $interiorMarginLg;
|
|
||||||
}
|
|
||||||
|
|
||||||
em.t-inspector-part-header {
|
em.t-inspector-part-header {
|
||||||
border-radius: $basicCr;
|
border-radius: $basicCr;
|
||||||
background-color: $colorInspectorSectionHeaderBg;
|
background-color: $colorInspectorSectionHeaderBg;
|
||||||
color: $colorInspectorSectionHeaderFg;
|
color: $colorInspectorSectionHeaderFg;
|
||||||
margin-bottom: $interiorMargin;
|
margin-top: $interiorMarginLg;
|
||||||
|
//margin-bottom: $interiorMargin;
|
||||||
padding: floor($formTBPad * .75) $formLRPad;
|
padding: floor($formTBPad * .75) $formLRPad;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
@ -201,3 +200,102 @@ mct-representation:not(.s-status-editing) .l-inspect {
|
|||||||
pointer-events: inherit;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,7 @@
|
|||||||
@import "fixed-position";
|
@import "fixed-position";
|
||||||
@import "lists/tabular";
|
@import "lists/tabular";
|
||||||
@import "plots/plots-main";
|
@import "plots/plots-main";
|
||||||
|
@import "plots/legend";
|
||||||
@import "iframe";
|
@import "iframe";
|
||||||
@import "views";
|
@import "views";
|
||||||
@import "items/item";
|
@import "items/item";
|
||||||
|
@ -316,23 +316,28 @@
|
|||||||
text-shadow: $shdwItemText;
|
text-shadow: $shdwItemText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin input-base($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) {
|
@mixin input-base() {
|
||||||
@include appearance(none);
|
@include appearance(none);
|
||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
box-shadow: inset $shdw;
|
&:focus { outline: 0; }
|
||||||
background: $bg;
|
|
||||||
border: none;
|
|
||||||
color: $fg;
|
|
||||||
outline: none;
|
|
||||||
&.error {
|
&.error {
|
||||||
background-color: $colorFormFieldErrorBg;
|
background-color: $colorFormFieldErrorBg;
|
||||||
color: $colorFormFieldErrorFg;
|
color: $colorFormFieldErrorFg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg) {
|
@mixin s-input($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) {
|
||||||
@include input-base($bg, $fg);
|
@include input-base();
|
||||||
|
background: $bg;
|
||||||
|
box-shadow: inset $shdw;
|
||||||
|
color: $fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin nice-input($bg: $colorInputBg, $fg: $colorInputFg, $shdw: rgba(black, 0.6) 0 1px 3px) {
|
||||||
|
@include s-input($bg, $fg, $shdw);
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin contextArrow() {
|
@mixin contextArrow() {
|
||||||
@ -344,7 +349,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mixin nice-textarea($bg: $colorBodyBg, $fg: $colorBodyFg) {
|
@mixin nice-textarea($bg: $colorBodyBg, $fg: $colorBodyFg) {
|
||||||
@include input-base($bg, $fg);
|
@include nice-input($bg, $fg);
|
||||||
padding: $interiorMargin;
|
padding: $interiorMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,6 @@
|
|||||||
content:'';
|
content:'';
|
||||||
font-family: symbolsfont;
|
font-family: symbolsfont;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
display: inline;
|
|
||||||
margin-right: $interiorMarginSm;
|
margin-right: $interiorMarginSm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.l-view-section {
|
.l-view-section {
|
||||||
|
//@include test(orange, 0.1);
|
||||||
@include absPosDefault(0);
|
@include absPosDefault(0);
|
||||||
h2 {
|
h2 {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
@ -19,11 +19,22 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
.s-palette-item {
|
||||||
|
$colorSelectedColor: #fff;
|
||||||
|
@include txtShdwSubtle(0.8);
|
||||||
|
@include trans-prop-nice-fade(0.25s);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
&:hover {
|
||||||
|
@include trans-prop-nice-fade(0);
|
||||||
|
border-color: $colorSelectedColor !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.l-color-palette {
|
.l-color-palette {
|
||||||
$d: 16px;
|
$d: 16px;
|
||||||
$colorsPerRow: 10;
|
$colorsPerRow: 10;
|
||||||
$m: 1;
|
$m: 1;
|
||||||
$colorSelectedColor: #fff;
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: $interiorMargin !important;
|
padding: $interiorMargin !important;
|
||||||
@ -35,10 +46,6 @@
|
|||||||
|
|
||||||
.l-palette-item {
|
.l-palette-item {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@include txtShdwSubtle(0.8);
|
|
||||||
@include trans-prop-nice-fade(0.25s);
|
|
||||||
border: 1px solid transparent;
|
|
||||||
color: $colorSelectedColor;
|
|
||||||
display: block;
|
display: block;
|
||||||
float: left;
|
float: left;
|
||||||
height: $d; width: $d;
|
height: $d; width: $d;
|
||||||
@ -51,13 +58,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-palette-item {
|
|
||||||
&:hover {
|
|
||||||
@include trans-prop-nice-fade(0);
|
|
||||||
border-color: $colorSelectedColor !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.l-palette-item-label {
|
.l-palette-item-label {
|
||||||
margin-left: $interiorMargin;
|
margin-left: $interiorMargin;
|
||||||
}
|
}
|
||||||
@ -69,10 +69,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-inline-color-palette {
|
||||||
|
// Is an <li>
|
||||||
|
.l-palette-row {
|
||||||
|
@include display(flex);
|
||||||
|
width: 100%;
|
||||||
|
.l-palette-item {
|
||||||
|
@include display(flex);
|
||||||
|
@include flex(1 0 auto);
|
||||||
|
margin: 1px;
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
padding-top: 75%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 */
|
/******************************************************** CUSTOM CHECKBOXES */
|
||||||
label.checkbox.custom,
|
label.checkbox.custom,
|
||||||
label.radio.custom {
|
label.radio.custom {
|
||||||
@ -234,12 +254,16 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************** INPUTS */
|
/******************************************************** INPUTS */
|
||||||
|
%input-base {
|
||||||
|
@include input-base();
|
||||||
|
}
|
||||||
|
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
input[type="search"],
|
input[type="search"],
|
||||||
input[type="number"] {
|
input[type="number"] {
|
||||||
@include nice-input();
|
@include nice-input();
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
padding: $inputTextP;
|
padding: $inputTextPTopBtm $inputTextPLeftRight;
|
||||||
&.numeric {
|
&.numeric {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
@ -283,6 +307,32 @@ textarea.lg { position: relative; height: 300px; }
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*[contenteditable].s-input-inline,
|
||||||
|
input[type="text"].s-input-inline,
|
||||||
|
.s-input-inline input[type="text"] {
|
||||||
|
// A text input or contenteditable element that indicates edit affordance on hover and looks like an input on focus
|
||||||
|
@extend %input-base;
|
||||||
|
@include trans-prop-nice((padding, box-shadow), 250ms);
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
min-width: 20px;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
padding-left: $inputTextPLeftRight;
|
||||||
|
padding-right: $inputTextPLeftRight;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
border-color: rgba($colorBodyFg, 0.2);
|
||||||
|
}
|
||||||
|
&:focus {
|
||||||
|
@include s-input();
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************** SELECTS */
|
/******************************************************** SELECTS */
|
||||||
.select {
|
.select {
|
||||||
@include btnSubtle($bg: $colorSelectBg);
|
@include btnSubtle($bg: $colorSelectBg);
|
||||||
@ -298,6 +348,7 @@ textarea.lg { position: relative; height: 300px; }
|
|||||||
select {
|
select {
|
||||||
@include appearance(none);
|
@include appearance(none);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
&:focus { outline: 0; }
|
||||||
background: none;
|
background: none;
|
||||||
color: $colorSelectFg;
|
color: $colorSelectFg;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -722,11 +773,15 @@ body.desktop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.overlay ::-webkit-scrollbar-thumb {
|
.overlay ::-webkit-scrollbar-thumb {
|
||||||
$lr: 15%;
|
|
||||||
background: $scrollbarThumbColorOverlay;
|
background: $scrollbarThumbColorOverlay;
|
||||||
&:hover { background: $scrollbarThumbColorOverlayHov; }
|
&:hover { background: $scrollbarThumbColorOverlayHov; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu ::-webkit-scrollbar-thumb {
|
||||||
|
background: $scrollbarThumbColorMenu;
|
||||||
|
&:hover { background: $scrollbarThumbColorMenuHov; }
|
||||||
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-corner {
|
::-webkit-scrollbar-corner {
|
||||||
background: $scrollbarTrackColorBg;
|
background: $scrollbarTrackColorBg;
|
||||||
}
|
}
|
||||||
|
@ -21,92 +21,87 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/******************************************************** MENU BUTTONS */
|
/******************************************************** MENU BUTTONS */
|
||||||
.s-menu-button {
|
.s-menu-button {
|
||||||
// Formerly .btn-menu
|
// Formerly .btn-menu
|
||||||
@extend .s-button;
|
@extend .s-button;
|
||||||
span.l-click-area {
|
span.l-click-area {
|
||||||
// In markup, this element should not enclose anything.
|
// In markup, this element should not enclose anything.
|
||||||
@extend .abs;
|
@extend .abs;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
font-size: 16px; //120%;
|
font-size: 16px; //120%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-label {
|
.title-label {
|
||||||
margin-left: $interiorMarginSm;
|
margin-left: $interiorMarginSm;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-swatch {
|
.color-swatch {
|
||||||
// Used in color menu buttons in toolbar
|
// Used in color menu buttons in toolbar
|
||||||
$d: 10px;
|
$d: 10px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 1px solid rgba($colorBtnFg, 0.2);
|
border: 1px solid rgba($colorBtnFg, 0.2);
|
||||||
height: $d; width: $d;
|
height: $d;
|
||||||
|
width: $d;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-left: $interiorMarginSm;
|
margin-left: $interiorMarginSm;
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
// Adds the downward facing 'context available / invoke menu' arrow element
|
// Adds the downward facing 'context available / invoke menu' arrow element
|
||||||
@include contextArrow();
|
@include contextArrow();
|
||||||
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
|
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
|
||||||
}
|
}
|
||||||
|
|
||||||
&.create-button {
|
&.create-button {
|
||||||
@extend .icon-plus;
|
@extend .icon-plus;
|
||||||
.title-label {
|
.title-label {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
left: 0;
|
left: 0;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************** MENUS THEMSELVES */
|
/******************************************************** MENUS THEMSELVES */
|
||||||
.menu-element {
|
.menu-element {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
|
||||||
|
|
||||||
.s-menu {
|
|
||||||
border-radius: $basicCr;
|
|
||||||
@include containerSubtle($colorMenuBg, $colorMenuFg);
|
|
||||||
@include boxShdw($shdwMenu);
|
|
||||||
@include txtShdw($shdwMenuText);
|
|
||||||
padding: $interiorMarginSm 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
// TODO: reduce size of icons
|
border-radius: $basicCr;
|
||||||
@extend .s-menu;
|
@include containerSubtle($colorMenuBg, $colorMenuFg);
|
||||||
display: block;
|
@include boxShdw($shdwMenu);
|
||||||
position: absolute;
|
@include txtShdw($shdwMenuText);
|
||||||
z-index: 10;
|
padding: $interiorMarginSm 0;
|
||||||
ul {
|
display: block;
|
||||||
@include menuUlReset();
|
position: absolute;
|
||||||
li {
|
z-index: 10;
|
||||||
box-sizing: border-box;
|
ul {
|
||||||
border-top: 1px solid pullForward($colorMenuBg, 10%);
|
@include menuUlReset();
|
||||||
|
li {
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-top: 1px solid pullForward($colorMenuBg, 10%);
|
||||||
color: $colorMenuFg;
|
color: $colorMenuFg;
|
||||||
//color: pullForward($colorMenuBg, 60%);
|
line-height: $menuLineH;
|
||||||
line-height: $menuLineH;
|
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;
|
||||||
padding: $interiorMarginSm $interiorMargin * 2 $interiorMarginSm ($interiorMargin * 2) + $treeTypeIconW;
|
position: relative;
|
||||||
position: relative;
|
white-space: nowrap;
|
||||||
white-space: nowrap;
|
&:first-child {
|
||||||
&:first-child {
|
border: none;
|
||||||
border: none;
|
}
|
||||||
}
|
&:hover {
|
||||||
&:hover {
|
background: $colorMenuHovBg;
|
||||||
background: $colorMenuHovBg;
|
color: $colorMenuHovFg;
|
||||||
color: $colorMenuHovFg;
|
|
||||||
&:before {
|
&:before {
|
||||||
color: $colorMenuHovIc;
|
color: $colorMenuHovIc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:before {
|
&:before {
|
||||||
@extend .ui-symbol;
|
@extend .ui-symbol;
|
||||||
@extend .type-icon;
|
@extend .type-icon;
|
||||||
@ -114,8 +109,8 @@
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
left: $interiorMargin * 2;
|
left: $interiorMargin * 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu,
|
.menu,
|
||||||
@ -123,94 +118,97 @@
|
|||||||
.context-menu,
|
.context-menu,
|
||||||
.super-menu,
|
.super-menu,
|
||||||
.s-menu-button .menu {
|
.s-menu-button .menu {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
ul li {
|
ul li {
|
||||||
a.menu-item-a {
|
a.menu-item-a {
|
||||||
color: $colorMenuFg;
|
color: $colorMenuFg;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
&:before,
|
&:before,
|
||||||
a.menu-item-a:before {
|
a.menu-item-a:before {
|
||||||
color: $colorMenuIc;
|
color: $colorMenuIc;
|
||||||
left: $interiorMargin;
|
left: $interiorMargin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-menu {
|
.checkbox-menu {
|
||||||
// Used in search dropdown in tree
|
// Used in search dropdown in tree
|
||||||
@extend .context-menu;
|
@extend .context-menu;
|
||||||
ul li {
|
ul li {
|
||||||
padding-left: 50px;
|
padding-left: 50px;
|
||||||
.checkbox {
|
.checkbox {
|
||||||
$d: 0.7rem;
|
$d: 0.7rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: $interiorMargin;
|
left: $interiorMargin;
|
||||||
top: ($menuLineH - $d) / 1.5;
|
top: ($menuLineH - $d) / 1.5;
|
||||||
em {
|
em {
|
||||||
height: $d;
|
height: $d;
|
||||||
width: $d;
|
width: $d;
|
||||||
&:before {
|
&:before {
|
||||||
font-size: 7px !important;
|
font-size: 7px !important;
|
||||||
height: $d;
|
height: $d;
|
||||||
width: $d;
|
width: $d;
|
||||||
line-height: $d;
|
line-height: $d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:before {
|
&:before {
|
||||||
// Type icon
|
// Type icon
|
||||||
left: 25px;
|
left: 25px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.super-menu,
|
||||||
|
.super-menu > mct-representation,
|
||||||
|
.super-menu > .contents {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.super-menu {
|
.super-menu {
|
||||||
$w: 500px;
|
$plw: 50%;
|
||||||
$h: $w - 20;
|
$prw: 100% - $plw;
|
||||||
$plw: 50%;
|
position: absolute;
|
||||||
$prw: 50%;
|
.w-menu {
|
||||||
display: block;
|
align-items: stretch;
|
||||||
width: $w;
|
display: flex;
|
||||||
height: $h;
|
flex-direction: row;
|
||||||
.contents {
|
margin: $interiorMarginLg;
|
||||||
@include absPosDefault($interiorMargin);
|
}
|
||||||
}
|
.col {
|
||||||
.pane {
|
box-sizing: border-box;
|
||||||
box-sizing: border-box;
|
flex: 1 1 auto;
|
||||||
&.menu-items {
|
overflow-x: hidden;
|
||||||
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
&.menu-items {
|
||||||
left: 0;
|
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
||||||
padding-right: $interiorMargin;
|
overflow-y: auto;
|
||||||
right: auto;
|
padding-right: $interiorMargin;
|
||||||
width: $plw;
|
width: $plw;
|
||||||
overflow-x: hidden;
|
ul {
|
||||||
overflow-y: auto;
|
li {
|
||||||
ul {
|
border-radius: $controlCr;
|
||||||
li {
|
padding-left: 30px;
|
||||||
border-radius: $controlCr;
|
border-top: none;
|
||||||
padding-left: 30px;
|
}
|
||||||
border-top: none;
|
}
|
||||||
}
|
}
|
||||||
}
|
&.menu-item-description {
|
||||||
}
|
$p: $interiorMargin * 3;
|
||||||
&.menu-item-description {
|
overflow-y: hidden;
|
||||||
left: auto;
|
padding: $p $p 0 $p;
|
||||||
right: 0;
|
width: $prw;
|
||||||
padding: $interiorMargin * 5;
|
|
||||||
width: $prw;
|
|
||||||
.desc-area {
|
.desc-area {
|
||||||
&.icon {
|
&.icon {
|
||||||
color: $colorCreateMenuLgIcon;
|
color: $colorCreateMenuLgIcon;
|
||||||
font-size: 8em;
|
|
||||||
margin-bottom: $interiorMargin * 3;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
&.title {
|
&.title {
|
||||||
color: $colorCreateMenuText;
|
color: $colorCreateMenuText;
|
||||||
font-size: 1.2em;
|
|
||||||
margin-bottom: $interiorMargin * 2;
|
|
||||||
}
|
}
|
||||||
&.description {
|
&.description {
|
||||||
color: pushBack($colorCreateMenuText, 20%);
|
color: pushBack($colorCreateMenuText, 20%);
|
||||||
@ -218,67 +216,104 @@
|
|||||||
line-height: 1.5em;
|
line-height: 1.5em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w-title-desc {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden; // Height set in specific menu instances
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specific menu instances
|
||||||
|
&.l-create-menu {
|
||||||
|
width: 500px;
|
||||||
|
.col {
|
||||||
|
max-height: 70vh;
|
||||||
|
}
|
||||||
|
.w-title-desc {
|
||||||
|
height: 190px;
|
||||||
|
}
|
||||||
|
.desc-area {
|
||||||
|
&.icon {
|
||||||
|
font-size: 8em;
|
||||||
|
height: 135px;
|
||||||
|
margin-bottom: $interiorMargin * 3;
|
||||||
|
}
|
||||||
|
&.title {
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin-bottom: $interiorMargin * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.mini {
|
&.mini {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
height: 300px;
|
.col {
|
||||||
.pane {
|
max-height: 50vh;
|
||||||
&.menu-items {
|
&.menu-items {
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
&.menu-item-description {
|
&.menu-item-description {
|
||||||
padding: $interiorMargin * 3;
|
$p: $interiorMargin * 2;
|
||||||
.desc-area {
|
padding: $p $p 0 $p;
|
||||||
&.icon {
|
}
|
||||||
font-size: 4em;
|
}
|
||||||
}
|
.w-title-desc {
|
||||||
&.title {
|
height: 180px;
|
||||||
font-size: 1em;
|
}
|
||||||
}
|
.desc-area {
|
||||||
}
|
&.icon {
|
||||||
|
font-size: 4em;
|
||||||
|
height: 70px;
|
||||||
|
margin-bottom: $interiorMargin * 3;
|
||||||
|
}
|
||||||
|
&.title {
|
||||||
|
font-size: 1em;
|
||||||
|
margin-bottom: $interiorMargin * 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-menu {
|
.context-menu {
|
||||||
font-size: 0.80rem;
|
font-size: 0.80rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-menu-holder,
|
.context-menu-holder,
|
||||||
.menu-holder {
|
.menu-holder {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 120;
|
z-index: 120;
|
||||||
.context-menu-wrapper {
|
.context-menu-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
&.go-left .context-menu,
|
&.go-left .context-menu,
|
||||||
&.go-left .menu {
|
&.go-left .menu {
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
&.go-up .context-menu,
|
&.go-up .context-menu,
|
||||||
&.go-up .menu {
|
&.go-up .menu {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-menu-holder {
|
.context-menu-holder {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
width: 170px;
|
width: 170px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-bar.right .menu,
|
.btn-bar.right .menu,
|
||||||
.menus-to-left .menu {
|
.menus-to-left .menu {
|
||||||
z-index: 79;
|
z-index: 79;
|
||||||
left: auto;
|
left: auto;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menus-up .menu {
|
.menus-up .menu {
|
||||||
bottom: $btnStdH; top: auto;
|
bottom: $btnStdH;
|
||||||
|
top: auto;
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.status-indicator {
|
.status-indicator {
|
||||||
|
background: none !important;
|
||||||
margin-right: $interiorMarginSm;
|
margin-right: $interiorMarginSm;
|
||||||
|
&[class*='s-status']:before {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.count {
|
.count {
|
||||||
@ -380,10 +384,6 @@ body.desktop .t-message-list {
|
|||||||
.object-header {
|
.object-header {
|
||||||
.t-object-alert {
|
.t-object-alert {
|
||||||
display: inline;
|
display: inline;
|
||||||
&.t-alert-unsynced {
|
|
||||||
@extend .icon-alert-triangle;
|
|
||||||
color: $colorPausedBg;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,9 +129,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.s-filter {
|
.s-filter {
|
||||||
input[type="search"] {
|
|
||||||
@include input-base();
|
|
||||||
}
|
|
||||||
.clear-icon,
|
.clear-icon,
|
||||||
.menu-icon,
|
.menu-icon,
|
||||||
&:before {
|
&:before {
|
||||||
|
@ -34,18 +34,7 @@ body.touch {
|
|||||||
line-height: $mobileTreeItemH !important;
|
line-height: $mobileTreeItemH !important;
|
||||||
.view-control {
|
.view-control {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
margin-right: $interiorMargin;
|
width: ceil($mobileTreeItemH * 0.5);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.t-object-label {
|
.t-object-label {
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
|
@ -156,6 +156,8 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
padding-right: $interiorMargin;
|
||||||
|
padding-bottom: $interiorMargin;
|
||||||
.field.l-input-med {
|
.field.l-input-med {
|
||||||
input[type='text'] {
|
input[type='text'] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
.abs.holder-plot {
|
.abs.holder-plot {
|
||||||
// Fend off the scrollbar when less than min-height;
|
right: $interiorMargin; // Fend off the scrollbar when less than min-height;
|
||||||
right: $interiorMargin;
|
.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 {
|
.gl-plot {
|
||||||
color: $colorPlotFg;
|
color: $colorPlotFg;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
@ -32,6 +64,19 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: $plotMinH;
|
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 {
|
.gl-plot-wrapper-display-area-and-x-axis {
|
||||||
// Holds the plot area and the X-axis only
|
// Holds the plot area and the X-axis only
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -49,7 +94,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.gl-plot-axis-area.gl-plot-x {
|
.gl-plot-axis-area.gl-plot-x {
|
||||||
//@include test(green);
|
|
||||||
top: auto;
|
top: auto;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -63,7 +107,7 @@
|
|||||||
.gl-plot-axis-area {
|
.gl-plot-axis-area {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
&.gl-plot-y {
|
&.gl-plot-y {
|
||||||
top: $plotLegendH + $interiorMargin;
|
top: nth($plotDisplayArea, 1);
|
||||||
right: auto;
|
right: auto;
|
||||||
bottom: nth($plotDisplayArea, 3);
|
bottom: nth($plotDisplayArea, 3);
|
||||||
left: 0;
|
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 */
|
/****************************** Limits and Out-of-Bounds data */
|
||||||
|
|
||||||
.l-limit-bar,
|
.l-limit-bar,
|
||||||
@ -235,39 +268,6 @@
|
|||||||
border: 1px solid $colorPlotAreaBorder;
|
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 {
|
.tick {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: 0 $colorPlotHash solid;
|
border: 0 $colorPlotHash solid;
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
ul.tree {
|
ul.tree {
|
||||||
@include menuUlReset();
|
@include menuUlReset();
|
||||||
@include user-select(none);
|
@include user-select(none);
|
||||||
li {
|
> li {
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@ -54,18 +54,10 @@ ul.tree {
|
|||||||
color: $colorItemTreeVC;
|
color: $colorItemTreeVC;
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
margin-right: $interiorMargin;
|
margin-right: $interiorMargin;
|
||||||
height: 100%;
|
|
||||||
line-height: inherit;
|
|
||||||
width: $treeVCW;
|
width: $treeVCW;
|
||||||
&.has-children {
|
&.no-children {
|
||||||
&:before {
|
&:before {
|
||||||
position: absolute;
|
content:"";
|
||||||
@include trans-prop-nice(transform, 100ms);
|
|
||||||
content: "\e904";
|
|
||||||
@include transform-origin(center);
|
|
||||||
}
|
|
||||||
&.expanded:before {
|
|
||||||
@include transform(rotate(90deg));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
&.t-object-type-hyperlink {
|
&.t-object-type-hyperlink {
|
||||||
// Hide the right side buttons for objects where they don't make sense
|
// Hide the right side buttons for objects where they don't make sense
|
||||||
// Note that this will hide the view Switcher button if applied
|
// Note that this will hide the view Switcher button if applied
|
||||||
// to an object that it.
|
// to an object that has it.
|
||||||
.object-browse-bar .right { display: none; }
|
.object-browse-bar .right { display: none; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +120,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************** OBJECT TYPES */
|
&.t-frame-outer .s-input-inline {
|
||||||
|
// Prevent inline inputs from being edited when nested in a Layout
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************** OBJECT TYPES */
|
||||||
.t-object-type-hyperlink {
|
.t-object-type-hyperlink {
|
||||||
.object-holder {
|
.object-holder {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<!-- look at action-button for example -->
|
<!-- look at action-button for example -->
|
||||||
<span class="t-filter l-filter s-filter"
|
<span class="t-filter l-filter"
|
||||||
ng-controller="GetterSetterController">
|
ng-controller="GetterSetterController">
|
||||||
<input type="search"
|
<input type="search"
|
||||||
class="t-filter-input"
|
class="t-filter-input"
|
||||||
|
@ -108,8 +108,11 @@ define(
|
|||||||
|
|
||||||
getMetadata();
|
getMetadata();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
|
var mutation = $scope.ngModel.selectedObject.getCapability('mutation');
|
||||||
|
var unlisten = mutation.listen(getMetadata);
|
||||||
|
$scope.$on('$destroy', unlisten);
|
||||||
|
}
|
||||||
return ObjectInspectorController;
|
return ObjectInspectorController;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -83,9 +83,9 @@ define([
|
|||||||
this.activeObject = domainObject;
|
this.activeObject = domainObject;
|
||||||
|
|
||||||
if (domainObject && domainObject.hasCapability('composition')) {
|
if (domainObject && domainObject.hasCapability('composition')) {
|
||||||
$(this.toggleView.elements()).addClass('has-children');
|
$(this.toggleView.elements()).removeClass('no-children');
|
||||||
} else {
|
} else {
|
||||||
$(this.toggleView.elements()).removeClass('has-children');
|
$(this.toggleView.elements()).addClass('no-children');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domainObject && domainObject.hasCapability('status')) {
|
if (domainObject && domainObject.hasCapability('status')) {
|
||||||
|
@ -39,10 +39,18 @@ define(
|
|||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockScope = jasmine.createSpyObj(
|
mockScope = jasmine.createSpyObj(
|
||||||
"$scope",
|
"$scope",
|
||||||
["$watch"]
|
["$watch", "$on"]
|
||||||
);
|
);
|
||||||
mockScope.ngModel = {};
|
mockScope.ngModel = {};
|
||||||
mockScope.ngModel.selectedObject = 'mock selected object';
|
mockScope.ngModel.selectedObject = {
|
||||||
|
getCapability: function () {
|
||||||
|
return {
|
||||||
|
listen: function () {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
mockObjectService = jasmine.createSpyObj(
|
mockObjectService = jasmine.createSpyObj(
|
||||||
"objectService",
|
"objectService",
|
||||||
|
@ -181,6 +181,8 @@ $colorPlotHash: $colorTick;
|
|||||||
$stylePlotHash: dashed;
|
$stylePlotHash: dashed;
|
||||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||||
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
||||||
|
$legendCollapsedNameMaxW: 50%;
|
||||||
|
$legendHoverValueBg: rgba($colorBodyFg, 0.1);
|
||||||
|
|
||||||
// Tree
|
// Tree
|
||||||
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
|
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
|
||||||
@ -201,13 +203,15 @@ $shdwItemTreeIcon: 0.6;
|
|||||||
$colorThumbHoverBg: $colorItemTreeHoverBg;
|
$colorThumbHoverBg: $colorItemTreeHoverBg;
|
||||||
|
|
||||||
// Scrollbar
|
// Scrollbar
|
||||||
$scrollbarTrackSize: 10px;
|
$scrollbarTrackSize: 7px;
|
||||||
$scrollbarTrackShdw: rgba(#000, 0.7) 0 1px 5px;
|
$scrollbarTrackShdw: rgba(#000, 0.5) 0 1px 5px;
|
||||||
$scrollbarTrackColorBg: rgba(#000, 0.4);
|
$scrollbarTrackColorBg: transparent; //rgba(#000, 0.4);
|
||||||
$scrollbarThumbColor: pullForward($colorBodyBg, 10%);
|
$scrollbarThumbColor: pullForward($colorBodyBg, 10%);
|
||||||
$scrollbarThumbColorHov: pullForward($scrollbarThumbColor, 2%);
|
$scrollbarThumbColorHov: pullForward($scrollbarThumbColor, 2%);
|
||||||
$scrollbarThumbColorOverlay: pullForward($colorOvrBg, 10%);
|
$scrollbarThumbColorOverlay: pullForward($colorOvrBg, 10%);
|
||||||
$scrollbarThumbColorOverlayHov: pullForward($scrollbarThumbColorOverlay, 2%);
|
$scrollbarThumbColorOverlayHov: pullForward($scrollbarThumbColorOverlay, 2%);
|
||||||
|
$scrollbarThumbColorMenu: pullForward($colorMenuBg, 20%);
|
||||||
|
$scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%);
|
||||||
|
|
||||||
// Splitter
|
// Splitter
|
||||||
// All splitterD* values MUST both be either odd or even numbers
|
// All splitterD* values MUST both be either odd or even numbers
|
||||||
|
@ -181,6 +181,8 @@ $colorPlotHash: $colorTick;
|
|||||||
$stylePlotHash: dashed;
|
$stylePlotHash: dashed;
|
||||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||||
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
||||||
|
$legendCollapsedNameMaxW: 50%;
|
||||||
|
$legendHoverValueBg: rgba($colorBodyFg, 0.2);
|
||||||
|
|
||||||
// Tree
|
// Tree
|
||||||
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
|
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
|
||||||
@ -201,13 +203,15 @@ $shdwItemTreeIcon: none;
|
|||||||
$colorThumbHoverBg: $colorItemTreeHoverBg;
|
$colorThumbHoverBg: $colorItemTreeHoverBg;
|
||||||
|
|
||||||
// Scrollbar
|
// Scrollbar
|
||||||
$scrollbarTrackSize: 10px;
|
$scrollbarTrackSize: 7px;
|
||||||
$scrollbarTrackShdw: rgba(#000, 0.2) 0 1px 2px;
|
$scrollbarTrackShdw: rgba(#000, 0.2) 0 1px 2px;
|
||||||
$scrollbarTrackColorBg: rgba(#000, 0.2);
|
$scrollbarTrackColorBg: rgba(#000, 0.2);
|
||||||
$scrollbarThumbColor: darken($colorBodyBg, 50%);
|
$scrollbarThumbColor: darken($colorBodyBg, 50%);
|
||||||
$scrollbarThumbColorHov: $colorKey;
|
$scrollbarThumbColorHov: $colorKey;
|
||||||
$scrollbarThumbColorOverlay: darken($colorOvrBg, 50%);
|
$scrollbarThumbColorOverlay: darken($colorOvrBg, 50%);
|
||||||
$scrollbarThumbColorOverlayHov: $scrollbarThumbColorHov;
|
$scrollbarThumbColorOverlayHov: $scrollbarThumbColorHov;
|
||||||
|
$scrollbarThumbColorMenu: pullForward($colorMenuBg, 10%);
|
||||||
|
$scrollbarThumbColorMenuHov: pullForward($scrollbarThumbColorMenu, 2%);
|
||||||
|
|
||||||
// Splitter
|
// Splitter
|
||||||
// All splitterD* values MUST both be either odd or even numbers
|
// All splitterD* values MUST both be either odd or even numbers
|
||||||
|
@ -94,31 +94,6 @@ define([
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"versions": [
|
|
||||||
{
|
|
||||||
"name": "Version",
|
|
||||||
"value": "@@version",
|
|
||||||
"priority": 999
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Built",
|
|
||||||
"value": "@@timestamp",
|
|
||||||
"description": "The date on which this version of the client was built.",
|
|
||||||
"priority": 990
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Revision",
|
|
||||||
"value": "@@revision",
|
|
||||||
"description": "A unique revision identifier for the client sources.",
|
|
||||||
"priority": 995
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Branch",
|
|
||||||
"value": "@@branch",
|
|
||||||
"description": "The name of the branch that was used during the build.",
|
|
||||||
"priority": 994
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"components": [
|
"components": [
|
||||||
{
|
{
|
||||||
"provides": "objectService",
|
"provides": "objectService",
|
||||||
|
@ -23,10 +23,13 @@
|
|||||||
define([
|
define([
|
||||||
"moment-timezone",
|
"moment-timezone",
|
||||||
"./src/indicators/ClockIndicator",
|
"./src/indicators/ClockIndicator",
|
||||||
|
"./src/indicators/FollowIndicator",
|
||||||
"./src/services/TickerService",
|
"./src/services/TickerService",
|
||||||
|
"./src/services/TimerService",
|
||||||
"./src/controllers/ClockController",
|
"./src/controllers/ClockController",
|
||||||
"./src/controllers/TimerController",
|
"./src/controllers/TimerController",
|
||||||
"./src/controllers/RefreshingController",
|
"./src/controllers/RefreshingController",
|
||||||
|
"./src/actions/FollowTimerAction",
|
||||||
"./src/actions/StartTimerAction",
|
"./src/actions/StartTimerAction",
|
||||||
"./src/actions/RestartTimerAction",
|
"./src/actions/RestartTimerAction",
|
||||||
"./src/actions/StopTimerAction",
|
"./src/actions/StopTimerAction",
|
||||||
@ -37,10 +40,13 @@ define([
|
|||||||
], function (
|
], function (
|
||||||
MomentTimezone,
|
MomentTimezone,
|
||||||
ClockIndicator,
|
ClockIndicator,
|
||||||
|
FollowIndicator,
|
||||||
TickerService,
|
TickerService,
|
||||||
|
TimerService,
|
||||||
ClockController,
|
ClockController,
|
||||||
TimerController,
|
TimerController,
|
||||||
RefreshingController,
|
RefreshingController,
|
||||||
|
FollowTimerAction,
|
||||||
StartTimerAction,
|
StartTimerAction,
|
||||||
RestartTimerAction,
|
RestartTimerAction,
|
||||||
StopTimerAction,
|
StopTimerAction,
|
||||||
@ -80,6 +86,11 @@ define([
|
|||||||
"CLOCK_INDICATOR_FORMAT"
|
"CLOCK_INDICATOR_FORMAT"
|
||||||
],
|
],
|
||||||
"priority": "preferred"
|
"priority": "preferred"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"implementation": FollowIndicator,
|
||||||
|
"depends": ["timerService"],
|
||||||
|
"priority": "fallback"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"services": [
|
"services": [
|
||||||
@ -90,6 +101,11 @@ define([
|
|||||||
"$timeout",
|
"$timeout",
|
||||||
"now"
|
"now"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timerService",
|
||||||
|
"implementation": TimerService,
|
||||||
|
"depends": ["openmct"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"controllers": [
|
"controllers": [
|
||||||
@ -134,6 +150,15 @@ define([
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"actions": [
|
"actions": [
|
||||||
|
{
|
||||||
|
"key": "timer.follow",
|
||||||
|
"implementation": FollowTimerAction,
|
||||||
|
"depends": ["timerService"],
|
||||||
|
"category": "contextual",
|
||||||
|
"name": "Follow Timer",
|
||||||
|
"cssClass": "icon-clock",
|
||||||
|
"priority": "optional"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "timer.start",
|
"key": "timer.start",
|
||||||
"implementation": StartTimerAction,
|
"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
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
* Administration. All rights reserved.
|
* Administration. All rights reserved.
|
||||||
*
|
*
|
||||||
@ -21,39 +21,36 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
["./SubPlot"],
|
[],
|
||||||
function (SubPlot) {
|
function () {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility factory; wraps the SubPlot constructor and adds
|
* Designates a specific timer for following. Timelines, for example,
|
||||||
* in a reference to the telemetryFormatter, which will be
|
* use the actively followed timer to display a time-of-interest line
|
||||||
* used to represent telemetry values (timestamps or data
|
* and interpret time conductor bounds in the Timeline's relative
|
||||||
* values) as human-readable strings.
|
* time frame.
|
||||||
* @memberof platform/features/plot
|
*
|
||||||
|
* @implements {Action}
|
||||||
|
* @memberof platform/features/clock
|
||||||
* @constructor
|
* @constructor
|
||||||
|
* @param {ActionContext} context the context for this action
|
||||||
*/
|
*/
|
||||||
function SubPlotFactory(telemetryFormatter) {
|
function FollowTimerAction(timerService, context) {
|
||||||
this.telemetryFormatter = telemetryFormatter;
|
var domainObject =
|
||||||
|
context.domainObject &&
|
||||||
|
context.domainObject.useCapability('adapter');
|
||||||
|
this.perform =
|
||||||
|
timerService.setTimer.bind(timerService, domainObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
FollowTimerAction.appliesTo = function (context) {
|
||||||
* Instantiate a new sub-plot.
|
var model =
|
||||||
* @param {DomainObject[]} telemetryObjects the domain objects
|
(context.domainObject && context.domainObject.getModel()) ||
|
||||||
* which will be plotted in this sub-plot
|
{};
|
||||||
* @param {PlotPanZoomStack} panZoomStack the stack of pan-zoom
|
|
||||||
* states which is applicable to this sub-plot
|
return model.type === 'timer';
|
||||||
* @returns {SubPlot} the instantiated sub-plot
|
|
||||||
* @method
|
|
||||||
*/
|
|
||||||
SubPlotFactory.prototype.createSubPlot = function (telemetryObjects, panZoomStack) {
|
|
||||||
return new SubPlot(
|
|
||||||
telemetryObjects,
|
|
||||||
panZoomStack,
|
|
||||||
this.telemetryFormatter
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return SubPlotFactory;
|
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
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
* Administration. All rights reserved.
|
* Administration. All rights reserved.
|
||||||
*
|
*
|
||||||
@ -20,21 +20,38 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([], function () {
|
define(
|
||||||
function AdaptedViewController($scope, openmct) {
|
['moment'],
|
||||||
function refresh(legacyObject) {
|
function (moment) {
|
||||||
if (!legacyObject) {
|
var NO_TIMER = "No timer being followed";
|
||||||
$scope.view = undefined;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var domainObject = legacyObject.useCapability('adapter');
|
/**
|
||||||
var providers = openmct.mainViews.get(domainObject);
|
* Indicator that displays the active timer, as well as its
|
||||||
$scope.view = providers[0] && providers[0].view(domainObject);
|
* current state.
|
||||||
|
* @implements {Indicator}
|
||||||
|
* @memberof platform/features/clock
|
||||||
|
*/
|
||||||
|
function FollowIndicator(timerService) {
|
||||||
|
this.timerService = timerService;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.$watch('domainObject', refresh);
|
FollowIndicator.prototype.getGlyphClass = function () {
|
||||||
}
|
return "";
|
||||||
|
};
|
||||||
|
|
||||||
return AdaptedViewController;
|
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;
|
||||||
|
}
|
||||||
|
);
|
113
platform/features/clock/src/services/TimerService.js
Normal file
113
platform/features/clock/src/services/TimerService.js
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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;
|
||||||
|
this.objects = openmct.objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
|
||||||
|
if (this.stopObserving) {
|
||||||
|
this.stopObserving();
|
||||||
|
delete this.stopObserving;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timer) {
|
||||||
|
this.stopObserving =
|
||||||
|
this.objects.observe(timer, '*', this.setTimer.bind(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.timerState !== 'stopped';
|
||||||
|
|
||||||
|
if (!canConvert) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = clock.currentValue();
|
||||||
|
var delta = this.timer.timerState === 'paused' ?
|
||||||
|
now - this.timer.pausedTime : 0;
|
||||||
|
var epoch = this.timer.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,87 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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;
|
||||||
|
var testAdaptedObject;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
testModel = {};
|
||||||
|
testContext = { domainObject: jasmine.createSpyObj('domainObject', [
|
||||||
|
'getModel',
|
||||||
|
'useCapability'
|
||||||
|
]) };
|
||||||
|
testAdaptedObject = { foo: 'bar' };
|
||||||
|
testContext.domainObject.getModel.andReturn(testModel);
|
||||||
|
testContext.domainObject.useCapability.andCallFake(function (c) {
|
||||||
|
return c === 'adapter' && testAdaptedObject;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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(testAdaptedObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
77
platform/features/clock/test/services/TimerServiceSpec.js
Normal file
77
platform/features/clock/test/services/TimerServiceSpec.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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') },
|
||||||
|
objects: { observe: jasmine.createSpy('observe') }
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("observes changes to an object", function () {
|
||||||
|
var newTimer = { name: "I am another timer." };
|
||||||
|
expect(mockmct.objects.observe).toHaveBeenCalledWith(
|
||||||
|
testTimer,
|
||||||
|
'*',
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
mockmct.objects.observe.mostRecentCall.args[2](newTimer);
|
||||||
|
expect(timerService.getTimer()).toBe(newTimer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,7 +1,7 @@
|
|||||||
$ueTimeConductorH: (25px, 16px, 20px); // Heights for Ticks, Data Visualization, Controls elements
|
$ueTimeConductorH: (25px, 16px, 20px); // Heights for Ticks, Data Visualization, Controls elements
|
||||||
$ueTimeConductorRtH: (25px, 3px, 20px); // Heights for elements in Real-time mode
|
$ueTimeConductorRtH: (25px, 3px, 20px); // Heights for elements in Real-time mode
|
||||||
$timeCondInputTimeSysDefW: 165px; // Default width for datetime value inputs
|
$timeCondInputTimeSysDefW: 165px; // Default width for datetime value inputs
|
||||||
$timeCondInputDeltaDefW: 60px; // Default width for delta value inputs, typically 00:00:00
|
$timeCondInputDeltaDefW: 65px; // Default width for delta value inputs, typically 00:00:00
|
||||||
$timeCondTOIIconD: 12px; // height and width of icon used for TOI indicator
|
$timeCondTOIIconD: 12px; // height and width of icon used for TOI indicator
|
||||||
$timeCondTOIValOffset: 0px;
|
$timeCondTOIValOffset: 0px;
|
||||||
$ticksBlockerFadeW: 50px;
|
$ticksBlockerFadeW: 50px;
|
||||||
|
@ -162,9 +162,6 @@
|
|||||||
.l-time-conductor-inputs {
|
.l-time-conductor-inputs {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
input[type="text"] {
|
|
||||||
@include trans-prop-nice(padding, 250ms);
|
|
||||||
}
|
|
||||||
.time-range-input input[type="text"] {
|
.time-range-input input[type="text"] {
|
||||||
width: $timeCondInputTimeSysDefW;
|
width: $timeCondInputTimeSysDefW;
|
||||||
}
|
}
|
||||||
@ -290,18 +287,6 @@
|
|||||||
|
|
||||||
.l-time-conductor-inputs-holder {
|
.l-time-conductor-inputs-holder {
|
||||||
.l-time-range-input-w {
|
.l-time-range-input-w {
|
||||||
input[type="text"]:not(.error) {
|
|
||||||
background: transparent;
|
|
||||||
box-shadow: none;
|
|
||||||
border-radius: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
@include nice-input();
|
|
||||||
padding: $inputTextP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.icon-calendar {
|
.icon-calendar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -309,8 +294,11 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
&.end-date {
|
&.end-date {
|
||||||
|
// Displays the current time
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
input[type="text"] {
|
input[type="text"] {
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
color: pullForward($colorTimeCondKeyBg, 5%);
|
color: pullForward($colorTimeCondKeyBg, 5%);
|
||||||
margin-right: $interiorMargin;
|
margin-right: $interiorMargin;
|
||||||
tab-index: -1;
|
tab-index: -1;
|
||||||
|
@ -19,8 +19,8 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="contents">
|
<div class="w-menu">
|
||||||
<div class="pane left menu-items">
|
<div class="col menu-items">
|
||||||
<ul>
|
<ul>
|
||||||
<li ng-repeat="metadata in ngModel.options"
|
<li ng-repeat="metadata in ngModel.options"
|
||||||
ng-click="ngModel.select(metadata)">
|
ng-click="ngModel.select(metadata)">
|
||||||
@ -32,13 +32,15 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="pane right menu-item-description">
|
<div class="col menu-item-description">
|
||||||
<div class="desc-area ui-symbol icon type-icon {{ngModel.activeMetadata.cssClass}}"></div>
|
<div class="desc-area ui-symbol icon type-icon {{ngModel.activeMetadata.cssClass}}"></div>
|
||||||
<div class="desc-area title">
|
<div class="w-title-desc">
|
||||||
{{ngModel.activeMetadata.name}}
|
<div class="desc-area title">
|
||||||
</div>
|
{{ngModel.activeMetadata.name}}
|
||||||
<div class="desc-area description">
|
</div>
|
||||||
{{ngModel.activeMetadata.description}}
|
<div class="desc-area description">
|
||||||
|
{{ngModel.activeMetadata.description}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
ng-click="modeController.toggle()">
|
ng-click="modeController.toggle()">
|
||||||
<span class="title-label">{{ngModel.selected.name}}</span>
|
<span class="title-label">{{ngModel.selected.name}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu super-menu mini mode-selector-menu"
|
<div class="menu super-menu mini l-mode-selector-menu"
|
||||||
ng-show="modeController.isActive()">
|
ng-show="modeController.isActive()">
|
||||||
<mct-include key="'mode-menu'"
|
<mct-include key="'mode-menu'"
|
||||||
ng-model="ngModel">
|
ng-model="ngModel">
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
ng-model="boundsModel"
|
ng-model="boundsModel"
|
||||||
ng-blur="tcController.setOffsetsFromView(boundsModel)"
|
ng-blur="tcController.setOffsetsFromView(boundsModel)"
|
||||||
field="'startOffset'"
|
field="'startOffset'"
|
||||||
class="hrs-min-input">
|
class="s-input-inline hrs-min-input">
|
||||||
</mct-control>
|
</mct-control>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -71,7 +71,7 @@
|
|||||||
ng-model="boundsModel"
|
ng-model="boundsModel"
|
||||||
ng-blur="tcController.setOffsetsFromView(boundsModel)"
|
ng-blur="tcController.setOffsetsFromView(boundsModel)"
|
||||||
field="'endOffset'"
|
field="'endOffset'"
|
||||||
class="hrs-min-input">
|
class="s-input-inline hrs-min-input">
|
||||||
</mct-control>
|
</mct-control>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
@ -201,7 +201,7 @@ define(
|
|||||||
var options = [{
|
var options = [{
|
||||||
key: 'fixed',
|
key: 'fixed',
|
||||||
name: 'Fixed Timespan Mode',
|
name: 'Fixed Timespan Mode',
|
||||||
description: 'Query and explore data that falls between two fixed datetimes',
|
description: 'Query and explore data that falls between two fixed datetimes.',
|
||||||
cssClass: 'icon-calendar'
|
cssClass: 'icon-calendar'
|
||||||
}];
|
}];
|
||||||
var clocks = {};
|
var clocks = {};
|
||||||
|
@ -334,46 +334,6 @@ define([
|
|||||||
"conversion": "number[]"
|
"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[]"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
<div class="abs object-browse-bar l-flex-row">
|
<div class="abs object-browse-bar l-flex-row">
|
||||||
<div class="left flex-elem l-flex-row grows">
|
<div class="left flex-elem l-flex-row grows">
|
||||||
<mct-representation
|
<mct-representation
|
||||||
key="'object-header'"
|
key="'object-header-frame'"
|
||||||
mct-object="domainObject"
|
mct-object="domainObject"
|
||||||
class="l-flex-row flex-elem object-header grows">
|
class="l-flex-row flex-elem object-header grows">
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
|
@ -26,8 +26,12 @@
|
|||||||
* @namespace platform/features/layout
|
* @namespace platform/features/layout
|
||||||
*/
|
*/
|
||||||
define(
|
define(
|
||||||
['./LayoutDrag'],
|
[
|
||||||
function (LayoutDrag) {
|
'./LayoutDrag'
|
||||||
|
],
|
||||||
|
function (
|
||||||
|
LayoutDrag
|
||||||
|
) {
|
||||||
|
|
||||||
var DEFAULT_DIMENSIONS = [12, 8],
|
var DEFAULT_DIMENSIONS = [12, 8],
|
||||||
DEFAULT_GRID_SIZE = [32, 32],
|
DEFAULT_GRID_SIZE = [32, 32],
|
||||||
@ -123,6 +127,8 @@ define(
|
|||||||
if (self.droppedIdToSelectAfterRefresh) {
|
if (self.droppedIdToSelectAfterRefresh) {
|
||||||
self.select(null, self.droppedIdToSelectAfterRefresh);
|
self.select(null, self.droppedIdToSelectAfterRefresh);
|
||||||
delete self.droppedIdToSelectAfterRefresh;
|
delete self.droppedIdToSelectAfterRefresh;
|
||||||
|
} else if (composition.indexOf(self.selectedId) === -1) {
|
||||||
|
self.clearSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -424,6 +424,17 @@ define(
|
|||||||
expect(selectedObj.showFrame).toEqual(jasmine.any(Function));
|
expect(selectedObj.showFrame).toEqual(jasmine.any(Function));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("deselects the object that is no longer in the composition", function () {
|
||||||
|
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||||
|
var childObj = mockCompositionObjects[0];
|
||||||
|
controller.select(mockEvent, childObj.getId());
|
||||||
|
|
||||||
|
var composition = ["b", "c"];
|
||||||
|
mockScope.$watchCollection.mostRecentCall.args[1](composition);
|
||||||
|
|
||||||
|
expect(controller.selected(childObj)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
{
|
{
|
||||||
"key": "ListViewController",
|
"key": "ListViewController",
|
||||||
"implementation": ListViewController,
|
"implementation": ListViewController,
|
||||||
"depends": ["$scope"]
|
"depends": ["$scope", "formatService"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"directives": [
|
"directives": [
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(function () {
|
define(function () {
|
||||||
function ListViewController($scope) {
|
function ListViewController($scope, formatService) {
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
$scope.orderByField = 'title';
|
$scope.orderByField = 'title';
|
||||||
$scope.reverseSort = false;
|
$scope.reverseSort = false;
|
||||||
@ -30,6 +30,8 @@ define(function () {
|
|||||||
var unlisten = $scope.domainObject.getCapability('mutation')
|
var unlisten = $scope.domainObject.getCapability('mutation')
|
||||||
.listen(this.updateView.bind(this));
|
.listen(this.updateView.bind(this));
|
||||||
|
|
||||||
|
this.utc = formatService.getFormat('utc');
|
||||||
|
|
||||||
$scope.$on('$destroy', function () {
|
$scope.$on('$destroy', function () {
|
||||||
unlisten();
|
unlisten();
|
||||||
});
|
});
|
||||||
@ -50,17 +52,13 @@ define(function () {
|
|||||||
icon: child.getCapability('type').getCssClass(),
|
icon: child.getCapability('type').getCssClass(),
|
||||||
title: child.getModel().name,
|
title: child.getModel().name,
|
||||||
type: child.getCapability('type').getName(),
|
type: child.getCapability('type').getName(),
|
||||||
persisted: new Date(
|
persisted: this.utc.format(child.getModel().persisted),
|
||||||
child.getModel().persisted
|
modified: this.utc.format(child.getModel().modified),
|
||||||
).toUTCString(),
|
|
||||||
modified: new Date(
|
|
||||||
child.getModel().modified
|
|
||||||
).toUTCString(),
|
|
||||||
asDomainObject: child,
|
asDomainObject: child,
|
||||||
location: child.getCapability('location'),
|
location: child.getCapability('location'),
|
||||||
action: child.getCapability('action')
|
action: child.getCapability('action')
|
||||||
};
|
};
|
||||||
});
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
return ListViewController;
|
return ListViewController;
|
||||||
|
@ -31,7 +31,9 @@ define(
|
|||||||
controller,
|
controller,
|
||||||
childModel,
|
childModel,
|
||||||
typeCapability,
|
typeCapability,
|
||||||
mutationCapability;
|
mutationCapability,
|
||||||
|
formatService;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
unlistenFunc = jasmine.createSpy("unlisten");
|
unlistenFunc = jasmine.createSpy("unlisten");
|
||||||
|
|
||||||
@ -41,6 +43,18 @@ define(
|
|||||||
);
|
);
|
||||||
mutationCapability.listen.andReturn(unlistenFunc);
|
mutationCapability.listen.andReturn(unlistenFunc);
|
||||||
|
|
||||||
|
formatService = jasmine.createSpyObj(
|
||||||
|
"formatService",
|
||||||
|
["getFormat"]
|
||||||
|
);
|
||||||
|
formatService.getFormat.andReturn(jasmine.createSpyObj(
|
||||||
|
'utc',
|
||||||
|
["format"]
|
||||||
|
));
|
||||||
|
formatService.getFormat().format.andCallFake(function (v) {
|
||||||
|
return "formatted " + v;
|
||||||
|
});
|
||||||
|
|
||||||
typeCapability = jasmine.createSpyObj(
|
typeCapability = jasmine.createSpyObj(
|
||||||
"typeCapability",
|
"typeCapability",
|
||||||
["getCssClass", "getName"]
|
["getCssClass", "getName"]
|
||||||
@ -94,20 +108,27 @@ define(
|
|||||||
);
|
);
|
||||||
scope.domainObject = domainObject;
|
scope.domainObject = domainObject;
|
||||||
|
|
||||||
controller = new ListViewController(scope);
|
controller = new ListViewController(scope, formatService);
|
||||||
|
|
||||||
waitsFor(function () {
|
waitsFor(function () {
|
||||||
return scope.children;
|
return scope.children;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses the UTC time format", function () {
|
||||||
|
expect(formatService.getFormat).toHaveBeenCalledWith('utc');
|
||||||
|
});
|
||||||
|
|
||||||
it("updates the view", function () {
|
it("updates the view", function () {
|
||||||
expect(scope.children[0]).toEqual(
|
expect(scope.children[0]).toEqual(
|
||||||
{
|
{
|
||||||
icon: "icon-folder",
|
icon: "icon-folder",
|
||||||
title: "Battery Charge Status",
|
title: "Battery Charge Status",
|
||||||
type: "Folder",
|
type: "Folder",
|
||||||
persisted: "Wed, 07 Jun 2017 20:34:57 GMT",
|
persisted: formatService.getFormat('utc')
|
||||||
modified: "Wed, 07 Jun 2017 20:34:57 GMT",
|
.format(childModel.persisted),
|
||||||
|
modified: formatService.getFormat('utc')
|
||||||
|
.format(childModel.modified),
|
||||||
asDomainObject: childObject,
|
asDomainObject: childObject,
|
||||||
location: ''
|
location: ''
|
||||||
}
|
}
|
||||||
|
@ -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();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user