Compare commits
32 Commits
compositio
...
testathon-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
514c48de04 | ||
|
|
2bdd6def7b | ||
|
|
d79001e7af | ||
|
|
fd31fa3b43 | ||
|
|
fa7298a752 | ||
|
|
1a23f2b390 | ||
|
|
4df6d6141b | ||
|
|
1c97138607 | ||
|
|
8db75bf41e | ||
|
|
00fb071fe2 | ||
|
|
2bf6a48d49 | ||
|
|
eca3c57bd8 | ||
|
|
7753703034 | ||
|
|
f9060a485d | ||
|
|
39a7f43cd0 | ||
|
|
5103207a70 | ||
|
|
8c2cc90f04 | ||
|
|
ce431848b3 | ||
|
|
5726fe6313 | ||
|
|
6145843e86 | ||
|
|
0225cbab6a | ||
|
|
e477beb587 | ||
|
|
ee5d59024a | ||
|
|
5288dadafb | ||
|
|
7f3cc09cbc | ||
|
|
94fa70abb1 | ||
|
|
12574a1333 | ||
|
|
dc91a94f0e | ||
|
|
0243aa6584 | ||
|
|
e5d869f01e | ||
|
|
8c72729a2a | ||
|
|
129ab1791b |
168
API.md
168
API.md
@@ -1,3 +1,57 @@
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
**Table of Contents**
|
||||
|
||||
- [Building Applications With Open MCT](#building-applications-with-open-mct)
|
||||
- [Scope and purpose of this document](#scope-and-purpose-of-this-document)
|
||||
- [Building From Source](#building-from-source)
|
||||
- [Starting an Open MCT application](#starting-an-open-mct-application)
|
||||
- [Plugins](#plugins)
|
||||
- [Defining and Installing a New Plugin](#defining-and-installing-a-new-plugin)
|
||||
- [Domain Objects and Identifiers](#domain-objects-and-identifiers)
|
||||
- [Object Attributes](#object-attributes)
|
||||
- [Domain Object Types](#domain-object-types)
|
||||
- [Root Objects](#root-objects)
|
||||
- [Object Providers](#object-providers)
|
||||
- [Composition Providers](#composition-providers)
|
||||
- [Adding Composition Providers](#adding-composition-providers)
|
||||
- [Default Composition Provider](#default-composition-provider)
|
||||
- [Telemetry API](#telemetry-api)
|
||||
- [Integrating Telemetry Sources](#integrating-telemetry-sources)
|
||||
- [Telemetry Metadata](#telemetry-metadata)
|
||||
- [Values](#values)
|
||||
- [Value Hints](#value-hints)
|
||||
- [The Time Conductor and Telemetry](#the-time-conductor-and-telemetry)
|
||||
- [Telemetry Providers](#telemetry-providers)
|
||||
- [Telemetry Requests](#telemetry-requests)
|
||||
- [Request Strategies **draft**](#request-strategies-draft)
|
||||
- [`latest` request strategy](#latest-request-strategy)
|
||||
- [`minmax` request strategy](#minmax-request-strategy)
|
||||
- [Telemetry Formats **draft**](#telemetry-formats-draft)
|
||||
- [Registering Formats](#registering-formats)
|
||||
- [Telemetry Data](#telemetry-data)
|
||||
- [Telemetry Datums](#telemetry-datums)
|
||||
- [Limit Evaluators **draft**](#limit-evaluators-draft)
|
||||
- [Telemetry Visualization APIs **draft**](#telemetry-visualization-apis-draft)
|
||||
- [Time API](#time-api)
|
||||
- [Time Systems and Bounds](#time-systems-and-bounds)
|
||||
- [Defining and Registering Time Systems](#defining-and-registering-time-systems)
|
||||
- [Getting and Setting the Active Time System](#getting-and-setting-the-active-time-system)
|
||||
- [Time Bounds](#time-bounds)
|
||||
- [Clocks](#clocks)
|
||||
- [Defining and registering clocks](#defining-and-registering-clocks)
|
||||
- [Getting and setting active clock](#getting-and-setting-active-clock)
|
||||
- [Stopping an active clock](#stopping-an-active-clock)
|
||||
- [Clock Offsets](#clock-offsets)
|
||||
- [Time Events](#time-events)
|
||||
- [List of Time Events](#list-of-time-events)
|
||||
- [The Time Conductor](#the-time-conductor)
|
||||
- [Time Conductor Configuration](#time-conductor-configuration)
|
||||
- [Example conductor configuration](#example-conductor-configuration)
|
||||
- [Included Plugins](#included-plugins)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
# Building Applications With Open MCT
|
||||
|
||||
## Scope and purpose of this document
|
||||
@@ -154,7 +208,7 @@ registry.
|
||||
|
||||
eg.
|
||||
```javascript
|
||||
openmct.types.addType('my-type', {
|
||||
openmct.types.addType('example.my-type', {
|
||||
name: "My Type",
|
||||
description: "This is a type that I added!",
|
||||
creatable: true
|
||||
@@ -162,8 +216,9 @@ openmct.types.addType('my-type', {
|
||||
```
|
||||
|
||||
The `addType` function accepts two arguments:
|
||||
* A `string` key identifying the type. This key is used when specifying a type
|
||||
for an object.
|
||||
* A `string` key identifying the type. This key is used when specifying a type
|
||||
for an object. We recommend prefixing your types with a namespace to avoid
|
||||
conflicts with other plugins.
|
||||
* An object type specification. An object type definition supports the following
|
||||
attributes
|
||||
* `name`: a `string` naming this object type
|
||||
@@ -194,7 +249,7 @@ To do so, use the `addRoot` method of the object API.
|
||||
eg.
|
||||
```javascript
|
||||
openmct.objects.addRoot({
|
||||
namespace: "my-namespace",
|
||||
namespace: "example.namespace",
|
||||
key: "my-key"
|
||||
});
|
||||
```
|
||||
@@ -235,13 +290,12 @@ It is expected that the `get` function will return a
|
||||
[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
|
||||
that resolves with the object being requested.
|
||||
|
||||
In future, object providers will support other methods to enable other operations
|
||||
with persistence stores, such as creating, updating, and deleting objects.
|
||||
In future, object providers will support other methods to enable other operations with persistence stores, such as creating, updating, and deleting objects.
|
||||
|
||||
## Composition Providers
|
||||
|
||||
The _composition_ of a domain object is the list of objects it contains, as shown
|
||||
(for example) in the tree for browsing. Open MCT provides a
|
||||
The _composition_ of a domain object is the list of objects it contains, as
|
||||
shown (for example) in the tree for browsing. Open MCT provides a
|
||||
[default solution](#default-composition-provider) for composition, but there
|
||||
may be cases where you want to provide the composition of a certain object
|
||||
(or type of object) dynamically.
|
||||
@@ -255,7 +309,7 @@ Composition Provider:
|
||||
```javascript
|
||||
openmct.composition.addProvider({
|
||||
appliesTo: function (domainObject) {
|
||||
return domainObject.type === 'my-type';
|
||||
return domainObject.type === 'example.my-type';
|
||||
},
|
||||
load: function (domainObject) {
|
||||
return Promise.resolve(myDomainObjects);
|
||||
@@ -273,8 +327,9 @@ These identifiers will be used to fetch Domain Objects from an [Object Provider]
|
||||
|
||||
### Default Composition Provider
|
||||
|
||||
The default composition provider applies to any domain object with a `composition`
|
||||
property. The value of `composition` should be an array of identifiers, e.g.:
|
||||
The default composition provider applies to any domain object with a
|
||||
`composition` property. The value of `composition` should be an array of
|
||||
identifiers, e.g.:
|
||||
|
||||
```javascript
|
||||
var domainObject = {
|
||||
@@ -295,13 +350,17 @@ var domainObject = {
|
||||
|
||||
## Telemetry API
|
||||
|
||||
The Open MCT telemetry API provides two main sets of interfaces-- one for integrating telemetry data into Open MCT, and another for developing Open MCT visualization plugins utilizing telemetry API.
|
||||
The Open MCT telemetry API provides two main sets of interfaces-- one for
|
||||
integrating telemetry data into Open MCT, and another for developing Open MCT
|
||||
visualization plugins utilizing the telemetry API.
|
||||
|
||||
The APIs for visualization plugins are still a work in progress and docs may change at any time. However, the APIs for integrating telemetry metadata into Open MCT are stable and documentation is included below.
|
||||
The APIs for visualization plugins are still a work in progress and docs may
|
||||
change at any time. However, the APIs for integrating telemetry metadata into
|
||||
Open MCT are stable and documentation is included below.
|
||||
|
||||
### Integrating Telemetry Sources
|
||||
|
||||
There are two main tasks for integrating telemetry sources-- describing telemetry objects with relevant metadata, and then providing telemetry data for those objects. You'll use an [Object Provider](#object-providers) to provide objects with the necessary [Telemetry Metadata](#telemetry-metadata), and then register a [Telemetry Provider](#telemetry-providers) to retrieve telemetry data for those objects.
|
||||
There are two main tasks for integrating telemetry sources-- describing telemetry objects with relevant metadata, and then providing telemetry data for those objects. You'll use an [Object Provider](#object-providers) to provide objects with the necessary [Telemetry Metadata](#telemetry-metadata), and then register a [Telemetry Provider](#telemetry-providers) to retrieve telemetry data for those objects. Alternatively, you can register a telemetry metadata provider to provide the necessary telemetry metadata.
|
||||
|
||||
For a step-by-step guide to building a telemetry adapter, please see the
|
||||
[Open MCT Tutorials](https://github.com/nasa/openmct-tutorial).
|
||||
@@ -355,7 +414,7 @@ attribute | type | flags | notes
|
||||
--- | --- | --- | ---
|
||||
`key` | string | required | unique identifier for this field.
|
||||
`hints` | object | required | Hints allow views to intelligently select relevant attributes for display, and are required for most views to function. See section on "Value Hints" below.
|
||||
`name` | string | optional | a human readible label for this field. If omitted, defaults to `key`.
|
||||
`name` | string | optional | a human readable label for this field. If omitted, defaults to `key`.
|
||||
`source` | string | optional | identifies the property of a datum where this value is stored. If omitted, defaults to `key`.
|
||||
`format` | string | optional | a specific format identifier, mapping to a formatter. If omitted, uses a default formatter. For enumerations, use `enum`. For timestamps, use `utc` if you are using utc dates, otherwise use a key mapping to your custom date format.
|
||||
`units` | string | optional | the units of this value, e.g. `km`, `seconds`, `parsecs`
|
||||
@@ -383,7 +442,7 @@ In order for the time conductor to work, there will always be an active "time sy
|
||||
|
||||
#### Telemetry Providers
|
||||
|
||||
Telemetry providers are responsible for providing historical and real time telemetry data for telemetry objects. Each telemetry provider determines which objects it can provide telemetry for, and then must implement methods to provide telemetry for those objects.
|
||||
Telemetry providers are responsible for providing historical and real-time telemetry data for telemetry objects. Each telemetry provider determines which objects it can provide telemetry for, and then must implement methods to provide telemetry for those objects.
|
||||
|
||||
A telemetry provider is a javascript object with up to four methods:
|
||||
|
||||
@@ -391,6 +450,10 @@ A telemetry provider is a javascript object with up to four methods:
|
||||
* `subscribe(domainObject, callback, options)` required if `supportsSubscribe` is implemented. Establish a subscription for realtime data for the given domain object. Should invoke `callback` with a single telemetry datum every time data is received. Must return an unsubscribe function. Multiple views can subscribe to the same telemetry object, so it should always return a new unsubscribe function.
|
||||
* `supportsRequest(domainObject, options)` optional. Must be implemented to provide historical telemetry. Should return `true` if the provider supports historical requests for the given domain object.
|
||||
* `request(domainObject, options)` required if `supportsRequest` is implemented. Must return a promise for an array of telemetry datums that fulfills the request. The `options` argument will include a `start`, `end`, and `domain` attribute representing the query bounds. For more request properties, see Request Properties below.
|
||||
* `supportsMetadata(domainObject)` optional. Implement and return `true` for objects that you want to provide dynamic metadata for.
|
||||
* `getMetadata(domainObject)` required if `supportsMetadata` is implemented. Must return a valid telemetry metadata definition that includes at least one valueMetadata definition.
|
||||
* `supportsLimits(domainObject)` optional. Implement and return `true` for domain objects that you want to provide a limit evaluator for.
|
||||
* `getLimitEvaluator(domainObject)` required if `supportsLimits` is implemented. Must return a valid LimitEvaluator for a given domain object.
|
||||
|
||||
Telemetry providers are registered by calling `openmct.telemetry.addProvider(provider)`, e.g.
|
||||
|
||||
@@ -398,14 +461,15 @@ Telemetry providers are registered by calling `openmct.telemetry.addProvider(pro
|
||||
openmct.telemetry.addProvider({
|
||||
supportsRequest: function (domainObject, options) { /*...*/ },
|
||||
request: function (domainObject, options) { /*...*/ },
|
||||
supportsSubscribe: function (domainObject, callback, options) { /*...*/ },
|
||||
subscribe: function (domainObject, callback, options) { /*...*/ }
|
||||
})
|
||||
```
|
||||
|
||||
Note: it is not required to implement all of the methods on every provider. Depending on the complexity of your implementation, it may be helpful to instantiate and register your realtime, historical, and metadata providers separately.
|
||||
|
||||
#### Telemetry Requests
|
||||
|
||||
Telemetry requests support time bounded queries. A call to a _Telemetry Provider_'s `request` function will include an `options` argument. These are simply javascript objects with attributes for the request parameters. An example of a telemetry request object with a start and end time is included below:
|
||||
|
||||
```javascript
|
||||
{
|
||||
start: 1487981997240,
|
||||
@@ -414,7 +478,54 @@ Telemetry requests support time bounded queries. A call to a _Telemetry Provider
|
||||
}
|
||||
```
|
||||
|
||||
#### Telemetry Formats
|
||||
In this case, the `domain` is the currently selected time-system, and the start and end dates are valid dates in that time system.
|
||||
|
||||
The response to a telemetry request is an array of telemetry datums.
|
||||
These datums must be sorted by `domain` in ascending order.
|
||||
|
||||
#### Request Strategies **draft**
|
||||
|
||||
To improve performance views may request a certain strategy for data reduction. These are intended to improve visualization performance by reducing the amount of data needed to be sent to the client. These strategies will be indicated by additional parameters in the request options. You may choose to handle them or ignore them.
|
||||
|
||||
Note: these strategies are currently being tested in core plugins and may change based on developer feedback.
|
||||
|
||||
##### `latest` request strategy
|
||||
|
||||
This request is a "depth based" strategy. When a view is only capable of
|
||||
displaying a single value (or perhaps the last ten values), then it can
|
||||
use the `latest` request strategy with a `size` parameter that specifies
|
||||
the number of results it desires. The `size` parameter is a hint; views
|
||||
must not assume the response will have the exact number of results requested.
|
||||
|
||||
example:
|
||||
```javascript
|
||||
{
|
||||
start: 1487981997240,
|
||||
end: 1487982897240,
|
||||
domain: 'utc',
|
||||
strategy: 'latest',
|
||||
size: 1
|
||||
}
|
||||
```
|
||||
|
||||
This strategy says "I want the lastest data point in this time range". A provider which recognizes this request should return only one value-- the latest-- in the requested time range. Depending on your back-end implementation, performing these queries in bulk can be a large performance increase. These are generally issued by views that are only capable of displaying a single value and only need to show the latest value.
|
||||
|
||||
##### `minmax` request strategy
|
||||
|
||||
example:
|
||||
```javascript
|
||||
{
|
||||
start: 1487981997240,
|
||||
end: 1487982897240,
|
||||
domain: 'utc',
|
||||
strategy: 'minmax',
|
||||
size: 720
|
||||
}
|
||||
```
|
||||
|
||||
MinMax queries are issued by plots, and may be issued by other types as well. The aim is to reduce the amount of data returned but still faithfully represent the full extent of the data. In order to do this, the view calculates the maximum data resolution it can display (i.e. the number of horizontal pixels in a plot) and sends that as the `size`. The response should include at least one minimum and one maximum value per point of resolution.
|
||||
|
||||
#### Telemetry Formats **draft**
|
||||
|
||||
Telemetry format objects define how to interpret and display telemetry data.
|
||||
They have a simple structure:
|
||||
@@ -484,6 +595,17 @@ The key-value pairs of this object are described by the telemetry metadata of
|
||||
a domain object, as discussed in the [Telemetry Metadata](#telemetry-metadata)
|
||||
section.
|
||||
|
||||
#### Limit Evaluators **draft**
|
||||
|
||||
Limit evaluators allow a telemetry integrator to define how limits should be
|
||||
applied to telemetry from a given domain object. For an example of a limit
|
||||
evaluator, take a look at `examples/generator/SinewaveLimitProvider.js`.
|
||||
|
||||
### Telemetry Consumer APIs **draft**
|
||||
|
||||
The APIs for requesting telemetry from Open MCT -- e.g. for use in custom views -- are currently in draft state and are being revised. If you'd like to experiement with them before they are finalized, please contact the team via the contact-us link on our website.
|
||||
|
||||
|
||||
## Time API
|
||||
|
||||
Open MCT provides API for managing the temporal state of the application.
|
||||
@@ -591,7 +713,7 @@ openmct.time.bounds({start: now - ONE_HOUR, now);
|
||||
To respond to bounds change events, listen for the [`'bounds'`](#time-events)
|
||||
event.
|
||||
|
||||
## Clocks
|
||||
### Clocks
|
||||
|
||||
The Time API can be set to follow a clock source which will cause the bounds
|
||||
to be updated automatically whenever the clock source "ticks". A clock is simply
|
||||
@@ -610,7 +732,7 @@ be defined to tick on some remote timing source.
|
||||
The values provided by clocks are simple `number`s, which are interpreted in the
|
||||
context of the active [Time System](#defining-and-registering-time-systems).
|
||||
|
||||
### Defining and registering clocks
|
||||
#### Defining and registering clocks
|
||||
|
||||
A clock is an object that defines certain required metadata and functions:
|
||||
|
||||
@@ -724,7 +846,7 @@ __Note:__ Setting the clock offsets will trigger an immediate bounds change, as
|
||||
new bounds will be calculated based on the `currentValue()` of the active clock.
|
||||
Clock offsets are only relevant when a clock source is active.
|
||||
|
||||
## Time Events
|
||||
### Time Events
|
||||
|
||||
The Time API is a standard event emitter; you can register callbacks for events using the `on` method and remove callbacks for events with the `off` method.
|
||||
|
||||
@@ -766,7 +888,7 @@ The events emitted by the Time API are:
|
||||
* `clockOffsets`: The new [clock offsets](#clock-offsets).
|
||||
|
||||
|
||||
## The Time Conductor
|
||||
### The Time Conductor
|
||||
|
||||
The Time Conductor provides a user interface for managing time bounds in Open
|
||||
MCT. It allows a user to select from configured time systems and clocks, and to set bounds and clock offsets.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"screenfull": "^3.0.0",
|
||||
"node-uuid": "^1.4.7",
|
||||
"comma-separated-values": "^3.6.4",
|
||||
"file-saver": "^1.3.3",
|
||||
"file-saver": "1.3.3",
|
||||
"zepto": "^1.1.6",
|
||||
"eventemitter3": "^1.2.0",
|
||||
"lodash": "3.10.1",
|
||||
@@ -25,4 +25,4 @@
|
||||
"html2canvas": "^0.4.1",
|
||||
"moment-timezone": "^0.5.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
108
example/generator/GeneratorMetadataProvider.js
Normal file
108
example/generator/GeneratorMetadataProvider.js
Normal 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.supportsMetadata = 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;
|
||||
|
||||
});
|
||||
@@ -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) {
|
||||
@@ -62,12 +66,12 @@ define([
|
||||
if (request && request.hasOwnProperty(prop)) {
|
||||
workerRequest[prop] = request[prop];
|
||||
}
|
||||
if (!workerRequest[prop]) {
|
||||
if (!workerRequest.hasOwnProperty(prop)) {
|
||||
workerRequest[prop] = REQUEST_DEFAULTS[prop];
|
||||
}
|
||||
workerRequest[prop] = Number(workerRequest[prop]);
|
||||
});
|
||||
|
||||
workerRequest.name = domainObject.name;
|
||||
return workerRequest;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
"use strict";
|
||||
|
||||
var RED = 0.9,
|
||||
YELLOW = 0.5,
|
||||
LIMITS = {
|
||||
rh: {
|
||||
cssClass: "s-limit-upr s-limit-red",
|
||||
low: RED,
|
||||
high: Number.POSITIVE_INFINITY,
|
||||
name: "Red High"
|
||||
},
|
||||
rl: {
|
||||
cssClass: "s-limit-lwr s-limit-red",
|
||||
high: -RED,
|
||||
low: Number.NEGATIVE_INFINITY,
|
||||
name: "Red Low"
|
||||
},
|
||||
yh: {
|
||||
cssClass: "s-limit-upr s-limit-yellow",
|
||||
low: YELLOW,
|
||||
high: RED,
|
||||
name: "Yellow High"
|
||||
},
|
||||
yl: {
|
||||
cssClass: "s-limit-lwr s-limit-yellow",
|
||||
low: -RED,
|
||||
high: -YELLOW,
|
||||
name: "Yellow Low"
|
||||
}
|
||||
};
|
||||
|
||||
function SinewaveLimitCapability(domainObject) {
|
||||
return {
|
||||
limits: function (range) {
|
||||
return LIMITS;
|
||||
},
|
||||
evaluate: function (datum, range) {
|
||||
range = range || 'sin';
|
||||
if (datum[range] > RED) {
|
||||
return LIMITS.rh;
|
||||
}
|
||||
if (datum[range] < -RED) {
|
||||
return LIMITS.rl;
|
||||
}
|
||||
if (datum[range] > YELLOW) {
|
||||
return LIMITS.yh;
|
||||
}
|
||||
if (datum[range] < -YELLOW) {
|
||||
return LIMITS.yl;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
SinewaveLimitCapability.appliesTo = function (model) {
|
||||
return model.type === 'generator';
|
||||
};
|
||||
|
||||
return SinewaveLimitCapability;
|
||||
}
|
||||
);
|
||||
88
example/generator/SinewaveLimitProvider.js
Normal file
88
example/generator/SinewaveLimitProvider.js
Normal file
@@ -0,0 +1,88 @@
|
||||
/*****************************************************************************
|
||||
* 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([
|
||||
|
||||
], function (
|
||||
|
||||
) {
|
||||
|
||||
var RED = 0.9,
|
||||
YELLOW = 0.5,
|
||||
LIMITS = {
|
||||
rh: {
|
||||
cssClass: "s-limit-upr s-limit-red",
|
||||
low: RED,
|
||||
high: Number.POSITIVE_INFINITY,
|
||||
name: "Red High"
|
||||
},
|
||||
rl: {
|
||||
cssClass: "s-limit-lwr s-limit-red",
|
||||
high: -RED,
|
||||
low: Number.NEGATIVE_INFINITY,
|
||||
name: "Red Low"
|
||||
},
|
||||
yh: {
|
||||
cssClass: "s-limit-upr s-limit-yellow",
|
||||
low: YELLOW,
|
||||
high: RED,
|
||||
name: "Yellow High"
|
||||
},
|
||||
yl: {
|
||||
cssClass: "s-limit-lwr s-limit-yellow",
|
||||
low: -RED,
|
||||
high: -YELLOW,
|
||||
name: "Yellow Low"
|
||||
}
|
||||
};
|
||||
|
||||
function SinewaveLimitProvider() {
|
||||
|
||||
}
|
||||
|
||||
SinewaveLimitProvider.prototype.supportsLimits = function (domainObject) {
|
||||
return domainObject.type === 'generator';
|
||||
};
|
||||
|
||||
SinewaveLimitProvider.prototype.getLimitEvaluator = function (domainObject) {
|
||||
return {
|
||||
evaluate: function (datum, valueMetadata) {
|
||||
var range = valueMetadata ? valueMetadata.key : 'sin'
|
||||
if (datum[range] > RED) {
|
||||
return LIMITS.rh;
|
||||
}
|
||||
if (datum[range] < -RED) {
|
||||
return LIMITS.rl;
|
||||
}
|
||||
if (datum[range] > YELLOW) {
|
||||
return LIMITS.yh;
|
||||
}
|
||||
if (datum[range] < -YELLOW) {
|
||||
return LIMITS.yl;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return SinewaveLimitProvider;
|
||||
});
|
||||
80
example/generator/StateGeneratorProvider.js
Normal file
80
example/generator/StateGeneratorProvider.js
Normal file
@@ -0,0 +1,80 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
|
||||
], function (
|
||||
|
||||
) {
|
||||
|
||||
function StateGeneratorProvider() {
|
||||
|
||||
}
|
||||
|
||||
function pointForTimestamp(timestamp, duration, name) {
|
||||
return {
|
||||
name: name,
|
||||
utc: Math.floor(timestamp / duration) * duration,
|
||||
value: Math.floor(timestamp / duration) % 2
|
||||
};
|
||||
}
|
||||
|
||||
StateGeneratorProvider.prototype.supportsSubscribe = function (domainObject) {
|
||||
return domainObject.type === 'example.state-generator';
|
||||
};
|
||||
|
||||
StateGeneratorProvider.prototype.subscribe = function (domainObject, callback) {
|
||||
var duration = domainObject.telemetry.duration * 1000;
|
||||
|
||||
var interval = setInterval(function () {
|
||||
var now = Date.now();
|
||||
callback(pointForTimestamp(now, duration, domainObject.name));
|
||||
}, duration);
|
||||
|
||||
return function () {
|
||||
clearInterval(interval);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
StateGeneratorProvider.prototype.supportsRequest = function (domainObject, options) {
|
||||
return domainObject.type === 'example.state-generator';
|
||||
};
|
||||
|
||||
StateGeneratorProvider.prototype.request = function (domainObject, options) {
|
||||
var start = options.start;
|
||||
var end = options.end;
|
||||
var duration = domainObject.telemetry.duration * 1000;
|
||||
if (options.strategy === 'latest' || options.size === 1) {
|
||||
start = end;
|
||||
}
|
||||
var data = [];
|
||||
while (start <= end && data.length < 5000) {
|
||||
data.push(pointForTimestamp(start, duration, domainObject.name));
|
||||
start += 5000;
|
||||
}
|
||||
return Promise.resolve(data);
|
||||
};
|
||||
|
||||
return StateGeneratorProvider;
|
||||
|
||||
});
|
||||
@@ -62,10 +62,11 @@
|
||||
self.postMessage({
|
||||
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) {
|
||||
|
||||
@@ -23,29 +23,46 @@
|
||||
|
||||
define([
|
||||
"./GeneratorProvider",
|
||||
"./SinewaveLimitCapability"
|
||||
"./SinewaveLimitProvider",
|
||||
"./StateGeneratorProvider",
|
||||
"./GeneratorMetadataProvider"
|
||||
], function (
|
||||
GeneratorProvider,
|
||||
SinewaveLimitCapability
|
||||
SinewaveLimitProvider,
|
||||
StateGeneratorProvider,
|
||||
GeneratorMetadataProvider
|
||||
) {
|
||||
|
||||
var legacyExtensions = {
|
||||
"capabilities": [
|
||||
{
|
||||
"key": "limit",
|
||||
"implementation": SinewaveLimitCapability
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
return function(openmct){
|
||||
//Register legacy extensions for things not yet supported by the new API
|
||||
Object.keys(legacyExtensions).forEach(function (type){
|
||||
var extensionsOfType = legacyExtensions[type];
|
||||
extensionsOfType.forEach(function (extension) {
|
||||
openmct.legacyExtension(type, extension)
|
||||
})
|
||||
|
||||
openmct.types.addType("example.state-generator", {
|
||||
name: "State Generator",
|
||||
description: "For development use. Generates test enumerated telemetry by cycling through a given set of states",
|
||||
cssClass: "icon-telemetry",
|
||||
creatable: true,
|
||||
form: [
|
||||
{
|
||||
name: "State Duration (seconds)",
|
||||
control: "textfield",
|
||||
cssClass: "l-input-sm l-numeric",
|
||||
key: "duration",
|
||||
required: true,
|
||||
property: [
|
||||
"telemetry",
|
||||
"duration"
|
||||
],
|
||||
pattern: "^\\d*(\\.\\d*)?$"
|
||||
}
|
||||
],
|
||||
initialize: function (object) {
|
||||
object.telemetry = {
|
||||
duration: 5
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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.",
|
||||
@@ -54,51 +71,58 @@ define([
|
||||
form: [
|
||||
{
|
||||
name: "Period",
|
||||
control: "textfield",
|
||||
control: "numberfield",
|
||||
cssClass: "l-input-sm l-numeric",
|
||||
key: "period",
|
||||
required: true,
|
||||
property: [
|
||||
"telemetry",
|
||||
"period"
|
||||
],
|
||||
pattern: "^\\d*(\\.\\d*)?$"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Amplitude",
|
||||
control: "textfield",
|
||||
control: "numberfield",
|
||||
cssClass: "l-input-sm l-numeric",
|
||||
key: "amplitude",
|
||||
required: true,
|
||||
property: [
|
||||
"telemetry",
|
||||
"amplitude"
|
||||
],
|
||||
pattern: "^\\d*(\\.\\d*)?$"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Offset",
|
||||
control: "textfield",
|
||||
control: "numberfield",
|
||||
cssClass: "l-input-sm l-numeric",
|
||||
key: "offset",
|
||||
required: true,
|
||||
property: [
|
||||
"telemetry",
|
||||
"offset"
|
||||
],
|
||||
pattern: "^\\d*(\\.\\d*)?$"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Data Rate (hz)",
|
||||
control: "textfield",
|
||||
control: "numberfield",
|
||||
cssClass: "l-input-sm l-numeric",
|
||||
key: "dataRateInHz",
|
||||
required: true,
|
||||
property: [
|
||||
"telemetry",
|
||||
"dataRateInHz"
|
||||
],
|
||||
pattern: "^\\d*(\\.\\d*)?$"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "Phase (radians)",
|
||||
control: "numberfield",
|
||||
cssClass: "l-input-sm l-numeric",
|
||||
key: "phase",
|
||||
required: true,
|
||||
property: [
|
||||
"telemetry",
|
||||
"phase"
|
||||
]
|
||||
}
|
||||
],
|
||||
initialize: function (object) {
|
||||
@@ -107,42 +131,14 @@ 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.addProvider(new GeneratorMetadataProvider());
|
||||
openmct.telemetry.addProvider(new SinewaveLimitProvider());
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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*)?$"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -58,11 +58,7 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.w-mct-example {
|
||||
div {
|
||||
margin-bottom: $interiorMarginLg;
|
||||
}
|
||||
}
|
||||
.w-mct-example > div { margin-bottom: $interiorMarginLg; }
|
||||
|
||||
code,
|
||||
pre {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -37,14 +37,15 @@ 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: 'node_modules/dom-to-image/dist/*', 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 +90,8 @@ module.exports = function(config) {
|
||||
"dist/reports/coverage",
|
||||
check: {
|
||||
global: {
|
||||
lines: 80
|
||||
lines: 80,
|
||||
excludes: ['src/plugins/plot/**/*.js']
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
11
openmct.js
11
openmct.js
@@ -49,7 +49,9 @@ 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",
|
||||
"dom-to-image": "node_modules/dom-to-image/dist/dom-to-image.min",
|
||||
"painterro": "node_modules/@cristian77/painterro/build/painterro.min"
|
||||
},
|
||||
"shim": {
|
||||
"angular": {
|
||||
@@ -67,6 +69,9 @@ requirejs.config({
|
||||
"moment-duration-format": {
|
||||
"deps": ["moment"]
|
||||
},
|
||||
"painterro": {
|
||||
"exports": "Painterro"
|
||||
},
|
||||
"saveAs": {
|
||||
"exports": "saveAs"
|
||||
},
|
||||
@@ -88,6 +93,9 @@ requirejs.config({
|
||||
},
|
||||
"d3-axis": {
|
||||
"exports": "d3-axis"
|
||||
},
|
||||
"dom-to-image": {
|
||||
"exports": "domtoimage"
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -101,6 +109,7 @@ define([
|
||||
var openmct = new MCT();
|
||||
|
||||
openmct.legacyRegistry = defaultRegistry;
|
||||
openmct.install(openmct.plugins.Plot());
|
||||
|
||||
if (typeof BUILD_CONSTANTS !== 'undefined') {
|
||||
openmct.install(buildInfo(BUILD_CONSTANTS));
|
||||
|
||||
26
package.json
26
package.json
@@ -1,18 +1,20 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "0.12.1-SNAPSHOT",
|
||||
"version": "0.13.3-SNAPSHOT",
|
||||
"description": "The Open MCT core platform",
|
||||
"dependencies": {
|
||||
"d3-array": "^1.0.2",
|
||||
"d3-axis": "^1.0.4",
|
||||
"d3-collection": "^1.0.2",
|
||||
"d3-color": "^1.0.2",
|
||||
"d3-format": "^1.0.2",
|
||||
"d3-interpolate": "^1.1.3",
|
||||
"d3-scale": "^1.0.4",
|
||||
"d3-selection": "^1.0.3",
|
||||
"d3-time": "^1.0.4",
|
||||
"d3-time-format": "^2.0.3",
|
||||
"d3-array": "1.2.x",
|
||||
"d3-axis": "1.0.x",
|
||||
"d3-collection": "1.0.x",
|
||||
"d3-color": "1.0.x",
|
||||
"d3-format": "1.2.x",
|
||||
"d3-interpolate": "1.1.x",
|
||||
"d3-scale": "1.0.x",
|
||||
"d3-selection": "1.3.x",
|
||||
"d3-time": "1.0.x",
|
||||
"d3-time-format": "2.1.x",
|
||||
"@cristian77/painterro": "^0.2.48",
|
||||
"dom-to-image": "^2.6.0",
|
||||
"express": "^4.13.1",
|
||||
"minimist": "^1.1.1",
|
||||
"request": "^2.69.0",
|
||||
@@ -28,7 +30,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",
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<div class='split-pane-component t-object pane primary-pane left'>
|
||||
<mct-representation mct-object="navigatedObject"
|
||||
key="navigatedObject.getCapability('status').get('editing') ? 'edit-object' : 'browse-object'"
|
||||
class="abs holder holder-object">
|
||||
class="abs holder holder-object t-main-view">
|
||||
</mct-representation>
|
||||
<a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect flush-right"
|
||||
title="{{ modelPaneInspect.visible()? 'Hide' : 'Show' }} the Inspection pane"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -47,8 +47,10 @@ define(
|
||||
urlService,
|
||||
defaultPath
|
||||
) {
|
||||
var initialPath = ($route.current.params.ids || defaultPath).split("/");
|
||||
var currentIds;
|
||||
var initialPath = ($route.current.params.ids || defaultPath).split("/"),
|
||||
currentIds,
|
||||
actions,
|
||||
previewAction;
|
||||
|
||||
$scope.treeModel = {
|
||||
selectedObject: undefined,
|
||||
@@ -56,7 +58,22 @@ define(
|
||||
navigationService.setNavigation(object, true);
|
||||
},
|
||||
allowSelection: function (object) {
|
||||
return navigationService.shouldNavigate();
|
||||
// return navigationService.shouldNavigate();
|
||||
if (navigationService.anyChecksBeforeNavigation()) {
|
||||
|
||||
actions = actions || $scope.domainObject.getCapability('action');
|
||||
previewAction = previewAction || actions.getActions({key: 'mct-preview-action'})[0];
|
||||
|
||||
if (previewAction && previewAction.perform) {
|
||||
previewAction.perform(object);
|
||||
return false;
|
||||
} else {
|
||||
return navigationService.shouldNavigate();
|
||||
}
|
||||
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -109,6 +109,10 @@ define(
|
||||
});
|
||||
};
|
||||
|
||||
NavigationService.prototype.anyChecksBeforeNavigation = function () {
|
||||
return this.checks.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if navigation should proceed. May prompt a user for input
|
||||
* if any checkFns return messages. Returns true if the user wishes to
|
||||
@@ -162,7 +166,6 @@ define(
|
||||
*/
|
||||
NavigationService.prototype.shouldWarnBeforeNavigate = function () {
|
||||
var reasons = [];
|
||||
|
||||
this.checks.forEach(function (checkFn) {
|
||||
var reason = checkFn();
|
||||
if (reason) {
|
||||
|
||||
@@ -64,8 +64,10 @@ define(
|
||||
var domainObject = selection[0].context.oldItem;
|
||||
self.refreshComposition(domainObject);
|
||||
|
||||
self.mutationListener = domainObject.getCapability('mutation')
|
||||
.listen(self.refreshComposition.bind(self, domainObject));
|
||||
if (domainObject) {
|
||||
self.mutationListener = domainObject.getCapability('mutation')
|
||||
.listen(self.refreshComposition.bind(self, domainObject));
|
||||
}
|
||||
}
|
||||
|
||||
$scope.filterBy = filterBy;
|
||||
@@ -86,11 +88,15 @@ define(
|
||||
* @private
|
||||
*/
|
||||
ElementsController.prototype.refreshComposition = function (domainObject) {
|
||||
var selectedObjectComposition = 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 = [];
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
@@ -127,6 +150,35 @@ define(
|
||||
expect(mockUnlisten).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not listen on mutation for element proxy selectable", function () {
|
||||
selectable[0] = {
|
||||
context: {
|
||||
elementProxy: {}
|
||||
}
|
||||
};
|
||||
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -49,6 +49,8 @@ define([
|
||||
"./src/directives/MCTSplitPane",
|
||||
"./src/directives/MCTSplitter",
|
||||
"./src/directives/MCTTree",
|
||||
"./src/directives/MCTPreview",
|
||||
"./src/actions/MCTPreviewAction",
|
||||
"./src/filters/ReverseFilter",
|
||||
"text!./res/templates/bottombar.html",
|
||||
"text!./res/templates/controls/action-button.html",
|
||||
@@ -69,6 +71,7 @@ define([
|
||||
"text!./res/templates/controls/selector.html",
|
||||
"text!./res/templates/controls/datetime-picker.html",
|
||||
"text!./res/templates/controls/datetime-field.html",
|
||||
"text!./res/templates/preview.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
UrlService,
|
||||
@@ -99,6 +102,8 @@ define([
|
||||
MCTSplitPane,
|
||||
MCTSplitter,
|
||||
MCTTree,
|
||||
MCTPreview,
|
||||
MCTPreviewAction,
|
||||
ReverseFilter,
|
||||
bottombarTemplate,
|
||||
actionButtonTemplate,
|
||||
@@ -119,6 +124,7 @@ define([
|
||||
selectorTemplate,
|
||||
datetimePickerTemplate,
|
||||
datetimeFieldTemplate,
|
||||
previewTemplate,
|
||||
legacyRegistry
|
||||
) {
|
||||
|
||||
@@ -395,6 +401,38 @@ define([
|
||||
"key": "mctTree",
|
||||
"implementation": MCTTree,
|
||||
"depends": ['gestureService']
|
||||
},
|
||||
{
|
||||
"key": "mctPreview",
|
||||
"implementation": MCTPreview,
|
||||
"depends": [
|
||||
"$rootScope",
|
||||
"$document",
|
||||
"exportImageService",
|
||||
"dialogService",
|
||||
"notificationService"
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"key": "mct-preview-action",
|
||||
"implementation": MCTPreviewAction,
|
||||
"name": "Preview",
|
||||
"cssClass": "hide-in-t-main-view icon-eye-open",
|
||||
"description": "Preview in large dialog",
|
||||
"category": [
|
||||
"contextual",
|
||||
"view-control"
|
||||
],
|
||||
"depends": [
|
||||
"$compile",
|
||||
"$rootScope",
|
||||
"dialogService",
|
||||
"notificationService",
|
||||
"linkService"
|
||||
],
|
||||
"priority": "preferred"
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
@@ -510,6 +548,10 @@ define([
|
||||
{
|
||||
"key": "object-inspector",
|
||||
"template": objectInspectorTemplate
|
||||
},
|
||||
{
|
||||
"key": "mct-preview",
|
||||
"template": previewTemplate
|
||||
}
|
||||
],
|
||||
"controls": [
|
||||
|
||||
@@ -99,7 +99,7 @@ $plotXBarH: 32px;
|
||||
$plotLegendH: 20px;
|
||||
$plotSwatchD: 8px;
|
||||
// 1: Top, 2: right, 3: bottom, 4: left
|
||||
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH, $plotYBarW);
|
||||
$plotDisplayArea: (0, 0, $plotXBarH, $plotYBarW);
|
||||
/* min plot height is based on user testing to find minimum useful height */
|
||||
$plotMinH: 95px;
|
||||
/*************** Bubbles */
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
* Use https://icomoon.io/app with icomoon-project-openmct-symbols-12px.json
|
||||
* to generate font files
|
||||
*/
|
||||
font-family: 'symbolsfont 12px';
|
||||
font-family: 'symbolsfont-12px';
|
||||
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot');
|
||||
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.eot?#iefix') format('embedded-opentype'),
|
||||
url($dirCommonRes + 'fonts/symbols/openmct-symbols-12px.woff') format('woff'),
|
||||
@@ -225,7 +225,8 @@ a.disabled {
|
||||
}
|
||||
|
||||
.hide,
|
||||
.hidden {
|
||||
.hidden,
|
||||
.t-main-view .hide-in-t-main-view {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -248,6 +249,12 @@ a.disabled {
|
||||
color: rgba(#fff, 0.2);
|
||||
}
|
||||
|
||||
.comma-list span {
|
||||
&:not(:first-child) {
|
||||
&:before { content: ', '; }
|
||||
}
|
||||
}
|
||||
|
||||
.test-stripes {
|
||||
@include bgDiagonalStripes();
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
@import "fixed-position";
|
||||
@import "lists/tabular";
|
||||
@import "plots/plots-main";
|
||||
@import "plots/legend";
|
||||
@import "iframe";
|
||||
@import "views";
|
||||
@import "items/item";
|
||||
|
||||
@@ -299,7 +299,7 @@
|
||||
color: $ic;
|
||||
}
|
||||
@if $bgHov != none {
|
||||
&:not(.disabled):hover {
|
||||
&:not([disabled="true"]):not(.disabled):hover {
|
||||
background: $bgHov;
|
||||
color: $fgHov;
|
||||
>.icon,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
}
|
||||
|
||||
.l-view-section {
|
||||
//@include test(orange, 0.1);
|
||||
@include absPosDefault(0);
|
||||
h2 {
|
||||
color: #fff;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -59,6 +54,10 @@ $pad: $interiorMargin * $baseRatio;
|
||||
.label, .title-label { display: none; }
|
||||
}
|
||||
|
||||
&[disabled="true"] {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
&.pause-play {
|
||||
@extend .icon-pause;
|
||||
&.paused {
|
||||
|
||||
@@ -139,7 +139,6 @@
|
||||
}
|
||||
|
||||
.s-local-controls {
|
||||
@include trans-prop-nice(opacity);
|
||||
font-size: 0.7rem;
|
||||
&.s-wrapper-transluc {
|
||||
// Semi-opaque wrapper to visually distinguish a control
|
||||
@@ -150,6 +149,39 @@
|
||||
}
|
||||
}
|
||||
|
||||
.has-local-controls {
|
||||
.local-control {
|
||||
@include trans-prop-nice($props: opacity, $dur: 250ms);
|
||||
opacity: 0;
|
||||
}
|
||||
&:hover {
|
||||
.local-control {
|
||||
@include trans-prop-nice($props: opacity, $dur: 10ms);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** 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 +350,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 +361,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 +371,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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
}
|
||||
min-width: 150px;
|
||||
.l-image-main {
|
||||
background-color: $colorPlotBg;
|
||||
margin-bottom: $interiorMargin;
|
||||
}
|
||||
.l-image-main-controlbar {
|
||||
@@ -76,6 +75,7 @@
|
||||
}
|
||||
|
||||
.s-image-main {
|
||||
background-color: $colorPlotBg;
|
||||
border: 1px solid transparent;
|
||||
&.paused {
|
||||
@extend .s-unsynced;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
208
platform/commonUI/general/res/sass/plots/_legend.scss
Normal file
208
platform/commonUI/general/res/sass/plots/_legend.scss
Normal file
@@ -0,0 +1,208 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
.gl-plot {
|
||||
.gl-plot-legend {
|
||||
min-height: $plotLegendH;
|
||||
|
||||
.view-control {
|
||||
font-size: 1em;
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
tr {
|
||||
display: table-row;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
@include ellipsize(); // Note: this won't work if table-layout uses anything other than fixed.
|
||||
display: table-cell;
|
||||
padding: 1px 3px; // Tighter than standard tabular padding
|
||||
}
|
||||
}
|
||||
|
||||
&.hover-on-plot {
|
||||
// User is hovering over the plot to get a value at a point
|
||||
.hover-value-enabled {
|
||||
background-color: $legendHoverValueBg;
|
||||
border-radius: $smallCr;
|
||||
padding: 0 $interiorMarginSm;
|
||||
&:before {
|
||||
opacity: 0.5;
|
||||
}
|
||||
&.cursor-hover,
|
||||
.value-to-display-nearestTimestamp,
|
||||
.value-to-display-nearestValue
|
||||
{
|
||||
@extend .icon-crosshair-12px;
|
||||
&:before {
|
||||
font-size: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
&.value-to-display-min:before {
|
||||
content: 'MIN ';
|
||||
}
|
||||
&.value-to-display-max:before {
|
||||
content: 'MAX ';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.plot-legend-collapsed .plot-wrapper-expanded-legend { display: none; }
|
||||
&.plot-legend-expanded .plot-wrapper-collapsed-legend { display: none; }
|
||||
|
||||
/***************** GENERAL STYLES, ALL STATES */
|
||||
.plot-legend-item {
|
||||
// General styles for legend items, both expanded and collapsed legend states
|
||||
.plot-series-color-swatch {
|
||||
border-radius: $smallCr;
|
||||
border: 1px solid $colorBodyBg;
|
||||
display: inline-block;
|
||||
height: $plotSwatchD;
|
||||
width: $plotSwatchD;
|
||||
}
|
||||
.plot-series-name {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.plot-series-value {
|
||||
@include ellipsize();
|
||||
}
|
||||
}
|
||||
|
||||
/***************** GENERAL STYLES, COLLAPSED */
|
||||
&.plot-legend-collapsed {
|
||||
// .plot-legend-item is a span of spans.
|
||||
&.plot-legend-top .gl-plot-legend { margin-bottom: $interiorMargin; }
|
||||
&.plot-legend-bottom .gl-plot-legend { margin-top: $interiorMargin; }
|
||||
&.plot-legend-right .gl-plot-legend { margin-left: $interiorMargin; }
|
||||
&.plot-legend-left .gl-plot-legend { margin-right: $interiorMargin; }
|
||||
|
||||
.plot-legend-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&:not(:first-child) {
|
||||
margin-left: $interiorMarginLg;
|
||||
}
|
||||
.plot-series-swatch-and-name,
|
||||
.plot-series-value {
|
||||
@include ellipsize();
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.plot-series-swatch-and-name {
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
|
||||
.plot-series-value {
|
||||
text-align: left;
|
||||
width: 170px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************** GENERAL STYLES, EXPANDED */
|
||||
&.plot-legend-expanded {
|
||||
.gl-plot-legend {
|
||||
max-height: 70%;
|
||||
}
|
||||
|
||||
.plot-wrapper-expanded-legend {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&.plot-legend-top .gl-plot-legend {
|
||||
margin-bottom: $interiorMargin;
|
||||
}
|
||||
&.plot-legend-bottom .gl-plot-legend {
|
||||
margin-top: $interiorMargin;
|
||||
}
|
||||
}
|
||||
|
||||
/***************** TOP OR BOTTOM */
|
||||
&.plot-legend-top,
|
||||
&.plot-legend-bottom {
|
||||
// General styles when legend is on the top or bottom
|
||||
@extend .l-flex-col;
|
||||
&.plot-legend-collapsed {
|
||||
// COLLAPSED ON TOP OR BOTTOM
|
||||
.plot-wrapper-collapsed-legend {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************** EITHER SIDE */
|
||||
&.plot-legend-left,
|
||||
&.plot-legend-right {
|
||||
@extend .l-flex-row;
|
||||
// If the legend is expanded, use flex-col instead so that the legend gets the width it needs.
|
||||
&.plot-legend-expanded {
|
||||
// EXPANDED, ON EITHER SIDE
|
||||
@extend .l-flex-col;
|
||||
}
|
||||
|
||||
&.plot-legend-collapsed {
|
||||
// COLLAPSED, ON EITHER SIDE
|
||||
.gl-plot-legend {
|
||||
max-height: inherit;
|
||||
width: 25%;
|
||||
}
|
||||
.plot-wrapper-collapsed-legend {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.plot-legend-item {
|
||||
margin-bottom: 1px;
|
||||
margin-left: 0;
|
||||
flex-wrap: wrap;
|
||||
.plot-series-swatch-and-name {
|
||||
flex: 0 1 auto;
|
||||
min-width: 20%;
|
||||
}
|
||||
.plot-series-value {
|
||||
flex: 0 1 auto;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***************** ON BOTTOM OR RIGHT */
|
||||
&.plot-legend-right:not(.plot-legend-expanded),
|
||||
&.plot-legend-bottom {
|
||||
.gl-plot-legend {
|
||||
order: 2;
|
||||
}
|
||||
.plot-wrapper-axis-and-display-area {
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ body.desktop .frame {
|
||||
// Hide local controls initially and show it them on hover when they're in an element that's in a frame context
|
||||
// Frame template is used because we need to target the lowest nested frame
|
||||
.object-browse-bar .btn-bar {
|
||||
@include trans-prop-nice($props: opacity, $dur: 250ms);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -166,6 +167,7 @@ body.desktop .frame {
|
||||
// Handles the case where we have layouts in layouts.
|
||||
&:hover > .object-browse-bar {
|
||||
.btn-bar {
|
||||
@include trans-prop-nice($props: opacity, $dur: 10ms);
|
||||
opacity: 1;
|
||||
pointer-events: inherit;
|
||||
}
|
||||
|
||||
45
platform/commonUI/general/res/templates/preview.html
Normal file
45
platform/commonUI/general/res/templates/preview.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<!--
|
||||
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="t-frame-inner abs t-object-type-{{ domainObject.getModel().type }}" mct-preview>
|
||||
<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">
|
||||
<mct-representation
|
||||
key="'switcher'"
|
||||
ng-model="representation"
|
||||
mct-object="domainObject">
|
||||
</mct-representation>
|
||||
</div>
|
||||
</div>
|
||||
<div class="abs object-holder">
|
||||
<mct-representation
|
||||
key="representation.selected.key"
|
||||
mct-object="representation.selected.key && domainObject">
|
||||
</mct-representation>
|
||||
</div>
|
||||
</div>
|
||||
66
platform/commonUI/general/src/actions/MCTPreviewAction.js
Normal file
66
platform/commonUI/general/src/actions/MCTPreviewAction.js
Normal 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.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
var PREVIEW_TEMPLATE = '<mct-representation key="\'mct-preview\'"' +
|
||||
'class="t-rep-frame holder"' +
|
||||
'mct-object="selObj">' +
|
||||
'</mct-representation>';
|
||||
|
||||
function MCTPreview($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;
|
||||
}
|
||||
|
||||
MCTPreview.prototype.perform = function (object) {
|
||||
var domainObj = object || this.domainObject,
|
||||
rootScope = this.$rootScope;
|
||||
|
||||
rootScope.newEntryText = '';
|
||||
this.$rootScope.selObj = domainObj;
|
||||
this.$rootScope.selValue = "";
|
||||
|
||||
var newScope = rootScope.$new();
|
||||
newScope.selObj = domainObj;
|
||||
newScope.selValue = "";
|
||||
|
||||
this.$compile(PREVIEW_TEMPLATE)(newScope);
|
||||
};
|
||||
|
||||
MCTPreview.appliesTo = function (context) {
|
||||
var domainObject = (context || {}).domainObject,
|
||||
status = domainObject.getCapability('status');
|
||||
|
||||
return !(status && status.get('editing'));
|
||||
};
|
||||
|
||||
return MCTPreview;
|
||||
}
|
||||
);
|
||||
137
platform/commonUI/general/src/directives/MCTPreview.js
Normal file
137
platform/commonUI/general/src/directives/MCTPreview.js
Normal file
@@ -0,0 +1,137 @@
|
||||
/*****************************************************************************
|
||||
* 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="top-bar flex-elem holder"></div>' +
|
||||
' <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>';
|
||||
/**
|
||||
|
||||
*/
|
||||
function MCTPreview($rootScope,$document,exportImageService,dialogService,notificationService) {
|
||||
var document = $document[0];
|
||||
|
||||
function link($scope, $element, $attrs) {
|
||||
var isOpen = false,
|
||||
toggleOverlay,
|
||||
overlay,
|
||||
closeButton,
|
||||
doneButton,
|
||||
blocker,
|
||||
overlayContainer,
|
||||
notebookButtonEl,
|
||||
browseBar;
|
||||
|
||||
var actions = $scope.domainObject.getCapability('action'),
|
||||
notebookAction = actions.getActions({key: 'notebook-new-entry'});
|
||||
|
||||
function openOverlay() {
|
||||
|
||||
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);
|
||||
overlayContainer.appendChild($element[0]);
|
||||
|
||||
if (notebookAction) {
|
||||
notebookButtonEl = document.createElement('div');
|
||||
$(notebookButtonEl).addClass('notebook-button-container holder flex-elem');
|
||||
notebookButtonEl.innerHTML = NEW_NOTEBOOK_BUTTON_TEMPLATE;
|
||||
browseBar = overlay.querySelector('.object-browse-bar .right');
|
||||
browseBar.prepend(notebookButtonEl);
|
||||
notebookButtonEl.addEventListener('click', function () {
|
||||
notebookAction[0].perform();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function closeOverlay() {
|
||||
|
||||
if (notebookButtonEl) {
|
||||
browseBar.removeChild(notebookButtonEl);
|
||||
notebookButtonEl.remove();
|
||||
notebookButtonEl = undefined;
|
||||
}
|
||||
|
||||
overlayContainer.removeChild($element[0]);
|
||||
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;
|
||||
}
|
||||
|
||||
toggleOverlay = function (event) {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
openOverlay();
|
||||
isOpen = true;
|
||||
} else {
|
||||
closeOverlay();
|
||||
isOpen = false;
|
||||
}
|
||||
};
|
||||
|
||||
toggleOverlay();
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
$element.remove();
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: link
|
||||
};
|
||||
}
|
||||
|
||||
return MCTPreview;
|
||||
|
||||
});
|
||||
@@ -83,9 +83,9 @@ define([
|
||||
this.activeObject = domainObject;
|
||||
|
||||
if (domainObject && domainObject.hasCapability('composition')) {
|
||||
$(this.toggleView.elements()).addClass('has-children');
|
||||
$(this.toggleView.elements()).removeClass('no-children');
|
||||
} else {
|
||||
$(this.toggleView.elements()).removeClass('has-children');
|
||||
$(this.toggleView.elements()).addClass('no-children');
|
||||
}
|
||||
|
||||
if (domainObject && domainObject.hasCapability('status')) {
|
||||
|
||||
@@ -181,6 +181,8 @@ $colorPlotHash: $colorTick;
|
||||
$stylePlotHash: dashed;
|
||||
$colorPlotAreaBorder: $colorInteriorBorder;
|
||||
$colorPlotLabelFg: pushBack($colorPlotFg, 20%);
|
||||
$legendCollapsedNameMaxW: 50%;
|
||||
$legendHoverValueBg: rgba($colorBodyFg, 0.1);
|
||||
|
||||
// Tree
|
||||
$colorItemTreeHoverBg: pullForward($colorBodyBg, $hoverRatioPercent);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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[]"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ define(
|
||||
self.resizeHandles = self.generateDragHandles(self.selectedElementProxy);
|
||||
} else {
|
||||
// Make fixed view selectable if it's not already.
|
||||
if (!self.fixedViewSelectable) {
|
||||
if (!self.fixedViewSelectable && selectable.length === 1) {
|
||||
self.fixedViewSelectable = true;
|
||||
selection.context.viewProxy = new FixedProxy(addElement, $q, dialogService);
|
||||
self.openmct.selection.select(selection);
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -465,7 +464,7 @@ define(
|
||||
|
||||
function filterForTelemetryObjects(objects) {
|
||||
return objects.filter(function (object) {
|
||||
return self.openmct.telemetry.canProvideTelemetry(object);
|
||||
return self.openmct.telemetry.isTelemetryObject(object);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ 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.
|
||||
@@ -67,6 +72,7 @@ define([
|
||||
}
|
||||
|
||||
frame = frame[0];
|
||||
|
||||
var layoutContainer = frame.parentElement,
|
||||
isOpen = false,
|
||||
toggleOverlay,
|
||||
@@ -74,9 +80,15 @@ define([
|
||||
closeButton,
|
||||
doneButton,
|
||||
blocker,
|
||||
overlayContainer;
|
||||
overlayContainer,
|
||||
notebookButtonLg,
|
||||
browseBar;
|
||||
|
||||
var actions = $scope.domainObject.getCapability('action'),
|
||||
notebookAction = actions.getActions({key: 'notebook-new-entry'});
|
||||
|
||||
function openOverlay() {
|
||||
|
||||
// Remove frame classes from being applied in a non-frame context
|
||||
$(frame).removeClass('frame frame-template');
|
||||
overlay = document.createElement('div');
|
||||
@@ -92,9 +104,25 @@ define([
|
||||
document.body.appendChild(overlay);
|
||||
layoutContainer.removeChild(frame);
|
||||
overlayContainer.appendChild(frame);
|
||||
|
||||
if (notebookAction) {
|
||||
notebookButtonLg = document.createElement('div');
|
||||
$(notebookButtonLg).addClass('notebook-button-container holder flex-elem');
|
||||
notebookButtonLg.innerHTML = NEW_NOTEBOOK_BUTTON_TEMPLATE;
|
||||
browseBar = overlay.querySelector('.object-browse-bar .right');
|
||||
browseBar.prepend(notebookButtonLg);
|
||||
notebookButtonLg.addEventListener('click', function () {
|
||||
notebookAction[0].perform(overlayContainer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function closeOverlay() {
|
||||
if (notebookButtonLg) {
|
||||
browseBar.removeChild(notebookButtonLg);
|
||||
notebookButtonLg.remove();
|
||||
}
|
||||
|
||||
$(frame).addClass('frame frame-template');
|
||||
overlayContainer.removeChild(frame);
|
||||
layoutContainer.appendChild(frame);
|
||||
@@ -109,7 +137,11 @@ define([
|
||||
overlay = undefined;
|
||||
}
|
||||
|
||||
toggleOverlay = function () {
|
||||
toggleOverlay = function (event) {
|
||||
if (event) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
openOverlay();
|
||||
isOpen = true;
|
||||
|
||||
@@ -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(
|
||||
@@ -150,13 +150,13 @@ define(
|
||||
[
|
||||
'subscribe',
|
||||
'request',
|
||||
'canProvideTelemetry',
|
||||
'isTelemetryObject',
|
||||
'getMetadata',
|
||||
'limitEvaluator',
|
||||
'getValueFormatter'
|
||||
]
|
||||
);
|
||||
mockTelemetryAPI.canProvideTelemetry.andReturn(true);
|
||||
mockTelemetryAPI.isTelemetryObject.andReturn(true);
|
||||
mockTelemetryAPI.request.andReturn(Promise.resolve([]));
|
||||
|
||||
testGrid = [123, 456];
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
314
platform/features/notebook/bundle.js
Normal file
314
platform/features/notebook/bundle.js
Normal 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 Notebook 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
283
platform/features/notebook/res/sass/_notebook-base.scss
Normal file
283
platform/features/notebook/res/sass/_notebook-base.scss
Normal file
@@ -0,0 +1,283 @@
|
||||
/*****************************************************************************
|
||||
* 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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************* PAINTERRO OVERRIDES */
|
||||
.annotation-dialog .abs.editor {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
#snap-annotation {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
}
|
||||
|
||||
#snap-annotation-wrapper,
|
||||
#snap-annotation-bar {
|
||||
position: relative;
|
||||
top: auto; right: auto; bottom: auto; left: auto;
|
||||
}
|
||||
|
||||
#snap-annotation-wrapper {
|
||||
order: 2;
|
||||
flex: 10 0 auto;
|
||||
}
|
||||
|
||||
#snap-annotation-bar {
|
||||
order: 1;
|
||||
flex: 0 0 auto;
|
||||
height: auto;
|
||||
background-color: transparent !important;
|
||||
margin-bottom: $interiorMargin;
|
||||
|
||||
> div,
|
||||
> div > span,
|
||||
.ptro-icon-btn,
|
||||
.ptro-named-btn,
|
||||
.ptro-color-btn,
|
||||
.ptro-bordered-btn,
|
||||
.ptro-tool-ctl-name,
|
||||
.ptro-color-btn,
|
||||
.tool-controls,
|
||||
.ptro-input {
|
||||
// Lot of resets for crappy CSS in Painterro
|
||||
&:first-child {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
$h: $btnToolbarH;
|
||||
display: inline-block;
|
||||
font-family: inherit;
|
||||
font-size: auto;
|
||||
height: $h !important;
|
||||
margin: 0 0 0 5px;
|
||||
position: relative;
|
||||
width: auto !important;
|
||||
line-height: $h !important;
|
||||
top: auto;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
left: auto;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.ptro-tool-ctl-name {
|
||||
border-radius: 0;
|
||||
background: none;
|
||||
top: auto;
|
||||
font-family: inherit;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ptro-color-btn {
|
||||
width: $btnToolbarH !important;
|
||||
}
|
||||
|
||||
.ptro-icon-btn,
|
||||
.ptro-named-btn {
|
||||
// .s-button class is added via JS in AnnotateSnapshot.js
|
||||
// TODO: redo this so that we don't need to use Zepto and JS
|
||||
i {
|
||||
font-size: 1.25em !important;
|
||||
}
|
||||
}
|
||||
|
||||
.tool-controls {
|
||||
font-size: 0.8rem !important;
|
||||
}
|
||||
|
||||
.ptro-info,
|
||||
.ptro-btn-color-checkers-bar,
|
||||
*[title="Font name"],
|
||||
*[title="Stroke color"],
|
||||
*[title="Stroke width"],
|
||||
*[data-id="fontName"],
|
||||
*[data-id="fontStrokeSize"],
|
||||
*[data-id="stroke"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************* 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;
|
||||
}
|
||||
*/
|
||||
92
platform/features/notebook/res/sass/_notebook-thematic.scss
Normal file
92
platform/features/notebook/res/sass/_notebook-thematic.scss
Normal file
@@ -0,0 +1,92 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.s-snapshot-datetime {
|
||||
color: rgba($colorBodyFg, 0.4);
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.snap-thumb {
|
||||
border: 1px solid $colorInteriorBorder;
|
||||
}
|
||||
|
||||
.s-status-taking-snapshot,
|
||||
.overlay.snapshot {
|
||||
// Applied to an object view when it's in the process of being snapshotted
|
||||
background: $colorBodyBg;
|
||||
}
|
||||
|
||||
/********************************************* PAINTERRO OVERRIDES */
|
||||
.ptro-wrapper {
|
||||
background: rgba($colorBodyBg, 0.3) !important;
|
||||
}
|
||||
|
||||
#snap-annotation-bar {
|
||||
.tool-controls {
|
||||
color: $colorBodyFg !important;
|
||||
}
|
||||
}
|
||||
|
||||
.s-button.ptro-color-active-control {
|
||||
background: $colorBtnMajorBg !important;
|
||||
color: $colorBtnMajorFg !important;
|
||||
&:hover {
|
||||
background: $colorBtnMajorBgHov !important;
|
||||
color: $colorBtnMajorFgHov !important;
|
||||
}
|
||||
}
|
||||
30
platform/features/notebook/res/sass/notebook-espresso.scss
Normal file
30
platform/features/notebook/res/sass/notebook-espresso.scss
Normal 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.
|
||||
*****************************************************************************/
|
||||
$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 "notebook-thematic";
|
||||
30
platform/features/notebook/res/sass/notebook-snow.scss
Normal file
30
platform/features/notebook/res/sass/notebook-snow.scss
Normal 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.
|
||||
*****************************************************************************/
|
||||
$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 "notebook-thematic";
|
||||
28
platform/features/notebook/res/sass/notebook.scss
Normal file
28
platform/features/notebook/res/sass/notebook.scss
Normal 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";
|
||||
2
platform/features/notebook/res/templates/annotation.html
Normal file
2
platform/features/notebook/res/templates/annotation.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<div class="snap-annotation" id="snap-annotation" ng-init="ngModel.tracker()">
|
||||
</div>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
38
platform/features/notebook/res/templates/entry.html
Normal file
38
platform/features/notebook/res/templates/entry.html
Normal 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>
|
||||
@@ -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>
|
||||
84
platform/features/notebook/res/templates/layoutNotebook.html
Normal file
84
platform/features/notebook/res/templates/layoutNotebook.html
Normal 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>
|
||||
124
platform/features/notebook/res/templates/notebook.html
Normal file
124
platform/features/notebook/res/templates/notebook.html
Normal 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" ng-click="selectContentEditable($event)">
|
||||
<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" id={{entry.id}} title="Delete Entry" ng-click="deleteEntry($event)"></a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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>
|
||||
131
platform/features/notebook/src/actions/AnnotateSnapshot.js
Normal file
131
platform/features/notebook/src/actions/AnnotateSnapshot.js
Normal file
@@ -0,0 +1,131 @@
|
||||
/*****************************************************************************
|
||||
* 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',
|
||||
activeColor: '#ff0000',
|
||||
activeColorAlpha: 1.0,
|
||||
activeFillColor: '#fff',
|
||||
activeFillColorAlpha: 0.0,
|
||||
backgroundFillColor: '#000',
|
||||
backgroundFillColorAlpha: 0.0,
|
||||
defaultFontSize: 16,
|
||||
defaultLineWidth: 2,
|
||||
defaultTool: 'ellipse',
|
||||
hiddenTools: ['save', 'open', 'close', 'eraser', 'pixelize', 'rotate', 'settings', 'resize'],
|
||||
translation: {
|
||||
name: 'en',
|
||||
strings: {
|
||||
lineColor: 'Line',
|
||||
fillColor: 'Fill',
|
||||
lineWidth: 'Size',
|
||||
textColor: 'Color',
|
||||
fontSize: 'Size',
|
||||
fontStyle: 'Style'
|
||||
}
|
||||
},
|
||||
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);
|
||||
$(document.body).find('.ptro-icon-btn').addClass('s-button');
|
||||
$(document.body).find('.ptro-input').addClass('s-button');
|
||||
};
|
||||
|
||||
ANNOTATION_STRUCT.model = {'tracker': tracker};
|
||||
|
||||
|
||||
|
||||
function saveNotes(param) {
|
||||
if (param === 'ok') {
|
||||
painterro.save();
|
||||
}else {
|
||||
ROOTSCOPE.snapshot = "annotationCancelled";
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return AnnotateSnapshot;
|
||||
}
|
||||
);
|
||||
65
platform/features/notebook/src/actions/CreateSnapshot.js
Normal file
65
platform/features/notebook/src/actions/CreateSnapshot.js
Normal 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;
|
||||
}
|
||||
);
|
||||
193
platform/features/notebook/src/actions/NewEntryContextual.js
Normal file
193
platform/features/notebook/src/actions/NewEntryContextual.js
Normal file
@@ -0,0 +1,193 @@
|
||||
/*****************************************************************************
|
||||
* 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.getModel().type !== 'notebook');
|
||||
};
|
||||
|
||||
return NewEntryContextual;
|
||||
}
|
||||
);
|
||||
72
platform/features/notebook/src/actions/RemoveEmbed.js
Normal file
72
platform/features/notebook/src/actions/RemoveEmbed.js
Normal 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;
|
||||
}
|
||||
);
|
||||
74
platform/features/notebook/src/actions/RemoveSnapshot.js
Normal file
74
platform/features/notebook/src/actions/RemoveSnapshot.js
Normal 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;
|
||||
}
|
||||
);
|
||||
169
platform/features/notebook/src/actions/ViewSnapshot.js
Normal file
169
platform/features/notebook/src/actions/ViewSnapshot.js
Normal file
@@ -0,0 +1,169 @@
|
||||
/*****************************************************************************
|
||||
* 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('div');
|
||||
$(img).addClass('abs object-holder t-image-holder s-image-holder');
|
||||
img.innerHTML = '<div class="image-main s-image-main" style="background-image: url(' + url + ');"></div>';
|
||||
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 abs l-view-header">' +
|
||||
'<div class="abs object-browse-bar l-flex-row">' +
|
||||
'<div class="left flex-elem l-flex-row grows">' +
|
||||
'<div class="object-header flex-elem l-flex-row grows">' +
|
||||
'<div class="type-icon flex-elem embed-icon holder" ng-class="cssClass"></div>' +
|
||||
'<div class="title-label flex-elem holder flex-can-shrink">{{entryName}}</div>' +
|
||||
'<a class="context-available flex-elem holder" ng-click="openMenu($event,embedType)""></a>' +
|
||||
'<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="menuPerform(menu)"' +
|
||||
'title="{{menu.name}}"' +
|
||||
'class="{{menu.cssClass}}">' +
|
||||
'{{menu.name}}' +
|
||||
'</li>' +
|
||||
'</ul>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div><!-- closes object-header -->' +
|
||||
'</div><!-- closes left -->' +
|
||||
'<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">' +
|
||||
'<div class="flex-elem holder flex-can-shrink s-snapshot-datetime">' +
|
||||
'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><!-- closes right -->' +
|
||||
'</div><!-- closes object-browse-bar -->' +
|
||||
'</div><!-- closes t-snapshot -->';
|
||||
return template;
|
||||
}
|
||||
|
||||
|
||||
ViewSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId,$scope,embed) {
|
||||
var isOpen = false;
|
||||
|
||||
// onclick for menu items in overlay header context menu
|
||||
$scope.menuPerform = function (menu) {
|
||||
menu.perform();
|
||||
closeOverlay();
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
);
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
400
platform/features/notebook/src/controllers/NotebookController.js
Normal file
400
platform/features/notebook/src/controllers/NotebookController.js
Normal file
@@ -0,0 +1,400 @@
|
||||
/*****************************************************************************
|
||||
* 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.findEntryEl = function (entryId) {
|
||||
var element = $($scope.entriesEl).find('#entry_' + entryId);
|
||||
|
||||
if (element[0]) {
|
||||
return element.find("[contenteditable='true']");
|
||||
} else {
|
||||
return $($scope.entriesEl.children().children()[0]).find("[contenteditable='true']");
|
||||
}
|
||||
};
|
||||
|
||||
$scope.findEntryPositionById = function (id) {
|
||||
var foundId = -1;
|
||||
|
||||
$scope.domainObject.model.entries.forEach(function (element, index) {
|
||||
if (element.id === id) {
|
||||
foundId = index;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
return foundId;
|
||||
};
|
||||
|
||||
$scope.newEntry = function ($event) {
|
||||
$scope.scrollToTop();
|
||||
|
||||
var entries = $scope.domainObject.model.entries,
|
||||
lastEntry = entries[entries.length - 1],
|
||||
id = Date.now();
|
||||
|
||||
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
|
||||
var createdEntry = {'id': id, 'createdOn': id};
|
||||
|
||||
$scope.domainObject.useCapability('mutation', function (model) {
|
||||
model.entries.push(createdEntry);
|
||||
});
|
||||
} else {
|
||||
$scope.findEntryEl(lastEntry.id).focus();
|
||||
|
||||
$scope.domainObject.useCapability('mutation', function (model) {
|
||||
model.entries[entries.length - 1].createdOn = id;
|
||||
});
|
||||
}
|
||||
$scope.entrySearch = '';
|
||||
};
|
||||
|
||||
|
||||
$scope.deleteEntry = function ($event) {
|
||||
var delId = +$event.currentTarget.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.findEntryPositionById(delId);
|
||||
|
||||
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();
|
||||
}
|
||||
}]
|
||||
});
|
||||
};
|
||||
|
||||
function setCaretToEndOfContenteditable(contentEditableElement) {
|
||||
var range,selection;
|
||||
if (document.createRange) {
|
||||
range = document.createRange();//Create a range (a range is a like the selection but invisible)
|
||||
range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
|
||||
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
|
||||
selection = window.getSelection();//get the selection object (allows you to change selection)
|
||||
selection.removeAllRanges();//remove any selections already made
|
||||
selection.addRange(range);//make the range you have just created the visible selection
|
||||
}
|
||||
}
|
||||
|
||||
$scope.selectContentEditable = function ($event) {
|
||||
var child = $($event.srcElement).children()[0];
|
||||
|
||||
if (child) {
|
||||
$($event.srcElement).children()[0].focus();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.textFocus = function ($event, entryId) {
|
||||
if ($event.srcElement && $event.srcElement.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.srcElement.innerText;
|
||||
setCaretToEndOfContenteditable($event.srcElement);
|
||||
} 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.findEntryPositionById(+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) {
|
||||
$scope.findEntryEl(lastEntry.id).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;
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
126
platform/features/notebook/src/directives/EntryDnd.js
Normal file
126
platform/features/notebook/src/directives/EntryDnd.js
Normal file
@@ -0,0 +1,126 @@
|
||||
/*****************************************************************************
|
||||
* 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 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.findEntryPositionById(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 ($(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;
|
||||
|
||||
});
|
||||
166
platform/features/notebook/src/directives/MCTModalNotebook.js
Normal file
166
platform/features/notebook/src/directives/MCTModalNotebook.js
Normal 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;
|
||||
|
||||
});
|
||||
104
platform/features/notebook/src/directives/MCTSnapshot.js
Normal file
104
platform/features/notebook/src/directives/MCTSnapshot.js
Normal file
@@ -0,0 +1,104 @@
|
||||
/*****************************************************************************
|
||||
* 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', 'dom-to-image'], function ($) {
|
||||
/**
|
||||
|
||||
*/
|
||||
function MCTSnapshot($rootScope,$document,exportImageService,dialogService,notificationService) {
|
||||
var document = $document[0];
|
||||
|
||||
function link($scope, $element, $attrs) {
|
||||
var objectElement = $(document.body).find('.overlay')[0] || $(document.body).find("[key='representation.selected.key']")[0],
|
||||
takeSnapshot,
|
||||
makeImg,
|
||||
saveImg;
|
||||
|
||||
$(objectElement).addClass("s-status-taking-snapshot");
|
||||
|
||||
takeSnapshot = function () {
|
||||
makeImg(objectElement);
|
||||
};
|
||||
|
||||
saveImg = function (url,entryId,embedId) {
|
||||
$scope.$parent.$parent.$parent.saveSnap(url, embedId, entryId);
|
||||
};
|
||||
|
||||
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.domtoimage.toBlob(el).then(function (img) {
|
||||
$(objectElement).removeClass("s-status-taking-snapshot");
|
||||
|
||||
if (img) {
|
||||
if ($element[0].dataset.entry && $element[0].dataset.embed) {
|
||||
saveImg(img, +$element[0].dataset.entry, +$element[0].dataset.embed);
|
||||
} 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()
|
||||
};
|
||||
scope.$destroy();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
|
||||
}, function (error) {
|
||||
if (dialog) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
}, 500);
|
||||
};
|
||||
|
||||
takeSnapshot();
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
$element.remove();
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: link
|
||||
};
|
||||
}
|
||||
|
||||
return MCTSnapshot;
|
||||
|
||||
});
|
||||
44
platform/features/notebook/src/policies/CompositionPolicy.js
Normal file
44
platform/features/notebook/src/policies/CompositionPolicy.js
Normal 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;
|
||||
}
|
||||
);
|
||||
40
platform/features/notebook/src/policies/ViewPolicy.js
Normal file
40
platform/features/notebook/src/policies/ViewPolicy.js
Normal 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;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
# Plot README
|
||||
|
||||
## Chart
|
||||
|
||||
The `mct-chart` directive is used to support drawing of simple charts. It is
|
||||
present to support the Plot view, and its functionality is limited to the
|
||||
functionality that is relevant for that view.
|
||||
|
||||
This directive is used at the element level and takes one attribute, `draw`
|
||||
which is an Angular expression which will should evaluate to a drawing object.
|
||||
This drawing object should contain the following properties:
|
||||
|
||||
* `dimensions`: The size, in logical coordinates, of the chart area. A
|
||||
two-element array or numbers.
|
||||
* `origin`: The position, in logical coordinates, of the lower-left corner of
|
||||
the chart area. A two-element array or numbers.
|
||||
* `lines`: An array of lines (e.g. as a plot line) to draw, where each line is
|
||||
expressed as an object containing:
|
||||
* `buffer`: A Float32Array containing points in the line, in logical
|
||||
coordinates, in sequential x,y pairs.
|
||||
* `color`: The color of the line, as a four-element RGBA array, where
|
||||
each element is a number in the range of 0.0-1.0.
|
||||
* `points`: The number of points in the line.
|
||||
* `boxes`: An array of rectangles to draw in the chart area. Each is an object
|
||||
containing:
|
||||
* `start`: The first corner of the rectangle, as a two-element array of
|
||||
numbers, in logical coordinates.
|
||||
* `end`: The opposite corner of the rectangle, as a two-element array of
|
||||
numbers, in logical coordinates. color : The color of the line, as a
|
||||
four-element RGBA array, where each element is a number in the range of
|
||||
0.0-1.0.
|
||||
|
||||
While `mct-chart` is intended to support plots specifically, it does perform
|
||||
some useful management of canvas objects (e.g. choosing between WebGL and Canvas
|
||||
2D APIs for drawing based on browser support) so its usage is recommended when
|
||||
its supported drawing primitives are sufficient for other charting tasks.
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
"./src/MCTChart",
|
||||
"./src/PlotController",
|
||||
"./src/policies/PlotViewPolicy",
|
||||
"./src/PlotOptionsController",
|
||||
"./src/services/ExportImageService",
|
||||
"text!./res/templates/plot.html",
|
||||
"text!./res/templates/plot-options-browse.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
MCTChart,
|
||||
PlotController,
|
||||
PlotViewPolicy,
|
||||
PlotOptionsController,
|
||||
exportImageService,
|
||||
plotTemplate,
|
||||
plotOptionsBrowseTemplate,
|
||||
legacyRegistry
|
||||
) {
|
||||
|
||||
legacyRegistry.register("platform/features/plot", {
|
||||
"name": "Plot view for telemetry",
|
||||
"extensions": {
|
||||
"views": [
|
||||
{
|
||||
"name": "Plot",
|
||||
"key": "plot",
|
||||
"cssClass": "icon-sine",
|
||||
"template": plotTemplate,
|
||||
"needs": [
|
||||
"telemetry"
|
||||
],
|
||||
"priority": "preferred",
|
||||
"delegation": true
|
||||
}
|
||||
],
|
||||
"directives": [
|
||||
{
|
||||
"key": "mctChart",
|
||||
"implementation": MCTChart,
|
||||
"depends": [
|
||||
"$interval",
|
||||
"$log"
|
||||
]
|
||||
}
|
||||
],
|
||||
"controllers": [
|
||||
{
|
||||
"key": "PlotController",
|
||||
"implementation": PlotController,
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$element",
|
||||
"exportImageService",
|
||||
"telemetryFormatter",
|
||||
"telemetryHandler",
|
||||
"throttle",
|
||||
"PLOT_FIXED_DURATION",
|
||||
"openmct"
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "PlotOptionsController",
|
||||
"implementation": PlotOptionsController,
|
||||
"depends": [
|
||||
"$scope"
|
||||
]
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key": "exportImageService",
|
||||
"implementation": exportImageService,
|
||||
"depends": [
|
||||
"$q",
|
||||
"$timeout",
|
||||
"$log",
|
||||
"EXPORT_IMAGE_TIMEOUT"
|
||||
]
|
||||
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"key": "PLOT_FIXED_DURATION",
|
||||
"value": 900000,
|
||||
"priority": "fallback",
|
||||
"comment": "Fifteen minutes."
|
||||
},
|
||||
{
|
||||
"key": "EXPORT_IMAGE_TIMEOUT",
|
||||
"value": 500,
|
||||
"priority": "fallback"
|
||||
}
|
||||
],
|
||||
"policies": [
|
||||
{
|
||||
"category": "view",
|
||||
"implementation": PlotViewPolicy,
|
||||
"depends": [
|
||||
"openmct"
|
||||
]
|
||||
}
|
||||
],
|
||||
"representations": [
|
||||
{
|
||||
"key": "plot-options-browse",
|
||||
"template": plotOptionsBrowseTemplate
|
||||
}
|
||||
],
|
||||
"licenses": [
|
||||
{
|
||||
"name": "FileSaver.js",
|
||||
"version": "0.0.2",
|
||||
"author": "Eli Grey",
|
||||
"description": "File download initiator (for file exports)",
|
||||
"website": "https://github.com/eligrey/FileSaver.js/",
|
||||
"copyright": "Copyright © 2015 Eli Grey.",
|
||||
"license": "license-mit",
|
||||
"link": "https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md"
|
||||
},
|
||||
{
|
||||
"name": "html2canvas",
|
||||
"version": "0.4.1",
|
||||
"author": "Niklas von Hertzen",
|
||||
"description": "JavaScript HTML renderer",
|
||||
"website": "https://github.com/niklasvh/html2canvas",
|
||||
"copyright": "Copyright © 2012 Niklas von Hertzen.",
|
||||
"license": "license-mit",
|
||||
"link": "https://github.com/niklasvh/html2canvas/blob/master/LICENSE"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,70 +0,0 @@
|
||||
<!--
|
||||
Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
|
||||
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Open MCT includes source code licensed under additional open source
|
||||
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div ng-controller="PlotOptionsController" class="flex-elem grows l-inspector-part">
|
||||
<em class="t-inspector-part-header" title="Display properties for this object">Plot Options</em>
|
||||
<mct-form
|
||||
ng-model="configuration.plot.xAxis"
|
||||
structure="xAxisForm"
|
||||
name="xAxisFormState"
|
||||
class="flex-elem l-flex-row no-margin">
|
||||
</mct-form>
|
||||
<mct-form
|
||||
ng-model="configuration.plot.yAxis"
|
||||
structure="yAxisForm"
|
||||
name="yAxisFormState"
|
||||
class="flex-elem l-flex-row no-margin">
|
||||
</mct-form>
|
||||
<div class="form">
|
||||
<div class="section-header ng-binding ng-scope">
|
||||
Plot Series
|
||||
</div>
|
||||
<ul class="first flex-elem grows vscroll">
|
||||
<ul class="tree">
|
||||
<li ng-repeat="child in children">
|
||||
<span ng-controller="ToggleController as toggle">
|
||||
<span ng-controller="TreeNodeController as treeNode">
|
||||
<span class="tree-item menus-to-left">
|
||||
<span
|
||||
class='ui-symbol view-control flex-elem has-children'
|
||||
ng-class="{ expanded: toggle.isActive() }"
|
||||
ng-click="toggle.toggle(); treeNode.trackExpansion()">
|
||||
</span>
|
||||
<mct-representation
|
||||
class="rep-object-label"
|
||||
key="'label'"
|
||||
mct-object="child">
|
||||
</mct-representation>
|
||||
</span>
|
||||
</span>
|
||||
<mct-form
|
||||
ng-class="{hidden: !toggle.isActive()}"
|
||||
ng-model="configuration.plot.series[$index]"
|
||||
structure="plotSeriesForm"
|
||||
name="plotOptionsState"
|
||||
class="flex-elem l-flex-row">
|
||||
</mct-form>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,165 +0,0 @@
|
||||
<!--
|
||||
Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
as represented by the Administrator of the National Aeronautics and Space
|
||||
Administration. All rights reserved.
|
||||
|
||||
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Open MCT includes source code licensed under additional open source
|
||||
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<span ng-controller="PlotController as plot"
|
||||
class="abs holder holder-plot has-control-bar">
|
||||
<div class="l-control-bar" ng-show="!plot.hideExportButtons">
|
||||
<span class="l-btn-set">
|
||||
<a class="s-button t-export labeled icon-download"
|
||||
ng-click="plot.exportPNG()"
|
||||
title="Export This View's Data as PNG">
|
||||
PNG
|
||||
</a>
|
||||
<a class="s-button t-export labeled"
|
||||
ng-click="plot.exportJPG()"
|
||||
title="Export This View's Data as JPG">
|
||||
JPG
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="l-view-section">
|
||||
<div class="gl-plot"
|
||||
ng-style="{ height: 100 / plot.getSubPlots().length + '%'}"
|
||||
ng-repeat="subplot in plot.getSubPlots()">
|
||||
<div class="gl-plot-legend">
|
||||
<span class='plot-legend-item'
|
||||
ng-repeat="telemetryObject in subplot.getTelemetryObjects()"
|
||||
ng-class="plot.getLegendClass(telemetryObject)">
|
||||
<span class='plot-color-swatch'
|
||||
ng-style="{ 'background-color': plot.getColor($index) }">
|
||||
</span>
|
||||
<span class='title-label'>{{telemetryObject.getModel().name}}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-axis-area gl-plot-y">
|
||||
<div class="gl-plot-label gl-plot-y-label">
|
||||
{{axes[1].active.name}}
|
||||
</div>
|
||||
<div ng-repeat="tick in subplot.getRangeTicks()"
|
||||
class="gl-plot-tick gl-plot-y-tick-label"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
</div>
|
||||
<div class="gl-plot-y-options gl-plot-local-controls"
|
||||
ng-if="axes[1].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[1].active"
|
||||
ng-options="option.name for option in axes[1].options">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-wrapper-display-area-and-x-axis">
|
||||
<mct-include key="'time-of-interest'"
|
||||
class="l-toi-holder show-val"
|
||||
ng-if="toiPerc"
|
||||
ng-class="{ 'pinned': toiPinned, 'val-to-left': toiPerc > 80 }"
|
||||
ng-style="{'left': toiPerc + '%'}"></mct-include>
|
||||
|
||||
<div class="gl-plot-coords"
|
||||
ng-if="subplot.isHovering() && subplot.getHoverCoordinates()">
|
||||
{{subplot.getHoverCoordinates()}}
|
||||
</div>
|
||||
|
||||
<div class="gl-plot-display-area"
|
||||
ng-mouseenter="subplot.isHovering(true);"
|
||||
ng-mouseleave="subplot.isHovering(false)"
|
||||
ng-class="{ loading: plot.isRequestPending() }">
|
||||
|
||||
<!-- Out-of-bounds data indicators -->
|
||||
<!-- ng-show is temporarily hard-coded in next element -->
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-up"></div>
|
||||
<div ng-show="false" class="l-oob-data l-oob-data-dwn"></div>
|
||||
<div class="gl-plot-hash hash-v"
|
||||
ng-repeat="tick in subplot.getDomainTicks()"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%', height: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)">
|
||||
</div>
|
||||
<div class="gl-plot-hash hash-h"
|
||||
ng-repeat="tick in subplot.getRangeTicks()"
|
||||
ng-style="{ bottom: (100 * $index / (subplot.getRangeTicks().length - 1)) + '%', width: '100%' }"
|
||||
ng-show="$index > 0 && $index < (subplot.getRangeTicks().length - 1)">
|
||||
</div>
|
||||
<mct-chart draw="subplot.getDrawingObject()"
|
||||
ng-if="subplot.getTelemetryObjects().length > 0"
|
||||
ng-mousemove="subplot.hover($event)"
|
||||
mct-drag="subplot.continueDrag($event)"
|
||||
mct-drag-down="subplot.startDrag($event)"
|
||||
mct-drag-up="subplot.endDrag($event); plot.update()">
|
||||
</mct-chart>
|
||||
<!-- TODO: Move into correct position; make part of group; infer from set of actions -->
|
||||
<div class="l-local-controls gl-plot-local-controls t-plot-display-controls"
|
||||
ng-if="$first">
|
||||
<a class="s-button icon-arrow-left"
|
||||
ng-click="plot.stepBackPanZoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Restore previous pan/zoom">
|
||||
</a>
|
||||
<a class="s-button icon-arrows-out"
|
||||
ng-click="plot.unzoom()"
|
||||
ng-show="plot.isZoomed()"
|
||||
title="Reset pan/zoom">
|
||||
</a>
|
||||
<div class="menu-element s-menu-button menus-to-left {{plot.getMode().cssClass}}"
|
||||
ng-if="plot.getModeOptions().length > 1"
|
||||
ng-controller="ClickAwayController as toggle">
|
||||
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
||||
<span>{{plot.getMode().name}}</span>
|
||||
<div class="menu" ng-show="toggle.isActive()">
|
||||
<ul>
|
||||
<li ng-repeat="option in plot.getModeOptions()"
|
||||
ng-click="plot.setMode(option); toggle.setState(false)"
|
||||
class="{{option.cssClass}}">
|
||||
{{option.name}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="$last" class="gl-plot-axis-area gl-plot-x">
|
||||
<div ng-repeat="tick in subplot.getDomainTicks()"
|
||||
class="gl-plot-tick gl-plot-x-tick-label"
|
||||
ng-show="$index > 0 && $index < (subplot.getDomainTicks().length - 1)"
|
||||
ng-style="{ left: (100 * $index / (subplot.getDomainTicks().length - 1)) + '%' }">
|
||||
{{tick.label | reverse}}
|
||||
</div>
|
||||
<div class="gl-plot-label gl-plot-x-label">
|
||||
{{axes[0].active.name}}
|
||||
</div>
|
||||
<div class="gl-plot-x-options gl-plot-local-controls"
|
||||
ng-if="axes[0].options.length > 1">
|
||||
<div class='form-control shell select'>
|
||||
<select class="form-control input shell"
|
||||
ng-model="axes[0].active"
|
||||
ng-options="option.name for option in axes[0].options">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
@@ -1,117 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Create a new chart which uses Canvas's 2D API for rendering.
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @implements {platform/features/plot.Chart}
|
||||
* @param {CanvasElement} canvas the canvas object to render upon
|
||||
* @throws {Error} an error is thrown if Canvas's 2D API is unavailable.
|
||||
*/
|
||||
function Canvas2DChart(canvas) {
|
||||
this.canvas = canvas;
|
||||
this.c2d = canvas.getContext('2d');
|
||||
this.width = canvas.width;
|
||||
this.height = canvas.height;
|
||||
this.dimensions = [this.width, this.height];
|
||||
this.origin = [0, 0];
|
||||
|
||||
if (!this.c2d) {
|
||||
throw new Error("Canvas 2d API unavailable.");
|
||||
}
|
||||
}
|
||||
|
||||
// Convert from logical to physical x coordinates
|
||||
Canvas2DChart.prototype.x = function (v) {
|
||||
return ((v - this.origin[0]) / this.dimensions[0]) * this.width;
|
||||
};
|
||||
|
||||
// Convert from logical to physical y coordinates
|
||||
Canvas2DChart.prototype.y = function (v) {
|
||||
return this.height -
|
||||
((v - this.origin[1]) / this.dimensions[1]) * this.height;
|
||||
};
|
||||
|
||||
// Set the color to be used for drawing operations
|
||||
Canvas2DChart.prototype.setColor = function (color) {
|
||||
var mappedColor = color.map(function (c, i) {
|
||||
return i < 3 ? Math.floor(c * 255) : (c);
|
||||
}).join(',');
|
||||
this.c2d.strokeStyle = "rgba(" + mappedColor + ")";
|
||||
this.c2d.fillStyle = "rgba(" + mappedColor + ")";
|
||||
};
|
||||
|
||||
|
||||
Canvas2DChart.prototype.clear = function () {
|
||||
var canvas = this.canvas;
|
||||
this.width = canvas.width;
|
||||
this.height = canvas.height;
|
||||
this.c2d.clearRect(0, 0, this.width, this.height);
|
||||
};
|
||||
|
||||
Canvas2DChart.prototype.setDimensions = function (newDimensions, newOrigin) {
|
||||
this.dimensions = newDimensions;
|
||||
this.origin = newOrigin;
|
||||
};
|
||||
|
||||
Canvas2DChart.prototype.drawLine = function (buf, color, points) {
|
||||
var i;
|
||||
|
||||
this.setColor(color);
|
||||
|
||||
// Configure context to draw two-pixel-thick lines
|
||||
this.c2d.lineWidth = 2;
|
||||
|
||||
// Start a new path...
|
||||
if (buf.length > 1) {
|
||||
this.c2d.beginPath();
|
||||
this.c2d.moveTo(this.x(buf[0]), this.y(buf[1]));
|
||||
}
|
||||
|
||||
// ...and add points to it...
|
||||
for (i = 2; i < points * 2; i = i + 2) {
|
||||
this.c2d.lineTo(this.x(buf[i]), this.y(buf[i + 1]));
|
||||
}
|
||||
|
||||
// ...before finally drawing it.
|
||||
this.c2d.stroke();
|
||||
};
|
||||
|
||||
Canvas2DChart.prototype.drawSquare = function (min, max, color) {
|
||||
var x1 = this.x(min[0]),
|
||||
y1 = this.y(min[1]),
|
||||
w = this.x(max[0]) - x1,
|
||||
h = this.y(max[1]) - y1;
|
||||
|
||||
this.setColor(color);
|
||||
this.c2d.fillRect(x1, y1, w, h);
|
||||
};
|
||||
|
||||
return Canvas2DChart;
|
||||
}
|
||||
);
|
||||
@@ -1,160 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Module defining GLPlot. Created by vwoeltje on 11/12/14.
|
||||
*/
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
// WebGL shader sources (for drawing plain colors)
|
||||
var FRAGMENT_SHADER = [
|
||||
"precision mediump float;",
|
||||
"uniform vec4 uColor;",
|
||||
"void main(void) {",
|
||||
"gl_FragColor = uColor;",
|
||||
"}"
|
||||
].join('\n'),
|
||||
VERTEX_SHADER = [
|
||||
"attribute vec2 aVertexPosition;",
|
||||
"uniform vec2 uDimensions;",
|
||||
"uniform vec2 uOrigin;",
|
||||
"void main(void) {",
|
||||
"gl_Position = vec4(2.0 * ((aVertexPosition - uOrigin) / uDimensions) - vec2(1,1), 0, 1);",
|
||||
"}"
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* Create a new chart which uses WebGL for rendering.
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @implements {platform/features/plot.Chart}
|
||||
* @param {CanvasElement} canvas the canvas object to render upon
|
||||
* @throws {Error} an error is thrown if WebGL is unavailable.
|
||||
*/
|
||||
function GLChart(canvas) {
|
||||
var gl = canvas.getContext("webgl", { preserveDrawingBuffer: true }) ||
|
||||
canvas.getContext("experimental-webgl", { preserveDrawingBuffer: true }),
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
program,
|
||||
aVertexPosition,
|
||||
uColor,
|
||||
uDimensions,
|
||||
uOrigin;
|
||||
|
||||
// Ensure a context was actually available before proceeding
|
||||
if (!gl) {
|
||||
throw new Error("WebGL unavailable.");
|
||||
}
|
||||
|
||||
// Initialize shaders
|
||||
vertexShader = gl.createShader(gl.VERTEX_SHADER);
|
||||
gl.shaderSource(vertexShader, VERTEX_SHADER);
|
||||
gl.compileShader(vertexShader);
|
||||
fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
gl.shaderSource(fragmentShader, FRAGMENT_SHADER);
|
||||
gl.compileShader(fragmentShader);
|
||||
|
||||
// Assemble vertex/fragment shaders into programs
|
||||
program = gl.createProgram();
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
gl.useProgram(program);
|
||||
|
||||
// Get locations for attribs/uniforms from the
|
||||
// shader programs (to pass values into shaders at draw-time)
|
||||
aVertexPosition = gl.getAttribLocation(program, "aVertexPosition");
|
||||
uColor = gl.getUniformLocation(program, "uColor");
|
||||
uDimensions = gl.getUniformLocation(program, "uDimensions");
|
||||
uOrigin = gl.getUniformLocation(program, "uOrigin");
|
||||
gl.enableVertexAttribArray(aVertexPosition);
|
||||
|
||||
// Create a buffer to holds points which will be drawn
|
||||
this.buffer = gl.createBuffer();
|
||||
|
||||
// Use a line width of 2.0 for legibility
|
||||
gl.lineWidth(2.0);
|
||||
|
||||
// Enable blending, for smoothness
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
this.gl = gl;
|
||||
this.aVertexPosition = aVertexPosition;
|
||||
this.uColor = uColor;
|
||||
this.uDimensions = uDimensions;
|
||||
this.uOrigin = uOrigin;
|
||||
}
|
||||
|
||||
// Utility function to handle drawing of a buffer;
|
||||
// drawType will determine whether this is a box, line, etc.
|
||||
GLChart.prototype.doDraw = function (drawType, buf, color, points) {
|
||||
var gl = this.gl;
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, buf, gl.DYNAMIC_DRAW);
|
||||
gl.vertexAttribPointer(this.aVertexPosition, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.uniform4fv(this.uColor, color);
|
||||
gl.drawArrays(drawType, 0, points);
|
||||
};
|
||||
|
||||
GLChart.prototype.clear = function () {
|
||||
var gl = this.gl;
|
||||
|
||||
// Set the viewport size; note that we use the width/height
|
||||
// that our WebGL context reports, which may be lower
|
||||
// resolution than the canvas we requested.
|
||||
gl.viewport(
|
||||
0,
|
||||
0,
|
||||
gl.drawingBufferWidth,
|
||||
gl.drawingBufferHeight
|
||||
);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT + gl.DEPTH_BUFFER_BIT);
|
||||
};
|
||||
|
||||
|
||||
GLChart.prototype.setDimensions = function (dimensions, origin) {
|
||||
var gl = this.gl;
|
||||
if (dimensions && dimensions.length > 0 &&
|
||||
origin && origin.length > 0) {
|
||||
gl.uniform2fv(this.uDimensions, dimensions);
|
||||
gl.uniform2fv(this.uOrigin, origin);
|
||||
}
|
||||
};
|
||||
|
||||
GLChart.prototype.drawLine = function (buf, color, points) {
|
||||
this.doDraw(this.gl.LINE_STRIP, buf, color, points);
|
||||
};
|
||||
|
||||
GLChart.prototype.drawSquare = function (min, max, color) {
|
||||
this.doDraw(this.gl.TRIANGLE_FAN, new Float32Array(
|
||||
min.concat([min[0], max[1]]).concat(max).concat([max[0], min[1]])
|
||||
), color, 4);
|
||||
};
|
||||
|
||||
return GLChart;
|
||||
}
|
||||
);
|
||||
@@ -1,250 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* Module defining MCTChart. Created by vwoeltje on 11/12/14.
|
||||
*/
|
||||
define(
|
||||
["./GLChart", "./Canvas2DChart"],
|
||||
function (GLChart, Canvas2DChart) {
|
||||
|
||||
var TEMPLATE = "<canvas style='position: absolute; background: none; width: 100%; height: 100%;'></canvas>";
|
||||
|
||||
/**
|
||||
* The mct-chart directive provides a canvas element which can be
|
||||
* drawn upon, to support Plot view and similar visualizations.
|
||||
*
|
||||
* This directive takes one attribute, "draw", which is an Angular
|
||||
* expression which will be two-way bound to a drawing object. This
|
||||
* drawing object should contain:
|
||||
*
|
||||
* * `dimensions`: An object describing the logical bounds of the
|
||||
* drawable area, containing two fields:
|
||||
* * `origin`: The position, in logical coordinates, of the
|
||||
* lower-left corner of the chart area. A two-element array.
|
||||
* * `dimensions`: A two-element array containing the width
|
||||
* and height of the chart area, in logical coordinates.
|
||||
* * `lines`: An array of lines to be drawn, where each line is
|
||||
* expressed as an object containing:
|
||||
* * `buffer`: A Float32Array containing points in the line,
|
||||
* in logical coordinate, in sequential x/y pairs.
|
||||
* * `color`: The color of the line, as a four-element RGBA
|
||||
* array, where each element is in the range of 0.0-1.0
|
||||
* * `points`: The number of points in the line.
|
||||
* * `boxes`: An array of rectangles to draw in the chart area
|
||||
* (used for marquee zoom). Each is an object containing:
|
||||
* * `start`: The first corner of the rectangle (as a two-element
|
||||
* array, logical coordinates)
|
||||
* * `end`: The opposite corner of the rectangle (again, as a
|
||||
* two-element array)
|
||||
* * `color`: The color of the box, as a four-element RGBA
|
||||
* array, where each element is in the range of 0.0-1.0
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
*/
|
||||
function MCTChart($interval, $log) {
|
||||
// Get an underlying chart implementation
|
||||
function getChart(Charts, canvas) {
|
||||
// Try the first available option...
|
||||
var Chart = Charts[0];
|
||||
|
||||
// This function recursively try-catches all options;
|
||||
// if these all fail, issue a warning.
|
||||
if (!Chart) {
|
||||
$log.warn("Cannot initialize mct-chart.");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Try first option; if it fails, try remaining options
|
||||
try {
|
||||
return new Chart(canvas);
|
||||
} catch (e) {
|
||||
$log.warn([
|
||||
"Could not instantiate chart",
|
||||
Chart.name,
|
||||
";",
|
||||
e.message
|
||||
].join(" "));
|
||||
|
||||
return getChart(Charts.slice(1), canvas);
|
||||
}
|
||||
}
|
||||
|
||||
function linkChart(scope, element) {
|
||||
var canvas = element.find("canvas")[0],
|
||||
activeInterval,
|
||||
chart;
|
||||
|
||||
// Handle drawing, based on contents of the "draw" object
|
||||
// in scope
|
||||
function doDraw(draw) {
|
||||
// Ensure canvas context has same resolution
|
||||
// as canvas element
|
||||
canvas.width = canvas.offsetWidth;
|
||||
canvas.height = canvas.offsetHeight;
|
||||
|
||||
// Clear previous contents
|
||||
chart.clear();
|
||||
|
||||
// Nothing to draw if no draw object defined
|
||||
if (!draw) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set logical boundaries for the chart
|
||||
chart.setDimensions(
|
||||
draw.dimensions || [1, 1],
|
||||
draw.origin || [0, 0]
|
||||
);
|
||||
|
||||
// Draw line segments
|
||||
(draw.lines || []).forEach(function (line) {
|
||||
chart.drawLine(
|
||||
line.buffer,
|
||||
line.color,
|
||||
line.points
|
||||
);
|
||||
});
|
||||
|
||||
// Draw boxes (e.g. marquee zoom rect)
|
||||
(draw.boxes || []).forEach(function (box) {
|
||||
chart.drawSquare(
|
||||
box.start,
|
||||
box.end,
|
||||
box.color
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Issue a drawing call, if-and-only-if canvas size
|
||||
// has changed. This will be called on a timer, since
|
||||
// there is no event to depend on.
|
||||
function drawIfResized() {
|
||||
if (canvas.width !== canvas.offsetWidth ||
|
||||
canvas.height !== canvas.offsetHeight) {
|
||||
doDraw(scope.draw);
|
||||
scope.$apply();
|
||||
}
|
||||
}
|
||||
|
||||
// Stop watching for changes to size (scope destroyed)
|
||||
function releaseInterval() {
|
||||
if (activeInterval) {
|
||||
$interval.cancel(activeInterval);
|
||||
}
|
||||
}
|
||||
|
||||
// Switch from WebGL to plain 2D if context is lost
|
||||
function fallbackFromWebGL() {
|
||||
element.html(TEMPLATE);
|
||||
canvas = element.find("canvas")[0];
|
||||
chart = getChart([Canvas2DChart], canvas);
|
||||
if (chart) {
|
||||
doDraw(scope.draw);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to initialize a chart.
|
||||
chart = getChart([GLChart, Canvas2DChart], canvas);
|
||||
|
||||
// If that failed, there's nothing more we can do here.
|
||||
// (A warning will already have been issued)
|
||||
if (!chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
// WebGL is a bit of a special case; it may work, then fail
|
||||
// later for various reasons, so we need to listen for this
|
||||
// and fall back to plain canvas drawing when it occurs.
|
||||
canvas.addEventListener("webglcontextlost", fallbackFromWebGL);
|
||||
|
||||
// Check for resize, on a timer
|
||||
activeInterval = $interval(drawIfResized, 1000, 0, false);
|
||||
|
||||
// Watch "draw" for external changes to the set of
|
||||
// things to be drawn.
|
||||
scope.$watchCollection("draw", doDraw);
|
||||
|
||||
// Stop checking for resize when scope is destroyed
|
||||
scope.$on("$destroy", releaseInterval);
|
||||
}
|
||||
|
||||
return {
|
||||
// Apply directive only to elements
|
||||
restrict: "E",
|
||||
|
||||
// Template to use (a canvas element)
|
||||
template: TEMPLATE,
|
||||
|
||||
// Link function; set up scope
|
||||
link: linkChart,
|
||||
|
||||
// Initial, isolate scope for the directive
|
||||
scope: { draw: "=" }
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface platform/features/plot.Chart
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Clear the chart.
|
||||
* @method platform/features/plot.Chart#clear
|
||||
*/
|
||||
/**
|
||||
* Set the logical boundaries of the chart.
|
||||
* @param {number[]} dimensions the horizontal and
|
||||
* vertical dimensions of the chart
|
||||
* @param {number[]} origin the horizontal/vertical
|
||||
* origin of the chart
|
||||
* @memberof platform/features/plot.Chart#setDimensions
|
||||
*/
|
||||
/**
|
||||
* Draw the supplied buffer as a line strip (a sequence
|
||||
* of line segments), in the chosen color.
|
||||
* @param {Float32Array} buf the line strip to draw,
|
||||
* in alternating x/y positions
|
||||
* @param {number[]} color the color to use when drawing
|
||||
* the line, as an RGBA color where each element
|
||||
* is in the range of 0.0-1.0
|
||||
* @param {number} points the number of points to draw
|
||||
* @memberof platform/features/plot.Chart#drawLine
|
||||
*/
|
||||
/**
|
||||
* Draw a rectangle extending from one corner to another,
|
||||
* in the chosen color.
|
||||
* @param {number[]} min the first corner of the rectangle
|
||||
* @param {number[]} max the opposite corner
|
||||
* @param {number[]} color the color to use when drawing
|
||||
* the rectangle, as an RGBA color where each element
|
||||
* is in the range of 0.0-1.0
|
||||
* @memberof platform/features/plot.Chart#drawSquare
|
||||
*/
|
||||
|
||||
return MCTChart;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,437 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* This bundle adds a "Plot" view for numeric telemetry data.
|
||||
* @namespace platform/features/plot
|
||||
*/
|
||||
define(
|
||||
[
|
||||
"./elements/PlotUpdater",
|
||||
"./elements/PlotPalette",
|
||||
"./elements/PlotAxis",
|
||||
"./elements/PlotLimitTracker",
|
||||
"./elements/PlotTelemetryFormatter",
|
||||
"./modes/PlotModeOptions",
|
||||
"./SubPlotFactory"
|
||||
],
|
||||
function (
|
||||
PlotUpdater,
|
||||
PlotPalette,
|
||||
PlotAxis,
|
||||
PlotLimitTracker,
|
||||
PlotTelemetryFormatter,
|
||||
PlotModeOptions,
|
||||
SubPlotFactory
|
||||
) {
|
||||
|
||||
var AXIS_DEFAULTS = [
|
||||
{ "name": "Time" },
|
||||
{ "name": "Value" }
|
||||
];
|
||||
|
||||
/**
|
||||
* The PlotController is responsible for any computation/logic
|
||||
* associated with displaying the plot view. Specifically, these
|
||||
* responsibilities include:
|
||||
*
|
||||
* * Describing axes and labeling.
|
||||
* * Handling user interactions.
|
||||
* * Deciding what needs to be drawn in the chart area.
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
*/
|
||||
function PlotController(
|
||||
$scope,
|
||||
$element,
|
||||
exportImageService,
|
||||
telemetryFormatter,
|
||||
telemetryHandler,
|
||||
throttle,
|
||||
PLOT_FIXED_DURATION,
|
||||
openmct
|
||||
) {
|
||||
var self = this,
|
||||
plotTelemetryFormatter =
|
||||
new PlotTelemetryFormatter(telemetryFormatter),
|
||||
subPlotFactory =
|
||||
new SubPlotFactory(plotTelemetryFormatter),
|
||||
cachedObjects = [],
|
||||
updater,
|
||||
lastBounds,
|
||||
lastRange,
|
||||
lastDomain,
|
||||
handle;
|
||||
var timeAPI = openmct.time;
|
||||
|
||||
// Populate the scope with axis information (specifically, options
|
||||
// available for each axis.)
|
||||
function setupAxes(metadatas) {
|
||||
$scope.axes.forEach(function (axis) {
|
||||
axis.updateMetadata(metadatas);
|
||||
});
|
||||
}
|
||||
|
||||
// Trigger an update of a specific subplot;
|
||||
// used in a loop to update all subplots.
|
||||
function updateSubplot(subplot) {
|
||||
subplot.update();
|
||||
}
|
||||
|
||||
// Set up available modes (stacked/overlaid), based on the
|
||||
// set of telemetry objects in this plot view.
|
||||
function setupModes(telemetryObjects) {
|
||||
if (cachedObjects !== telemetryObjects) {
|
||||
cachedObjects = telemetryObjects;
|
||||
self.modeOptions = new PlotModeOptions(
|
||||
telemetryObjects || [],
|
||||
subPlotFactory
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Change the displayable bounds
|
||||
function setBasePanZoom(bounds) {
|
||||
var start = bounds.start,
|
||||
end = bounds.end;
|
||||
if (updater) {
|
||||
updater.setDomainBounds(start, end);
|
||||
self.update();
|
||||
}
|
||||
lastBounds = bounds;
|
||||
}
|
||||
|
||||
// Reinstantiate the plot updater (e.g. because we have a
|
||||
// new subscription.) This will clear the plot.
|
||||
function recreateUpdater() {
|
||||
var domain = $scope.axes[0].active.key,
|
||||
range = $scope.axes[1].active.key,
|
||||
duration = PLOT_FIXED_DURATION;
|
||||
|
||||
updater = new PlotUpdater(handle, domain, range, duration);
|
||||
lastDomain = domain;
|
||||
lastRange = range;
|
||||
|
||||
self.limitTracker = new PlotLimitTracker(handle, range);
|
||||
|
||||
// Keep any externally-provided bounds
|
||||
if (lastBounds) {
|
||||
setBasePanZoom(lastBounds);
|
||||
}
|
||||
}
|
||||
|
||||
function getUpdater() {
|
||||
if (!updater) {
|
||||
recreateUpdater();
|
||||
}
|
||||
return updater;
|
||||
}
|
||||
|
||||
// Handle new telemetry data in this plot
|
||||
function updateValues() {
|
||||
self.pending = false;
|
||||
if (handle) {
|
||||
setupModes(handle.getTelemetryObjects());
|
||||
setupAxes(handle.getMetadata());
|
||||
getUpdater().update();
|
||||
self.modeOptions.getModeHandler().plotTelemetry(updater);
|
||||
self.limitTracker.update();
|
||||
self.update();
|
||||
}
|
||||
}
|
||||
|
||||
// Display new historical data as it becomes available
|
||||
function addHistoricalData(domainObject, series) {
|
||||
self.pending = false;
|
||||
getUpdater().addHistorical(domainObject, series);
|
||||
self.modeOptions.getModeHandler().plotTelemetry(updater);
|
||||
self.update();
|
||||
}
|
||||
|
||||
// Issue a new request for historical telemetry
|
||||
function requestTelemetry() {
|
||||
if (handle) {
|
||||
handle.request({}, addHistoricalData);
|
||||
}
|
||||
}
|
||||
|
||||
// Requery for data entirely
|
||||
function replot() {
|
||||
if (handle) {
|
||||
updater = undefined;
|
||||
requestTelemetry();
|
||||
}
|
||||
}
|
||||
|
||||
function changeTimeOfInterest(timeOfInterest) {
|
||||
if (timeOfInterest !== undefined) {
|
||||
var bounds = timeAPI.bounds();
|
||||
var range = bounds.end - bounds.start;
|
||||
$scope.toiPerc = ((timeOfInterest - bounds.start) / range) * 100;
|
||||
$scope.toiPinned = true;
|
||||
} else {
|
||||
$scope.toiPerc = undefined;
|
||||
$scope.toiPinned = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new subscription; telemetrySubscriber gets
|
||||
// to do the meaningful work here.
|
||||
function subscribe(domainObject) {
|
||||
if (handle) {
|
||||
handle.unsubscribe();
|
||||
}
|
||||
handle = domainObject && telemetryHandler.handle(
|
||||
domainObject,
|
||||
updateValues,
|
||||
true // Lossless
|
||||
);
|
||||
replot();
|
||||
|
||||
changeTimeOfInterest(timeAPI.timeOfInterest());
|
||||
timeAPI.on("timeOfInterest", changeTimeOfInterest);
|
||||
}
|
||||
|
||||
// Release the current subscription (called when scope is destroyed)
|
||||
function releaseSubscription() {
|
||||
if (handle) {
|
||||
handle.unsubscribe();
|
||||
handle = undefined;
|
||||
}
|
||||
timeAPI.off("timeOfInterest", changeTimeOfInterest);
|
||||
}
|
||||
|
||||
function requery() {
|
||||
self.pending = true;
|
||||
releaseSubscription();
|
||||
subscribe($scope.domainObject);
|
||||
}
|
||||
|
||||
function updateDomainFormat() {
|
||||
var domainAxis = $scope.axes[0];
|
||||
plotTelemetryFormatter
|
||||
.setDomainFormat(domainAxis.active.format);
|
||||
}
|
||||
|
||||
function domainRequery(newDomain) {
|
||||
if (newDomain !== lastDomain) {
|
||||
updateDomainFormat();
|
||||
requery();
|
||||
}
|
||||
}
|
||||
|
||||
function rangeRequery(newRange) {
|
||||
if (newRange !== lastRange) {
|
||||
requery();
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to a display bounds change (requery for data)
|
||||
function changeDisplayBounds(event, bounds, follow) {
|
||||
//'hack' for follow mode
|
||||
if (follow === true) {
|
||||
setBasePanZoom(bounds);
|
||||
} else {
|
||||
var domainAxis = $scope.axes[0];
|
||||
|
||||
if (bounds.domain) {
|
||||
domainAxis.chooseOption(bounds.domain);
|
||||
}
|
||||
updateDomainFormat();
|
||||
setBasePanZoom(bounds);
|
||||
requery();
|
||||
}
|
||||
self.setUnsynchedStatus($scope.domainObject, follow && self.isZoomed());
|
||||
changeTimeOfInterest(timeAPI.timeOfInterest());
|
||||
}
|
||||
|
||||
this.modeOptions = new PlotModeOptions([], subPlotFactory);
|
||||
this.updateValues = updateValues;
|
||||
|
||||
// Create a throttled update function
|
||||
this.scheduleUpdate = throttle(function () {
|
||||
self.modeOptions.getModeHandler().getSubPlots()
|
||||
.forEach(updateSubplot);
|
||||
});
|
||||
|
||||
self.pending = true;
|
||||
self.$element = $element;
|
||||
self.exportImageService = exportImageService;
|
||||
|
||||
// Initialize axes; will get repopulated when telemetry
|
||||
// metadata becomes available.
|
||||
$scope.axes = [
|
||||
new PlotAxis("domains", [], AXIS_DEFAULTS[0]),
|
||||
new PlotAxis("ranges", [], AXIS_DEFAULTS[1])
|
||||
];
|
||||
|
||||
//Are some initialized bounds defined?
|
||||
var bounds = timeAPI.bounds();
|
||||
if (bounds &&
|
||||
bounds.start !== undefined &&
|
||||
bounds.end !== undefined) {
|
||||
changeDisplayBounds(undefined, timeAPI.bounds(), timeAPI.clock() !== undefined);
|
||||
}
|
||||
|
||||
// Watch for changes to the selected axis
|
||||
$scope.$watch("axes[0].active.key", domainRequery);
|
||||
$scope.$watch("axes[1].active.key", rangeRequery);
|
||||
|
||||
// Subscribe to telemetry when a domain object becomes available
|
||||
$scope.$watch('domainObject', subscribe);
|
||||
|
||||
// Respond to external bounds changes
|
||||
$scope.$on("telemetry:display:bounds", changeDisplayBounds);
|
||||
|
||||
// Unsubscribe when the plot is destroyed
|
||||
$scope.$on("$destroy", releaseSubscription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color (as a style-friendly string) to use
|
||||
* for plotting the trace at the specified index.
|
||||
* @param {number} index the index of the trace
|
||||
* @returns {string} the color, in #RRGGBB form
|
||||
*/
|
||||
PlotController.prototype.getColor = function (index) {
|
||||
return PlotPalette.getStringColor(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the plot is zoomed or panned out
|
||||
* of its default state (to determine whether back/unzoom
|
||||
* controls should be shown)
|
||||
* @returns {boolean} true if not in default state
|
||||
*/
|
||||
PlotController.prototype.isZoomed = function () {
|
||||
return this.modeOptions.getModeHandler().isZoomed();
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo the most recent pan/zoom change and restore
|
||||
* the prior state.
|
||||
*/
|
||||
PlotController.prototype.stepBackPanZoom = function () {
|
||||
return this.modeOptions.getModeHandler().stepBackPanZoom();
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo all pan/zoom changes and restore the initial state.
|
||||
*/
|
||||
PlotController.prototype.unzoom = function () {
|
||||
return this.modeOptions.getModeHandler().unzoom();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the mode options (Stacked/Overlaid) that are applicable
|
||||
* for this plot.
|
||||
*/
|
||||
PlotController.prototype.getModeOptions = function () {
|
||||
return this.modeOptions.getModeOptions();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current mode that is applicable to this plot. This
|
||||
* will include key, name, and cssClass fields.
|
||||
*/
|
||||
PlotController.prototype.getMode = function () {
|
||||
return this.modeOptions.getMode();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the mode which should be active in this plot.
|
||||
* @param mode one of the mode options returned from
|
||||
* getModeOptions()
|
||||
*/
|
||||
PlotController.prototype.setMode = function (mode) {
|
||||
this.modeOptions.setMode(mode);
|
||||
this.updateValues();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all individual plots contained within this Plot view.
|
||||
* (Multiple may be contained when in Stacked mode).
|
||||
* @returns {SubPlot[]} all subplots in this Plot view
|
||||
*/
|
||||
PlotController.prototype.getSubPlots = function () {
|
||||
return this.modeOptions.getModeHandler().getSubPlots();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the CSS class to apply to the legend for this domain
|
||||
* object; this will reflect limit state.
|
||||
* @returns {string} the CSS class
|
||||
*/
|
||||
PlotController.prototype.getLegendClass = function (telemetryObject) {
|
||||
return this.limitTracker &&
|
||||
this.limitTracker.getLegendClass(telemetryObject);
|
||||
};
|
||||
|
||||
/**
|
||||
* Explicitly update all plots.
|
||||
*/
|
||||
PlotController.prototype.update = function () {
|
||||
this.scheduleUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if a request is pending (to show the wait spinner)
|
||||
*/
|
||||
PlotController.prototype.isRequestPending = function () {
|
||||
// Placeholder; this should reflect request state
|
||||
// when requesting historical telemetry
|
||||
return this.pending;
|
||||
};
|
||||
|
||||
PlotController.prototype.setUnsynchedStatus = function (domainObject, status) {
|
||||
if (domainObject.hasCapability('status')) {
|
||||
domainObject.getCapability('status').set('timeconductor-unsynced', status);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Export the plot to PNG
|
||||
*/
|
||||
PlotController.prototype.exportPNG = function () {
|
||||
var self = this;
|
||||
self.hideExportButtons = true;
|
||||
self.exportImageService.exportPNG(self.$element[0], "plot.png", '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;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['./PlotOptionsForm'],
|
||||
function (PlotOptionsForm) {
|
||||
|
||||
/**
|
||||
* Notes on implementation of plot options
|
||||
*
|
||||
* Multiple y-axes will have to be handled with multiple forms as
|
||||
* they will need to be stored on distinct model object
|
||||
*
|
||||
* Likewise plot series options per-child will need to be separate
|
||||
* forms.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The LayoutController is responsible for supporting the
|
||||
* Layout view. It arranges frames according to saved configuration
|
||||
* and provides methods for updating these based on mouse
|
||||
* movement.
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {Scope} $scope the controller's Angular scope
|
||||
*/
|
||||
function PlotOptionsController($scope) {
|
||||
|
||||
var self = this;
|
||||
this.$scope = $scope;
|
||||
this.domainObject = $scope.domainObject;
|
||||
this.configuration = this.domainObject.getModel().configuration || {};
|
||||
this.plotOptionsForm = new PlotOptionsForm();
|
||||
this.composition = [];
|
||||
this.watches = [];
|
||||
|
||||
/*
|
||||
Listen for changes to the domain object and update the object's
|
||||
children.
|
||||
*/
|
||||
this.mutationListener = this.domainObject.getCapability('mutation').listen(function (model) {
|
||||
if (self.hasCompositionChanged(self.composition, model.composition)) {
|
||||
self.updateChildren();
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Set form structures on scope
|
||||
*/
|
||||
$scope.plotSeriesForm = this.plotOptionsForm.plotSeriesForm;
|
||||
$scope.xAxisForm = this.plotOptionsForm.xAxisForm;
|
||||
$scope.yAxisForm = this.plotOptionsForm.yAxisForm;
|
||||
|
||||
$scope.$on("$destroy", function () {
|
||||
//Clean up any listeners on destruction of controller
|
||||
self.mutationListener();
|
||||
});
|
||||
|
||||
this.defaultConfiguration();
|
||||
this.updateChildren();
|
||||
|
||||
/*
|
||||
* Setup a number of watches for changes to form values. On
|
||||
* change, update the model configuration via mutation
|
||||
*/
|
||||
$scope.$watchCollection('configuration.plot.yAxis', function (newValue, oldValue) {
|
||||
self.updateConfiguration(newValue, oldValue);
|
||||
});
|
||||
$scope.$watchCollection('configuration.plot.xAxis', function (newValue, oldValue) {
|
||||
self.updateConfiguration(newValue, oldValue);
|
||||
});
|
||||
|
||||
this.watchSeries();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all watches for series data (ie. the configuration for
|
||||
* child objects)
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.clearSeriesWatches = function () {
|
||||
this.watches.forEach(function (watch) {
|
||||
watch();
|
||||
});
|
||||
this.watches = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Attach watches for each object in the plot's composition
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.watchSeries = function () {
|
||||
var self = this;
|
||||
|
||||
this.clearSeriesWatches();
|
||||
|
||||
(self.$scope.children || []).forEach(function (child, index) {
|
||||
self.watches.push(
|
||||
self.$scope.$watchCollection(
|
||||
'configuration.plot.series[' + index + ']',
|
||||
function (newValue, oldValue) {
|
||||
self.updateConfiguration(newValue, oldValue);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine whether the changes to the model that triggered a
|
||||
* mutation event were purely compositional.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.hasCompositionChanged = function (oldComposition, newComposition) {
|
||||
// Framed slightly strangely, but the boolean logic is
|
||||
// easier to follow for the unchanged case.
|
||||
var isUnchanged = oldComposition === newComposition ||
|
||||
(
|
||||
oldComposition.length === newComposition.length &&
|
||||
oldComposition.every(function (currentValue, index) {
|
||||
return newComposition[index] && currentValue === newComposition[index];
|
||||
})
|
||||
);
|
||||
return !isUnchanged;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default the plot options model
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.defaultConfiguration = function () {
|
||||
this.configuration.plot = this.configuration.plot || {};
|
||||
this.configuration.plot.xAxis = this.configuration.plot.xAxis || {};
|
||||
this.configuration.plot.yAxis = this.configuration.plot.yAxis || {}; // y-axes will be associative array keyed on axis key
|
||||
this.configuration.plot.series = this.configuration.plot.series || []; // series will be associative array keyed on sub-object id
|
||||
this.$scope.configuration = this.configuration;
|
||||
};
|
||||
|
||||
/**
|
||||
* When a child is added to, or removed from a plot, update the
|
||||
* plot options model
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.updateChildren = function () {
|
||||
var self = this;
|
||||
this.domainObject.useCapability('composition').then(function (children) {
|
||||
self.$scope.children = children;
|
||||
self.composition = self.domainObject.getModel().composition;
|
||||
children.forEach(function (child, index) {
|
||||
self.configuration.plot.series[index] =
|
||||
self.configuration.plot.series[index] || {'id': child.getId()};
|
||||
});
|
||||
self.watchSeries();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* On changes to the form, update the configuration on the domain
|
||||
* object
|
||||
* @private
|
||||
*/
|
||||
PlotOptionsController.prototype.updateConfiguration = function () {
|
||||
var self = this;
|
||||
this.domainObject.useCapability('mutation', function (model) {
|
||||
model.configuration = model.configuration || {};
|
||||
model.configuration.plot = self.configuration.plot;
|
||||
});
|
||||
};
|
||||
|
||||
return PlotOptionsController;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* A class for encapsulating structure and behaviour of the plot
|
||||
* options form
|
||||
* @memberOf platform/features/plot
|
||||
* @param topic
|
||||
* @constructor
|
||||
*/
|
||||
function PlotOptionsForm() {
|
||||
|
||||
/*
|
||||
Defined below are the form structures for the plot options.
|
||||
*/
|
||||
this.xAxisForm = {
|
||||
'name': 'x-axis',
|
||||
'sections': [{
|
||||
'name': 'x-axis',
|
||||
'rows': [
|
||||
{
|
||||
'name': 'Domain',
|
||||
'control': 'select',
|
||||
'key': 'key',
|
||||
'options': [
|
||||
{'name': 'SCET', 'value': 'scet'},
|
||||
{'name': 'SCLK', 'value': 'sclk'},
|
||||
{'name': 'LST', 'value': 'lst'}
|
||||
]
|
||||
}
|
||||
]
|
||||
}]};
|
||||
|
||||
this.yAxisForm = {
|
||||
'name': 'y-axis',
|
||||
'sections': [{
|
||||
// Will need to be repeated for each y-axis, with a
|
||||
// distinct name for each. Ideally the name of the axis
|
||||
// itself.
|
||||
'name': 'y-axis',
|
||||
'rows': [
|
||||
{
|
||||
'name': 'Range',
|
||||
'control': 'select',
|
||||
'key': 'key',
|
||||
'options': [
|
||||
{'name': 'EU', 'value': 'eu'},
|
||||
{'name': 'DN', 'value': 'dn'},
|
||||
{'name': 'Status', 'value': 'status'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'name': 'Autoscale',
|
||||
'control': 'checkbox',
|
||||
'key': 'autoscale'
|
||||
},
|
||||
{
|
||||
'name': 'Min',
|
||||
'control': 'textfield',
|
||||
'key': 'min',
|
||||
'pattern': '[0-9]',
|
||||
'inputsize' : 'sm'
|
||||
},
|
||||
{
|
||||
'name': 'Max',
|
||||
'control': 'textfield',
|
||||
'key': 'max',
|
||||
'pattern': '[0-9]',
|
||||
'inputsize' : 'sm'
|
||||
}
|
||||
]
|
||||
}]
|
||||
};
|
||||
this.plotSeriesForm = {
|
||||
'name': 'Series Options',
|
||||
'sections': [
|
||||
{
|
||||
rows: [
|
||||
{
|
||||
'name': 'Color',
|
||||
'control': 'color',
|
||||
'key': 'color'
|
||||
}]
|
||||
},
|
||||
{
|
||||
'rows': [
|
||||
{
|
||||
'name': 'Markers',
|
||||
'control': 'checkbox',
|
||||
'key': 'markers',
|
||||
'layout': 'control-first'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'rows': [
|
||||
{
|
||||
'name': 'No Line',
|
||||
'control': 'radio',
|
||||
'key': 'lineType',
|
||||
'value': 'noLine',
|
||||
'layout': 'control-first'
|
||||
},
|
||||
{
|
||||
'name': 'Step Line',
|
||||
'control': 'radio',
|
||||
'key': 'lineType',
|
||||
'value': 'stepLine',
|
||||
'layout': 'control-first'
|
||||
},
|
||||
{
|
||||
'name': 'Linear Line',
|
||||
'control': 'radio',
|
||||
'key': 'lineType',
|
||||
'value': 'linearLine',
|
||||
'layout': 'control-first'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
return PlotOptionsForm;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,415 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[
|
||||
'./elements/PlotPosition',
|
||||
'./elements/PlotTickGenerator'
|
||||
],
|
||||
function (PlotPosition, PlotTickGenerator) {
|
||||
|
||||
var DOMAIN_TICKS = 5,
|
||||
RANGE_TICKS = 7;
|
||||
|
||||
/**
|
||||
* A SubPlot is an individual plot within a Plot View (which
|
||||
* may contain multiple plots, specifically when in Stacked
|
||||
* plot mode.)
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {DomainObject[]} telemetryObjects the domain objects
|
||||
* which will be plotted in this sub-plot
|
||||
* @param {PlotPanZoomStack} panZoomStack the stack of pan-zoom
|
||||
* states which is applicable to this sub-plot
|
||||
* @param {TelemetryFormatter} telemetryFormatter the telemetry
|
||||
* formatting service; used to convert domain/range values
|
||||
* from telemetry data sets to a human-readable form.
|
||||
*/
|
||||
function SubPlot(telemetryObjects, panZoomStack, telemetryFormatter) {
|
||||
// We are used from a template often, so maintain
|
||||
// state in local variables to allow for fast look-up,
|
||||
// as is normal for controllers.
|
||||
this.telemetryObjects = telemetryObjects;
|
||||
this.domainTicks = [];
|
||||
this.rangeTicks = [];
|
||||
this.formatter = telemetryFormatter;
|
||||
this.draw = {};
|
||||
this.hovering = false;
|
||||
this.panZoomStack = panZoomStack;
|
||||
|
||||
// Start with the right initial drawing bounds,
|
||||
// tick marks
|
||||
this.updateDrawingBounds();
|
||||
this.updateTicks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether this subplot has domain data to show for the current pan/zoom level. Absence of domain data
|
||||
* implies that there is no range data displayed either
|
||||
* @returns {boolean} true if domain data exists for the current pan/zoom level
|
||||
*/
|
||||
SubPlot.prototype.hasDomainData = function () {
|
||||
return this.panZoomStack &&
|
||||
this.panZoomStack.getDimensions()[0] > 0;
|
||||
};
|
||||
|
||||
// Utility function for filtering out empty strings.
|
||||
function isNonEmpty(v) {
|
||||
return typeof v === 'string' && v !== "";
|
||||
}
|
||||
|
||||
// Converts from pixel coordinates to domain-range,
|
||||
// to interpret mouse gestures.
|
||||
SubPlot.prototype.mousePositionToDomainRange = function (mousePosition) {
|
||||
return new PlotPosition(
|
||||
mousePosition.x,
|
||||
mousePosition.y,
|
||||
mousePosition.width,
|
||||
mousePosition.height,
|
||||
this.panZoomStack
|
||||
).getPosition();
|
||||
};
|
||||
|
||||
// Utility function to get the mouse position (in x,y
|
||||
// pixel coordinates in the canvas area) from a mouse
|
||||
// event object.
|
||||
SubPlot.prototype.toMousePosition = function ($event) {
|
||||
var bounds = this.subPlotBounds;
|
||||
|
||||
return {
|
||||
x: $event.clientX - bounds.left,
|
||||
y: $event.clientY - bounds.top,
|
||||
width: bounds.width,
|
||||
height: bounds.height
|
||||
};
|
||||
};
|
||||
|
||||
// Convert a domain-range position to a displayable
|
||||
// position. This will subtract the domain offset, which
|
||||
// is used to bias domain values to minimize loss-of-precision
|
||||
// associated with conversion to a 32-bit floating point
|
||||
// format (which is needed in the chart area itself, by WebGL.)
|
||||
SubPlot.prototype.toDisplayable = function (position) {
|
||||
return [position[0] - this.domainOffset, position[1]];
|
||||
};
|
||||
|
||||
// Update the current hover coordinates
|
||||
SubPlot.prototype.updateHoverCoordinates = function () {
|
||||
var formatter = this.formatter;
|
||||
|
||||
// Utility, for map/forEach loops. Index 0 is domain,
|
||||
// index 1 is range.
|
||||
function formatValue(v, i) {
|
||||
return i ?
|
||||
formatter.formatRangeValue(v) :
|
||||
formatter.formatDomainValue(v);
|
||||
}
|
||||
|
||||
this.hoverCoordinates = this.mousePosition &&
|
||||
this.mousePositionToDomainRange(this.mousePosition)
|
||||
.map(formatValue)
|
||||
.filter(isNonEmpty)
|
||||
.join(", ");
|
||||
};
|
||||
|
||||
// Update the drawable marquee area to reflect current
|
||||
// mouse position (or don't show it at all, if no marquee
|
||||
// zoom is in progress)
|
||||
SubPlot.prototype.updateMarqueeBox = function () {
|
||||
// Express this as a box in the draw object, which
|
||||
// is passed to an mct-chart in the template for rendering.
|
||||
this.draw.boxes = this.marqueeStart ?
|
||||
[{
|
||||
start: this.toDisplayable(
|
||||
this.mousePositionToDomainRange(this.marqueeStart)
|
||||
),
|
||||
end: this.toDisplayable(
|
||||
this.mousePositionToDomainRange(this.mousePosition)
|
||||
),
|
||||
color: [1, 1, 1, 0.5]
|
||||
}] : undefined;
|
||||
};
|
||||
|
||||
// Update the bounds (origin and dimensions) of the drawing area.
|
||||
SubPlot.prototype.updateDrawingBounds = function () {
|
||||
var panZoom = this.panZoomStack.getPanZoom();
|
||||
|
||||
// Communicate pan-zoom state from stack to the draw object
|
||||
// which is passed to mct-chart in the template.
|
||||
this.draw.dimensions = panZoom.dimensions;
|
||||
this.draw.origin = [
|
||||
panZoom.origin[0] - this.domainOffset,
|
||||
panZoom.origin[1]
|
||||
];
|
||||
};
|
||||
|
||||
// Update tick marks in scope.
|
||||
SubPlot.prototype.updateTicks = function () {
|
||||
var tickGenerator =
|
||||
new PlotTickGenerator(this.panZoomStack, this.formatter);
|
||||
|
||||
this.domainTicks =
|
||||
tickGenerator.generateDomainTicks(DOMAIN_TICKS);
|
||||
this.rangeTicks =
|
||||
tickGenerator.generateRangeTicks(RANGE_TICKS);
|
||||
};
|
||||
|
||||
SubPlot.prototype.updatePan = function () {
|
||||
var start, current, delta, nextOrigin;
|
||||
|
||||
// Clear the previous panning pan-zoom state
|
||||
this.panZoomStack.popPanZoom();
|
||||
|
||||
// Calculate what the new resulting pan-zoom should be
|
||||
start = this.mousePositionToDomainRange(
|
||||
this.panStart,
|
||||
this.panZoomStack
|
||||
);
|
||||
current = this.mousePositionToDomainRange(
|
||||
this.mousePosition,
|
||||
this.panZoomStack
|
||||
);
|
||||
|
||||
delta = [current[0] - start[0], current[1] - start[1]];
|
||||
nextOrigin = [
|
||||
this.panStartBounds.origin[0] - delta[0],
|
||||
this.panStartBounds.origin[1] - delta[1]
|
||||
];
|
||||
|
||||
// ...and push a new one at the current mouse position
|
||||
this.panZoomStack
|
||||
.pushPanZoom(nextOrigin, this.panStartBounds.dimensions);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the set of domain objects which are being
|
||||
* represented in this sub-plot.
|
||||
* @returns {DomainObject[]} the domain objects which
|
||||
* will have data plotted in this sub-plot
|
||||
*/
|
||||
SubPlot.prototype.getTelemetryObjects = function () {
|
||||
return this.telemetryObjects;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get ticks mark information appropriate for using in the
|
||||
* template for this sub-plot's domain axis, as prepared
|
||||
* by the PlotTickGenerator.
|
||||
* @returns {Array} tick marks for the domain axis
|
||||
*/
|
||||
SubPlot.prototype.getDomainTicks = function () {
|
||||
return this.domainTicks;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get ticks mark information appropriate for using in the
|
||||
* template for this sub-plot's range axis, as prepared
|
||||
* by the PlotTickGenerator.
|
||||
* @returns {Array} tick marks for the range axis
|
||||
*/
|
||||
SubPlot.prototype.getRangeTicks = function () {
|
||||
return this.rangeTicks;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the drawing object associated with this sub-plot;
|
||||
* this object will be passed to the mct-chart in which
|
||||
* this sub-plot's lines will be plotted, as its "draw"
|
||||
* attribute, and should have the same internal format
|
||||
* expected by that directive.
|
||||
* @return {object} the drawing object
|
||||
*/
|
||||
SubPlot.prototype.getDrawingObject = function () {
|
||||
return this.draw;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the coordinates (as displayable text) for the
|
||||
* current mouse position.
|
||||
* @returns {string[]} the displayable domain and range
|
||||
* coordinates over which the mouse is hovered
|
||||
*/
|
||||
SubPlot.prototype.getHoverCoordinates = function () {
|
||||
return this.hoverCoordinates;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle mouse movement over the chart area.
|
||||
* @param $event the mouse event
|
||||
* @memberof platform/features/plot.SubPlot#
|
||||
*/
|
||||
SubPlot.prototype.hover = function ($event) {
|
||||
this.hovering = true;
|
||||
this.subPlotBounds = $event.target.getBoundingClientRect();
|
||||
this.mousePosition = this.toMousePosition($event);
|
||||
//If there is a domain to display, show hover coordinates, otherwise hover coordinates are meaningless
|
||||
if (this.hasDomainData()) {
|
||||
this.updateHoverCoordinates();
|
||||
}
|
||||
if (this.marqueeStart) {
|
||||
this.updateMarqueeBox();
|
||||
}
|
||||
if (this.panStart) {
|
||||
this.updatePan();
|
||||
this.updateDrawingBounds();
|
||||
this.updateTicks();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Continue a previously-start pan or zoom gesture.
|
||||
* @param $event the mouse event
|
||||
* @memberof platform/features/plot.SubPlot#
|
||||
*/
|
||||
SubPlot.prototype.continueDrag = function ($event) {
|
||||
this.mousePosition = this.toMousePosition($event);
|
||||
if (this.marqueeStart) {
|
||||
this.updateMarqueeBox();
|
||||
}
|
||||
if (this.panStart) {
|
||||
this.updatePan();
|
||||
this.updateDrawingBounds();
|
||||
this.updateTicks();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initiate a marquee zoom action.
|
||||
* @param $event the mouse event
|
||||
*/
|
||||
SubPlot.prototype.startDrag = function ($event) {
|
||||
this.subPlotBounds = $event.target.getBoundingClientRect();
|
||||
this.mousePosition = this.toMousePosition($event);
|
||||
// Treat any modifier key as a pan
|
||||
if ($event.altKey || $event.shiftKey || $event.ctrlKey) {
|
||||
// Start panning
|
||||
this.panStart = this.mousePosition;
|
||||
this.panStartBounds = this.panZoomStack.getPanZoom();
|
||||
// We're starting a pan, so add this back as a
|
||||
// state on the stack; it will get replaced
|
||||
// during the pan.
|
||||
this.panZoomStack.pushPanZoom(
|
||||
this.panStartBounds.origin,
|
||||
this.panStartBounds.dimensions
|
||||
);
|
||||
$event.preventDefault();
|
||||
} else {
|
||||
// Start marquee zooming
|
||||
this.marqueeStart = this.mousePosition;
|
||||
this.updateMarqueeBox();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Complete a marquee zoom action.
|
||||
* @param $event the mouse event
|
||||
*/
|
||||
SubPlot.prototype.endDrag = function ($event) {
|
||||
var self = this;
|
||||
|
||||
// Perform a marquee zoom.
|
||||
function marqueeZoom(start, end) {
|
||||
// Determine what boundary is described by the marquee,
|
||||
// in domain-range values. Use the minima for origin, so that
|
||||
// it doesn't matter what direction the user marqueed in.
|
||||
var a = self.mousePositionToDomainRange(start),
|
||||
b = self.mousePositionToDomainRange(end),
|
||||
origin = [
|
||||
Math.min(a[0], b[0]),
|
||||
Math.min(a[1], b[1])
|
||||
],
|
||||
dimensions = [
|
||||
Math.max(a[0], b[0]) - origin[0],
|
||||
Math.max(a[1], b[1]) - origin[1]
|
||||
];
|
||||
|
||||
// Proceed with zoom if zoom dimensions are non zeros
|
||||
if (!(dimensions[0] === 0 && dimensions[1] === 0)) {
|
||||
// Push the new state onto the pan-zoom stack
|
||||
self.panZoomStack.pushPanZoom(origin, dimensions);
|
||||
|
||||
// Make sure tick marks reflect new bounds
|
||||
self.updateTicks();
|
||||
}
|
||||
}
|
||||
|
||||
this.mousePosition = this.toMousePosition($event);
|
||||
this.subPlotBounds = undefined;
|
||||
if (this.marqueeStart) {
|
||||
marqueeZoom(this.marqueeStart, this.mousePosition);
|
||||
this.marqueeStart = undefined;
|
||||
this.updateMarqueeBox();
|
||||
this.updateDrawingBounds();
|
||||
this.updateTicks();
|
||||
}
|
||||
if (this.panStart) {
|
||||
// End panning
|
||||
this.panStart = undefined;
|
||||
this.panStartBounds = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the drawing bounds, marquee box, and
|
||||
* tick marks for this subplot.
|
||||
*/
|
||||
SubPlot.prototype.update = function () {
|
||||
this.updateDrawingBounds();
|
||||
this.updateMarqueeBox();
|
||||
this.updateTicks();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the domain offset associated with this sub-plot.
|
||||
* A domain offset is subtracted from all domain
|
||||
* before lines are drawn to avoid artifacts associated
|
||||
* with the use of 32-bit floats when domain values
|
||||
* are often timestamps (due to insufficient precision.)
|
||||
* A SubPlot will be drawing boxes (for marquee zoom) in
|
||||
* the same offset coordinate space, so it needs to know
|
||||
* the value of this to position that marquee box
|
||||
* correctly.
|
||||
* @param {number} value the domain offset
|
||||
*/
|
||||
SubPlot.prototype.setDomainOffset = function (value) {
|
||||
this.domainOffset = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* When used with no argument, check whether or not the user
|
||||
* is currently hovering over this subplot. When used with
|
||||
* an argument, set that state.
|
||||
* @param {boolean} [state] the new hovering state
|
||||
* @returns {boolean} the hovering state
|
||||
*/
|
||||
SubPlot.prototype.isHovering = function (state) {
|
||||
if (state !== undefined) {
|
||||
this.hovering = state;
|
||||
}
|
||||
return this.hovering;
|
||||
};
|
||||
|
||||
return SubPlot;
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* A PlotAxis provides a template-ready set of options
|
||||
* for the domain or range axis, sufficient to populate
|
||||
* selectors.
|
||||
*
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {string} axisType the field in metadatas to
|
||||
* look at for axis options; usually one of
|
||||
* "domains" or "ranges"
|
||||
* @param {object[]} metadatas metadata objects, as
|
||||
* returned by the `getMetadata()` method of
|
||||
* a `telemetry` capability.
|
||||
* @param {object} defaultValue the value to use for the
|
||||
* active state in the event that no options are
|
||||
* found; should contain "name" and "key" at
|
||||
* minimum.
|
||||
*
|
||||
*/
|
||||
function PlotAxis(axisType, metadatas, defaultValue) {
|
||||
this.axisType = axisType;
|
||||
this.defaultValue = defaultValue;
|
||||
this.optionKeys = {};
|
||||
|
||||
/**
|
||||
* The currently chosen option for this axis. An
|
||||
* initial value is provided; this will be updated
|
||||
* directly form the plot template.
|
||||
* @memberof platform/features/plot.PlotAxis#
|
||||
*/
|
||||
this.active = defaultValue;
|
||||
|
||||
/**
|
||||
* The set of options applicable for this axis;
|
||||
* an array of objects, where each object contains a
|
||||
* "key" field and a "name" field (for machine- and
|
||||
* human-readable names respectively)
|
||||
* @memberof platform/features/plot.PlotAxis#
|
||||
*/
|
||||
this.options = [];
|
||||
|
||||
// Initialize options from metadata objects
|
||||
this.updateMetadata(metadatas);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update axis options to reflect current metadata.
|
||||
* @param {TelemetryMetadata[]} metadata objects describing
|
||||
* applicable telemetry
|
||||
*/
|
||||
PlotAxis.prototype.updateMetadata = function (metadatas) {
|
||||
var axisType = this.axisType,
|
||||
optionKeys = this.optionKeys,
|
||||
newOptions = {},
|
||||
toAdd = [];
|
||||
|
||||
function isValid(option) {
|
||||
return option && optionKeys[option.key];
|
||||
}
|
||||
|
||||
metadatas.forEach(function (m) {
|
||||
(m[axisType] || []).forEach(function (option) {
|
||||
var key = option.key;
|
||||
if (!optionKeys[key] && !newOptions[key]) {
|
||||
toAdd.push(option);
|
||||
}
|
||||
newOptions[key] = true;
|
||||
});
|
||||
});
|
||||
|
||||
optionKeys = this.optionKeys = newOptions;
|
||||
|
||||
// General approach here is to avoid changing object
|
||||
// instances unless something has really changed, since
|
||||
// Angular is watching; don't want to trigger extra digests.
|
||||
if (!this.options.every(isValid)) {
|
||||
this.options = this.options.filter(isValid);
|
||||
}
|
||||
|
||||
if (toAdd.length > 0) {
|
||||
this.options = this.options.concat(toAdd);
|
||||
}
|
||||
|
||||
if (!isValid(this.active)) {
|
||||
this.active = this.options[0] || this.defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Change the domain/range selection for this axis. If the
|
||||
* provided `key` is not recognized as an option, no change
|
||||
* will occur.
|
||||
* @param {string} key the identifier for the domain/range
|
||||
*/
|
||||
PlotAxis.prototype.chooseOption = function (key) {
|
||||
var self = this;
|
||||
this.options.forEach(function (option) {
|
||||
if (option.key === key) {
|
||||
self.active = option;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return PlotAxis;
|
||||
|
||||
}
|
||||
);
|
||||
@@ -1,78 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Tracks the limit state of telemetry objects being plotted.
|
||||
* @memberof platform/features/plot
|
||||
* @constructor
|
||||
* @param {platform/telemetry.TelemetryHandle} handle the handle
|
||||
* to telemetry access
|
||||
* @param {string} range the key to use when looking up range values
|
||||
*/
|
||||
function PlotLimitTracker(handle, range) {
|
||||
this.handle = handle;
|
||||
this.range = range;
|
||||
this.legendClasses = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update limit states to reflect the latest data.
|
||||
*/
|
||||
PlotLimitTracker.prototype.update = function () {
|
||||
var legendClasses = {},
|
||||
range = this.range,
|
||||
handle = this.handle;
|
||||
|
||||
function updateLimit(telemetryObject) {
|
||||
var limit = telemetryObject.getCapability('limit'),
|
||||
datum = handle.getDatum(telemetryObject);
|
||||
|
||||
if (limit && datum) {
|
||||
legendClasses[telemetryObject.getId()] =
|
||||
(limit.evaluate(datum, range) || {}).cssClass;
|
||||
}
|
||||
}
|
||||
|
||||
handle.getTelemetryObjects().forEach(updateLimit);
|
||||
|
||||
this.legendClasses = legendClasses;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the CSS class associated with any limit violations for this
|
||||
* telemetry object.
|
||||
* @param {DomainObject} domainObject the telemetry object to check
|
||||
* @returns {string} the CSS class name, if any
|
||||
*/
|
||||
PlotLimitTracker.prototype.getLegendClass = function (domainObject) {
|
||||
var id = domainObject && domainObject.getId();
|
||||
return id && this.legendClasses[id];
|
||||
};
|
||||
|
||||
return PlotLimitTracker;
|
||||
|
||||
}
|
||||
);
|
||||
@@ -1,118 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
['./PlotSeriesWindow'],
|
||||
function (PlotSeriesWindow) {
|
||||
|
||||
|
||||
/**
|
||||
* Represents a single line or trace of a plot.
|
||||
* @param {{PlotLineBuffer}} buffer the plot buffer
|
||||
* @constructor
|
||||
*/
|
||||
function PlotLine(buffer) {
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a point to this plot line.
|
||||
* @param {number} domainValue the domain value
|
||||
* @param {number} rangeValue the range value
|
||||
*/
|
||||
PlotLine.prototype.addPoint = function (domainValue, rangeValue) {
|
||||
var buffer = this.buffer,
|
||||
index;
|
||||
|
||||
// Make sure we got real/useful values here...
|
||||
if (domainValue !== undefined && rangeValue !== undefined) {
|
||||
index = buffer.findInsertionIndex(domainValue);
|
||||
|
||||
// Already in the buffer? Skip insertion
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert the point
|
||||
if (!buffer.insertPoint(domainValue, rangeValue, index)) {
|
||||
// If insertion failed, trim from the beginning...
|
||||
buffer.trim(1);
|
||||
// ...and try again.
|
||||
buffer.insertPoint(domainValue, rangeValue, index);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a series of telemetry data to this plot line.
|
||||
* @param {TelemetrySeries} series the data series
|
||||
* @param {string} [domain] the key indicating which domain
|
||||
* to use when looking up data from this series
|
||||
* @param {string} [range] the key indicating which range
|
||||
* to use when looking up data from this series
|
||||
*/
|
||||
PlotLine.prototype.addSeries = function (series, domain, range) {
|
||||
var buffer = this.buffer;
|
||||
|
||||
// Insert a time-windowed data series into the buffer
|
||||
function insertSeriesWindow(seriesWindow) {
|
||||
var count = seriesWindow.getPointCount();
|
||||
|
||||
function doInsert() {
|
||||
var firstTimestamp = seriesWindow.getDomainValue(0),
|
||||
lastTimestamp = seriesWindow.getDomainValue(count - 1),
|
||||
startIndex = buffer.findInsertionIndex(firstTimestamp),
|
||||
endIndex = buffer.findInsertionIndex(lastTimestamp);
|
||||
|
||||
// Does the whole series fit in between two adjacent indexes?
|
||||
if ((startIndex === endIndex) && startIndex > -1) {
|
||||
// Insert it in between
|
||||
buffer.insert(seriesWindow, startIndex);
|
||||
} else {
|
||||
// Split it up, and add the two halves
|
||||
seriesWindow.split().forEach(insertSeriesWindow);
|
||||
}
|
||||
}
|
||||
|
||||
// Only insert if there are points to insert
|
||||
if (count > 0) {
|
||||
doInsert();
|
||||
}
|
||||
}
|
||||
|
||||
// Should try to add via insertion if a
|
||||
// clear insertion point is available;
|
||||
// if not, should split and add each half.
|
||||
// Insertion operation also needs to factor out
|
||||
// redundant timestamps, for overlapping data
|
||||
insertSeriesWindow(new PlotSeriesWindow(
|
||||
series,
|
||||
domain,
|
||||
range,
|
||||
0,
|
||||
series.getPointCount()
|
||||
));
|
||||
};
|
||||
|
||||
return PlotLine;
|
||||
}
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user