Compare commits

..

28 Commits

Author SHA1 Message Date
Henry
4d279481a5 Merged notebook branch based on painterro fix 2018-04-09 12:40:17 -07:00
Henry
674f608188 Limit Web GL instances to 14 to prevent loss of contexts in plots 2018-04-09 12:38:41 -07:00
Henry
efd34e54db Merged summary-widget-telemetry-provider 2018-04-09 12:38:41 -07:00
Pete Richards
fa7298a752 [Plot] Wait for width before loading (#1975)
Plot waits for element to have width before loading.  Otherwise,
it may make a minmax request with an invalid size parameter.

Fixes https://github.com/nasa/openmct/issues/1974
2018-04-03 15:03:35 -07:00
Pete Richards
1a23f2b390 Allow property values of zero. (#1961)
Allow SWG properties of zero to be used instead of overwriting
them with defaults.
2018-03-27 10:02:15 -07:00
Harry
4df6d6141b [Testing] Create unit tests for Types API (#1890)
* [Testing] Create unit tests for Tests API

* [Documentation] Fix spelling in TypeRegistry.js

* update TypeRegistrySpec to conform to code review
use .get() on typeRegistryInstance

Fixes #1514
2018-03-26 13:28:56 -07:00
Pete Richards
1c97138607 Pin d3 major/minor versoins (#1963)
Pin to specific d3 major/minor versions while allowing new
patches.  Previously, we were pinned to major versions only, and
minor versions contained changes to the distributed files which
would result in broken builds.

This should hopefully prevent broken builds from dependency updates.
2018-03-26 11:30:28 -07:00
Pete Richards
8db75bf41e [API] Support dynamic telemetry metadata (#1941)
* [API] Support dynamic telemetry metadata

Add support for dynamic telemetry metadata via custom telemetry
metadata providers.  

The metadata provider API should be considered unstable in it's
current invocation.

* Perform deprecation checking at runtime
* SWG uses telemetry metadata provider
* Don't throw with no matched metadata provider
* Update API docs
* Add license header
* Combine metadata providers with general telemetry providers
* Replace `TelemetryAPI.canProvideTelemetry` with
`TelemetryAPI.isTelemetryObject`.  
* CanProvideTelemetry is deprecated
* Change SWG inputs to numberfield
2018-03-21 14:18:08 -07:00
Pete Richards
00fb071fe2 Lock filesaver version (#1956)
Lock filesaver version as there have been a large number of broken
builds from what should be non-breaking version increases.

Fixes currently broken build.
2018-03-19 14:52:47 -07:00
Victor Woeltjen
2bf6a48d49 Merge pull request #1953 from nasa/eagle-sprint-release
Eagle sprint release
2018-03-16 10:38:49 -07:00
Henry
eca3c57bd8 Updated version number for Enterprise release 2018-03-16 10:26:01 -07:00
Henry
7753703034 Removed snapshot from version number to close sprint eagle 2018-03-16 10:15:29 -07:00
Pete Richards
f9060a485d [Plugin] Add imported root plugin (#1784)
* [Plugin] Add static root plugin

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

* Include all src files
2018-03-09 12:39:25 -08:00
Deep Tailor
39a7f43cd0 Fixes Issue #1938 - Fixed Positon should display enumerated value (#1939)
* Fixes #1938
Fixed usage of Telemetry API, use hints to get valueMetadata, use valueMetadata to get formatter

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

* fix broken tests

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

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

Fixes https://github.com/nasa/openmct/issues/1927
2018-02-28 12:04:14 -08:00
Victor Woeltjen
5288dadafb Merge pull request #1926 from nasa/discovery-eagle
Version bump - 0.12.0 -> 0.13.1
2018-02-26 17:45:03 -08:00
Henry
7f3cc09cbc Updated version number for start of release Eagle 2018-02-23 13:42:19 -08:00
Henry
94fa70abb1 Updated version number to close sprint 'Discovery One' 2018-02-23 13:32:31 -08:00
Pete Richards
12574a1333 Tests for Composition API providers 2018-02-20 09:40:57 -08:00
Tobias Brown
8c72729a2a [DateTimePicker] Replaced tabs with spaces
Addresses #1872
2018-01-17 09:33:28 +11:00
Tobias Brown
129ab1791b [DateTimePicker] Re-added .s-menu styles removed in bc7d92ee0d
Addresses #1872
2018-01-15 12:43:16 +11:00
123 changed files with 6585 additions and 802 deletions

168
API.md
View File

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

View File

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

View File

@@ -0,0 +1,108 @@
define([
'lodash'
], function (
_
) {
var METADATA_BY_TYPE = {
'generator': {
values: [
{
key: "name",
name: "Name"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "yesterday",
name: "Yesterday",
format: "utc",
hints: {
domain: 2
}
},
{
key: "sin",
name: "Sine",
hints: {
range: 1
}
},
{
key: "cos",
name: "Cosine",
hints: {
range: 2
}
}
]
},
'example.state-generator': {
values: [
{
key: "name",
name: "Name"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "state",
source: "value",
name: "State",
format: "enum",
enumerations: [
{
value: 0,
string: "OFF"
},
{
value: 1,
string: "ON"
}
],
hints: {
range: 1
}
},
{
key: "value",
name: "Value",
hints: {
range: 2
}
}
]
}
}
function GeneratorMetadataProvider() {
}
GeneratorMetadataProvider.prototype.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;
});

View File

@@ -66,7 +66,7 @@ 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]);

View File

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

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

View File

@@ -23,31 +23,17 @@
define([
"./GeneratorProvider",
"./SinewaveLimitCapability",
"./StateGeneratorProvider"
"./SinewaveLimitProvider",
"./StateGeneratorProvider",
"./GeneratorMetadataProvider"
], function (
GeneratorProvider,
SinewaveLimitCapability,
StateGeneratorProvider
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",
@@ -70,47 +56,7 @@ define([
],
initialize: function (object) {
object.telemetry = {
duration: 5,
values: [
{
key: "name",
name: "Name"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "state",
source: "value",
name: "State",
format: "enum",
enumerations: [
{
value: 0,
string: "OFF"
},
{
value: 1,
string: "ON"
}
],
hints: {
range: 1
}
},
{
key: "value",
name: "Value",
hints: {
range: 2
}
}
]
duration: 5
}
}
});
@@ -125,63 +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: "textfield",
control: "numberfield",
cssClass: "l-input-sm l-numeric",
key: "phase",
required: true,
property: [
"telemetry",
"phase"
],
pattern: "^\\d*(\\.\\d*)?$"
]
}
],
initialize: function (object) {
@@ -190,48 +131,14 @@ define([
amplitude: 1,
offset: 0,
dataRateInHz: 1,
phase: 0,
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
}
}
]
phase: 0
};
}
});
openmct.telemetry.addProvider(new GeneratorProvider());
openmct.telemetry.addProvider(new GeneratorMetadataProvider());
openmct.telemetry.addProvider(new SinewaveLimitProvider());
};
});

View File

@@ -1,146 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define([
'legacyRegistry',
'../../platform/commonUI/browse/src/InspectorRegion',
'../../platform/commonUI/regions/src/Region'
], function (
legacyRegistry,
InspectorRegion,
Region
) {
"use strict";
/**
* Add a 'plot options' region part to the inspector region for the
* Telemetry Plot type only. {@link InspectorRegion} is a default region
* implementation that is added automatically to all types. In order to
* customize what appears in the inspector region, you can start from a
* blank slate by using Region, or customize the default inspector
* region by using {@link InspectorRegion}.
*/
var plotInspector = new InspectorRegion(),
/**
* Two region parts are defined here. One that appears only in browse
* mode, and one that appears only in edit mode. For not they both point
* to the same representation, but a different key could be used here to
* include a customized representation for edit mode.
*/
plotOptionsBrowseRegion = new Region({
name: "plot-options",
title: "Plot Options",
modes: ['browse'],
content: {
key: "plot-options-browse"
}
}),
plotOptionsEditRegion = new Region({
name: "plot-options",
title: "Plot Options",
modes: ['edit'],
content: {
key: "plot-options-browse"
}
});
/**
* Both parts are added, and policies of type 'region' will determine
* which is shown based on domain object state. A default policy is
* provided which will check the 'modes' attribute of the region part
* definition.
*/
plotInspector.addRegion(plotOptionsBrowseRegion);
plotInspector.addRegion(plotOptionsEditRegion);
legacyRegistry.register("example/plotType", {
"name": "Plot Type",
"description": "Example illustrating registration of a new object type",
"extensions": {
"types": [
{
"key": "plot",
"name": "Example Telemetry Plot",
"cssClass": "icon-telemetry-panel",
"description": "For development use. A plot for displaying telemetry.",
"priority": 10,
"delegates": [
"telemetry"
],
"features": "creation",
"contains": [
{
"has": "telemetry"
}
],
"model": {
"composition": []
},
"inspector": plotInspector,
"telemetry": {
"source": "generator",
"domains": [
{
"key": "time",
"name": "Time"
},
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta",
"format": "example.delta"
}
],
"ranges": [
{
"key": "sin",
"name": "Sine"
},
{
"key": "cos",
"name": "Cosine"
}
]
},
"properties": [
{
"name": "Period",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "period",
"required": true,
"property": [
"telemetry",
"period"
],
"pattern": "^\\d*(\\.\\d*)?$"
}
]
}
]
}
});
});

View File

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

View File

@@ -37,14 +37,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'
],

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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": [

View File

@@ -225,7 +225,8 @@ a.disabled {
}
.hide,
.hidden {
.hidden,
.t-main-view .hide-in-t-main-view {
display: none !important;
}

View File

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

View File

@@ -299,7 +299,7 @@
color: $ic;
}
@if $bgHov != none {
&:not(.disabled):hover {
&:not([disabled="true"]):not(.disabled):hover {
background: $bgHov;
color: $fgHov;
>.icon,

View File

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

View File

@@ -34,11 +34,6 @@ $pad: $interiorMargin * $baseRatio;
line-height: $btnStdH;
padding: 0 $pad;
&.labeled:before {
// Icon when it's included
margin-right: $interiorMarginSm;
}
&.lg {
font-size: 1rem;
}
@@ -59,6 +54,10 @@ $pad: $interiorMargin * $baseRatio;
.label, .title-label { display: none; }
}
&[disabled="true"] {
opacity: 0.3;
}
&.pause-play {
@extend .icon-pause;
&.paused {

View File

@@ -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,19 @@
}
}
.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
@@ -338,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 {
@@ -349,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
}
@@ -359,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;

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@@ -0,0 +1,67 @@
/*****************************************************************************
* 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', '../services/Overlay'], function ($, Overlay) {
/**
*/
function MCTPreview($rootScope,$document,exportImageService,dialogService,notificationService) {
function link($scope, $element, $attrs) {
var actions = $scope.domainObject.getCapability('action'),
notebookAction = actions.getActions({key: 'notebook-new-entry'})[0];
var notebookButton = notebookAction ?
[
{
class: 'icon-notebook new-notebook-entry',
title: 'New Notebook Entry',
clickHandler: function (event) {
event.stopPropagation();
notebookAction.perform();
}
}
] : [];
var overlayService = new Overlay({
$document: $document,
$element: $element[0],
$scope: $scope,
browseBarButtons: notebookButton
});
overlayService.toggleOverlay();
$scope.$on('$destroy', function () {
$element.remove();
});
}
return {
restrict: 'A',
link: link
};
}
return MCTPreview;
});

View File

@@ -0,0 +1,191 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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 OverlayService. Created by deeptailor on 03/29/2018
*/
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>';
/*
* An Overlay Service when instantiated creates an overlay dialog.
* @param {Object} options The options object required to instantiate the overlay service
* options = {
* $document: document object,
* $scope: angular $scope object,
* element: node to be injected into overlay as a view,
* overlayWillMount: callback executed before overlay is injected,
* overlayWillUnmount: callback executed before overlay is removed,
* overlayDidMount: callback executed after overlay is injected,
* overlayDidUnmount: callback executed after overlay is removed
* browseBarButtons: an array of desired buttons to be added to the browse bar of the overlay.
* the array should consist of button objects containing:
* a) class - css class to be added to the button div
* b) title - desired button title
* c) clickHandler - callback to be added to the click event listener of the button
* }
* $document, $scope and element are required
*/
function Overlay(options) {
this.element = options.$element;
this.document = options.$document[0];
this.$scope = options.$scope;
this.overlayWillMount = options.overlayWillMount;
this.overlayWillUnmount = options.overlayWillUnmount;
this.overlayDidMount = options.overlayDidMount;
this.overlayDidUnmount = options.overlayDidUnmount;
this.browseBarButtons = options.browseBarButtons || [];
this.buttons = [];
this.openOverlay = this.openOverlay.bind(this);
this.closeOverlay = this.closeOverlay.bind(this);
this.toggleOverlay = this.toggleOverlay.bind(this);
this.removeButtons = this.removeButtons.bind(this);
this.isOverlayOpen = false;
}
Overlay.prototype.openOverlay = function () {
if (this.overlayWillMount && typeof this.overlayWillMount === 'function') {
this.overlayWillMount();
}
this.overlay = this.document.createElement('div');
$(this.overlay).addClass('abs overlay l-large-view');
this.overlay.innerHTML = OVERLAY_TEMPLATE;
this.overlayContainer = this.overlay.querySelector('.t-contents');
this.closeButton = this.overlay.querySelector('a.close');
this.closeButton.addEventListener('click', this.toggleOverlay);
this.doneButton = this.overlay.querySelector('a.t-done');
this.doneButton.addEventListener('click', this.toggleOverlay);
this.blocker = this.overlay.querySelector('.abs.blocker');
this.blocker.addEventListener('click', this.toggleOverlay);
this.document.body.appendChild(this.overlay);
this.overlayContainer.appendChild(this.element);
this.browseBar = this.overlay.querySelector('.object-browse-bar .right');
if (this.browseBarButtons && Array.isArray(this.browseBarButtons)) {
this.browseBarButtons.forEach(function (buttonObject) {
var button = newButtonTemplate(buttonObject.class, buttonObject.title);
this.browseBar.prepend(button);
button.addEventListener('click', buttonObject.clickHandler);
this.buttons.push(button);
}.bind(this));
}
if (this.overlayDidMount && typeof this.overlayDidMount === 'function') {
this.overlayDidMount();
}
};
Overlay.prototype.closeOverlay = function () {
if (this.overlayWillUnmount && typeof this.overlayWillUnmount === 'function') {
this.overlayWillUnmount();
}
this.overlayContainer.removeChild(this.element);
this.document.body.removeChild(this.overlay);
this.closeButton.removeEventListener('click', this.toggleOverlay);
this.closeButton = undefined;
this.doneButton.removeEventListener('click', this.toggleOverlay);
this.doneButton = undefined;
this.blocker.removeEventListener('click', this.toggleOverlay);
this.blocker = undefined;
this.overlayContainer = undefined;
this.overlay = undefined;
// if (this.notebookButton) {
// this.browseBar.removeChild(this.notebookButton);
// this.notebookButton.remove();
// this.notebookButton = undefined;
// }
this.removeButtons();
if (this.overlayDidUnmount && typeof this.overlayDidUnmount === 'function') {
this.overlayDidUnmount();
}
};
Overlay.prototype.toggleOverlay = function (event) {
if (event) {
event.stopPropagation();
}
if (!this.isOverlayOpen) {
this.openOverlay();
this.isOverlayOpen = true;
} else {
this.closeOverlay();
this.isOverlayOpen = false;
}
};
Overlay.prototype.removeButtons = function () {
this.buttons.forEach(function (button) {
button.remove();
}.bind(this));
this.buttons = [];
};
function newButtonTemplate(classString, title) {
var NEW_BUTTON_TEMPLATE = '<a class="s-button labeled' + classString + '">' +
'<span class="title-label">' + title + '</span>' +
'</a>';
var button = document.createElement('div');
$(button).addClass('notebook-button-container holder flex-elem');
button.innerHTML = NEW_BUTTON_TEMPLATE;
return button;
}
return Overlay;
});

View File

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

View File

@@ -21,23 +21,12 @@
*****************************************************************************/
define([
'zepto'
'zepto',
'../../../commonUI/general/src/services/Overlay'
], function (
$
$,
Overlay
) {
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>';
/**
* 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.
@@ -50,9 +39,11 @@ define([
* descendent of a `.frame` element.
*/
function MCTTriggerModal($document) {
var document = $document[0];
function link($scope, $element) {
var actions = $scope.domainObject.getCapability('action'),
notebookAction = actions.getActions({key: 'notebook-new-entry'})[0];
var frame = $element.parent();
for (var i = 0; i < 10; i++) {
@@ -67,61 +58,39 @@ define([
}
frame = frame[0];
var layoutContainer = frame.parentElement,
isOpen = false,
toggleOverlay,
overlay,
closeButton,
doneButton,
blocker,
overlayContainer;
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);
}
var layoutContainer = frame.parentElement;
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;
}
var notebookButton = notebookAction ?
[
{
class: 'icon-notebook new-notebook-entry',
title: 'New Notebook Entry',
clickHandler: function (event) {
event.stopPropagation();
notebookAction.perform();
}
}
] : [];
toggleOverlay = function () {
if (!isOpen) {
openOverlay();
isOpen = true;
} else {
closeOverlay();
isOpen = false;
}
};
var overlayService = new Overlay ({
$document: $document,
$scope: $scope,
$element: frame,
overlayWillMount: function () {
$(frame).removeClass('frame frame-template');
layoutContainer.removeChild(frame);
},
overlayDidUnmount: function () {
$(frame).addClass('frame frame-template');
layoutContainer.appendChild(frame);
},
browseBarButtons: notebookButton
});
$element.on('click', toggleOverlay);
$element.on('click', overlayService.toggleOverlay);
$scope.$on('$destroy', function () {
$element.off('click', toggleOverlay);
$element.off('click', overlayService.toggleOverlay);
});
}

View File

@@ -106,8 +106,8 @@ define(
'telemetryFormatter',
['format']
);
mockFormatter.format.andCallFake(function (value) {
return "Formatted " + value;
mockFormatter.format.andCallFake(function (valueMetadata) {
return "Formatted " + valueMetadata.value;
});
mockDomainObject = jasmine.createSpyObj(
@@ -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');
});

View File

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

View File

@@ -0,0 +1,314 @@
define([
"legacyRegistry",
"./src/controllers/NotebookController",
"./src/controllers/NewEntryController",
"./src/controllers/SelectSnapshotController",
"./src/controllers/LayoutNotebookController",
"./src/directives/MCTSnapshot",
"../layout/src/MCTTriggerModal",
"./src/directives/EntryDnd",
"./src/actions/ViewSnapshot",
"./src/actions/AnnotateSnapshot",
"./src/actions/RemoveEmbed",
"./src/actions/CreateSnapshot",
"./src/actions/RemoveSnapshot",
"./src/actions/NewEntryContextual",
"./src/capabilities/NotebookCapability",
"./src/policies/CompositionPolicy",
"./src/policies/ViewPolicy",
"text!./res/templates/layoutNotebook.html",
"text!./res/templates/notebook.html",
"text!./res/templates/entry.html",
"text!./res/templates/annotation.html",
"text!./res/templates/notifications.html",
"text!../layout/res/templates/frame.html",
"text!./res/templates/controls/embedControl.html",
"text!./res/templates/controls/snapSelect.html"
], function (
legacyRegistry,
NotebookController,
NewEntryController,
SelectSnapshotController,
LayoutNotebookController,
MCTSnapshot,
MCTModalNotebook,
MCTEntryDnd,
ViewSnapshotAction,
AnnotateSnapshotAction,
RemoveEmbedAction,
CreateSnapshotAction,
RemoveSnapshotAction,
newEntryAction,
NotebookCapability,
CompositionPolicy,
ViewPolicy,
layoutNotebookTemplate,
notebookTemplate,
entryTemplate,
annotationTemplate,
notificationsTemplate,
frameTemplate,
embedControlTemplate,
snapSelectTemplate
) {
legacyRegistry.register("platform/features/notebook", {
"name": "Notebook Plugin",
"description": "Create and save timestamped notes with embedded object snapshots.",
"extensions":
{
"types": [
{
"key": "notebook",
"name": "Notebook",
"cssClass": "icon-notebook",
"description": "Create and save timestamped notes with embedded object snapshots.",
"features": ["creation"],
"model": {
"entries": [],
"composition": [],
"entryTypes": []
}
}
],
"views": [
{
"key": "notebook.view",
"type": "notebook",
"cssClass": "icon-notebook",
"name": "notebook",
"template": notebookTemplate,
"editable": false,
"uses": [
"composition",
"action"
],
"gestures": [
"drop"
]
}
],
"controllers": [
{
"key": "NotebookController",
"implementation": NotebookController,
"depends": ["$scope",
"dialogService",
"popupService",
"agentService",
"objectService",
"navigationService",
"now",
"actionService",
"$timeout",
"$element",
"$sce"
]
},
{
"key": "NewEntryController",
"implementation": NewEntryController,
"depends": ["$scope",
"$rootScope"
]
},
{
"key": "selectSnapshotController",
"implementation": SelectSnapshotController,
"depends": ["$scope",
"$rootScope"
]
},
{
"key": "LayoutNotebookController",
"implementation": LayoutNotebookController,
"depends": ["$scope"]
}
],
"representations": [
{
"key": "draggedEntry",
"template": entryTemplate
},
{
"key": "frameLayoutNotebook",
"template": frameTemplate
}
],
"templates": [
{
"key": "annotate-snapshot",
"template": annotationTemplate
},
{
"key": "notificationTemplate",
"template": notificationsTemplate
}
],
"directives": [
{
"key": "mctSnapshot",
"implementation": MCTSnapshot,
"depends": [
"$rootScope",
"$document",
"exportImageService",
"dialogService",
"notificationService"
]
},
{
"key": "mctEntryDnd",
"implementation": MCTEntryDnd,
"depends": [
"$rootScope",
"$compile",
"dndService",
"typeService",
"notificationService"
]
},
{
"key": "mctModalNotebook",
"implementation": MCTModalNotebook,
"depends": [
"$document"
]
}
],
"actions": [
{
"key": "view-snapshot",
"implementation": ViewSnapshotAction,
"name": "View Snapshot",
"description": "View the large image in a modal",
"category": "embed",
"depends": [
"$compile"
]
},
{
"key": "annotate-snapshot",
"implementation": AnnotateSnapshotAction,
"name": "Annotate Snapshot",
"cssClass": "icon-pencil labeled",
"description": "Annotate embed's snapshot",
"category": "embed",
"depends": [
"dialogService",
"dndService",
"$rootScope"
]
},
{
"key": "remove-embed",
"implementation": RemoveEmbedAction,
"name": "Remove...",
"cssClass": "icon-trash labeled",
"description": "Remove this embed",
"category": [
"embed",
"embed-no-snap"
],
"depends": [
"dialogService"
]
},
{
"key": "remove-snapshot",
"implementation": RemoveSnapshotAction,
"name": "Remove Snapshot",
"cssClass": "icon-trash labeled",
"description": "Remove Snapshot of the embed",
"category": "embed",
"depends": [
"dialogService"
]
},
{
"key": "create-snapshot",
"implementation": CreateSnapshotAction,
"name": "Create Snapshot",
"description": "Create a snapshot for the embed",
"category": "embed-no-snap",
"priority": "preferred",
"depends": [
"$compile"
]
},
{
"key": "notebook-new-entry",
"implementation": newEntryAction,
"name": "New Notebook Entry",
"cssClass": "icon-notebook labeled",
"description": "Add a new 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"
}
]
}
});
});

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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,51 @@
<!--
Open MCT, Copyright (c) 2009-2016, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<!--
This element appears in the overlay dialog when initiating a new Notebook Entry from a view's Notebook button -->
<div class='form-control'>
<ng-form name="mctControl">
<div class='fields' ng-controller="NewEntryController">
<div class="l-flex-row new-notebook-entry-embed l-entry-embed {{cssClass}}"
ng-class="{ 'has-snapshot' : snapToggle }">
<div class="holder flex-elem snap-thumb"
ng-if="snapToggle">
<img ng-src="{{snapshot.src}}" alt="{{snapshot.modified}}">
</div>
<div class="holder flex-elem embed-info">
<div class="embed-title">{{objectName}}</div>
<div class="embed-date"
ng-if="snapToggle">{{snapshot.modified| date:'yyyy-MM-dd HH:mm:ss'}}</div>
</div>
<div class="holder flex-elem annotate-new"
ng-if="snapToggle">
<a class="s-button flex-elem icon-pencil "
title="Annotate this snapshot"
ng-click="annotateSnapshot()">
<span class="title-label">Annotate</span>
</a>
</div>
</div>
</div>
</ng-form>
</div>

View File

@@ -0,0 +1,30 @@
<!--
Open MCT, Copyright (c) 2014-2017, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class='form-control select' ng-controller="selectSnapshotController">
<select
ng-model="selectModel"
ng-options="opt.value as opt.name for opt in options"
ng-required="ngRequired"
name="mctControl">
<!-- <option value="" ng-show="!ngModel[field]">- Select One -</option> -->
</select>
</div>

View File

@@ -0,0 +1,38 @@
<!--
Open MCT, Copyright (c) 2014-2017, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="frame snap-frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
<div class="abs object-browse-bar l-flex-row">
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<mct-representation
key="'switcher'"
ng-model="representation"
mct-object="domainObject">
</mct-representation>
</div>
</div>
<div class="abs object-holder" data-entry = "{{parameters.entry}}" data-embed = "{{parameters.embed}}" mct-snapshot ng-if="representation.selected.key">
<mct-representation
key="representation.selected.key"
mct-object="representation.selected.key && domainObject">
</mct-representation>
</div>
</div>

View File

@@ -0,0 +1,54 @@
<!--
Open MCT, Copyright (c) 2014-2017, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
<div class="abs object-browse-bar l-flex-row">
<div class="left flex-elem l-flex-row grows">
<mct-representation
key="'object-header-frame'"
mct-object="domainObject"
class="l-flex-row flex-elem object-header grows">
</mct-representation>
</div>
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<a class="s-button icon-notebook"
title="New Notebook Entry"
ng-if="parameters"
ng-click="ngModel()">
</a>
<mct-representation
key="'switcher'"
ng-model="representation"
mct-object="domainObject">
</mct-representation>
<a class="s-button icon-expand t-btn-view-large"
title="View large"
mct-modal-notebook>
</a>
</div>
</div>
<div class="abs object-holder">
<mct-representation
key="representation.selected.key"
mct-object="representation.selected.key && domainObject">
</mct-representation>
</div>
</div>

View File

@@ -0,0 +1,84 @@
<!--
Open MCT, Copyright (c) 2014-2017, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="abs l-layout"
ng-controller="LayoutController as controller"
ng-click="controller.clearSelection()">
<!-- Background grid -->
<div class="l-grid-holder" ng-click="controller.clearSelection()">
<div class="l-grid l-grid-x"
ng-if="!controller.getGridSize()[0] < 3"
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
<div class="l-grid l-grid-y"
ng-if="!controller.getGridSize()[1] < 3"
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
</div>
<div class='abs frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border'
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-selected':controller.selected(childObject) }"
ng-repeat="childObject in composition"
ng-click="controller.select($event, childObject.getId())"
ng-style="controller.getFrameStyle(childObject.getId())">
<div ng-controller="LayoutNotebookController as controller">
<mct-representation key="'frameLayoutNotebook'"
class="t-rep-frame holder contents abs"
parameters = "hasNotebookAction"
ng-model="newNotebook"
mct-object="childObject">
</mct-representation>
</div>
<!-- Drag handles -->
<span class="abs t-edit-handle-holder s-hover-border" ng-if="controller.selected(childObject)">
<span class="edit-handle edit-move"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-nw"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [-1,-1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-ne"
mct-drag-down="controller.startDrag(childObject.getId(), [0,1], [1,-1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-sw"
mct-drag-down="controller.startDrag(childObject.getId(), [1,0], [-1,1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-se"
mct-drag-down="controller.startDrag(childObject.getId(), [0,0], [1,1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
</span>
</div>
</div>

View File

@@ -0,0 +1,124 @@
<div ng-controller="NotebookController as controller" class="mct-notebook w-notebook l-flex-col">
<div class="l-notebook-head holder l-flex-row flex-elem">
<div class="l-flex-row holder grows holder-search">
<div class="search-bar flex-elem l-flex-row grows"
ng-class="{ holder: !(entrySearch === '' || entrySearch === undefined) }">
<div class="holder flex-elem grows">
<input class="search-input"
type="text" tabindex="10000"
ng-model="entrySearch"
ng-keyup="controller.search()"/>
<a class="clear-icon clear-input icon-x-in-circle"
ng-class="{show: !(entrySearch === '' || entrySearch === undefined)}"
ng-click="entrySearch = ''; controller.search()"></a>
</div>
</div>
</div>
<div class="notebook-filters right l-flex-row flex-elem grows flex-justify-end">
<div class="select">
<select ng-model="showTime">
<option value="0" selected="selected">Show all</option>
<option value="1">Last hour</option>
<option value="8">Last 8 hours</option>
<option value="24">Last 24 hours</option>
</select>
</div>
<div class="select">
<select ng-model="sortEntries">
<option value="-createdOn" selected="selected">Newest first</option>
<option value="createdOn">Oldest first</option>
</select>
</div>
</div>
</div>
<!-- drag area -->
<div class="holder flex-elem l-flex-row icon-plus labeled l-notebook-drag-area" ng-click="newEntry($event)"
id="newEntry" mct-entry-dnd>
<span class="label">To start a new entry, click here or drag and drop any object</span>
</div>
<!-- entries -->
<div class="holder flex-elem grows w-notebook-entries t-entries-list" ng-mouseover="handleActive()">
<ul>
<li class="l-flex-row has-local-controls l-notebook-entry s-notebook-entry"
id="{{'entry_'+ entry.id}}"
ng-if="hoursFilter(showTime,entry.createdOn)"
ng-repeat="entry in model.entries | filter:entrySearch | orderBy: sortEntries track by $index"
ng-init="$last && finished(model.entries)"
mct-entry-dnd>
<div class="holder flex-elem l-flex-row grows w-notebook-entry-time-and-content" 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>

View File

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

View File

@@ -0,0 +1,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", "../shims/painterro-shim"],
function (Painterro, $, painterroShim) {
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;
var painterro;
var controller = ['$scope', '$timeout', function PainterroController ($scope, $timeout) {
$(document.body).find('.l-dialog .outer-holder').addClass('annotation-dialog');
$timeout(function () {
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);
});
painterroShim(painterro);
$(document.body).find('.ptro-icon-btn').addClass('s-button');
$(document.body).find('.ptro-input').addClass('s-button');
$scope.$on('$destroy', function () {
painterro.removeEventHandlers();
});
}];
ANNOTATION_STRUCT.model = {'controller': controller};
function saveNotes(param) {
if (param === 'ok') {
painterro.save();
}else {
ROOTSCOPE.snapshot = "annotationCancelled";
}
}
this.dialogService.getUserChoice(ANNOTATION_STRUCT)
.then(saveNotes);
};
return AnnotateSnapshot;
}
);

View File

@@ -0,0 +1,65 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
var SNAPSHOT_TEMPLATE = '<mct-representation key="\'draggedEntry\'"' +
'parameters="{entry:entryId,embed:embedId}"' +
'class="t-rep-frame holder"' +
'mct-object="selObj">' +
'</mct-representation>';
function CreateSnapshot($compile,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.context = context;
this.$compile = $compile;
}
CreateSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId,$scope) {
var compile = this.$compile;
var model = this.domainObject.model;
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
var embedType = entryEmbeds[embedPos].type;
$scope.getDomainObj(embedType).then(function (resp) {
if (entryId >= 0 && embedId >= 0) {
$scope.selObj = resp[embedType];
$scope.entryId = elementPos;
$scope.embedId = embedPos;
compile(SNAPSHOT_TEMPLATE)($scope);
}
});
};
return CreateSnapshot;
}
);

View File

@@ -0,0 +1,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;
}
);

View File

@@ -0,0 +1,72 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
function RemoveEmbed(dialogService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
}
RemoveEmbed.prototype.perform = function ($event,snapshot,embedId,entryId) {
var DOMAIN_OBJ = this.domainObject;
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Embed. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
remove();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
function remove() {
DOMAIN_OBJ.useCapability('mutation', function (model) {
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
model.entries[elementPos].embeds.splice(embedPos, 1);
});
}
};
return RemoveEmbed;
}
);

View File

@@ -0,0 +1,74 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
function RemoveSnapshot(dialogService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
}
RemoveSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId) {
var DOMAIN_OBJ = this.domainObject;
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Snapshot. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
remove();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
function remove() {
DOMAIN_OBJ.useCapability('mutation', function (model) {
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
model.entries[elementPos].embeds[embedPos].snapshot = "";
});
}
};
return RemoveSnapshot;
}
);

View File

@@ -0,0 +1,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;
}
);

View File

@@ -0,0 +1,50 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
function () {
/**
* The notebook capability allows a domain object to know whether the
* notebook plugin is present or not.
*
* @constructor
*/
function NotebookCapability(typeService, domainObject) {
this.domainObject = domainObject;
this.typeService = typeService;
return this;
}
/**
* Returns true if there is a notebook domain Object.
*
* @returns {Boolean}
*/
NotebookCapability.prototype.isNotebook = function () {
return !!this.typeService.getType('notebook');
};
return NotebookCapability;
}
);

View File

@@ -0,0 +1,54 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* This bundle implements object types and associated views for
* display-building.
*/
define(
[],
function () {
/**
* The LayoutNotebookController is responsible for supporting the
* notebook feature creation on theLayout view.
**/
function LayoutNotebookController($scope) {
$scope.hasNotebookAction = undefined;
$scope.newNotebook = undefined;
var actions = $scope.domainObject.getCapability('action');
var notebookAction = actions.getActions({'key': 'notebook-new-entry'});
if (notebookAction.length > 0) {
$scope.hasNotebookAction = true;
$scope.newNotebook = function () {
notebookAction[0].perform();
};
}
}
return LayoutNotebookController;
}
);

View File

@@ -0,0 +1,66 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* Module defining NewEntryController. */
define(
[],
function () {
function NewEntryController($scope,$rootScope) {
$scope.snapshot = undefined;
$scope.snapToggle = true;
$scope.entryText = '';
var annotateAction = $rootScope.selObj.getCapability('action').getActions(
{category: 'embed'})[1];
$scope.$parent.$parent.ngModel[$scope.$parent.$parent.field] = $rootScope.selObj;
$scope.objectName = $rootScope.selObj.getModel().name;
$scope.cssClass = $rootScope.selObj.getCapability('type').typeDef.cssClass;
$scope.annotateSnapshot = function ($event) {
if ($rootScope.currentDialog.value) {
$rootScope.newEntryText = $scope.$parent.$parent.ngModel.entry;
$rootScope.currentDialog.cancel();
annotateAction.perform($event, $rootScope.snapshot.src);
$rootScope.currentDialog = undefined;
}
};
function updateSnapshot(img) {
$scope.snapshot = img;
}
// Update set of actions whenever the action capability
// changes or becomes available.
$rootScope.$watch("snapshot", updateSnapshot);
$rootScope.$watch("selValue", toggleEmbed);
function toggleEmbed(value) {
$scope.snapToggle = value;
}
}
return NewEntryController;
}
);

View File

@@ -0,0 +1,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;
});

View File

@@ -0,0 +1,44 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* Module defining SelectSnapshotController. */
define(
[],
function () {
function SelectSnapshotController($scope,$rootScope) {
$scope.selectModel = true;
function selectprint(value) {
$rootScope.selValue = value;
$scope.$parent.$parent.ngModel[$scope.$parent.$parent.field] = value;
}
$scope.$watch("selectModel", selectprint);
}
return SelectSnapshotController;
}
);

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

View File

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

View File

@@ -0,0 +1,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;
});

View File

@@ -0,0 +1,44 @@
/******************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/**
* This bundle implements "containment" rules, which determine which objects
* can be contained within a notebook.
*/
define(
[],
function () {
function CompositionPolicy() {
}
CompositionPolicy.prototype.allow = function (parent, child) {
var parentDef = parent.getCapability('type').getName();
if (parentDef === 'Notebook' && child.getCapability('status').list().length) {
return false;
}
return true;
};
return CompositionPolicy;
}
);

View File

@@ -0,0 +1,40 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
function () {
function ViewPolicy() {
}
ViewPolicy.prototype.allow = function (view, domainObject) {
// if (view.key === 'layout') {
// return false;
// }
return true;
};
return ViewPolicy;
}
);

View File

@@ -0,0 +1,13 @@
define(function () {
return function (painterroInstance) {
painterroInstance.removeEventHandlers = function() {
Object.keys(this.documentHandlers).forEach((key) => {
this.doc.removeEventListener(key, this.documentHandlers[key]);
});
Object.keys(this.windowHandlers).forEach((key) => {
window.removeEventListener(key, this.windowHandlers[key]);
});
}.bind(painterroInstance);
}
});

View File

@@ -399,14 +399,14 @@ define(
var compositionApi = this.openmct.composition;
function filterForTelemetry(objects) {
return objects.filter(telemetryApi.canProvideTelemetry.bind(telemetryApi));
return objects.filter(telemetryApi.isTelemetryObject.bind(telemetryApi));
}
/*
* If parent object is a telemetry object, subscribe to it. Do not
* test composees.
*/
if (telemetryApi.canProvideTelemetry(this.domainObject)) {
if (telemetryApi.isTelemetryObject(this.domainObject)) {
return Promise.resolve([this.domainObject]);
} else {
/*

View File

@@ -91,7 +91,7 @@ define(
mockScope.domainObject = mockDomainObject;
mockTelemetryAPI = jasmine.createSpyObj("telemetryAPI", [
"canProvideTelemetry",
"isTelemetryObject",
"subscribe",
"getMetadata",
"commonValuesForHints",
@@ -117,7 +117,7 @@ define(
return formatter;
});
mockTelemetryAPI.canProvideTelemetry.andReturn(false);
mockTelemetryAPI.isTelemetryObject.andReturn(false);
mockTimeout = jasmine.createSpy("timeout");
mockTimeout.andReturn(1); // Return something
@@ -199,7 +199,7 @@ define(
mockComposition.load.andReturn(Promise.resolve(mockChildren));
mockCompositionAPI.get.andReturn(mockComposition);
mockTelemetryAPI.canProvideTelemetry.andCallFake(function (obj) {
mockTelemetryAPI.isTelemetryObject.andCallFake(function (obj) {
return obj.identifier.key === mockTelemetryObject.identifier.key;
});
@@ -287,7 +287,7 @@ define(
mockChildren = mockChildren.concat(mockTelemetryChildren);
mockComposition.load.andReturn(Promise.resolve(mockChildren));
mockTelemetryAPI.canProvideTelemetry.andCallFake(function (object) {
mockTelemetryAPI.isTelemetryObject.andCallFake(function (object) {
if (object === mockTelemetryObject) {
return false;
} else {

View File

@@ -62,8 +62,10 @@ define(
scope.key && templateMap[scope.key]
);
scope.$watch('key', function (key) {
changeTemplate(key && templateMap[key]);
scope.$watch('key', function (newKey, oldKey) {
if (newKey !== oldKey) {
changeTemplate(newKey && templateMap[newKey]);
}
});
}

View File

@@ -21,7 +21,7 @@
-->
<div class="angular-w l-flex-col flex-elem grows holder" ng-controller="SearchController as controller">
<div class="l-flex-col flex-elem grows holder holder-search" ng-controller="SearchMenuController as menuController">
<div class="search-bar flex-elem l-flex-row"
<div class="search-bar search-filter-by-type flex-elem l-flex-row"
ng-controller="ToggleController as toggle"
ng-class="{ holder: !(ngModel.input === '' || ngModel.input === undefined) }">
<div class="holder flex-elem grows">

View File

@@ -31,7 +31,8 @@ define([
'./policies/AdapterCompositionPolicy',
'./policies/AdaptedViewPolicy',
'./runs/AlternateCompositionInitializer',
'./runs/TimeSettingsURLHandler'
'./runs/TimeSettingsURLHandler',
'./runs/TypeDeprecationChecker'
], function (
legacyRegistry,
ActionDialogDecorator,
@@ -43,7 +44,8 @@ define([
AdapterCompositionPolicy,
AdaptedViewPolicy,
AlternateCompositionInitializer,
TimeSettingsURLHandler
TimeSettingsURLHandler,
TypeDeprecationChecker
) {
legacyRegistry.register('src/adapter', {
"extensions": {
@@ -107,6 +109,10 @@ define([
}
],
runs: [
{
implementation: TypeDeprecationChecker,
depends: ["types[]"]
},
{
implementation: AlternateCompositionInitializer,
depends: ["openmct"]

View File

@@ -0,0 +1,47 @@
/*****************************************************************************
* Open openmct, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open openmct 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 openmct 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 console */
define([
], function (
) {
function checkForDeprecatedFunctionality(typeDef) {
if (typeDef.hasOwnProperty('telemetry')) {
console.warn(
'DEPRECATION WARNING: Telemetry data on type ' +
'registrations will be deprecated in a future version, ' +
'please convert to a custom telemetry metadata provider ' +
'for type: ' + typeDef.key
);
}
}
function TypeDeprecationChecker(types) {
types.forEach(checkForDeprecatedFunctionality);
}
return TypeDeprecationChecker;
});

View File

@@ -0,0 +1,269 @@
define([
'./CompositionAPI',
'./CompositionCollection'
], function (
CompositionAPI,
CompositionCollection
) {
describe('The Composition API', function () {
var publicAPI;
var compositionAPI;
var topicService;
var mutationTopic;
beforeEach(function () {
mutationTopic = jasmine.createSpyObj('mutationTopic', [
'listen'
]);
topicService = jasmine.createSpy('topicService');
topicService.andReturn(mutationTopic);
publicAPI = {};
publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [
'get'
]);
publicAPI.objects.get.andCallFake(function (identifier) {
return Promise.resolve({identifier: identifier});
});
publicAPI.$injector = jasmine.createSpyObj('$injector', [
'get'
]);
publicAPI.$injector.get.andReturn(topicService);
compositionAPI = new CompositionAPI(publicAPI);
});
it('returns falsy if an object does not support composition', function () {
expect(compositionAPI.get({})).toBeFalsy();
});
describe('default composition', function () {
var domainObject;
var composition;
beforeEach(function () {
domainObject = {
name: 'test folder',
identifier: {
namespace: 'test',
key: '1'
},
composition: [
{
namespace: 'test',
key: 'a'
}
]
};
composition = compositionAPI.get(domainObject);
});
it('returns composition collection', function () {
expect(composition).toBeDefined();
expect(composition).toEqual(jasmine.any(CompositionCollection));
});
it('loads composition from domain object', function () {
var listener = jasmine.createSpy('addListener');
var loaded = false;
composition.on('add', listener);
composition.load()
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(listener.calls.length).toBe(1);
expect(listener).toHaveBeenCalledWith({
identifier: {namespace: 'test', key: 'a'}
});
});
});
// TODO: Implement add/removal in new default provider.
xit('synchronizes changes between instances', function () {
var otherComposition = compositionAPI.get(domainObject);
var addListener = jasmine.createSpy('addListener');
var removeListener = jasmine.createSpy('removeListener');
var otherAddListener = jasmine.createSpy('otherAddListener');
var otherRemoveListener = jasmine.createSpy('otherRemoveListener');
composition.on('add', addListener);
composition.on('remove', removeListener);
otherComposition.on('add', otherAddListener);
otherComposition.on('remove', otherRemoveListener);
var loaded = false;
Promise.all([composition.load(), otherComposition.load()])
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
expect(removeListener).not.toHaveBeenCalled();
expect(otherRemoveListener).not.toHaveBeenCalled();
var object = addListener.mostRecentCall.args[0];
composition.remove(object);
expect(removeListener).toHaveBeenCalled();
expect(otherRemoveListener).toHaveBeenCalled();
addListener.reset();
otherAddListener.reset();
composition.add(object);
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
removeListener.reset();
otherRemoveListener.reset();
otherComposition.remove(object);
expect(removeListener).toHaveBeenCalled();
expect(otherRemoveListener).toHaveBeenCalled();
addListener.reset();
otherAddListener.reset();
otherComposition.add(object);
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
});
});
});
describe('static custom composition', function () {
var customProvider;
var domainObject;
var composition;
beforeEach(function () {
// A simple custom provider, returns the same composition for
// all objects of a given type.
customProvider = {
appliesTo: function (object) {
return object.type === 'custom-object-type';
},
load: function (object) {
return Promise.resolve([
{
namespace: 'custom',
key: 'thing'
}
]);
}
};
domainObject = {
identifier: {
namespace: 'test',
key: '1'
},
type: 'custom-object-type'
};
compositionAPI.addProvider(customProvider);
composition = compositionAPI.get(domainObject);
});
it('supports listening and loading', function () {
var listener = jasmine.createSpy('addListener');
var loaded = false;
composition.on('add', listener);
composition.load()
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(listener.calls.length).toBe(1);
expect(listener).toHaveBeenCalledWith({
identifier: {namespace: 'custom', key: 'thing'}
});
});
});
});
describe('dynamic custom composition', function () {
var customProvider;
var domainObject;
var composition;
beforeEach(function () {
// A dynamic provider, loads an empty composition and exposes
// listener functions.
customProvider = jasmine.createSpyObj('dynamicProvider', [
'appliesTo',
'load',
'on',
'off'
]);
customProvider.appliesTo.andReturn('true');
customProvider.load.andReturn(Promise.resolve([]));
domainObject = {
identifier: {
namespace: 'test',
key: '1'
},
type: 'custom-object-type'
};
compositionAPI.addProvider(customProvider);
composition = compositionAPI.get(domainObject);
});
it('supports listening and loading', function () {
var addListener = jasmine.createSpy('addListener');
var removeListener = jasmine.createSpy('removeListener');
var loaded = false;
composition.on('add', addListener);
composition.on('remove', removeListener);
expect(customProvider.on).toHaveBeenCalledWith(
domainObject,
'add',
jasmine.any(Function),
jasmine.any(CompositionCollection)
);
expect(customProvider.on).toHaveBeenCalledWith(
domainObject,
'remove',
jasmine.any(Function),
jasmine.any(CompositionCollection)
);
var add = customProvider.on.calls[0].args[2];
var remove = customProvider.on.calls[1].args[2];
composition.load()
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(addListener).not.toHaveBeenCalled();
expect(removeListener).not.toHaveBeenCalled();
add({namespace: 'custom', key: 'thing'});
});
waitsFor(function () {
return addListener.calls.length > 0;
});
runs(function () {
expect(addListener).toHaveBeenCalledWith({
identifier: {namespace: 'custom', key: 'thing'}
});
remove(addListener.mostRecentCall.args[0]);
});
waitsFor(function () {
return removeListener.calls.length > 0;
});
runs(function () {
expect(removeListener).toHaveBeenCalledWith({
identifier: {namespace: 'custom', key: 'thing'}
});
});
});
});
});
});

View File

@@ -0,0 +1,128 @@
/*****************************************************************************
* Open openmct, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open openmct 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 openmct 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([
'lodash'
], function (
_
) {
/**
* This is the default metadata provider; for any object with a "telemetry"
* property, this provider will return the value of that property as the
* telemetry metadata.
*
* This provider also implements legacy support for telemetry metadata
* defined on the type. Telemetry metadata definitions on type will be
* depreciated in the future.
*/
function DefaultMetadataProvider(openmct) {
this.openmct = openmct;
}
/**
* Applies to any domain object with a telemetry property, or whose type
* definition has a telemetry property.
*/
DefaultMetadataProvider.prototype.supportsMetadata = function (domainObject) {
return !!domainObject.telemetry || !!this.typeHasTelemetry(domainObject);
};
/**
* Retrieves valueMetadata from legacy metadata.
* @private
*/
function valueMetadatasFromOldFormat(metadata) {
var valueMetadatas = [];
valueMetadatas.push({
key: 'name',
name: 'Name'
});
metadata.domains.forEach(function (domain, index) {
var valueMetadata = _.clone(domain);
valueMetadata.hints = {
domain: index + 1
};
valueMetadatas.push(valueMetadata);
});
metadata.ranges.forEach(function (range, index) {
var valueMetadata = _.clone(range);
valueMetadata.hints = {
range: index,
priority: index + metadata.domains.length + 1
};
if (valueMetadata.type === 'enum') {
valueMetadata.key = 'enum';
valueMetadata.hints.y -= 10;
valueMetadata.hints.range -= 10;
valueMetadata.enumerations =
_.sortBy(valueMetadata.enumerations.map(function (e) {
return {
string: e.string,
value: +e.value
};
}), 'e.value');
valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
valueMetadata.max = _.max(valueMetadata.values);
valueMetadata.min = _.min(valueMetadata.values);
}
valueMetadatas.push(valueMetadata);
});
return valueMetadatas;
}
/**
* Returns telemetry metadata for a given domain object.
*/
DefaultMetadataProvider.prototype.getMetadata = function (domainObject) {
var metadata = domainObject.telemetry || {};
if (this.typeHasTelemetry(domainObject)) {
var typeMetadata = this.typeService.getType(domainObject.type).typeDef.telemetry;
_.extend(metadata, typeMetadata);
if (!metadata.values) {
metadata.values = valueMetadatasFromOldFormat(metadata);
}
}
return metadata;
};
/**
* @private
*/
DefaultMetadataProvider.prototype.typeHasTelemetry = function (domainObject) {
if (!this.typeService) {
this.typeService = this.openmct.$injector.get('typeService');
}
return !!this.typeService.getType(domainObject.type).typeDef.telemetry;
};
return DefaultMetadataProvider;
});

View File

@@ -141,17 +141,20 @@ define([
return capability.subscribe(callbackWrapper, request);
};
LegacyTelemetryProvider.prototype.limitEvaluator = function (domainObject) {
LegacyTelemetryProvider.prototype.supportsLimits = function (domainObject) {
var oldObject = this.instantiate(
utils.toOldFormat(domainObject),
utils.makeKeyString(domainObject.identifier));
var limitEvaluator = oldObject.getCapability("limit");
utils.makeKeyString(domainObject.identifier)
);
return oldObject.hasCapability("limit");
};
if (!limitEvaluator) {
return {
evaluate: function () {}
};
}
LegacyTelemetryProvider.prototype.getLimitEvaluator = function (domainObject) {
var oldObject = this.instantiate(
utils.toOldFormat(domainObject),
utils.makeKeyString(domainObject.identifier)
);
var limitEvaluator = oldObject.getCapability("limit");
return {
evaluate: function (datum, property) {
@@ -166,6 +169,7 @@ define([
openmct.telemetry.legacyProvider = provider;
openmct.telemetry.requestProviders.push(provider);
openmct.telemetry.subscriptionProviders.push(provider);
openmct.telemetry.limitProviders.push(provider);
};
});

View File

@@ -3,7 +3,7 @@
* 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
* Open openmct 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.
@@ -14,20 +14,22 @@
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* Open openmct 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 console*/
define([
'./TelemetryMetadataManager',
'./TelemetryValueFormatter',
'./DefaultMetadataProvider',
'../objects/object-utils',
'lodash'
], function (
TelemetryMetadataManager,
TelemetryValueFormatter,
DefaultMetadataProvider,
objectUtils,
_
) {
@@ -122,7 +124,6 @@ define([
*/
/**
* An interface for retrieving telemetry data associated with a domain
* object.
@@ -131,15 +132,29 @@ define([
* @augments module:openmct.TelemetryAPI~TelemetryProvider
* @memberof module:openmct
*/
function TelemetryAPI(MCT) {
this.MCT = MCT;
function TelemetryAPI(openmct) {
this.openmct = openmct;
this.requestProviders = [];
this.subscriptionProviders = [];
this.metadataProviders = [new DefaultMetadataProvider(this.openmct)];
this.limitProviders = [];
this.metadataCache = new WeakMap();
this.formatMapCache = new WeakMap();
this.valueFormatterCache = new WeakMap();
}
/**
* Return true if the given domainObject is a telemetry object. A telemetry
* object is any object which has telemetry metadata-- regardless of whether
* the telemetry object has an available telemetry provider.
*
* @param {module:openmct.DomainObject} domainObject
* @returns {boolean} true if the object is a telemetry object.
*/
TelemetryAPI.prototype.isTelemetryObject = function (domainObject) {
return !!this.findMetadataProvider(domainObject);
};
/**
* Check if this provider can supply telemetry data associated with
* this domain object.
@@ -151,6 +166,11 @@ define([
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
TelemetryAPI.prototype.canProvideTelemetry = function (domainObject) {
console.warn(
'DEPRECATION WARNING: openmct.telemetry.canProvideTelemetry ' +
'will not be supported in future versions of Open MCT. Please ' +
'use openmct.telemetry.isTelemetryObject instead.'
);
return !!this.findSubscriptionProvider(domainObject) ||
!!this.findRequestProvider(domainObject);
};
@@ -170,6 +190,12 @@ define([
if (provider.supportsSubscribe) {
this.subscriptionProviders.unshift(provider);
}
if (provider.supportsMetadata) {
this.metadataProviders.unshift(provider);
}
if (provider.supportsLimits) {
this.limitProviders.unshift(provider);
}
};
/**
@@ -196,18 +222,36 @@ define([
return this.requestProviders.filter(supportsDomainObject)[0];
};
/**
* @private
*/
TelemetryAPI.prototype.findMetadataProvider = function (domainObject) {
return this.metadataProviders.filter(function (p) {
return p.supportsMetadata(domainObject);
})[0];
};
/**
* @private
*/
TelemetryAPI.prototype.findLimitEvaluator = function (domainObject) {
return this.limitProviders.filter(function (p) {
return p.supportsLimits(domainObject);
})[0];
};
/**
* @private
*/
TelemetryAPI.prototype.standardizeRequestOptions = function (options) {
if (!options.hasOwnProperty('start')) {
options.start = this.MCT.time.bounds().start;
options.start = this.openmct.time.bounds().start;
}
if (!options.hasOwnProperty('end')) {
options.end = this.MCT.time.bounds().end;
options.end = this.openmct.time.bounds().end;
}
if (!options.hasOwnProperty('domain')) {
options.domain = this.MCT.time.timeSystem().key;
options.domain = this.openmct.time.timeSystem().key;
}
};
@@ -300,12 +344,15 @@ define([
*/
TelemetryAPI.prototype.getMetadata = function (domainObject) {
if (!this.metadataCache.has(domainObject)) {
if (!this.typeService) {
this.typeService = this.MCT.$injector.get('typeService');
var metadataProvider = this.findMetadataProvider(domainObject);
if (!metadataProvider) {
return;
}
var metadata = metadataProvider.getMetadata(domainObject);
this.metadataCache.set(
domainObject,
new TelemetryMetadataManager(domainObject, this.typeService)
new TelemetryMetadataManager(metadata)
);
}
return this.metadataCache.get(domainObject);
@@ -343,7 +390,7 @@ define([
TelemetryAPI.prototype.getValueFormatter = function (valueMetadata) {
if (!this.valueFormatterCache.has(valueMetadata)) {
if (!this.formatService) {
this.formatService = this.MCT.$injector.get('formatService');
this.formatService = this.openmct.$injector.get('formatService');
}
this.valueFormatterCache.set(
valueMetadata,
@@ -375,7 +422,7 @@ define([
* @param {Format} format the
*/
TelemetryAPI.prototype.addFormat = function (format) {
this.MCT.legacyExtension('formats', {
this.openmct.legacyExtension('formats', {
key: format.key,
implementation: function () {
return format;
@@ -385,7 +432,9 @@ define([
/**
* Get a limit evaluator for this domain object.
* Limit Evaluators help you evaluate limit and alarm status of individual telemetry datums for display purposes without having to interact directly with the Limit API.
* Limit Evaluators help you evaluate limit and alarm status of individual
* telemetry datums for display purposes without having to interact directly
* with the Limit API.
*
* This method is optional.
* If a provider does not implement this method, it is presumed
@@ -397,8 +446,34 @@ define([
* @method limitEvaluator
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
TelemetryAPI.prototype.limitEvaluator = function () {
return this.legacyProvider.limitEvaluator.apply(this.legacyProvider, arguments);
TelemetryAPI.prototype.limitEvaluator = function (domainObject) {
return this.getLimitEvaluator(domainObject);
};
/**
* Get a limit evaluator for this domain object.
* Limit Evaluators help you evaluate limit and alarm status of individual
* telemetry datums for display purposes without having to interact directly
* with the Limit API.
*
* This method is optional.
* If a provider does not implement this method, it is presumed
* that no limits are defined for this domain object's telemetry.
*
* @param {module:openmct.DomainObject} domainObject the domain
* object for which to evaluate limits
* @returns {module:openmct.TelemetryAPI~LimitEvaluator}
* @method limitEvaluator
* @memberof module:openmct.TelemetryAPI~TelemetryProvider#
*/
TelemetryAPI.prototype.getLimitEvaluator = function (domainObject) {
var provider = this.findLimitEvaluator(domainObject);
if (!provider) {
return {
evaluate: function () {}
};
}
return provider.getLimitEvaluator(domainObject);
};
return TelemetryAPI;

View File

@@ -1,3 +1,25 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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([
'./TelemetryAPI'
], function (

View File

@@ -27,51 +27,6 @@ define([
_
) {
function valueMetadatasFromOldFormat(metadata) {
var valueMetadatas = [];
valueMetadatas.push({
key: 'name',
name: 'Name'
});
metadata.domains.forEach(function (domain, index) {
var valueMetadata = _.clone(domain);
valueMetadata.hints = {
domain: index + 1
};
valueMetadatas.push(valueMetadata);
});
metadata.ranges.forEach(function (range, index) {
var valueMetadata = _.clone(range);
valueMetadata.hints = {
range: index,
priority: index + metadata.domains.length + 1
};
if (valueMetadata.type === 'enum') {
valueMetadata.key = 'enum';
valueMetadata.hints.y -= 10;
valueMetadata.hints.range -= 10;
valueMetadata.enumerations =
_.sortBy(valueMetadata.enumerations.map(function (e) {
return {
string: e.string,
value: +e.value
};
}), 'e.value');
valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
valueMetadata.max = _.max(valueMetadata.values);
valueMetadata.min = _.min(valueMetadata.values);
}
valueMetadatas.push(valueMetadata);
});
return valueMetadatas;
}
function applyReasonableDefaults(valueMetadata, index) {
valueMetadata.source = valueMetadata.source || valueMetadata.key;
valueMetadata.hints = valueMetadata.hints || {};
@@ -119,24 +74,14 @@ define([
}
/**
* Utility class for handling telemetry metadata for a domain object.
* Wraps old format metadata to new format metadata.
* Provides methods for interrogating telemetry metadata.
* Utility class for handling and inspecting telemetry metadata. Applies
* reasonable defaults to simplify the task of providing metadata, while
* also providing methods for interrogating telemetry metadata.
*/
function TelemetryMetadataManager(domainObject, typeService) {
this.metadata = domainObject.telemetry || {};
function TelemetryMetadataManager(metadata) {
this.metadata = metadata;
if (this.metadata.values) {
this.valueMetadatas = this.metadata.values;
} else {
var typeMetadata = typeService
.getType(domainObject.type).typeDef.telemetry;
_.extend(this.metadata, typeMetadata);
this.valueMetadatas = valueMetadatasFromOldFormat(this.metadata);
}
this.valueMetadatas = this.valueMetadatas.map(applyReasonableDefaults);
this.valueMetadatas = this.metadata.values.map(applyReasonableDefaults);
}
/**

View File

@@ -90,7 +90,7 @@ define(['./Type'], function (Type) {
/**
* Retrieve a registered type by its key.
* @method get
* @param {string} typeKey the key for htis type
* @param {string} typeKey the key for this type
* @memberof module:openmct.TypeRegistry#
* @returns {module:openmct.Type} the registered type
*/

View File

@@ -0,0 +1,55 @@
/*****************************************************************************
* 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(['./TypeRegistry', './Type'], function (TypeRegistry, Type) {
describe('The Type API', function () {
var typeRegistryInstance;
beforeEach(function () {
typeRegistryInstance = new TypeRegistry ();
typeRegistryInstance.addType('testType', {
name: 'Test Type',
description: 'This is a test type.',
creatable: true
});
});
it('types can be standardized', function () {
typeRegistryInstance.addType('standardizationTestType', {
label: 'Test Type',
description: 'This is a test type.',
creatable: true
});
typeRegistryInstance.standardizeType(typeRegistryInstance.types.standardizationTestType);
expect(typeRegistryInstance.get('standardizationTestType').definition.label).toBeUndefined();
expect(typeRegistryInstance.get('standardizationTestType').definition.name).toBe('Test Type');
});
it('new types are registered successfully and can be retrieved', function () {
expect(typeRegistryInstance.get('testType').definition.name).toBe('Test Type');
});
it('type registry contains new keys', function () {
expect(typeRegistryInstance.listKeys ()).toContain('testType');
});
});
});

View File

@@ -38,7 +38,6 @@ define([
'../example/msl/bundle',
'../example/notifications/bundle',
'../example/persistence/bundle',
'../example/plotOptions/bundle',
'../example/policy/bundle',
'../example/profiling/bundle',
'../example/scratchpad/bundle',

View File

@@ -166,11 +166,6 @@
ng-show="plotHistory.length"
style="position: absolute; top: 8px; right: 8px;">
<a class="s-button icon-brackets"
ng-click="plot.syncConductor()"
title="Synchronize Time Conductor to plot bounds">
</a>
<a class="s-button icon-arrow-left"
ng-click="plot.back()"
title="Restore previous pan/zoom">

View File

@@ -75,7 +75,6 @@ function (
this.$scope.$watch('highlights', this.scheduleDraw);
this.$scope.$watch('rectangles', this.scheduleDraw);
this.config.series.forEach(this.onSeriesAdd, this);
window.chart = this;
}
eventHelpers.extend(MCTChartController.prototype);

View File

@@ -85,6 +85,19 @@ define([
this.listenTo(this, 'destroy', this.onDestroy, this);
},
/**
* Retrieve the persisted series config for a given identifier.
*/
getPersistedSeriesConfig: function (identifier) {
var domainObject = this.get('domainObject');
if (!domainObject.configuration || !domainObject.configuration.series) {
return;
}
return domainObject.configuration.series.filter(function (seriesConfig) {
return seriesConfig.identifier.key === identifier.key &&
seriesConfig.identifier.namespace === identifier.namespace;
})[0];
},
/**
* Update the domain object with the given value.
*/

View File

@@ -165,10 +165,13 @@ define([
return;
}
var valueMetadata = this.metadata.value(newKey);
if (valueMetadata.format === 'enum') {
this.set('interpolate', 'stepAfter');
} else {
this.set('interpolate', 'linear');
var persistedConfig = this.get('persistedConfiguration');
if (!persistedConfig || !persistedConfig.interpolate) {
if (valueMetadata.format === 'enum') {
this.set('interpolate', 'stepAfter');
} else {
this.set('interpolate', 'linear');
}
}
this.evaluate = function (datum) {
return this.limitEvaluator.evaluate(datum, valueMetadata);
@@ -233,8 +236,6 @@ define([
* @returns {Promise}
*/
load: function (options) {
this.resetOnAppend = true;
return this.fetch(options)
.then(function (res) {
this.emit('load');

View File

@@ -41,6 +41,7 @@ define([
this.palette = new color.ColorPalette();
this.listenTo(this, 'add', this.onSeriesAdd, this);
this.listenTo(this, 'remove', this.onSeriesRemove, this);
this.listenTo(this.plot, 'change:domainObject', this.trackPersistedConfig, this);
var domainObject = this.plot.get('domainObject');
if (domainObject.telemetry) {
@@ -49,6 +50,14 @@ define([
this.watchTelemetryContainer(domainObject);
}
},
trackPersistedConfig: function (domainObject) {
domainObject.configuration.series.forEach(function (seriesConfig) {
var series = this.byIdentifier(seriesConfig.identifier);
if (series) {
series.set('persistedConfiguration', seriesConfig);
}
}, this);
},
watchTelemetryContainer: function (domainObject) {
var composition = this.openmct.composition.get(domainObject);
this.listenTo(composition, 'add', this.addTelemetryObject, this);
@@ -75,6 +84,9 @@ define([
seriesConfig = JSON.parse(JSON.stringify(seriesConfig));
}
seriesConfig.persistedConfiguration =
this.plot.getPersistedSeriesConfig(domainObject.identifier);
this.add(new PlotSeries({
model: seriesConfig,
domainObject: domainObject,
@@ -125,12 +137,19 @@ define([
},
updateColorPalette: function (newColor, oldColor) {
this.palette.remove(newColor);
var seriesWithColor = this.series.filter(function (series) {
var seriesWithColor = this.filter(function (series) {
return series.get('color') === newColor;
})[0];
if (!seriesWithColor) {
this.palette.return(oldColor);
}
},
byIdentifier: function (identifier) {
return this.filter(function (series) {
var seriesIdentifier = series.get('identifier');
return seriesIdentifier.namespace === identifier.namespace &&
seriesIdentifier.key === identifier.key;
})[0];
}
});

View File

@@ -30,7 +30,7 @@ define(
var CHARTS = [
{
MAX_INSTANCES: 16,
MAX_INSTANCES: 14,
API: DrawWebGL,
ALLOCATIONS: []
},

View File

@@ -48,7 +48,6 @@ define([
this.configId = $scope.domainObject.getId();
this.setUpScope();
window.config = this;
}
eventHelpers.extend(PlotOptionsController.prototype);

View File

@@ -33,13 +33,12 @@ define([
* It supports pan and zoom, implements zoom history, and supports locating
* values near the cursor.
*/
function MCTPlotController($scope, $element, $window, openmct) {
function MCTPlotController($scope, $element, $window) {
this.$scope = $scope;
this.$scope.config = this.config;
this.$scope.plot = this;
this.$element = $element;
this.$window = $window;
this.openmct = openmct;
this.xScale = new LinearScale(this.config.xAxis.get('displayRange'));
this.yScale = new LinearScale(this.config.yAxis.get('displayRange'));
@@ -54,7 +53,6 @@ define([
this.listenTo(this.$scope, 'plot:clearHistory', this.clear, this);
this.initialize();
window.control = this;
}
MCTPlotController.$inject = ['$scope', '$element', '$window'];
@@ -333,13 +331,6 @@ define([
this.$scope.$emit('user:viewport:change:end');
};
MCTPlotController.prototype.syncConductor = function () {
var xDisplayRange = this.config.xAxis.get('displayRange');
this.openmct.time.stopClock();
this.openmct.time.bounds({start: xDisplayRange.min, end: xDisplayRange.max});
};
MCTPlotController.prototype.destroy = function () {
this.stopListening();
};

View File

@@ -33,7 +33,7 @@ define([
return {
restrict: "E",
template: PlotTemplate,
controller: ['$scope', '$element', '$window', 'openmct', MCTPlotController],
controller: MCTPlotController,
controllerAs: 'mctPlotController',
bindToController: {
config: "="

View File

@@ -117,12 +117,6 @@ define([
this.$scope = $scope;
this.$element = $element;
if (!window.ticks) {
window.ticks = [];
}
window.ticks.push(this);
this.tickCount = 4;
this.tickUpdate = false;
this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this);

View File

@@ -141,6 +141,17 @@ define(
});
};
/**
* Takes a screenshot of a DOM node in PNG format.
* @param {node} element to be exported
* @param {string} filename the exported image
* @returns {promise}
*/
ExportImageService.prototype.exportPNGtoSRC = function (element) {
return this.renderElement(element, "png");
};
/**
* canvas.toBlob() not supported in IE < 10, Opera, and Safari. This polyfill
* implements the method in browsers that would not otherwise support it.

View File

@@ -71,7 +71,6 @@ define([
this.config.series.forEach(this.addSeries, this);
this.followTimeConductor();
window.plot = this;
}
eventHelpers.extend(PlotController.prototype);
@@ -83,6 +82,10 @@ define([
};
PlotController.prototype.loadSeriesData = function (series) {
if (this.$element[0].offsetWidth === 0) {
this.scheduleLoad(series);
return;
}
this.startLoading();
var options = {
size: this.$element[0].offsetWidth,
@@ -93,6 +96,26 @@ define([
.then(this.stopLoading.bind(this));
};
PlotController.prototype.scheduleLoad = function (series) {
if (!this.scheduledLoads) {
this.startLoading();
this.scheduledLoads = [];
this.checkForSize = setInterval(function () {
if (this.$element[0].offsetWidth === 0) {
return;
}
this.stopLoading();
this.scheduledLoads.forEach(this.loadSeriesData, this);
delete this.scheduledLoads;
clearInterval(this.checkForSize);
delete this.checkForSize;
}.bind(this));
}
if (this.scheduledLoads.indexOf(series) === -1) {
this.scheduledLoads.push(series);
}
};
PlotController.prototype.addSeries = function (series) {
this.listenTo(series, 'change:yKey', function () {
this.loadSeriesData(series);
@@ -127,6 +150,10 @@ define([
PlotController.prototype.destroy = function () {
configStore.untrack(this.config.id);
this.stopListening();
if (this.checkForSize) {
clearInterval(this.checkForSize);
delete this.checkForSize;
}
};
PlotController.prototype.loadMoreData = function (range, purge) {

View File

@@ -27,11 +27,13 @@ define([
'./autoflow/AutoflowTabularPlugin',
'./timeConductor/plugin',
'../../example/imagery/plugin',
'../../platform/features/notebook/bundle',
'../../platform/import-export/bundle',
'./summaryWidget/plugin',
'./URLIndicatorPlugin/URLIndicatorPlugin',
'./telemetryMean/plugin',
'./plot/plugin'
'./plot/plugin',
'./staticRootPlugin/plugin'
], function (
_,
UTCTimeSystem,
@@ -39,11 +41,13 @@ define([
AutoflowPlugin,
TimeConductorPlugin,
ExampleImagery,
Notebook,
ImportExport,
SummaryWidget,
URLIndicatorPlugin,
TelemetryMean,
PlotPlugin
PlotPlugin,
StaticRootPlugin
) {
var bundleMap = {
CouchDB: 'platform/persistence/couch',
@@ -51,6 +55,7 @@ define([
Espresso: 'platform/commonUI/themes/espresso',
LocalStorage: 'platform/persistence/local',
MyItems: 'platform/features/my-items',
Notebook: 'platform/features/notebook',
Snow: 'platform/commonUI/themes/snow'
};
@@ -66,6 +71,8 @@ define([
plugins.ImportExport = ImportExport;
plugins.StaticRootPlugin = StaticRootPlugin;
/**
* A tabular view showing the latest values of multiple telemetry points at
* once. Formatted so that labels and values are aligned.

View File

@@ -0,0 +1,78 @@
define([
'../../api/objects/object-utils'
], function (
objectUtils
) {
/**
* Transforms an import json blob into a object map that can be used to
* provide objects. Rewrites root identifier in import data with provided
* rootIdentifier, and rewrites all child object identifiers so that they
* exist in the same namespace as the rootIdentifier.
*/
function rewriteObjectIdentifiers(importData, rootIdentifier) {
var rootId = importData.rootId;
var objectString = JSON.stringify(importData.openmct);
Object.keys(importData.openmct).forEach(function (originalId, i) {
var newId;
if (originalId === rootId) {
newId = objectUtils.makeKeyString(rootIdentifier);
} else {
newId = objectUtils.makeKeyString({
namespace: rootIdentifier.namespace,
key: i
});
}
while (objectString.indexOf(originalId) !== -1) {
objectString = objectString.replace(
'"' + originalId + '"',
'"' + newId + '"'
);
}
});
return JSON.parse(objectString);
}
/**
* Convets all objects in an object make from old format objects to new
* format objects.
*/
function convertToNewObjects(oldObjectMap) {
return Object.keys(oldObjectMap)
.reduce(function (newObjectMap, key) {
newObjectMap[key] = objectUtils.toNewFormat(oldObjectMap[key], key);
return newObjectMap;
}, {});
}
/* Set the root location correctly for a top-level object */
function setRootLocation(objectMap, rootIdentifier) {
objectMap[objectUtils.makeKeyString(rootIdentifier)].location = 'ROOT';
return objectMap;
}
/**
* Takes importData (as provided by the ImportExport plugin) and exposes
* an object provider to fetch those objects.
*/
function StaticModelProvider(importData, rootIdentifier) {
var oldFormatObjectMap = rewriteObjectIdentifiers(importData, rootIdentifier);
var newFormatObjectMap = convertToNewObjects(oldFormatObjectMap);
this.objectMap = setRootLocation(newFormatObjectMap, rootIdentifier);
}
/**
* Standard "Get".
*/
StaticModelProvider.prototype.get = function (identifier) {
var keyString = objectUtils.makeKeyString(identifier);
if (this.objectMap[keyString]) {
return this.objectMap[keyString];
}
throw new Error(keyString + ' not found in import models.');
};
return StaticModelProvider;
});

View File

@@ -0,0 +1,133 @@
define([
'./StaticModelProvider',
'text!./static-provider-test.json'
], function (
StaticModelProvider,
testStaticDataText
) {
describe('StaticModelProvider', function () {
var staticProvider;
beforeEach(function () {
var staticData = JSON.parse(testStaticDataText);
staticProvider = new StaticModelProvider(staticData, {
namespace: 'my-import',
key: 'root'
});
});
describe('rootObject', function () {
var rootModel;
beforeEach(function () {
rootModel = staticProvider.get({
namespace: 'my-import',
key: 'root'
});
});
it('is located at top level', function () {
expect(rootModel.location).toBe('ROOT');
});
it('has new-format identifier', function () {
expect(rootModel.identifier).toEqual({
namespace: 'my-import',
key: 'root'
});
});
it('has new-format composition', function () {
expect(rootModel.composition).toContain({
namespace: 'my-import',
key: '1'
});
expect(rootModel.composition).toContain({
namespace: 'my-import',
key: '2'
});
});
});
describe('childObjects', function () {
var swg;
var layout;
var fixed;
beforeEach(function () {
swg = staticProvider.get({
namespace: 'my-import',
key: '1'
});
layout = staticProvider.get({
namespace: 'my-import',
key: '2'
});
fixed = staticProvider.get({
namespace: 'my-import',
key: '3'
});
});
it('match expected ordering', function () {
// this is a sanity check to make sure the identifiers map in
// the correct order.
expect(swg.type).toBe('generator');
expect(layout.type).toBe('layout');
expect(fixed.type).toBe('telemetry.fixed');
});
it('have new-style identifiers', function () {
expect(swg.identifier).toEqual({
namespace: 'my-import',
key: '1'
});
expect(layout.identifier).toEqual({
namespace: 'my-import',
key: '2'
});
expect(fixed.identifier).toEqual({
namespace: 'my-import',
key: '3'
});
});
it('have new-style composition', function () {
expect(layout.composition).toContain({
namespace: 'my-import',
key: '1'
});
expect(layout.composition).toContain({
namespace: 'my-import',
key: '3'
});
expect(fixed.composition).toContain({
namespace: 'my-import',
key: '1'
});
});
it('rewrites locations', function () {
expect(swg.location).toBe('my-import:root');
expect(layout.location).toBe('my-import:root');
expect(fixed.location).toBe('my-import:2');
});
it('rewrites matched identifiers in objects', function () {
expect(layout.configuration.layout.panels['my-import:1'])
.toBeDefined();
expect(layout.configuration.layout.panels['my-import:3'])
.toBeDefined();
expect(layout.configuration.layout.panels['483c00d4-bb1d-4b42-b29a-c58e06b322a0'])
.not.toBeDefined();
expect(layout.configuration.layout.panels['20273193-f069-49e9-b4f7-b97a87ed755d'])
.not.toBeDefined();
expect(fixed.configuration['fixed-display'].elements[0].id)
.toBe('my-import:1');
});
});
});
});

View File

@@ -0,0 +1,51 @@
define([
'./StaticModelProvider'
], function (
StaticModelProvider
) {
/**
* Static Root Plugin: takes an export file and exposes it as a new root
* object.
*/
function StaticRootPlugin(namespace, exportUrl) {
var rootIdentifier = {
namespace: namespace,
key: 'root'
};
var cachedProvider;
var loadProvider = function () {
return fetch(exportUrl)
.then(function (response) {
return response.json();
})
.then(function (importData) {
cachedProvider = new StaticModelProvider(importData, rootIdentifier);
return cachedProvider;
});
};
var getProvider = function () {
if (!cachedProvider) {
cachedProvider = loadProvider();
}
return Promise.resolve(cachedProvider);
};
return function install(openmct) {
openmct.objects.addRoot(rootIdentifier);
openmct.objects.addProvider(namespace, {
get: function (identifier) {
return getProvider().then(function (provider) {
return provider.get(identifier);
});
}
});
};
}
return StaticRootPlugin;
});

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