Compare commits

...

106 Commits

Author SHA1 Message Date
292134a080 Merged summary widgets as telemetry source 2018-03-15 13:16:38 -07:00
d689b7aac5 fix layout display overload, remove viewpolicy and notebookLayout.html. Fix delete error - issues found when deploying for testathon 2018-03-14 14:01:35 -07:00
7a5a3b0cf0 disable view policy - to allow layouts to function - needs more investigation 2018-03-13 11:10:44 -07:00
49759a39b8 Inlined templates for notebook 2018-03-12 18:27:09 -07:00
cc50186700 include painterro in karma config 2018-03-09 16:34:45 -08:00
1bd4fe0de2 fix export image after merging with master 2018-03-09 16:21:40 -08:00
f44bf30b02 merge master 2018-03-09 16:09:12 -08:00
f9060a485d [Plugin] Add imported root plugin (#1784)
* [Plugin] Add static root plugin

Add StaticRootPlugin, which allows a file exported with the ImportExport
plugin to be mounted as a static root in Open MCT.  Allows deployers
to configure standard displays for deployments by exporting displays they
have already created.

* Include all src files
2018-03-09 12:39:25 -08:00
166cc0421d add comments, clean out some code, and fix broken tests 2018-03-08 13:13:49 -08:00
4b13d0374b fix issue of preserving line breaks when text is received from a persisted source 2018-03-08 10:57:41 -08:00
39a7f43cd0 Fixes Issue #1938 - Fixed Positon should display enumerated value (#1939)
* Fixes #1938
Fixed usage of Telemetry API, use hints to get valueMetadata, use valueMetadata to get formatter

* dont circumvent chooseTelemetryKeyToDisplay, which was causing a regression when imagery is added to fp

* fix broken tests

* pass valueMetaData to limitEvaluator.evaluate, rename #chooseTelemetryKeyToDisplay to getValueMetadata, to reflect what is returned.
update tests

* change getValueMetadata to chooseValueMetadataToDisplay
2018-03-07 13:49:11 -08:00
5103207a70 Merge pull request #1940 from nasa/update-gulp-sass
Updated version range of gulp-sass to allow later versions
2018-03-06 12:58:22 -08:00
8c2cc90f04 Updated version range of gulp-sass to allow later versions 2018-03-06 09:31:09 -08:00
ce431848b3 Remove example plotOptions. (#1936)
Fixes https://github.com/nasa/openmct/issues/731
2018-03-02 15:52:02 -08:00
5726fe6313 new-plot import (#1557)
Merge of new plot
* Introduces new Plot object and view
* Removes Old Plot
* Add LAD support and state type to generators
* Removes Telemetry Panel Type
* Telemetry API Updates
* UTCFormat.parse: passthrough numbers
* TelemetryAPI: default request arguments
* TelemetryAPI: fix enum formatting
* Markup and styling to support new plots
2018-03-02 14:29:34 -08:00
6145843e86 [Inspector] Add check to prevent race condition before setting the scope composition. (#1931)
Fixes #1918
2018-02-28 13:38:00 -08:00
0225cbab6a Merge pull request #1930 from nasa/navigate-via-breadcrumbs
Restore navigation via breadcrumbs
2018-02-28 13:36:16 -08:00
e477beb587 Merge pull request #1875 from tobiasbrown/open1872
[DateTimePicker] Time Conductor Date Picker menu is missing background
2018-02-28 13:29:21 -08:00
ee5d59024a Restore navigation via breadcrumbs
Fix navigation via inspector breadcrumbs.

Fixes https://github.com/nasa/openmct/issues/1927
2018-02-28 12:04:14 -08:00
5288dadafb Merge pull request #1926 from nasa/discovery-eagle
Version bump - 0.12.0 -> 0.13.1
2018-02-26 17:45:03 -08:00
02b1ec1343 remove frame layout duplicate and use frame.html 2018-02-26 14:43:24 -08:00
7f3cc09cbc Updated version number for start of release Eagle 2018-02-23 13:42:19 -08:00
94fa70abb1 Updated version number to close sprint 'Discovery One' 2018-02-23 13:32:31 -08:00
ad1b2681d2 [Frontend] WIP Snapshot styling
Fixes #1896
- Better class names;
- Moved buttons in frame layout;
2018-02-22 18:17:45 -08:00
9751236da5 Merge branch 'notebook-integration-deep' of https://github.com/nasa/openmct into notebook-integration-deep 2018-02-22 16:04:18 -08:00
db14b806d1 prevent multiple new notebook entry divs from being created on open overlay, instead create on initialization 2018-02-22 15:47:27 -08:00
18bae253bd Merge branch 'notebook-integration-deep' of https://github.com/nasa/openmct into notebook-integration-deep 2018-02-22 15:45:34 -08:00
43c9804326 [Frontend] WIP Mobile styling
Fixes #1896
- Fixed general approach to portrait orientation in
mobile/_layout.scss to use media query;
- Fixed portrait layout in _notebook_base.scss
to use media query;
2018-02-22 15:45:13 -08:00
63bc8178a9 remove duplicate code from MCTModalNotebook and roll changes into MCTTriggerModal 2018-02-22 15:38:38 -08:00
3fbb1833bf [Frontend] Fix custom Selects
Fixes #1896
- Custom Selects now much more
solid, handle width compression better;
2018-02-22 14:36:29 -08:00
50420a5eff [Frontend] Fixes to search control
Fixes #1896
- Search control now more robust, added
.search-filter-by-type class selector;
2018-02-22 14:35:48 -08:00
ab748026ab fix expand in layout, which was causing snapshot at expand 2018-02-22 13:28:21 -08:00
f34bb48398 [Frontend] WIP Mobile styling
Fixes #1896
- phone portrait entry layout optimization
2018-02-21 19:02:38 -08:00
c47defd6bc [Frontend] Mobile styling
Fixes #1896
- Mod .has-local-controls to not apply when in touch context
2018-02-21 18:31:33 -08:00
3d926f2ff7 [Frontend] CSS sanding and cleanups
Fixes #1896
- Removing unused classes;
- Finessed margin and padding;
2018-02-21 18:30:35 -08:00
12574a1333 Tests for Composition API providers 2018-02-20 09:40:57 -08:00
f653ff4890 added modifiedOn time for entries that are changed, and fixed issue regarding inner text being filled when new entry button clicked 2018-02-14 12:29:11 -08:00
b8e8ca8332 [Frontend] CSS normalizing, apply general styles in markup
Fixes #1896
- Notebook class names more individualized;
- Apply .labeled and .has-local-controls general classes;
- Apply .s-input-inline to contenteditable div;
- Look and feel cleanups for drag area and entry elements;
2018-02-13 19:05:12 -08:00
4eebec5c13 [Merge] Generalized labeled icon-* elements
Fixes #1896
2018-02-13 19:02:29 -08:00
09ef7d8d38 [Merge] Generalized hover hide/show of local controls
Fixes #1896
2018-02-13 19:01:45 -08:00
d430f3e621 [Front-end] Refined styling of entry embeds
Fixes #1896
2018-02-09 16:57:47 -08:00
585f05d576 fix drag and drop, delete entries 2018-02-08 11:56:32 -08:00
54fe6d0e59 Merge branch 'notebook-integration-1896-styling' of https://github.com/nasa/openmct into notebook-integration-deep 2018-02-07 11:48:28 -08:00
ab95f947e8 [Merge] WIP Mods related to MCTModalNotebook.js
Fixes #1896
Fixes #1906
2018-02-06 19:05:23 -08:00
179e69867b [Merge] WIP SCSS and markup polishing
Fixes #1896
- Significant style and markup changes;
- Styles, layout, etc. relating to embed elements;
- Fixes in both notebook.html and embedControl.html;
- Class name normalization;
2018-02-06 18:15:54 -08:00
b2fb958a77 [Merge] Cleanups in JS
Fixes #1896
- Removed and commented out logging statements
2018-02-06 18:12:53 -08:00
3ee6a04db0 [Front-end] Notebook thematic styling; test console
Fixes #1896
- Added thematic styles and config;
- Really, really, really WIP!!
- DnD doesn't work properly, and drag to
existing entry no longer works.
2018-02-05 15:54:06 -08:00
cca8b558bd Merge branch 'master' of https://github.com/nasa/openmct into notebook-integration-deep 2018-02-05 11:54:10 -08:00
59fd3415d6 [Merge] JS debugging
Fixes #1896
- Really, really WIP
- DnD doesn't work properly, and drag to
existing entry no longer works.
2018-02-02 17:08:58 -08:00
8aeef6ae37 [Merge] JS debugging
Fixes #1896
- Very much WIP!
2018-02-02 14:30:23 -08:00
0543aa9521 [Merge] WIP markup and styling
Fixes #1896
- Very much WIP, attempting to convert
textarea to contenteditable;
2018-01-31 19:42:42 -08:00
78ae568302 [Merge] WIP markup and styling
Fixes #1896
- Very much WIP, currently having issues with
hovering and jiggling
2018-01-30 18:46:42 -08:00
fad3afbd3c [Notebook] Fix require config for painterro 2018-01-30 15:46:47 -08:00
6f975c2d9d [Notebook] Include painterro for tests 2018-01-30 15:42:45 -08:00
9fd397b34e [Notebook] Run gulp fixstyle 2018-01-30 15:36:35 -08:00
cbddfac96f [Notebook] Fix lint issues 2018-01-30 15:31:42 -08:00
b5153b0f27 [Notebook] Fix lint issues 2018-01-30 15:28:23 -08:00
b766a22034 [Notebook] Fix lint issues 2018-01-30 15:26:35 -08:00
4bde1c1ab8 [Notebook] Fix lint issues 2018-01-30 15:18:47 -08:00
1d314a91e0 [Notebook] Fix lint issues 2018-01-30 15:17:51 -08:00
e370fbd6c5 [Notebook] Fix lint issues 2018-01-30 15:14:26 -08:00
25cce4dd0d [Notebook] Fix lint issues 2018-01-30 15:13:01 -08:00
78dd80d081 [Notebook] Fix lint issues 2018-01-30 15:10:16 -08:00
944fb5e53a [Notebook] Fix lint issues 2018-01-30 15:08:58 -08:00
ac7ea7ba44 [Notebook] Use dot notation instead of brackets
...for checkstyle
2018-01-30 15:05:31 -08:00
5d34628dd8 [Notebook] Run gulp fixstyle 2018-01-30 15:04:47 -08:00
c3a2ac3dd5 [Notebook] Remove extra comma 2018-01-30 15:04:25 -08:00
62ebcd7277 [Notebook] Use dot notation instead of brackets
...for checkstyle
2018-01-30 15:01:27 -08:00
79a5e479cb [Notebook] Run gulp fixstyle 2018-01-30 15:00:06 -08:00
d9b66e23aa [Notebook] Remove demo entries 2018-01-30 14:59:03 -08:00
f1c3a56147 [Notebook] Expose via openmct.plugins 2018-01-30 14:58:34 -08:00
02e5defbdc [Notebook] Restore original index.html 2018-01-30 14:54:08 -08:00
9897883335 [Notebook] Remove obsolete README 2018-01-30 14:53:47 -08:00
c15bb45411 [Notebook] Relocate to platform/features/notebook 2018-01-30 14:49:44 -08:00
a085b2a7b8 Merge remote-tracking branch 'origin/master' into notebook-integration-1896
Fixes #1896
2018-01-30 14:41:31 -08:00
8c72729a2a [DateTimePicker] Replaced tabs with spaces
Addresses #1872
2018-01-17 09:33:28 +11:00
129ab1791b [DateTimePicker] Re-added .s-menu styles removed in bc7d92ee0d
Addresses #1872
2018-01-15 12:43:16 +11:00
3366f21155 Merge pull request #37 from cristianmusic7/master
Naming conventions fixes
2017-12-28 12:16:27 +07:00
45d5b0752b filenames fix 2017-12-28 00:08:10 -05:00
07d7f8fd5f names and package fixes 2017-12-28 00:03:49 -05:00
2b7b4a552c Merge pull request #36 from cristianmusic7/master
Code issues fixed
2017-12-27 15:11:03 +07:00
3f9bad1805 Code issues fixes:
NotificationLaunchIndicator deleted.
Inappropriate modifications to domain object models fixed.
Implemented $destroy listener on entryDnd directive.
Naming conventions fixed.
Unnecessary changes made to platform handled.
Painterro dependency handled
gulp verify fix.
2017-12-27 00:26:27 -05:00
fec1d38f7e Merge pull request #28 from cristianmusic7/master
Test case functional and cosmetic issues fixed.
2017-12-18 10:31:31 +07:00
499655def2 updated file saver library 2017-12-17 22:09:34 -05:00
d5985b844c Merge branch 'master' of https://github.com/cristianmusic7/openmct 2017-12-15 23:56:37 -05:00
59bb5d3d55 Test cases functional and cosmetic issues fixed. 2017-12-15 23:53:14 -05:00
5b298525e3 Test case functional and cosmetic issues fixed. 2017-12-15 23:41:10 -05:00
1540fbcf54 Merge pull request #27 from cristianmusic7/master
CouchDB setup documentation added.
2017-11-13 14:15:26 +07:00
b15f193f5d CouchDB setup documentation added 2017-11-11 01:48:00 -05:00
bdf856526b CouchDB documentation added
Screenshots added.
2017-11-11 01:43:43 -05:00
9aafb5cbf9 CouchDB documentation added 2017-11-11 01:27:19 -05:00
2ccf3f0fb1 Merge pull request #26 from cristianmusic7/master
NASA review fixes:
2017-11-09 14:38:22 +07:00
46d7c117cb NASA review fixes:
css files adjusted
notebook children tree removed
embed's title links to live object
2017-11-09 01:47:01 -05:00
60168ea87b Merge pull request #24 from cristianmusic7/master
Painterro .map file issue fix
2017-10-26 23:52:46 +07:00
5ba3afb1a8 painterro .map file issue fixed. 2017-10-26 11:48:44 -05:00
3974991cfd Merge pull request #23 from cristianmusic7/master
Annotation toolbar UI style fixes, added annotation functionality on new entry dialog
2017-10-21 13:28:18 +07:00
39c5b1bbfd Annotation toolbar UI style fixes, added annotation functionality on new entry dialog 2017-10-21 01:22:48 -05:00
c82563c80f Merge pull request #22 from cristianmusic7/master
NASA reported issues fixed.
2017-10-20 08:11:30 +07:00
01fda1adfc NASA reported issues fixed:
objects saved in notebook, delete entry dialog, style files, and new entry from drag objects fixed.
2017-10-19 18:20:43 -05:00
bf5ed84f94 Merge pull request #19 from cristianmusic7/master
drag and drop style, new entry focus and delete display fixes
2017-10-15 16:45:43 +07:00
7c34f03a95 drag and drop style fix, new entry focus, delete display fix 2017-10-15 03:08:27 -05:00
3cf1ad8e40 Merge pull request #18 from cristianmusic7/master
Code updates, review fixes
2017-10-14 15:58:44 +07:00
3fab80c276 Code updates:
-Topcoder final fixes
-NASA review fixes
2017-10-14 02:52:07 -05:00
194dc43e24 Merge pull request #2 from cristianmusic7/master
Initial submission
2017-10-11 17:15:30 +07:00
16e0eeff67 NASA - OPEN MCT NOTEBOOK UI PROTOTYPE CHALLENGE
https://www.topcoder.com/challenge-details/30059614/
Initial submission
2017-10-11 02:51:39 -05:00
37be478a08 Merge pull request #1 from nasa/master
Add Notebook icon (#1742)
2017-10-10 11:34:53 +07:00
207 changed files with 11740 additions and 8656 deletions

View File

@ -25,4 +25,4 @@
"html2canvas": "^0.4.1",
"moment-timezone": "^0.5.13"
}
}
}

View File

@ -0,0 +1,108 @@
define([
'lodash'
], function (
_
) {
var METADATA_BY_TYPE = {
'generator': {
values: [
{
key: "name",
name: "Name"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "yesterday",
name: "Yesterday",
format: "utc",
hints: {
domain: 2
}
},
{
key: "sin",
name: "Sine",
hints: {
range: 1
}
},
{
key: "cos",
name: "Cosine",
hints: {
range: 2
}
}
]
},
'example.state-generator': {
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
}
}
]
}
}
function GeneratorMetadataProvider() {
}
GeneratorMetadataProvider.prototype.appliesTo = function (domainObject) {
return METADATA_BY_TYPE.hasOwnProperty(domainObject.type);
};
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
return _.extend(
{},
domainObject.telemetry,
METADATA_BY_TYPE[domainObject.type]
);
};
return GeneratorMetadataProvider;
});

View File

@ -30,7 +30,8 @@ define([
amplitude: 1,
period: 10,
offset: 0,
dataRateInHz: 1
dataRateInHz: 1,
phase: 0
};
function GeneratorProvider() {
@ -50,9 +51,12 @@ define([
'amplitude',
'period',
'offset',
'dataRateInHz'
'dataRateInHz',
'phase',
];
request = request || {};
var workerRequest = {};
props.forEach(function (prop) {
@ -67,7 +71,7 @@ define([
}
workerRequest[prop] = Number(workerRequest[prop]);
});
workerRequest.name = domainObject.name;
return workerRequest;
};

View 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;
});

View File

@ -62,10 +62,11 @@
self.postMessage({
id: message.id,
data: {
name: data.name,
utc: nextStep,
yesterday: nextStep - 60*60*24*1000,
sin: sin(nextStep, data.period, data.amplitude, data.offset),
cos: cos(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, data.phase)
}
});
nextStep += step;
@ -82,21 +83,22 @@
}
function onRequest(message) {
var data = message.data;
if (data.end == undefined) {
data.end = Date.now();
var request = message.data;
if (request.end == undefined) {
request.end = Date.now();
}
if (data.start == undefined){
data.start = data.end - FIFTEEN_MINUTES;
if (request.start == undefined){
request.start = request.end - FIFTEEN_MINUTES;
}
var now = Date.now();
var start = data.start;
var end = data.end > now ? now : data.end;
var amplitude = data.amplitude;
var period = data.period;
var offset = data.offset;
var dataRateInHz = data.dataRateInHz;
var start = request.start;
var end = request.end > now ? now : request.end;
var amplitude = request.amplitude;
var period = request.period;
var offset = request.offset;
var dataRateInHz = request.dataRateInHz;
var phase = request.phase;
var step = 1000 / dataRateInHz;
var nextStep = start - (start % step) + step;
@ -105,10 +107,11 @@
for (; nextStep < end && data.length < 5000; nextStep += step) {
data.push({
name: request.name,
utc: nextStep,
yesterday: nextStep - 60*60*24*1000,
sin: sin(nextStep, period, amplitude, offset),
cos: cos(nextStep, period, amplitude, offset)
sin: sin(nextStep, period, amplitude, offset, phase),
cos: cos(nextStep, period, amplitude, offset, phase)
});
}
self.postMessage({
@ -117,14 +120,14 @@
});
}
function cos(timestamp, period, amplitude, offset) {
function cos(timestamp, period, amplitude, offset, phase) {
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 *
Math.sin(timestamp / period / 1000 * Math.PI * 2) + offset;
Math.sin(phase + (timestamp / period / 1000 * Math.PI * 2)) + offset;
}
function sendError(error, message) {

View File

@ -23,10 +23,14 @@
define([
"./GeneratorProvider",
"./SinewaveLimitCapability"
"./SinewaveLimitCapability",
"./StateGeneratorProvider",
"./GeneratorMetadataProvider"
], function (
GeneratorProvider,
SinewaveLimitCapability
SinewaveLimitCapability,
StateGeneratorProvider,
GeneratorMetadataProvider
) {
var legacyExtensions = {
@ -46,6 +50,35 @@ define([
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
}
}
});
openmct.telemetry.addProvider(new StateGeneratorProvider());
openmct.types.addType("generator", {
name: "Sine Wave Generator",
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
@ -99,6 +132,18 @@ define([
"dataRateInHz"
],
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) {
@ -107,42 +152,13 @@ define([
amplitude: 1,
offset: 0,
dataRateInHz: 1,
values: [
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "yesterday",
name: "Yesterday",
format: "utc",
hints: {
domain: 2
}
},
{
key: "sin",
name: "Sine",
hints: {
range: 1
}
},
{
key: "cos",
name: "Cosine",
hints: {
range: 2
}
}
]
phase: 0
};
}
});
openmct.telemetry.addProvider(new GeneratorProvider());
openmct.telemetry.addMetadataProvider(new GeneratorMetadataProvider());
};
});

View File

@ -48,8 +48,9 @@ define([
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg"
];
function pointForTimestamp(timestamp) {
function pointForTimestamp(timestamp, name) {
return {
name: name,
utc: Math.floor(timestamp / 5000) * 5000,
url: IMAGE_SAMPLES[Math.floor(timestamp / 5000) % IMAGE_SAMPLES.length]
};
@ -61,7 +62,7 @@ define([
},
subscribe: function (domainObject, callback) {
var interval = setInterval(function () {
callback(pointForTimestamp(Date.now()));
callback(pointForTimestamp(Date.now(), domainObject.name));
}, 5000);
return function (interval) {
@ -79,8 +80,8 @@ define([
var start = options.start;
var end = options.end;
var data = [];
while (start < end && data.length < 5000) {
data.push(pointForTimestamp(start));
while (start <= end && data.length < 5000) {
data.push(pointForTimestamp(start, domainObject.name));
start += 5000;
}
return Promise.resolve(data);
@ -93,7 +94,7 @@ define([
options.strategy === 'latest';
},
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) {
object.telemetry = {
values: [
{
name: 'Name',
key: 'name'
},
{
name: 'Time',
key: 'utc',

View File

@ -1,146 +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.
*****************************************************************************/
/*global define*/
define([
'legacyRegistry',
'../../platform/commonUI/browse/src/InspectorRegion',
'../../platform/commonUI/regions/src/Region'
], function (
legacyRegistry,
InspectorRegion,
Region
) {
"use strict";
/**
* Add a 'plot options' region part to the inspector region for the
* Telemetry Plot type only. {@link InspectorRegion} is a default region
* implementation that is added automatically to all types. In order to
* customize what appears in the inspector region, you can start from a
* blank slate by using Region, or customize the default inspector
* region by using {@link InspectorRegion}.
*/
var plotInspector = new InspectorRegion(),
/**
* Two region parts are defined here. One that appears only in browse
* mode, and one that appears only in edit mode. For not they both point
* to the same representation, but a different key could be used here to
* include a customized representation for edit mode.
*/
plotOptionsBrowseRegion = new Region({
name: "plot-options",
title: "Plot Options",
modes: ['browse'],
content: {
key: "plot-options-browse"
}
}),
plotOptionsEditRegion = new Region({
name: "plot-options",
title: "Plot Options",
modes: ['edit'],
content: {
key: "plot-options-browse"
}
});
/**
* Both parts are added, and policies of type 'region' will determine
* which is shown based on domain object state. A default policy is
* provided which will check the 'modes' attribute of the region part
* definition.
*/
plotInspector.addRegion(plotOptionsBrowseRegion);
plotInspector.addRegion(plotOptionsEditRegion);
legacyRegistry.register("example/plotType", {
"name": "Plot Type",
"description": "Example illustrating registration of a new object type",
"extensions": {
"types": [
{
"key": "plot",
"name": "Example Telemetry Plot",
"cssClass": "icon-telemetry-panel",
"description": "For development use. A plot for displaying telemetry.",
"priority": 10,
"delegates": [
"telemetry"
],
"features": "creation",
"contains": [
{
"has": "telemetry"
}
],
"model": {
"composition": []
},
"inspector": plotInspector,
"telemetry": {
"source": "generator",
"domains": [
{
"key": "time",
"name": "Time"
},
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta",
"format": "example.delta"
}
],
"ranges": [
{
"key": "sin",
"name": "Sine"
},
{
"key": "cos",
"name": "Cosine"
}
]
},
"properties": [
{
"name": "Period",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "period",
"required": true,
"property": [
"telemetry",
"period"
],
"pattern": "^\\d*(\\.\\d*)?$"
}
]
}
]
}
});
});

View File

@ -58,11 +58,7 @@
position: relative;
}
.w-mct-example {
div {
margin-bottom: $interiorMarginLg;
}
}
.w-mct-example > div { margin-bottom: $interiorMarginLg; }
code,
pre {

View File

@ -68,6 +68,7 @@
]
}));
openmct.install(openmct.plugins.SummaryWidget());
openmct.install(openmct.plugins.Notebook());
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
openmct.time.timeSystem('utc');
openmct.start();

View File

@ -37,14 +37,14 @@ module.exports = function(config) {
{pattern: 'bower_components/**/*.js', included: false},
{pattern: 'node_modules/d3-*/**/*.js', included: false},
{pattern: 'node_modules/vue/**/*.js', included: false},
{pattern: 'src/**/*.js', included: false},
{pattern: 'src/**/*', included: false},
{pattern: 'node_modules/@cristian77/**/*.js', included: false},
{pattern: 'example/**/*.html', included: false},
{pattern: 'example/**/*.js', included: false},
{pattern: 'example/**/*.json', included: false},
{pattern: 'platform/**/*.js', included: false},
{pattern: 'warp/**/*.js', included: false},
{pattern: 'platform/**/*.html', included: false},
{pattern: 'src/**/*.html', included: false},
'test-main.js'
],
@ -89,7 +89,8 @@ module.exports = function(config) {
"dist/reports/coverage",
check: {
global: {
lines: 80
lines: 80,
excludes: ['src/plugins/plot/**/*.js']
}
}
},

View File

@ -49,7 +49,8 @@ requirejs.config({
"d3-format": "node_modules/d3-format/build/d3-format.min",
"d3-interpolate": "node_modules/d3-interpolate/build/d3-interpolate.min",
"d3-time": "node_modules/d3-time/build/d3-time.min",
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min"
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min",
"painterro": "node_modules/@cristian77/painterro/build/painterro.min"
},
"shim": {
"angular": {
@ -67,6 +68,9 @@ requirejs.config({
"moment-duration-format": {
"deps": ["moment"]
},
"painterro": {
"exports": "Painterro"
},
"saveAs": {
"exports": "saveAs"
},
@ -101,6 +105,7 @@ define([
var openmct = new MCT();
openmct.legacyRegistry = defaultRegistry;
openmct.install(openmct.plugins.Plot());
if (typeof BUILD_CONSTANTS !== 'undefined') {
openmct.install(buildInfo(BUILD_CONSTANTS));

View File

@ -1,8 +1,9 @@
{
"name": "openmct",
"version": "0.12.1-SNAPSHOT",
"version": "0.13.2-SNAPSHOT",
"description": "The Open MCT core platform",
"dependencies": {
"@cristian77/painterro": "^0.2.48",
"d3-array": "^1.0.2",
"d3-axis": "^1.0.4",
"d3-collection": "^1.0.2",
@ -28,7 +29,7 @@
"gulp-jshint-html-reporter": "^0.1.3",
"gulp-rename": "^1.2.2",
"gulp-requirejs-optimize": "^0.3.1",
"gulp-sass": "^2.2.0",
"gulp-sass": "^3.1.0",
"gulp-sourcemaps": "^1.6.0",
"jasmine-core": "^2.3.0",
"jscs-html-reporter": "^0.1.0",

View File

@ -38,6 +38,7 @@
ng-class="{ last:($index + 1) === contextualParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-click="parent.getCapability('action').perform('navigate')"
class="location-item">
</mct-representation>
</span>
@ -49,6 +50,7 @@
ng-class="{ last:($index + 1) === primaryParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-click="parent.getCapability('action').perform('navigate')"
class="location-item">
</mct-representation>
</span>

View File

@ -88,11 +88,15 @@ define(
* @private
*/
ElementsController.prototype.refreshComposition = function (domainObject) {
var selectedObjectComposition = domainObject && domainObject.useCapability('composition');
var refreshTracker = {};
this.currentRefresh = refreshTracker;
var selectedObjectComposition = domainObject && domainObject.useCapability('composition');
if (selectedObjectComposition) {
selectedObjectComposition.then(function (composition) {
this.scope.composition = composition;
if (this.currentRefresh === refreshTracker) {
this.scope.composition = composition;
}
}.bind(this));
} else {
this.scope.composition = [];

View File

@ -31,11 +31,34 @@ define(
mockSelection,
mockDomainObject,
mockMutationCapability,
mockCompositionCapability,
mockCompositionObjects,
mockComposition,
mockUnlisten,
selectable = [],
controller;
function mockPromise(value) {
return {
then: function (thenFunc) {
return mockPromise(thenFunc(value));
}
};
}
function createDomainObject() {
return {
useCapability: function () {
return mockCompositionCapability;
}
};
}
beforeEach(function () {
mockComposition = ["a", "b"];
mockCompositionObjects = mockComposition.map(createDomainObject);
mockCompositionCapability = mockPromise(mockCompositionObjects);
mockUnlisten = jasmine.createSpy('unlisten');
mockMutationCapability = jasmine.createSpyObj("mutationCapability", [
"listen"
@ -45,7 +68,7 @@ define(
"getCapability",
"useCapability"
]);
mockDomainObject.useCapability.andCallThrough();
mockDomainObject.useCapability.andReturn(mockCompositionCapability);
mockDomainObject.getCapability.andReturn(mockMutationCapability);
mockScope = jasmine.createSpyObj("$scope", ['$on']);
@ -65,7 +88,7 @@ define(
}
};
spyOn(ElementsController.prototype, 'refreshComposition');
spyOn(ElementsController.prototype, 'refreshComposition').andCallThrough();
controller = new ElementsController(mockScope, mockOpenMCT);
});
@ -137,6 +160,25 @@ define(
expect(mockDomainObject.getCapability).not.toHaveBeenCalledWith('mutation');
});
it("checks concurrent changes to composition", function () {
var secondMockComposition = ["a", "b", "c"],
secondMockCompositionObjects = secondMockComposition.map(createDomainObject),
firstCompositionCallback,
secondCompositionCallback;
spyOn(mockCompositionCapability, "then").andCallThrough();
controller.refreshComposition(mockDomainObject);
controller.refreshComposition(mockDomainObject);
firstCompositionCallback = mockCompositionCapability.then.calls[0].args[0];
secondCompositionCallback = mockCompositionCapability.then.calls[1].args[0];
secondCompositionCallback(secondMockCompositionObjects);
firstCompositionCallback(mockCompositionObjects);
expect(mockScope.composition).toBe(secondMockCompositionObjects);
});
});
}
);

View File

@ -99,7 +99,7 @@ $plotXBarH: 32px;
$plotLegendH: 20px;
$plotSwatchD: 8px;
// 1: Top, 2: right, 3: bottom, 4: left
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH, $plotYBarW);
$plotDisplayArea: (0, 0, $plotXBarH, $plotYBarW);
/* min plot height is based on user testing to find minimum useful height */
$plotMinH: 95px;
/*************** Bubbles */

View File

@ -40,7 +40,7 @@
* Use https://icomoon.io/app with icomoon-project-openmct-symbols-12px.json
* to generate font files
*/
font-family: 'symbolsfont 12px';
font-family: 'symbolsfont-12px';
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot');
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot?#iefix') format('embedded-opentype'),
url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.woff') format('woff'),
@ -248,6 +248,12 @@ a.disabled {
color: rgba(#fff, 0.2);
}
.comma-list span {
&:not(:first-child) {
&:before { content: ', '; }
}
}
.test-stripes {
@include bgDiagonalStripes();
}

View File

@ -33,6 +33,14 @@
}
}
[class*="icon-"].labeled {
// Moved from .s-button and generalized
&:before {
// Fend off label from icon when it's included
margin-right: $interiorMarginSm;
}
}
/************************** CHAR UNICODES */
$glyph-icon-alert-rect: '\e900';

View File

@ -44,6 +44,12 @@
}
}
.t-alert-unsynced {
@extend .icon-alert-triangle;
color: $colorPausedBg;
}
.bar .ui-symbol {
display: inline-block;
}
@ -81,18 +87,5 @@
@include transform(scale(0.3));
z-index: 2;
}
/* .t-item-icon-glyph {
&:after {
color: $colorIconLink;
content: '\e921'; //$glyph-icon-link;
height: auto; width: auto;
position: absolute;
left: 0; top: 0; right: 0; bottom: 20%;
@include transform-origin(bottom left);
@include transform(scale(0.3));
z-index: 2;
}
}*/
}
}

View File

@ -53,6 +53,7 @@
.l-inspector-part {
box-sizing: border-box;
padding-right: $interiorMargin;
.tree .form {
margin-left: $treeVCW + $interiorMarginLg;
}
@ -78,6 +79,7 @@
}
}
.form-row {
// To be replaced with .inspector-config, see below.
@include align-items(center);
border: none !important;
margin-bottom: 0 !important;
@ -99,15 +101,12 @@
position: relative;
}
ul li {
margin-bottom: $interiorMarginLg;
}
em.t-inspector-part-header {
border-radius: $basicCr;
background-color: $colorInspectorSectionHeaderBg;
color: $colorInspectorSectionHeaderFg;
margin-bottom: $interiorMargin;
margin-top: $interiorMarginLg;
//margin-bottom: $interiorMargin;
padding: floor($formTBPad * .75) $formLRPad;
text-transform: uppercase;
}
@ -201,3 +200,102 @@ mct-representation:not(.s-status-editing) .l-inspect {
pointer-events: inherit;
}
}
// NEW COMPACT FORM, FOR USE IN INSPECTOR
// ul > li > label, control
// Make a new UL for each form section
// Allow control-first, controls-below
.l-inspect .tree ul li,
.inspector-config ul li {
padding: 2px 0;
}
.inspector-config {
$labelW: 40%;
$minW: $labelW;
ul {
margin-bottom: $interiorMarginLg;
li {
@include display(flex);
@include flex-wrap(wrap);
@include align-items(center);
label,
.control {
@include display(flex);
min-width: $minW;
}
label {
line-height: inherit;
padding: $interiorMarginSm 0;
width: $labelW;
}
.control {
@include flex-grow(1);
}
&:not(.section-header) {
&:not(.connects-to-previous) {
//border-top: 1px solid $colorFormLines;
}
}
&.connects-to-previous {
padding-top: 0 !important;
}
&.section-header {
margin-top: $interiorMarginLg;
border-top: 1px solid $colorFormLines;
}
&.controls-first {
.control {
@include flex-grow(0);
margin-right: $interiorMargin;
min-width: 0;
order: 1;
width: auto;
}
label {
@include flex-grow(1);
order: 2;
width: auto;
}
}
&.controls-under {
display: block;
.control, label {
display: block;
width: auto;
}
ul li {
border-top: none !important;
padding: 0;
}
}
}
}
.form-error {
// Block element that visually flags an error and contains a message
background-color: $colorFormFieldErrorBg;
color: $colorFormFieldErrorFg;
border-radius: $basicCr;
display: block;
padding: 1px 6px;
&:before {
content: $glyph-icon-alert-triangle;
display: inline;
font-family: symbolsfont;
margin-right: $interiorMarginSm;
}
}
}
.tree .inspector-config {
margin-left: $treeVCW + $interiorMarginLg;
}

View File

@ -70,6 +70,7 @@
@import "fixed-position";
@import "lists/tabular";
@import "plots/plots-main";
@import "plots/legend";
@import "iframe";
@import "views";
@import "items/item";

View File

@ -5,6 +5,7 @@
}
.l-view-section {
//@include test(orange, 0.1);
@include absPosDefault(0);
h2 {
color: #fff;

View File

@ -270,37 +270,4 @@
@extend .s-summary-widget;
@extend .l-summary-widget;
padding: $interiorMarginSm $interiorMargin;
}
// Hide and show elements in the rule-header on hover
.l-widget-rule,
.l-widget-test-data-item {
.grippy,
.l-rule-action-buttons-wrapper,
.l-condition-action-buttons-wrapper,
.l-widget-test-data-item-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 500ms);
opacity: 0;
}
&:hover {
.grippy,
.l-rule-action-buttons-wrapper,
.l-widget-test-data-item-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 0);
opacity: 1;
}
}
.l-rule-action-buttons-wrapper {
.t-delete {
margin-left: 10px;
}
}
.t-condition {
&:hover {
.l-condition-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 0);
opacity: 1;
}
}
}
}

View File

@ -34,11 +34,6 @@ $pad: $interiorMargin * $baseRatio;
line-height: $btnStdH;
padding: 0 $pad;
&.labeled:before {
// Icon when it's included
margin-right: $interiorMarginSm;
}
&.lg {
font-size: 1rem;
}

View File

@ -150,6 +150,26 @@
}
}
/******************************************************** VIEW CONTROLS */
// Expand/collapse > and v arrows, used in tree and plot legend
// Moved this over from a tree-only context 5/18/17
.view-control {
@extend .ui-symbol;
cursor: pointer;
height: 1em; width: 1em;
line-height: inherit;
&:before {
position: absolute;
@include trans-prop-nice(transform, 100ms);
content: $glyph-icon-arrow-right;
@include transform-origin(center);
}
&.expanded:before {
@include transform(rotate(90deg));
}
}
/******************************************************** CUSTOM CHECKBOXES */
label.checkbox.custom,
label.radio.custom {
@ -318,7 +338,7 @@ input[type="text"].s-input-inline,
@include btnSubtle($bg: $colorSelectBg);
@extend .icon-arrow-down; // Context arrow
display: inline-block;
padding: 0 $interiorMargin;
line-height: 180%;
overflow: hidden;
position: relative;
select {
@ -329,8 +349,8 @@ input[type="text"].s-input-inline,
color: $colorSelectFg;
cursor: pointer;
border: none !important;
padding: 4px 25px 2px 0px;
width: 130%;
padding: 0 20px 0 $interiorMargin;
width: 100%;
option {
margin: $interiorMargin 0; // Firefox
}
@ -339,6 +359,7 @@ input[type="text"].s-input-inline,
@include transform(translateY(-50%));
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
display: block;
font-size: 0.8em;
pointer-events: none;
position: absolute;
right: $interiorMargin;

View File

@ -77,6 +77,14 @@
position: relative;
}
.s-menu {
border-radius: $basicCr;
@include containerSubtle($colorMenuBg, $colorMenuFg);
@include boxShdw($shdwMenu);
@include txtShdw($shdwMenuText);
padding: $interiorMarginSm 0;
}
.menu {
border-radius: $basicCr;
@include containerSubtle($colorMenuBg, $colorMenuFg);

View File

@ -398,10 +398,6 @@ body.desktop .t-message-list {
.object-header {
.t-object-alert {
display: inline;
&.t-alert-unsynced {
@extend .icon-alert-triangle;
color: $colorPausedBg;
}
}
}
}

View File

@ -20,53 +20,70 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
.l-palette {
$d: 16px;
$colorsPerRow: 10;
$m: 1;
box-sizing: border-box;
padding: $interiorMargin !important;
}
.l-palette-row {
@include clearfix;
line-height: $d;
width: ($d * $colorsPerRow) + ($m * $colorsPerRow);
.l-palette-row {
$d: 16px;
$m: 1;
$colorsPerRow: 10;
display: flex;
flex-wrap: wrap;
line-height: $d;
width: ($d * $colorsPerRow) + ($m * $colorsPerRow);
&.l-option-row {
margin-bottom: $interiorMargin;
.s-palette-item {
border-color: $colorPaletteFg;
&.l-option-row {
margin-bottom: $interiorMargin;
.s-palette-item {
border-color: $colorPaletteFg;
}
}
.l-palette-item {
box-sizing: border-box;
display: block;
height: $d; width: $d;
min-width: $d;
line-height: $d * 0.9;
margin: 0 ($m * 1px) ($m * 1px) 0;
position: relative;
text-align: center;
}
}
.s-palette-item {
border: 1px solid transparent;
color: $colorPaletteFg;
text-shadow: $shdwPaletteFg;
@include trans-prop-nice-fade(0.25s);
&:hover {
@include trans-prop-nice-fade(0);
border-color: $colorPaletteSelected !important;
}
&.selected {
border-color: $colorPaletteSelected;
box-shadow: $shdwPaletteSelected; //Needed to see selection rect on light colored swatches
}
}
.l-palette-item-label {
margin-left: $interiorMargin;
}
.l-inline-palette {
.l-palette-row {
width: 100%;
.l-palette-item {
//@include display(flex);
@include flex(1 0 auto);
margin: 1px;
min-width: auto;
width: auto;
&:before {
content: '';
padding-top: 75%;
}
}
.l-palette-item {
box-sizing: border-box;
display: block;
float: left;
height: $d; width: $d;
line-height: $d * 0.9;
margin: 0 ($m * 1px) ($m * 1px) 0;
position: relative;
text-align: center;
}
.s-palette-item {
border: 1px solid transparent;
color: $colorPaletteFg;
text-shadow: $shdwPaletteFg;
@include trans-prop-nice-fade(0.25s);
&:hover {
@include trans-prop-nice-fade(0);
border-color: $colorPaletteSelected !important;
}
&.selected {
border-color: $colorPaletteSelected;
box-shadow: $shdwPaletteSelected; //Needed to see selection rect on light colored swatches
}
}
.l-palette-item-label {
margin-left: $interiorMargin;
}
}
}
}
}

View File

@ -131,16 +131,18 @@ body.mobile {
}
}
body.phone.portrait {
.pane-tree-showing {
.pane.left.treeview {
width: $proporMenuOnly !important;
}
.pane.right.items {
left: 0 !important;
@include transform(translateX($proporMenuOnly));
.holder-object-and-inspector {
opacity: 0;
@include phonePortrait() {
body.phone {
.pane-tree-showing {
.pane.left.treeview {
width: $proporMenuOnly !important;
}
.pane.right.items {
left: 0 !important;
@include transform(translateX($proporMenuOnly));
.holder-object-and-inspector {
opacity: 0;
}
}
}
}

View File

@ -34,18 +34,7 @@ body.touch {
line-height: $mobileTreeItemH !important;
.view-control {
font-size: 1em;
margin-right: $interiorMargin;
width: ceil($mobileTreeItemH * 0.75);
&.has-children {
&:before {
content: $glyph-icon-arrow-down;
left: 50%;
@include transform(translateX(-50%) rotate(-90deg));
}
&.expanded:before {
@include transform(translateX(-50%) rotate(0deg));
}
}
width: ceil($mobileTreeItemH * 0.5);
}
.t-object-label {
line-height: inherit;

View 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;
}
}
}

View File

@ -20,18 +20,64 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
.abs.holder-plot {
// Fend off the scrollbar when less than min-height;
right: $interiorMargin;
right: $interiorMargin; // Fend off the scrollbar when less than min-height;
.t-object-alert.t-alert-unsynced {
display: none;
}
}
/********************************************* STACKED PLOT LAYOUT */
.t-plot-stacked {
.l-view-section {
// Make this a flex container
display: flex;
flex-flow: column nowrap;
.gl-plot.child-frame {
mct-plot {
display: flex;
flex: 1 1 auto;
height: 100%;
position: relative;
}
flex: 1 1 auto;
&:not(:first-child) {
margin-top: $interiorMargin;
}
}
}
.s-status-timeconductor-unsynced .holder-plot {
.t-object-alert.t-alert-unsynced {
display: block;
}
}
}
.gl-plot {
color: $colorPlotFg;
display: flex;
font-size: 0.7rem;
position: relative;
width: 100%;
height: 100%;
min-height: $plotMinH;
/********************************************* AXIS AND DISPLAY AREA */
.plot-wrapper-axis-and-display-area {
margin-top: $interiorMargin; // Keep the top tick label from getting clipped
position: relative;
flex: 1 1 auto;
.t-object-alert {
position: absolute;
display: block;
font-size: 1.5em;
top: $interiorMarginSm; left: $interiorMarginSm;
}
}
.gl-plot-wrapper-display-area-and-x-axis {
// Holds the plot area and the X-axis only
position: absolute;
@ -49,7 +95,6 @@
}
.gl-plot-axis-area.gl-plot-x {
//@include test(green);
top: auto;
right: 0;
bottom: 0;
@ -63,7 +108,7 @@
.gl-plot-axis-area {
position: absolute;
&.gl-plot-y {
top: $plotLegendH + $interiorMargin;
top: nth($plotDisplayArea, 1);
right: auto;
bottom: nth($plotDisplayArea, 3);
left: 0;
@ -158,17 +203,6 @@
}
}
.gl-plot-legend {
position: absolute;
top: 0;
right: 0;
bottom: auto;
left: 0;
height: $plotLegendH;
overflow-x: hidden;
overflow-y: auto;
}
/****************************** Limits and Out-of-Bounds data */
.l-limit-bar,
@ -235,39 +269,6 @@
border: 1px solid $colorPlotAreaBorder;
}
.gl-plot-legend,
.legend {
.plot-legend-item,
.legend-item {
display: inline-block;
margin-right: $interiorMarginLg;
margin-bottom: $interiorMarginSm;
span {
vertical-align: middle;
}
.plot-color-swatch,
.color-swatch {
border-radius: 2px;
display: inline-block;
height: $plotSwatchD;
width: $plotSwatchD;
}
}
}
.gl-plot-legend {
.plot-legend-item {
border-radius: $smallCr;
line-height: 1.5em;
padding: 0px $itemPadLR;
.plot-color-swatch {
border: 1px solid $colorBodyBg;
height: $plotSwatchD + 1;
width: $plotSwatchD + 1;
}
}
}
.tick {
position: absolute;
border: 0 $colorPlotHash solid;

View File

@ -34,7 +34,7 @@
$iconEdgeM: 4px;
$iconD: $treeSearchInputBarH - ($iconEdgeM*2);
@extend .icon-magnify;
font-size: 0.8em;
font-size: 0.8rem;
position: relative;
.search-input {
@ -60,7 +60,7 @@
position: relative;
width: 100%;
padding-left: $iconD + $interiorMargin !important;
padding-right: ($iconD * 2) + ($interiorMargin * 2) !important;
padding-right: $iconD + $interiorMargin !important;
// Make work for mct-control textfield
input {
@ -82,8 +82,7 @@
}
.clear-input {
// Hiding for now with addition of Cancel button
right: $iconD + $interiorMargin;
right: $interiorMargin;
// Icon is visible only when there is text input
visibility: hidden;
@ -98,16 +97,25 @@
}
}
.menu-icon {
// 'v' invoke menu icon
font-size: 0.8em;
padding-right: $iconEdgeM;
right: $iconEdgeM;
text-align: right;
&:hover {
color: pullForward($colorInputIcon, 10%);
}
}
&.search-filter-by-type {
.search-input {
padding-right: ($iconD * 2) + ($interiorMargin * 2) !important; // Allow room for menu-icon
}
.menu-icon {
// 'v' invoke menu icon for filtering by type
font-size: 0.8em;
padding-right: $iconEdgeM;
right: $iconEdgeM;
text-align: right;
&:hover {
color: pullForward($colorInputIcon, 10%);
}
}
.clear-input {
right: $iconD + $interiorMargin;
}
}
.search-menu-holder {
float: right;

View File

@ -23,7 +23,7 @@
ul.tree {
@include menuUlReset();
@include user-select(none);
li {
> li {
display: block;
position: relative;
}
@ -53,12 +53,10 @@ ul.tree {
.view-control {
color: $colorItemTreeVC;
margin-right: $interiorMargin;
height: 100%;
line-height: inherit;
width: $treeVCW;
&:before { display: none; }
&.has-children {
&:before { display: block; }
&:before { display: block; }
&.no-children {
&:before { display: none; }
}
}

View File

@ -83,9 +83,9 @@ define([
this.activeObject = domainObject;
if (domainObject && domainObject.hasCapability('composition')) {
$(this.toggleView.elements()).addClass('has-children');
$(this.toggleView.elements()).removeClass('no-children');
} else {
$(this.toggleView.elements()).removeClass('has-children');
$(this.toggleView.elements()).addClass('no-children');
}
if (domainObject && domainObject.hasCapability('status')) {

View File

@ -181,6 +181,8 @@ $colorPlotHash: $colorTick;
$stylePlotHash: dashed;
$colorPlotAreaBorder: $colorInteriorBorder;
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
$legendCollapsedNameMaxW: 50%;
$legendHoverValueBg: rgba($colorBodyFg, 0.1);
// Tree
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);

View File

@ -181,6 +181,8 @@ $colorPlotHash: $colorTick;
$stylePlotHash: dashed;
$colorPlotAreaBorder: $colorInteriorBorder;
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
$legendCollapsedNameMaxW: 50%;
$legendHoverValueBg: rgba($colorBodyFg, 0.2);
// Tree
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);

View File

@ -337,46 +337,6 @@ define([
"conversion": "number[]"
}
]
},
{
"key": "telemetry.panel",
"name": "Telemetry Panel",
"cssClass": "icon-telemetry-panel",
"description": "A panel for collecting telemetry elements.",
"priority": 899,
"delegates": [
"telemetry"
],
"features": "creation",
"contains": [
{
"has": "telemetry"
}
],
"model": {
"composition": []
},
"properties": [
{
"name": "Layout Grid",
"control": "composite",
"items": [
{
"name": "Horizontal grid (px)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric"
},
{
"name": "Vertical grid (px)",
"control": "textfield",
"cssClass": "l-input-sm l-numeric"
}
],
"pattern": "^(\\d*[1-9]\\d*)?$",
"property": "layoutGrid",
"conversion": "number[]"
}
]
}
]
}

View File

@ -373,10 +373,10 @@ define(
*/
FixedController.prototype.updateView = function (telemetryObject, datum) {
var metadata = this.openmct.telemetry.getMetadata(telemetryObject);
var telemetryKeyToDisplay = this.chooseTelemetryKeyToDisplay(metadata);
var formattedTelemetryValue = this.getFormattedTelemetryValueForKey(telemetryKeyToDisplay, datum, metadata);
var valueMetadata = this.chooseValueMetadataToDisplay(metadata);
var formattedTelemetryValue = this.getFormattedTelemetryValueForKey(valueMetadata, datum);
var limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, telemetryKeyToDisplay);
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, valueMetadata);
this.setDisplayedValue(
telemetryObject,
@ -389,29 +389,28 @@ define(
/**
* @private
*/
FixedController.prototype.getFormattedTelemetryValueForKey = function (telemetryKeyToDisplay, datum, metadata) {
var valueMetadata = metadata.value(telemetryKeyToDisplay);
FixedController.prototype.getFormattedTelemetryValueForKey = function (valueMetadata, datum) {
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
return formatter.format(datum[valueMetadata.key]);
return formatter.format(datum);
};
/**
* @private
*/
FixedController.prototype.chooseTelemetryKeyToDisplay = function (metadata) {
FixedController.prototype.chooseValueMetadataToDisplay = function (metadata) {
// If there is a range value, show that preferentially
var telemetryKeyToDisplay = metadata.valuesForHints(['range'])[0];
var valueMetadata = metadata.valuesForHints(['range'])[0];
// If no range is defined, default to the highest priority non time-domain data.
if (telemetryKeyToDisplay === undefined) {
if (valueMetadata === undefined) {
var valuesOrderedByPriority = metadata.values();
telemetryKeyToDisplay = valuesOrderedByPriority.filter(function (valueMetadata) {
return !(valueMetadata.hints.domain);
valueMetadata = valuesOrderedByPriority.filter(function (values) {
return !(values.hints.domain);
})[0];
}
return telemetryKeyToDisplay.source;
return valueMetadata;
};
/**

View File

@ -38,6 +38,10 @@ define([
' </div>' +
' </div>';
var NEW_NOTEBOOK_BUTTON_TEMPLATE = '<a class="s-button labeled icon-notebook new-notebook-entry" title="New Notebook Entry">' +
'<span class="title-label">New Notebook Entry</span>' +
'</a>';
/**
* MCT Trigger Modal is intended for use in only one location: inside the
* object-header to allow views in a layout to be popped out in a modal.
@ -74,9 +78,28 @@ define([
closeButton,
doneButton,
blocker,
overlayContainer;
overlayContainer,
notebookButtonEl,
notebookButton,
actions = $scope.domainObject.getCapability('action'),
notebookAction = actions.getActions({'key': 'notebook-new-entry'});
if (notebookAction) {
if (notebookAction.length > 0) {
notebookButtonEl = document.createElement('div');
$(notebookButtonEl).addClass('notebook-button-container');
notebookButtonEl.innerHTML = NEW_NOTEBOOK_BUTTON_TEMPLATE;
notebookButton = frame.querySelector('.object-browse-bar .right');
notebookButton.prepend(notebookButtonEl);
// $(frame.querySelector('.object-holder')).addClass('container-notebook');
notebookButton.addEventListener('click', function () {
notebookAction[0].perform();
});
}
}
function openOverlay() {
// Remove frame classes from being applied in a non-frame context
$(frame).removeClass('frame frame-template');
overlay = document.createElement('div');
@ -109,7 +132,11 @@ define([
overlay = undefined;
}
toggleOverlay = function () {
toggleOverlay = function (event) {
if (event) {
event.stopPropagation();
}
if (!isOpen) {
openOverlay();
isOpen = true;

View File

@ -106,8 +106,8 @@ define(
'telemetryFormatter',
['format']
);
mockFormatter.format.andCallFake(function (value) {
return "Formatted " + value;
mockFormatter.format.andCallFake(function (valueMetadata) {
return "Formatted " + valueMetadata.value;
});
mockDomainObject = jasmine.createSpyObj(
@ -697,7 +697,7 @@ define(
source: 'range'
}
]);
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
var key = controller.chooseValueMetadataToDisplay(mockMetadata).source;
expect(key).toEqual('range');
});
@ -719,7 +719,7 @@ define(
}
}
]);
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
var key = controller.chooseValueMetadataToDisplay(mockMetadata).source;
expect(key).toEqual('image');
});

View File

@ -52,6 +52,12 @@ define([
beforeEach(function () {
$scope = jasmine.createSpyObj('$scope', ['$on']);
$scope.domainObject = { getCapability: function () {
return { getActions: function () {
}};
}};
$element = jasmine.createSpyObj('$element', [
'parent',
'remove',

View File

@ -0,0 +1,314 @@
define([
"legacyRegistry",
"./src/controllers/NotebookController",
"./src/controllers/NewEntryController",
"./src/controllers/SelectSnapshotController",
"./src/controllers/LayoutNotebookController",
"./src/directives/MCTSnapshot",
"../layout/src/MCTTriggerModal",
"./src/directives/EntryDnd",
"./src/actions/ViewSnapshot",
"./src/actions/AnnotateSnapshot",
"./src/actions/RemoveEmbed",
"./src/actions/CreateSnapshot",
"./src/actions/RemoveSnapshot",
"./src/actions/NewEntryContextual",
"./src/capabilities/NotebookCapability",
"./src/policies/CompositionPolicy",
"./src/policies/ViewPolicy",
"text!./res/templates/layoutNotebook.html",
"text!./res/templates/notebook.html",
"text!./res/templates/entry.html",
"text!./res/templates/annotation.html",
"text!./res/templates/notifications.html",
"text!../layout/res/templates/frame.html",
"text!./res/templates/controls/embedControl.html",
"text!./res/templates/controls/snapSelect.html"
], function (
legacyRegistry,
NotebookController,
NewEntryController,
SelectSnapshotController,
LayoutNotebookController,
MCTSnapshot,
MCTModalNotebook,
MCTEntryDnd,
ViewSnapshotAction,
AnnotateSnapshotAction,
RemoveEmbedAction,
CreateSnapshotAction,
RemoveSnapshotAction,
newEntryAction,
NotebookCapability,
CompositionPolicy,
ViewPolicy,
layoutNotebookTemplate,
notebookTemplate,
entryTemplate,
annotationTemplate,
notificationsTemplate,
frameTemplate,
embedControlTemplate,
snapSelectTemplate
) {
legacyRegistry.register("platform/features/notebook", {
"name": "Notebook Plugin",
"description": "Create and save timestamped notes with embedded object snapshots.",
"extensions":
{
"types": [
{
"key": "notebook",
"name": "Notebook",
"cssClass": "icon-notebook",
"description": "Create and save timestamped notes with embedded object snapshots.",
"features": ["creation"],
"model": {
"entries": [],
"composition": [],
"entryTypes": []
}
}
],
"views": [
{
"key": "notebook.view",
"type": "notebook",
"cssClass": "icon-notebook",
"name": "notebook",
"template": notebookTemplate,
"editable": false,
"uses": [
"composition",
"action"
],
"gestures": [
"drop"
]
}
],
"controllers": [
{
"key": "NotebookController",
"implementation": NotebookController,
"depends": ["$scope",
"dialogService",
"popupService",
"agentService",
"objectService",
"navigationService",
"now",
"actionService",
"$timeout",
"$element",
"$sce"
]
},
{
"key": "NewEntryController",
"implementation": NewEntryController,
"depends": ["$scope",
"$rootScope"
]
},
{
"key": "selectSnapshotController",
"implementation": SelectSnapshotController,
"depends": ["$scope",
"$rootScope"
]
},
{
"key": "LayoutNotebookController",
"implementation": LayoutNotebookController,
"depends": ["$scope"]
}
],
"representations": [
{
"key": "draggedEntry",
"template": entryTemplate
},
{
"key": "frameLayoutNotebook",
"template": frameTemplate
}
],
"templates": [
{
"key": "annotate-snapshot",
"template": annotationTemplate
},
{
"key": "notificationTemplate",
"template": notificationsTemplate
}
],
"directives": [
{
"key": "mctSnapshot",
"implementation": MCTSnapshot,
"depends": [
"$rootScope",
"$document",
"exportImageService",
"dialogService",
"notificationService"
]
},
{
"key": "mctEntryDnd",
"implementation": MCTEntryDnd,
"depends": [
"$rootScope",
"$compile",
"dndService",
"typeService",
"notificationService"
]
},
{
"key": "mctModalNotebook",
"implementation": MCTModalNotebook,
"depends": [
"$document"
]
}
],
"actions": [
{
"key": "view-snapshot",
"implementation": ViewSnapshotAction,
"name": "View Snapshot",
"description": "View the large image in a modal",
"category": "embed",
"depends": [
"$compile"
]
},
{
"key": "annotate-snapshot",
"implementation": AnnotateSnapshotAction,
"name": "Annotate Snapshot",
"cssClass": "icon-pencil labeled",
"description": "Annotate embed's snapshot",
"category": "embed",
"depends": [
"dialogService",
"dndService",
"$rootScope"
]
},
{
"key": "remove-embed",
"implementation": RemoveEmbedAction,
"name": "Remove...",
"cssClass": "icon-trash labeled",
"description": "Remove this embed",
"category": [
"embed",
"embed-no-snap"
],
"depends": [
"dialogService"
]
},
{
"key": "remove-snapshot",
"implementation": RemoveSnapshotAction,
"name": "Remove Snapshot",
"cssClass": "icon-trash labeled",
"description": "Remove Snapshot of the embed",
"category": "embed",
"depends": [
"dialogService"
]
},
{
"key": "create-snapshot",
"implementation": CreateSnapshotAction,
"name": "Create Snapshot",
"description": "Create a snapshot for the embed",
"category": "embed-no-snap",
"priority": "preferred",
"depends": [
"$compile"
]
},
{
"key": "notebook-new-entry",
"implementation": newEntryAction,
"name": "New Notebook Entry",
"cssClass": "icon-notebook labeled",
"description": "Add a new entry",
"category": [
"contextual",
"view-control"
],
"depends": [
"$compile",
"$rootScope",
"dialogService",
"notificationService",
"linkService"
],
"priority": "preferred"
}
],
"licenses": [
{
"name": "painterro",
"version": "4.1.0",
"author": "Mike Bostock",
"description": "D3 (or D3.js) is a JavaScript library for visualizing data using web standards. D3 helps you bring data to life using SVG, Canvas and HTML. D3 combines powerful visualization and interaction techniques with a data-driven approach to DOM manipulation, giving you the full capabilities of modern browsers and the freedom to design the right visual interface for your data.",
"website": "https://d3js.org/",
"copyright": "Copyright 2010-2016 Mike Bostock",
"license": "BSD-3-Clause",
"link": "https://github.com/d3/d3/blob/master/LICENSE"
}
],
"capabilities": [
{
"key": "notebook",
"name": "Notebook Capability",
"description": "Provides a capability for looking for a notebook domain object",
"implementation": NotebookCapability,
"depends": [
"typeService"
]
}
],
"policies": [
{
"category": "composition",
"implementation": CompositionPolicy,
"message": "Objects of this type cannot contain objects of that type."
}
],
"controls": [
{
"key": "embed-control",
"template": embedControlTemplate
},
{
"key": "snapshot-select",
"template": snapSelectTemplate
}
],
"stylesheets": [
{
"stylesheetUrl": "css/notebook.css"
},
{
"stylesheetUrl": "css/notebook-espresso.css",
"theme": "espresso"
},
{
"stylesheetUrl": "css/notebook-snow.css",
"theme": "snow"
}
]
}
});
});

View File

@ -0,0 +1,236 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
.w-notebook {
font-size: 0.8rem;
overflow: hidden;
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
width: auto;
height: auto;
}
.l-notebook-drag-area {
padding: 10px;
font-style: italic;
cursor: pointer;
&:before { margin-right: 7px !important; }
.label {
@include ellipsize();
}
}
.frame {
.icon-notebook {
margin-right: 5px;
}
}
.overlay.l-dialog .title{
white-space: normal;
}
.w-notebook-entries {
//@include test($a: 0.1);
padding-right: $interiorMarginSm;
position: relative;
overflow-x: hidden;
overflow-y: scroll;
.t-entries-list {
}
}
.l-notebook-entry {
$p: $interiorMarginSm;
box-sizing: border-box;
margin-bottom: $p;
padding: $p $interiorMargin;
.s-notebook-entry-time,
.s-notebook-entry-text,
.notebook-entry-delete {
padding-top: $p;
padding-bottom: $p;
}
.s-notebook-entry-time {
border: 1px solid transparent; // Needed to maintain vertical alignment with s-notebook-entry-text
}
.l-notebook-entry-content{
.s-notebook-entry-text {
// Contenteditable div that holds text
min-height: 24px; // Needed in Firefox when field is blank
}
.entry-embeds{
flex-wrap: wrap;
}
.snap-thumb {
cursor: pointer;
}
}
}
.l-entry-embed {
$m: $interiorMarginSm;
position: relative;
margin: $m $m 0 0;
padding: $interiorMarginSm;
&.has-snapshot {
&:before {
position: absolute;
text-shadow: rgba(black, 0.7) 0 1px 5px;
z-index: 2;
}
}
.snap-thumb {
$d: 50px;
width: $d;
height: $d;
border-radius: 5px;
overflow: hidden;
img {
height: 100%;
width: 100%;
}
}
.embed-info {
margin-left: $interiorMargin;
.embed-title {
font-weight: bold;
}
}
}
.t-contents,
.snap-annotation {
// Todo: don't write this to t-contents, add a l- class
overflow: hidden;
img{
width: 100%;
}
}
.notebook-filters {
.select {
margin-left: $interiorMargin;
}
}
/********************************************* MOBILE */
@include phonePortrait() {
body.phone {
.w-notebook-entry-time-and-content {
flex-direction: column !important;
}
.s-notebook-entry-time,
.notebook-entry-delete {
padding-top: 0;
padding-bottom: 0;
}
}
}
/********************************************* SNAPSHOT VIEWING IN OVERLAY CONTEXT */
.t-snapshot {
.l-view-header {
.view-date {
float: right;
}
.view-snap-info {
float: left;
}
.s-button {
clear: both;
float: left;
margin-top: 10px;
font-size: 14px;
}
.view-info {
float: left;
.embed-icon {
display: inline-block;
}
.embed-title {
display: inline-block;
margin: 0 5px 0 10px;
}
.object-header {
display: inline-block;
}
}
}
}
.ptro-color-main{
top: 0;
z-index: 10;
position: relative;
height: auto;
min-height: 41px;
}
.ptro-wrapper{
bottom: 0;
top: 40px;
}
/********************************************* New Notebook Entry from Large View overlay */
.notebook-button-container {
//margin-right: $interiorMargin; // TODO: change to apply margin-left to view switcher button in Large View
}
/********************************************* NO IDEA WHAT THERE ARE APPLYING TO */
.context-available {
outline: none;
}
.menu-element.menu-view{
z-index: 999;
}
.overlay.l-dialog .abs.editor{
padding-right: 0;
}
.overlay.l-dialog .outer-holder.annotation-dialog{
width: 90%;
height: 90%;
}
.snap-annotation-wrapper{
padding-top: 40px;
}
.t-console {
// Temp console-like reporting element
max-height: 200px;
box-sizing: border-box;
padding: 5px;
}

View File

@ -0,0 +1,68 @@
/*****************************************************************************
* 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.
*****************************************************************************/
.l-notebook-drag-area {
border: 1px dashed rgba($colorKey, 0.7);
border-radius: $controlCr;
color: rgba($colorBodyFg, 0.7);
&:hover {
background: rgba($colorKey, 0.2);
color: $colorBodyFg;
}
&.drag-active{
border-color: $colorKey;
}
}
.s-notebook-entry {
background-color: rgba($colorBodyFg, 0.1);
border-radius: $basicCr;
&:hover {
background-color: rgba($colorBodyFg, 0.2);
}
.s-notebook-entry-time {
color: rgba($colorBodyFg, 0.5);
}
}
.l-entry-embed {
border-radius: $controlCr;
background-color: rgba($colorBodyFg, 0.1);
&.has-snapshot {
&:before {
color: $colorBodyFg;
}
}
}
.snapshot {
background: $colorBodyBg;
}
.snap-thumb {
border: 1px solid $colorInteriorBorder;
}

View File

@ -0,0 +1,32 @@
/*****************************************************************************
* 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.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "../../../../commonUI/general/res/sass/constants";
@import "../../../../commonUI/general/res/sass/mixins";
@import "../../../../commonUI/general/res/sass/glyphs";
@import "../../../../commonUI/themes/espresso/res/sass/constants";
@import "../../../../commonUI/themes/espresso/res/sass/mixins";
//@import "constants";
//@import "constants-espresso";
@import "notebook-thematic";

View File

@ -0,0 +1,32 @@
/*****************************************************************************
* 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.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "../../../../commonUI/general/res/sass/constants";
@import "../../../../commonUI/general/res/sass/mixins";
@import "../../../../commonUI/general/res/sass/glyphs";
@import "../../../../commonUI/themes/snow/res/sass/constants";
@import "../../../../commonUI/themes/snow/res/sass/mixins";
//@import "constants";
//@import "constants-snow";
@import "notebook-thematic";

View File

@ -0,0 +1,28 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "../../../../commonUI/general/res/sass/constants";
@import "../../../../commonUI/general/res/sass/mixins";
@import "../../../../commonUI/general/res/sass/mobile/constants";
@import "../../../../commonUI/general/res/sass/mobile/mixins";
@import "notebook-base";

View File

@ -0,0 +1,2 @@
<div class="snap-annotation" id="snap-annotation" ng-init="ngModel.tracker()">
</div>

View File

@ -0,0 +1,51 @@
<!--
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.
-->
<!--
This element appears in the overlay dialog when initiating a new Notebook Entry from a view's Notebook button -->
<div class='form-control'>
<ng-form name="mctControl">
<div class='fields' ng-controller="NewEntryController">
<div class="l-flex-row new-notebook-entry-embed l-entry-embed {{cssClass}}"
ng-class="{ 'has-snapshot' : snapToggle }">
<div class="holder flex-elem snap-thumb"
ng-if="snapToggle">
<img ng-src="{{snapshot.src}}" alt="{{snapshot.modified}}">
</div>
<div class="holder flex-elem embed-info">
<div class="embed-title">{{objectName}}</div>
<div class="embed-date"
ng-if="snapToggle">{{snapshot.modified| date:'yyyy-MM-dd HH:mm:ss'}}</div>
</div>
<div class="holder flex-elem annotate-new"
ng-if="snapToggle">
<a class="s-button flex-elem icon-pencil "
title="Annotate this snapshot"
ng-click="annotateSnapshot()">
<span class="title-label">Annotate</span>
</a>
</div>
</div>
</div>
</ng-form>
</div>

View File

@ -0,0 +1,30 @@
<!--
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 class='form-control select' ng-controller="selectSnapshotController">
<select
ng-model="selectModel"
ng-options="opt.value as opt.name for opt in options"
ng-required="ngRequired"
name="mctControl">
<!-- <option value="" ng-show="!ngModel[field]">- Select One -</option> -->
</select>
</div>

View File

@ -0,0 +1,38 @@
<!--
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 class="frame snap-frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
<div class="abs object-browse-bar l-flex-row">
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<mct-representation
key="'switcher'"
ng-model="representation"
mct-object="domainObject">
</mct-representation>
</div>
</div>
<div class="abs object-holder" data-entry = "{{parameters.entry}}" data-embed = "{{parameters.embed}}" mct-snapshot ng-if="representation.selected.key">
<mct-representation
key="representation.selected.key"
mct-object="representation.selected.key && domainObject">
</mct-representation>
</div>
</div>

View File

@ -0,0 +1,54 @@
<!--
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 class="frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
<div class="abs object-browse-bar l-flex-row">
<div class="left flex-elem l-flex-row grows">
<mct-representation
key="'object-header-frame'"
mct-object="domainObject"
class="l-flex-row flex-elem object-header grows">
</mct-representation>
</div>
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<a class="s-button icon-notebook"
title="New Notebook Entry"
ng-if="parameters"
ng-click="ngModel()">
</a>
<mct-representation
key="'switcher'"
ng-model="representation"
mct-object="domainObject">
</mct-representation>
<a class="s-button icon-expand t-btn-view-large"
title="View large"
mct-modal-notebook>
</a>
</div>
</div>
<div class="abs object-holder">
<mct-representation
key="representation.selected.key"
mct-object="representation.selected.key && domainObject">
</mct-representation>
</div>
</div>

View File

@ -0,0 +1,84 @@
<!--
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 class="abs l-layout"
ng-controller="LayoutController as controller"
ng-click="controller.clearSelection()">
<!-- Background grid -->
<div class="l-grid-holder" ng-click="controller.clearSelection()">
<div class="l-grid l-grid-x"
ng-if="!controller.getGridSize()[0] < 3"
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
<div class="l-grid l-grid-y"
ng-if="!controller.getGridSize()[1] < 3"
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
</div>
<div class='abs frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border'
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-selected':controller.selected(childObject) }"
ng-repeat="childObject in composition"
ng-click="controller.select($event, childObject.getId())"
ng-style="controller.getFrameStyle(childObject.getId())">
<div ng-controller="LayoutNotebookController as controller">
<mct-representation key="'frameLayoutNotebook'"
class="t-rep-frame holder contents abs"
parameters = "hasNotebookAction"
ng-model="newNotebook"
mct-object="childObject">
</mct-representation>
</div>
<!-- Drag handles -->
<span class="abs t-edit-handle-holder s-hover-border" ng-if="controller.selected(childObject)">
<span class="edit-handle edit-move"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-nw"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [-1,-1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-ne"
mct-drag-down="controller.startDrag(childObject.getId(), [0,1], [1,-1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-sw"
mct-drag-down="controller.startDrag(childObject.getId(), [1,0], [-1,1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-se"
mct-drag-down="controller.startDrag(childObject.getId(), [0,0], [1,1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
</span>
</div>
</div>

View File

@ -0,0 +1,124 @@
<div ng-controller="NotebookController as controller" class="mct-notebook w-notebook l-flex-col">
<div class="l-notebook-head holder l-flex-row flex-elem">
<div class="l-flex-row holder grows holder-search">
<div class="search-bar flex-elem l-flex-row grows"
ng-class="{ holder: !(entrySearch === '' || entrySearch === undefined) }">
<div class="holder flex-elem grows">
<input class="search-input"
type="text" tabindex="10000"
ng-model="entrySearch"
ng-keyup="controller.search()"/>
<a class="clear-icon clear-input icon-x-in-circle"
ng-class="{show: !(entrySearch === '' || entrySearch === undefined)}"
ng-click="entrySearch = ''; controller.search()"></a>
</div>
</div>
</div>
<div class="notebook-filters right l-flex-row flex-elem grows flex-justify-end">
<div class="select">
<select ng-model="showTime">
<option value="0" selected="selected">Show all</option>
<option value="1">Last hour</option>
<option value="8">Last 8 hours</option>
<option value="24">Last 24 hours</option>
</select>
</div>
<div class="select">
<select ng-model="sortEntries">
<option value="-createdOn" selected="selected">Newest first</option>
<option value="createdOn">Oldest first</option>
</select>
</div>
</div>
</div>
<!-- drag area -->
<div class="holder flex-elem l-flex-row icon-plus labeled l-notebook-drag-area" ng-click="newEntry($event)"
id="newEntry" mct-entry-dnd>
<span class="label">To start a new entry, click here or drag and drop any object</span>
</div>
<!-- entries -->
<div class="holder flex-elem grows w-notebook-entries t-entries-list" ng-mouseover="handleActive()">
<ul>
<li class="l-flex-row has-local-controls l-notebook-entry s-notebook-entry"
id="{{'entry_'+ entry.id}}"
ng-if="hoursFilter(showTime,entry.createdOn)"
ng-repeat="entry in model.entries | filter:entrySearch | orderBy: sortEntries track by $index"
ng-init="$last && finished(model.entries)"
mct-entry-dnd>
<div class="holder flex-elem l-flex-row grows w-notebook-entry-time-and-content">
<div class="holder flex-elem s-notebook-entry-time">
<span>{{entry.createdOn | date:'yyyy-MM-dd'}}</span>
<span>{{entry.createdOn | date:'HH:mm:ss'}}</span>
</div>
<div class="holder flex-elem l-flex-col grows l-notebook-entry-content">
<div contenteditable="true"
ng-blur="textBlur($event, entry.id)"
ng-focus="textFocus($event, entry.id)"
ng-model="entry.text"
placeholder="Enter text here"
class="flex-elem s-input-inline t-notebook-entry-input s-notebook-entry-text"
ng-bind-html="trustedHtml(entry.text)">
</div>
<!-- embeds -->
<div class="flex-elem entry-embeds l-flex-row">
<div class="l-flex-row l-entry-embed {{embed.cssClass}}"
ng-repeat="embed in entry.embeds track by $index"
ng-class="{ 'has-snapshot' : embed.snapshot }"
id="{{embed.id}}">
<div class="snap-thumb"
ng-if="embed.snapshot"
ng-click="viewSnapshot($event,embed.snapshot.src,embed.id,entry.createdOn,this,embed)">
<img ng-src="{{embed.snapshot.src}}" src="//:0" alt="{{embed.id}}">
</div>
<div class="embed-info l-flex-col">
<div class="embed-title object-header">
<a ng-click='navigate($event,embed.type)'>{{embed.name}}</a>
<a class='context-available' ng-click='openMenu($event,embed.type)'></a>
</div>
<div class="hide-menu" ng-show="false">
<div class="menu-element context-menu-wrapper mobile-disable-select">
<div class="menu context-menu">
<ul>
<li ng-repeat="menu in menuEmbed"
ng-click="menu.perform($event,embed.snapshot.src,embed.id,entry.createdOn,this,embed)"
title="{{menu.getMetadata().description}}"
class="{{menu.getMetadata().cssClass}}"
ng-if="embed.snapshot">
{{menu.getMetadata().name}}
</li>
<li ng-repeat="menu in menuEmbedNoSnap"
ng-click="menu.perform($event,embed.snapshot.src,embed.id,entry.createdOn,this)"
title="{{menu.getMetadata().description}}"
class="{{menu.getMetadata().cssClass}}"
ng-if="!embed.snapshot">
{{menu.getMetadata().name}}
</li>
<li ng-repeat="menu in embedActions"
ng-click="menu.perform()"
title="{{menu.name}}"
class="{{menu.cssClass}}">
{{menu.name}}
</li>
</ul>
</div>
</div>
</div>
<div class="embed-date"
ng-if="embed.snapshot">{{embed.id| date:'yyyy-MM-dd HH:mm:ss'}}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- delete entry -->
<div class="holder flex-elem local-control notebook-entry-delete">
<a class="s-icon-button icon-trash" title="Delete Entry" ng-click="deleteEntry($event)"></a>
</div>
</li>
</ul>
</div>
</div>

View File

@ -0,0 +1,8 @@
<span class="status block">
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<span class="status-indicator icon-bell"></span>
<span class="label">
Notifications
</span>
<span class="count"></span>
</span>

View File

@ -0,0 +1,111 @@
/*****************************************************************************
* 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 viewSnapshot (Originally NewWindowAction). Created by vwoeltje on 11/18/14.
*/
define(
["painterro", "zepto"],
function (Painterro, $) {
var ANNOTATION_STRUCT = {
title: "Annotate Snapshot",
template: "annotate-snapshot",
options: [{
name: "OK",
key: "ok",
description: "save annotation"
},
{
name: "Cancel",
key: "cancel",
description: "cancel editing"
}]
};
function AnnotateSnapshot(dialogService,dndService,$rootScope,context) {
context = context || {};
// Choose the object to be opened into a new tab
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
this.dndService = dndService;
this.$rootScope = $rootScope;
}
AnnotateSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId,$scope) {
var DOMAIN_OBJECT = this.domainObject;
var ROOTSCOPE = this.$rootScope;
this.dialogService.getUserChoice(ANNOTATION_STRUCT)
.then(saveNotes);
var painterro;
var tracker = function () {
$(document.body).find('.l-dialog .outer-holder').addClass('annotation-dialog');
painterro = Painterro({
id: 'snap-annotation',
backgroundFillColor: '#eee',
hiddenTools: ['save', 'open', 'close','eraser'],
saveHandler: function (image, done) {
if (entryId && embedId) {
var elementPos = DOMAIN_OBJECT.model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = DOMAIN_OBJECT.model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
$scope.saveSnap(image.asBlob(), embedPos, elementPos);
}else {
ROOTSCOPE.snapshot = {'src': image.asDataURL('image/png'),
'modified': Date.now()};
}
done(true);
}
}).show(snapshot);
};
ANNOTATION_STRUCT.model = {'tracker': tracker};
function saveNotes(param) {
if (param === 'ok') {
painterro.save();
}else {
ROOTSCOPE.snapshot = "annotationCancelled";
}
}
};
return AnnotateSnapshot;
}
);

View File

@ -0,0 +1,65 @@
/*****************************************************************************
* 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 SNAPSHOT_TEMPLATE = '<mct-representation key="\'draggedEntry\'"' +
'parameters="{entry:entryId,embed:embedId}"' +
'class="t-rep-frame holder"' +
'mct-object="selObj">' +
'</mct-representation>';
function CreateSnapshot($compile,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.context = context;
this.$compile = $compile;
}
CreateSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId,$scope) {
var compile = this.$compile;
var model = this.domainObject.model;
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
var embedType = entryEmbeds[embedPos].type;
$scope.getDomainObj(embedType).then(function (resp) {
if (entryId >= 0 && embedId >= 0) {
$scope.selObj = resp[embedType];
$scope.entryId = elementPos;
$scope.embedId = embedPos;
compile(SNAPSHOT_TEMPLATE)($scope);
}
});
};
return CreateSnapshot;
}
);

View File

@ -0,0 +1,195 @@
/*****************************************************************************
* 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 SNAPSHOT_TEMPLATE = '<mct-representation key="\'draggedEntry\'"' +
'class="t-rep-frame holder"' +
'mct-object="selObj">' +
'</mct-representation>';
var NEW_TASK_FORM = {
name: "Create a Notebook Entry",
hint: "Please select one Notebook",
sections: [{
rows: [{
name: 'Entry',
key: 'entry',
control: 'textarea',
required: true,
"cssClass": "l-textarea-sm"
},
{
name: 'Embed Type',
key: 'withSnapshot',
control: 'snapshot-select',
"options": [
{
"name": "Link and Snapshot",
"value": true
},
{
"name": "Link only",
"value": false
}
]
},
{
name: 'Embed',
key: 'embedObject',
control: 'embed-control'
},
{
name: 'Save in Notebook',
key: 'saveNotebook',
control: 'locator',
validate: validateLocation
}]
}]
};
function NewEntryContextual($compile,$rootScope,dialogService,notificationService,linkService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
this.notificationService = notificationService;
this.linkService = linkService;
this.$rootScope = $rootScope;
this.$compile = $compile;
}
function validateLocation(newParentObj) {
return newParentObj.model.type === 'notebook';
}
NewEntryContextual.prototype.perform = function () {
var self = this;
var domainObj = this.domainObject;
var notification = this.notificationService;
var dialogService = this.dialogService;
var rootScope = this.$rootScope;
rootScope.newEntryText = '';
// Create the overlay element and add it to the document's body
this.$rootScope.selObj = domainObj;
this.$rootScope.selValue = "";
var newScope = rootScope.$new();
newScope.selObj = domainObj;
newScope.selValue = "";
this.$compile(SNAPSHOT_TEMPLATE)(newScope);
//newScope.$destroy();
this.$rootScope.$watch("snapshot", setSnapshot);
function setSnapshot(value) {
if (value === "annotationCancelled") {
rootScope.snapshot = rootScope.lastValue;
rootScope.lastValue = '';
}else if (value && value !== rootScope.lastValue) {
var overlayModel = {
title: NEW_TASK_FORM.name,
message: NEW_TASK_FORM.message,
structure: NEW_TASK_FORM,
value: {'entry': rootScope.newEntryText || ""}
};
rootScope.currentDialog = overlayModel;
dialogService.getDialogResponse(
"overlay-dialog",
overlayModel,
function () {
return overlayModel.value;
}
).then(addNewEntry);
rootScope.lastValue = value;
}
}
function addNewEntry(options) {
options.selectedModel = options.embedObject.getModel();
options.cssClass = options.embedObject.getCapability('type').typeDef.cssClass;
if (self.$rootScope.snapshot) {
options.snapshot = self.$rootScope.snapshot;
self.$rootScope.snapshot = undefined;
}else {
options.snapshot = undefined;
}
if (!options.withSnapshot) {
options.snapshot = '';
}
createSnap(options);
}
function createSnap(options) {
options.saveNotebook.useCapability('mutation', function (model) {
var entries = model.entries;
var lastEntry = entries[entries.length - 1];
var date = Date.now();
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
model.entries.push({
'id': date,
'createdOn': date,
'text': options.entry,
'embeds': [{'type': options.embedObject.getId(),
'id': '' + date,
'cssClass': options.cssClass,
'name': options.selectedModel.name,
'snapshot': options.snapshot
}]
});
}else {
model.entries[entries.length - 1] = {
'id': date,
'createdOn': date,
'text': options.entry,
'embeds': [{'type': options.embedObject.getId(),
'id': '' + date,
'cssClass': options.cssClass,
'name': options.selectedModel.name,
'snapshot': options.snapshot
}]
};
}
});
notification.info({
title: "Notebook Entry created"
});
}
};
NewEntryContextual.appliesTo = function (context) {
var domainObject = context.domainObject;
return domainObject && domainObject.hasCapability("notebook") &&
domainObject.getCapability("notebook").isNotebook();
};
return NewEntryContextual;
}
);

View File

@ -0,0 +1,72 @@
/*****************************************************************************
* 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 RemoveEmbed(dialogService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
}
RemoveEmbed.prototype.perform = function ($event,snapshot,embedId,entryId) {
var DOMAIN_OBJ = this.domainObject;
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Embed. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
remove();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
function remove() {
DOMAIN_OBJ.useCapability('mutation', function (model) {
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
model.entries[elementPos].embeds.splice(embedPos, 1);
});
}
};
return RemoveEmbed;
}
);

View File

@ -0,0 +1,74 @@
/*****************************************************************************
* 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 RemoveSnapshot(dialogService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
}
RemoveSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId) {
var DOMAIN_OBJ = this.domainObject;
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Snapshot. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
remove();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
function remove() {
DOMAIN_OBJ.useCapability('mutation', function (model) {
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
model.entries[elementPos].embeds[embedPos].snapshot = "";
});
}
};
return RemoveSnapshot;
}
);

View File

@ -0,0 +1,160 @@
/*****************************************************************************
* 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 ViewSnapshot
*/
define(
['zepto'],
function ($) {
var OVERLAY_TEMPLATE = '' +
' <div class="abs blocker"></div>' +
' <div class="abs outer-holder">' +
' <a class="close icon-x-in-circle"></a>' +
' <div class="abs inner-holder l-flex-col">' +
' <div class="t-contents flex-elem holder grows"></div>' +
' <div class="bottom-bar flex-elem holder">' +
' <a class="t-done s-button major">Done</a>' +
' </div>' +
' </div>' +
' </div>';
var toggleOverlay,
overlay,
closeButton,
doneButton,
blocker,
overlayContainer,
img,
annotateButton,
annotateImg;
function ViewSnapshot($compile,context) {
context = context || {};
this.$compile = $compile;
}
function openOverlay(url,header) {
overlay = document.createElement('div');
$(overlay).addClass('abs overlay l-large-view');
overlay.innerHTML = OVERLAY_TEMPLATE;
overlayContainer = overlay.querySelector('.t-contents');
closeButton = overlay.querySelector('a.close');
closeButton.addEventListener('click', toggleOverlay);
doneButton = overlay.querySelector('a.t-done');
doneButton.addEventListener('click', toggleOverlay);
blocker = overlay.querySelector('.abs.blocker');
blocker.addEventListener('click', toggleOverlay);
annotateButton = header.querySelector('a.icon-pencil');
annotateButton.addEventListener('click', annotateImg);
document.body.appendChild(overlay);
img = document.createElement('img');
img.src = url;
overlayContainer.appendChild(header);
overlayContainer.appendChild(img);
}
function closeOverlay() {
overlayContainer.removeChild(img);
document.body.removeChild(overlay);
closeButton.removeEventListener('click', toggleOverlay);
closeButton = undefined;
doneButton.removeEventListener('click', toggleOverlay);
doneButton = undefined;
blocker.removeEventListener('click', toggleOverlay);
blocker = undefined;
overlayContainer = undefined;
overlay = undefined;
img = undefined;
}
function headerTemplate() {
var template = '<div class="t-snapshot l-view-header">' +
'<div class="view-info">' +
'<div ng-class="cssClass" class="embed-icon"></div>' +
'<div class="embed-title">{{entryName}}</div>' +
'<div class="object-header">' +
'<a href="" class="context-available" ng-click="openMenu($event,embedType)""></a>' +
'</div>' +
'<div class="hide-menu" ng-show="false">' +
'<div class="menu-element menu-view context-menu-wrapper mobile-disable-select">' +
'<div class="menu context-menu">' +
'<ul>' +
'<li ng-repeat="menu in embedActions"' +
'ng-click="menu.perform()"' +
'title="{{menu.name}}"' +
'class="{{menu.cssClass}}">' +
'{{menu.name}}' +
'</li>' +
'</ul>' +
'</div>' +
'</div>' +
'</div>' +
'</div>' +
'<div class="view-date">' +
'<span class="icon-alert-rect" title="Snapshot">' +
'</span> ' +
'SNAPSHOT {{snapDate | date:\'yyyy-MM-dd HH:mm:ss\'}}' +
'</div>' +
'<a class="s-button icon-pencil" title="Annotate">' +
'<span class="title-label">Annotate</span>' +
'</a>' +
'</div>';
return template;
}
ViewSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId,$scope,embed) {
var isOpen = false;
// Create the overlay element and add it to the document's body
$scope.cssClass = embed.cssClass;
$scope.embedType = embed.type;
$scope.entryName = embed.name;
$scope.snapDate = +embedId;
var element = this.$compile(headerTemplate())($scope);
var annotateAction = $scope.action.getActions({category: 'embed'})[1];
toggleOverlay = function () {
if (!isOpen) {
openOverlay(snapshot, element[0]);
isOpen = true;
} else {
closeOverlay();
isOpen = false;
}
};
annotateImg = function () {
closeOverlay();
annotateAction.perform($event, snapshot, embedId, entryId, $scope);
};
toggleOverlay();
};
return ViewSnapshot;
}
);

View File

@ -20,40 +20,31 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["./SubPlot"],
function (SubPlot) {
function () {
/**
* Utility factory; wraps the SubPlot constructor and adds
* in a reference to the telemetryFormatter, which will be
* used to represent telemetry values (timestamps or data
* values) as human-readable strings.
* @memberof platform/features/plot
* The notebook capability allows a domain object to know whether the
* notebook plugin is present or not.
*
* @constructor
*/
function SubPlotFactory(telemetryFormatter) {
this.telemetryFormatter = telemetryFormatter;
function NotebookCapability(typeService, domainObject) {
this.domainObject = domainObject;
this.typeService = typeService;
return this;
}
/**
* Instantiate a new sub-plot.
* @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
* @returns {SubPlot} the instantiated sub-plot
* @method
* Returns true if there is a notebook domain Object.
*
* @returns {Boolean}
*/
SubPlotFactory.prototype.createSubPlot = function (telemetryObjects, panZoomStack) {
return new SubPlot(
telemetryObjects,
panZoomStack,
this.telemetryFormatter
);
NotebookCapability.prototype.isNotebook = function () {
return this.typeService.getType('notebook');
};
return SubPlotFactory;
return NotebookCapability;
}
);

View File

@ -0,0 +1,54 @@
/*****************************************************************************
* 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 implements object types and associated views for
* display-building.
*/
define(
[],
function () {
/**
* The LayoutNotebookController is responsible for supporting the
* notebook feature creation on theLayout view.
**/
function LayoutNotebookController($scope) {
$scope.hasNotebookAction = undefined;
$scope.newNotebook = undefined;
var actions = $scope.domainObject.getCapability('action');
var notebookAction = actions.getActions({'key': 'notebook-new-entry'});
if (notebookAction.length > 0) {
$scope.hasNotebookAction = true;
$scope.newNotebook = function () {
notebookAction[0].perform();
};
}
}
return LayoutNotebookController;
}
);

View File

@ -0,0 +1,66 @@
/*****************************************************************************
* 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 NewEntryController. */
define(
[],
function () {
function NewEntryController($scope,$rootScope) {
$scope.snapshot = undefined;
$scope.snapToggle = true;
$scope.entryText = '';
var annotateAction = $rootScope.selObj.getCapability('action').getActions(
{category: 'embed'})[1];
$scope.$parent.$parent.ngModel[$scope.$parent.$parent.field] = $rootScope.selObj;
$scope.objectName = $rootScope.selObj.getModel().name;
$scope.cssClass = $rootScope.selObj.getCapability('type').typeDef.cssClass;
$scope.annotateSnapshot = function ($event) {
if ($rootScope.currentDialog.value) {
$rootScope.newEntryText = $scope.$parent.$parent.ngModel.entry;
$rootScope.currentDialog.cancel();
annotateAction.perform($event, $rootScope.snapshot.src);
$rootScope.currentDialog = undefined;
}
};
function updateSnapshot(img) {
$scope.snapshot = img;
}
// Update set of actions whenever the action capability
// changes or becomes available.
$rootScope.$watch("snapshot", updateSnapshot);
$rootScope.$watch("selValue", toggleEmbed);
function toggleEmbed(value) {
$scope.snapToggle = value;
}
}
return NewEntryController;
}
);

View File

@ -0,0 +1,355 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/*-- main controller file, here is the core functionality of the notebook plugin --*/
define(
['zepto'],
function ($) {
function NotebookController(
$scope,
dialogService,
popupService,
agentService,
objectService,
navigationService,
now,
actionService,
$timeout,
$element,
$sce
) {
$scope.entriesEl = $(document.body).find('.t-entries-list');
$scope.sortEntries = '-createdOn';
$scope.showTime = "0";
$scope.editEntry = false;
$scope.entrySearch = '';
$scope.entryTypes = [];
$scope.embedActions = [];
$scope.currentEntryValue = '';
/*--seconds in an hour--*/
var SECONDS_IN_AN_HOUR = 60 * 60 * 1000;
this.scope = $scope;
$scope.hoursFilter = function (hours,entryTime) {
if (+hours) {
return entryTime > (Date.now() - SECONDS_IN_AN_HOUR * (+hours));
}else {
return true;
}
};
$scope.scrollToTop = function () {
var entriesContainer = $scope.entriesEl.parent();
entriesContainer[0].scrollTop = 0;
};
$scope.newEntry = function ($event) {
$scope.scrollToTop();
var entries = $scope.domainObject.model.entries;
var lastEntry = entries[entries.length - 1];
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
$scope.domainObject.useCapability('mutation', function (model) {
var id = Date.now();
model.entries.push({'id': id, 'createdOn': id});
});
} else {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entries.length - 1].createdOn = Date.now();
});
}
$scope.entrySearch = '';
};
$scope.deleteEntry = function ($event) {
/* This is really brittle - change the markup and this doesn't work */
var delId = $event.currentTarget.parentElement.parentElement.id;
var errorDialog = dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Notebook entry. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
var elementPos = $scope.domainObject.model.entries.map(function (x) {
return x.id;
}).indexOf(+delId.replace('entry_', ''));
if (elementPos !== -1) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries.splice(elementPos, 1);
});
} else {
window.console.log('delete error');
}
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
};
$scope.textFocus = function ($event, entryId) {
if ($event.currentTarget && $event.currentTarget.innerText) {
/*
On focus, if the currentTarget isn't blank, set the global currentEntryValue = the
content of the current focus. This will be used at blur to determine if the
current entry has been modified or not.
Not sure this is right, would think we'd always want to set curEntVal even if blank
*/
$scope.currentEntryValue = $event.currentTarget.innerText;
} else {
$event.target.innerText = '';
}
};
//On text blur(when focus is removed)
$scope.textBlur = function ($event, entryId) {
// entryId is the unique numeric based on the original createdOn
if ($event.target && $event.target.innerText !== "") {
var elementPos = $scope.domainObject.model.entries.map(function (x) {
return x.id;
}).indexOf(+(entryId));
// If the text of an entry has been changed, then update the text and the modifiedOn numeric
// Otherwise, don't do anything
if ($scope.currentEntryValue !== $event.target.innerText) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[elementPos].text = $event.target.innerText;
model.entries[elementPos].modified = Date.now();
});
}
}
};
$scope.finished = function (model) {
var lastEntry = model[model.length - 1];
if (!lastEntry.text) {
var newEntry = $scope.entriesEl.children().children()[0];
$(newEntry).find('.t-notebook-entry-input').focus();
}
};
$scope.handleActive = function () {
var newEntry = $scope.entriesEl.find('.active');
if (newEntry) {
newEntry.removeClass('active');
}
};
$scope.clearSearch = function () {
$scope.entrySearch = '';
};
$scope.viewSnapshot = function ($event,snapshot,embedId,entryId,$innerScope,domainObject) {
var viewAction = $scope.action.getActions({category: 'embed'})[0];
viewAction.perform($event, snapshot, embedId, entryId, $innerScope, domainObject);
};
$scope.parseText = function (text) {
if (text) {
return text.split(/\r\n|\r|\n/gi);
}
};
//parse text and add line breaks where needed
$scope.trustedHtml = function (text) {
if (text) {
return $sce.trustAsHtml(this.parseText(text).join('<br>'));
}
};
$scope.renderImage = function (img) {
return URL.createObjectURL(img);
};
$scope.getDomainObj = function (id) {
return objectService.getObjects([id]);
};
function refreshComp(change) {
if (change && change.length) {
change[0].getCapability('action').getActions({key: 'remove'})[0].perform();
}
}
$scope.actionToMenuOption = function (action) {
return {
key: action.getMetadata().key,
name: action.getMetadata().name,
cssClass: action.getMetadata().cssClass,
perform: action.perform
};
};
// Maintain all "conclude-editing" and "save" actions in the
// present context.
function updateActions() {
$scope.menuEmbed = $scope.action ?
$scope.action.getActions({category: 'embed'}) :
[];
$scope.menuEmbedNoSnap = $scope.action ?
$scope.action.getActions({category: 'embed-no-snap'}) :
[];
$scope.menuActions = $scope.action ?
$scope.action.getActions({key: 'window'}) :
[];
}
// Update set of actions whenever the action capability
// changes or becomes available.
$scope.$watch("action", updateActions);
$scope.navigate = function ($event,embedType) {
if ($event) {
$event.preventDefault();
}
$scope.getDomainObj(embedType).then(function (resp) {
navigationService.setNavigation(resp[embedType]);
});
};
$scope.saveSnap = function (url,embedPos,entryPos) {
var snapshot = false;
if (url) {
if (embedPos !== -1 && entryPos !== -1) {
var reader = new window.FileReader();
reader.readAsDataURL(url);
reader.onloadend = function () {
snapshot = reader.result;
$scope.domainObject.useCapability('mutation', function (model) {
if (model.entries[entryPos]) {
model.entries[entryPos].embeds[embedPos].snapshot = {
'src': snapshot,
'type': url.type,
'size': url.size,
'modified': Date.now()
};
model.entries[entryPos].embeds[embedPos].id = Date.now();
}
});
};
}
}else {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryPos].embeds[embedPos].snapshot = snapshot;
});
}
};
/*---popups menu embeds----*/
function getEmbedActions(embedType) {
if (!$scope.embedActions.length) {
$scope.getDomainObj(embedType).then(function (resp) {
$scope.embedActions = [];
$scope.embedActions.push($scope.actionToMenuOption(
$scope.action.getActions({key: 'window',selectedObject: resp[embedType]})[0]
));
$scope.embedActions.push({
key: 'navigate',
name: 'Go to Original',
cssClass: '',
perform: function () {
$scope.navigate('', embedType);
}
});
});
}
}
$scope.openMenu = function ($event,embedType) {
$event.preventDefault();
getEmbedActions(embedType);
var body = $(document).find('body'),
initiatingEvent = agentService.isMobile() ?
'touchstart' : 'mousedown',
dismissExistingMenu,
menu,
popup;
var container = $($event.currentTarget).parent().parent();
menu = container.find('.menu-element');
// Remove the context menu
function dismiss() {
container.find('.hide-menu').append(menu);
body.off("mousedown", dismiss);
dismissExistingMenu = undefined;
$scope.embedActions = [];
}
// Dismiss any menu which was already showing
if (dismissExistingMenu) {
dismissExistingMenu();
}
// ...and record the presence of this menu.
dismissExistingMenu = dismiss;
popup = popupService.display(menu, [$event.pageX,$event.pageY], {
marginX: 0,
marginY: -50
});
// Stop propagation so that clicks or touches on the menu do not close the menu
menu.on(initiatingEvent, function (event) {
event.stopPropagation();
$timeout(dismiss, 300);
});
// Dismiss the menu when body is clicked/touched elsewhere
// ('mousedown' because 'click' breaks left-click context menus)
// ('touchstart' because 'touch' breaks context menus up)
body.on(initiatingEvent, dismiss);
};
$scope.$watchCollection("composition", refreshComp);
$scope.$on('$destroy', function () {});
}
return NotebookController;
});

View File

@ -20,28 +20,25 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* Module defining SelectSnapshotController. */
define(
['../src/PlotOptionsForm'],
function (PlotOptionsForm) {
[],
function () {
describe("The Plot Options form", function () {
var plotOptionsForm;
function SelectSnapshotController($scope,$rootScope) {
beforeEach(function () {
$scope.selectModel = true;
plotOptionsForm = new PlotOptionsForm();
});
function selectprint(value) {
$rootScope.selValue = value;
$scope.$parent.$parent.ngModel[$scope.$parent.$parent.field] = value;
}
it("defines form specs for x-axis, y-axis, and series data", function () {
expect(plotOptionsForm.xAxisForm).toBeDefined();
expect(plotOptionsForm.xAxisForm.sections).toBeDefined();
expect(plotOptionsForm.xAxisForm.sections[0].rows).toBeDefined();
expect(plotOptionsForm.xAxisForm.sections[0].rows.length).toBeGreaterThan(0);
$scope.$watch("selectModel", selectprint);
expect(plotOptionsForm.yAxisForm).toBeDefined();
expect(plotOptionsForm.plotSeriesForm).toBeDefined();
});
}
});
return SelectSnapshotController;
}
);

View File

@ -0,0 +1,140 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['zepto'], function ($) {
var SNAPSHOT_TEMPLATE = '<mct-representation key="\'draggedEntry\'"' +
'parameters="{entry:entryId,embed:embedId}"' +
'class="t-rep-frame holder"' +
'mct-object="selObj">' +
'</mct-representation>';
function EntryDnd($rootScope,$compile,dndService,typeService,notificationService) {
function link($scope, $element) {
function drop(e) {
var selectedObject = dndService.getData('mct-domain-object');
var selectedModel = selectedObject.getModel();
var cssClass = selectedObject.getCapability('type').typeDef.cssClass;
var entryId = -1;
var embedId = -1;
$scope.clearSearch();
if ($element[0].id === 'newEntry') {
entryId = $scope.domainObject.model.entries.length;
embedId = 0;
var lastEntry = $scope.domainObject.model.entries[entryId - 1];
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries.push({'createdOn': +Date.now(),
'id': +Date.now(),
'embeds': [{'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
}]
});
});
}else {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryId - 1] =
{'createdOn': +Date.now(),
'embeds': [{'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
}]
};
});
}
$scope.scrollToTop();
notificationService.info({
title: "Notebook Entry created"
});
}else {
entryId = $scope.domainObject.model.entries.map(function (x) {
return (x.id);
}).indexOf(Number($element[0].id.replace('entry_', '')));
if (!$scope.domainObject.model.entries[entryId].embeds) {
$scope.domainObject.model.entries[entryId].embeds = [];
}
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryId].embeds.push({'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
});
});
embedId = $scope.domainObject.model.entries[entryId].embeds.length - 1;
if (selectedObject) {
e.preventDefault();
}
}
if (entryId >= 0 && embedId >= 0) {
$scope.selObj = selectedObject;
$scope.entryId = entryId;
$scope.embedId = embedId;
$compile(SNAPSHOT_TEMPLATE)($scope);
}
if ($(e.currentTarget).hasClass('drag-active')) {
$(e.currentTarget).removeClass('drag-active');
}
}
function dragover(e) {
if (!$(e.currentTarget).hasClass('drag-active')) {
$(e.currentTarget).addClass('drag-active');
}
}
// Listen for the drop itself
$element.on('dragover', dragover);
$element.on('drop', drop);
$scope.$on('$destroy', function () {
$element.off('dragover', dragover);
$element.off('drop', drop);
});
}
return {
restrict: 'A',
link: link
};
}
return EntryDnd;
});

View File

@ -0,0 +1,166 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'zepto'
], function (
$
) {
var OVERLAY_TEMPLATE = '' +
' <div class="abs blocker"></div>' +
' <div class="abs outer-holder">' +
' <a class="close icon-x-in-circle"></a>' +
' <div class="abs inner-holder l-flex-col">' +
' <div class="t-contents flex-elem holder grows"></div>' +
' <div class="bottom-bar flex-elem holder">' +
' <a class="t-done s-button major">Done</a>' +
' </div>' +
' </div>' +
' </div>';
var NEW_NOTEBOOK_BUTTON_TEMPLATE = '<a class="s-button labeled icon-notebook new-notebook-entry" title="New Notebook Entry">' +
'<span class="title-label">New Notebook Entry</span>' +
'</a>';
/**
* MCT Trigger Modal is intended for use in only one location: inside the
* object-header to allow views in a layout to be popped out in a modal.
* Users can close the modal and go back to normal, and everything generally
* just works fine.
*
* This code is sensitive to how our html is constructed-- particularly with
* how it locates the the container of an element in a layout. However, it
* should be able to handle slight relocations so long as it is always a
* descendent of a `.frame` element.
*/
function MCTModalNotebook($document) {
var document = $document[0];
function link($scope, $element) {
var frame = $element.parent();
for (var i = 0; i < 10; i++) {
if (frame.hasClass('frame')) {
break;
}
frame = frame.parent();
}
if (!frame.hasClass('frame')) {
$element.remove();
return;
}
frame = frame[0];
var layoutContainer = frame.parentElement,
isOpen = false,
toggleOverlay,
overlay,
closeButton,
doneButton,
notebookButton,
blocker,
overlayContainer,
notebookButtonEl;
function openOverlay() {
// Remove frame classes from being applied in a non-frame context
$(frame).removeClass('frame frame-template');
overlay = document.createElement('div');
$(overlay).addClass('abs overlay l-large-view');
overlay.innerHTML = OVERLAY_TEMPLATE;
overlayContainer = overlay.querySelector('.t-contents');
closeButton = overlay.querySelector('a.close');
closeButton.addEventListener('click', toggleOverlay);
doneButton = overlay.querySelector('a.t-done');
doneButton.addEventListener('click', toggleOverlay);
blocker = overlay.querySelector('.abs.blocker');
blocker.addEventListener('click', toggleOverlay);
document.body.appendChild(overlay);
layoutContainer.removeChild(frame);
overlayContainer.appendChild(frame);
//verify if there is a new notebook entry action
var actions = $scope.domainObject.getCapability('action');
var notebookAction = actions.getActions({'key': 'notebook-new-entry'});
if (notebookAction.length > 0) {
notebookButtonEl = document.createElement('div');
$(notebookButtonEl).addClass('notebook-button-container');
notebookButtonEl.innerHTML = NEW_NOTEBOOK_BUTTON_TEMPLATE;
notebookButton = frame.querySelector('.object-browse-bar .right');
notebookButton.prepend(notebookButtonEl);
// $(frame.querySelector('.object-holder')).addClass('container-notebook');
notebookButton.addEventListener('click', function () {
notebookAction[0].perform();
});
}
}
function closeOverlay() {
$(frame).addClass('frame frame-template');
overlayContainer.removeChild(frame);
layoutContainer.appendChild(frame);
document.body.removeChild(overlay);
closeButton.removeEventListener('click', toggleOverlay);
closeButton = undefined;
doneButton.removeEventListener('click', toggleOverlay);
doneButton = undefined;
blocker.removeEventListener('click', toggleOverlay);
blocker = undefined;
overlayContainer = undefined;
overlay = undefined;
if (notebookButton) {
notebookButton.removeChild(notebookButtonEl);
}
}
toggleOverlay = function (event) {
event.stopPropagation();
if (!isOpen) {
openOverlay();
isOpen = true;
} else {
closeOverlay();
isOpen = false;
}
};
$element.on('click', toggleOverlay);
$scope.$on('$destroy', function () {
$element.off('click', toggleOverlay);
});
}
return {
restrict: 'A',
link: link
};
}
return MCTModalNotebook;
});

View File

@ -0,0 +1,133 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['zepto'], function ($) {
/**
*/
function MCTSnapshot($rootScope,$document,exportImageService,dialogService,notificationService) {
var document = $document[0];
function link($scope, $element,$attrs) {
var element = $element[0];
var layoutContainer = element.parentElement,
toggleOverlay,
makeImg,
saveImg,
snapshot = document.createElement('div');
function openOverlay() {
// Remove frame classes from being applied in a non-frame context
$(snapshot).addClass('abs overlay l-large-view snapshot');
snapshot.appendChild(element);
document.body.appendChild(snapshot);
}
function closeOverlay() {
if (snapshot) {
snapshot.removeChild(element);
layoutContainer.remove();
}
document.body.removeChild(snapshot);
snapshot = undefined;
$element.remove();
}
toggleOverlay = function () {
openOverlay();
makeImg(element);
};
makeImg = function (el) {
var scope = $scope;
var dialog = dialogService.showBlockingMessage({
title: "Saving...",
hint: "Taking Snapshot...",
unknownProgress: true,
severity: "info",
delay: true
});
window.setTimeout(function () {
window.EXPORT_IMAGE_TIMEOUT = 5000;
exportImageService.exportPNGtoSRC(el).then(function (img) {
if (img) {
if (dialog) {
dialog.dismiss();
}
if ($element[0].dataset.entry && $element[0].dataset.embed) {
saveImg(img, +$element[0].dataset.entry, +$element[0].dataset.embed);
closeOverlay();
} else {
var reader = new window.FileReader();
reader.readAsDataURL(img);
reader.onloadend = function () {
$($element[0]).attr("data-snapshot", reader.result);
$rootScope.snapshot = {'src': reader.result,
'type': img.type,
'size': img.size,
'modified': Date.now()
};
closeOverlay(false);
scope.$destroy();
};
}
} else {
dialog.dismiss();
}
}, function (error) {
if (dialog) {
dialog.dismiss();
}
closeOverlay();
});
}, 500);
window.EXPORT_IMAGE_TIMEOUT = 500;
};
saveImg = function (url,entryId,embedId) {
$scope.$parent.$parent.$parent.saveSnap(url, embedId, entryId);
};
if ($(document.body).find('.overlay.snapshot').length === 0) {
toggleOverlay();
}
$scope.$on('$destroy', function () {
$element.off('click', toggleOverlay);
$element.remove();
});
}
return {
restrict: 'A',
link: link
};
}
return MCTSnapshot;
});

View File

@ -0,0 +1,44 @@
/******************************************************************************
* 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 implements "containment" rules, which determine which objects
* can be contained within a notebook.
*/
define(
[],
function () {
function CompositionPolicy() {
}
CompositionPolicy.prototype.allow = function (parent, child) {
var parentDef = parent.getCapability('type').getName();
if (parentDef === 'Notebook' && child.getCapability('status').list().length) {
return false;
}
return true;
};
return CompositionPolicy;
}
);

View File

@ -0,0 +1,40 @@
/*****************************************************************************
* 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 ViewPolicy() {
}
ViewPolicy.prototype.allow = function (view, domainObject) {
// if (view.key === 'layout') {
// return false;
// }
return true;
};
return ViewPolicy;
}
);

View File

@ -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.

View File

@ -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"
}
]
}
});
});

View File

@ -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>

View File

@ -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>

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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", 'white').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", 'white').finally(function () {
self.hideExportButtons = false;
});
};
return PlotController;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

View File

@ -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;
}
);

Some files were not shown because too many files have changed in this diff Show More