Compare commits
198 Commits
revert-126
...
api-tutori
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0de23091b | ||
|
|
722e384df4 | ||
|
|
ab83beb17c | ||
|
|
370421b4a5 | ||
|
|
3e5f4c8b88 | ||
|
|
b1f1568657 | ||
|
|
33d24c9b60 | ||
|
|
33c060473a | ||
|
|
3b01b9ee51 | ||
|
|
26a0aed159 | ||
|
|
1c4a67ebd1 | ||
|
|
f6fd572e4f | ||
|
|
2ec1f76fc5 | ||
|
|
8f618a1f35 | ||
|
|
6a89e6da50 | ||
|
|
fa962b42bc | ||
|
|
b28eb049dc | ||
|
|
e712edba4e | ||
|
|
35d8024aaa | ||
|
|
17564aa489 | ||
|
|
3ae0fd7bc9 | ||
|
|
df7d59bc9c | ||
|
|
4f24c46e9b | ||
|
|
d262c4428e | ||
|
|
ea74385ac8 | ||
|
|
06cc95efb1 | ||
|
|
09ebeeb8e4 | ||
|
|
7e8e861468 | ||
|
|
df2ce72e39 | ||
|
|
fe8398017c | ||
|
|
c2253f5010 | ||
|
|
290dd0abf0 | ||
|
|
49560698f6 | ||
|
|
1ce1d29c87 | ||
|
|
d522d105ad | ||
|
|
72b753c67f | ||
|
|
16ec65f38c | ||
|
|
ed67866f45 | ||
|
|
36b5197733 | ||
|
|
fa28393c14 | ||
|
|
077f076c43 | ||
|
|
c7ae520d7e | ||
|
|
39eb7ba862 | ||
|
|
116bb2c25f | ||
|
|
6bf293f96b | ||
|
|
eceaa38ed8 | ||
|
|
b6aa087536 | ||
|
|
d94e8b10d8 | ||
|
|
590a0fe080 | ||
|
|
41d0e953f5 | ||
|
|
3436455201 | ||
|
|
bb63e13770 | ||
|
|
62685cb892 | ||
|
|
6fe0ce70eb | ||
|
|
bee22311a7 | ||
|
|
2cde80237f | ||
|
|
3b93454c53 | ||
|
|
a5a17b9502 | ||
|
|
381f3d9b69 | ||
|
|
94319df69b | ||
|
|
1666c42f78 | ||
|
|
f1870e286d | ||
|
|
94194ff675 | ||
|
|
d37caa7665 | ||
|
|
a768b12985 | ||
|
|
6f257593c8 | ||
|
|
c4d47ddc26 | ||
|
|
60d1b73160 | ||
|
|
96c054415d | ||
|
|
89be1c810a | ||
|
|
6328bd9354 | ||
|
|
fcda211800 | ||
|
|
daa71c4f69 | ||
|
|
f2d61604f7 | ||
|
|
f0b9292458 | ||
|
|
0b79ec1235 | ||
|
|
b9601ff819 | ||
|
|
6f417fc4c7 | ||
|
|
d99b4d35ab | ||
|
|
6936ab6100 | ||
|
|
532f7a76f9 | ||
|
|
09e79d38ff | ||
|
|
1c40fa88ce | ||
|
|
a4ff5ffb43 | ||
|
|
799c418124 | ||
|
|
215e9b26a8 | ||
|
|
43132ea6f8 | ||
|
|
90a7ca8ae5 | ||
|
|
f077f4fc1b | ||
|
|
0cd849fe42 | ||
|
|
06a24e3a7d | ||
|
|
abd9fdec38 | ||
|
|
bfdbb4dacb | ||
|
|
b5b68e7439 | ||
|
|
a920220122 | ||
|
|
83ed4f6b0d | ||
|
|
6a32c53d05 | ||
|
|
0785129b7f | ||
|
|
45de84c183 | ||
|
|
b2da0cb12f | ||
|
|
e121c0f8ac | ||
|
|
bf006b45e4 | ||
|
|
a3f5873501 | ||
|
|
86b337ec88 | ||
|
|
7dda85cc5f | ||
|
|
68d6920d38 | ||
|
|
f540fd08fa | ||
|
|
aaf4877daa | ||
|
|
828e5629c5 | ||
|
|
44ceb4e865 | ||
|
|
88f954b437 | ||
|
|
f85595665f | ||
|
|
73b3ae7264 | ||
|
|
eaae401d16 | ||
|
|
efed5f68af | ||
|
|
79b4f9a0f4 | ||
|
|
c98286d426 | ||
|
|
d368bc2351 | ||
|
|
6a7b77ccf5 | ||
|
|
242eb6d733 | ||
|
|
2991e9894d | ||
|
|
3dc8bc87fc | ||
|
|
cfb99eaf80 | ||
|
|
5dee588c36 | ||
|
|
2f8c03ecb2 | ||
|
|
2dfde9a612 | ||
|
|
3dade275d4 | ||
|
|
2f5dc8a887 | ||
|
|
9b11684ae9 | ||
|
|
251e3b5646 | ||
|
|
03b47b43ad | ||
|
|
18e51aaff7 | ||
|
|
5c31c6084c | ||
|
|
b1464efdaf | ||
|
|
3e6e068f7f | ||
|
|
f7a08c7087 | ||
|
|
dfa4591834 | ||
|
|
4d3ec398c9 | ||
|
|
b169089156 | ||
|
|
5b6f95bd4a | ||
|
|
66a6b6d89b | ||
|
|
d74eba1922 | ||
|
|
9a7f69a614 | ||
|
|
0578a651da | ||
|
|
f991dcfb76 | ||
|
|
42c48cb93b | ||
|
|
67b763c4c0 | ||
|
|
2708562872 | ||
|
|
9578fb0cd8 | ||
|
|
a728f2368c | ||
|
|
547696d797 | ||
|
|
025b69541e | ||
|
|
5aa95c0415 | ||
|
|
d63c401e44 | ||
|
|
c9ac85089a | ||
|
|
a8c9b6f7fe | ||
|
|
eca9968a9f | ||
|
|
736c89cfc6 | ||
|
|
9a0fcc045c | ||
|
|
a3459679d0 | ||
|
|
12333f3417 | ||
|
|
2bf05ae40f | ||
|
|
833bad067e | ||
|
|
23eff4b924 | ||
|
|
30b769d741 | ||
|
|
d813029046 | ||
|
|
81de6119fe | ||
|
|
365af918f3 | ||
|
|
40fb144d09 | ||
|
|
8cacff37ab | ||
|
|
70985c5dbd | ||
|
|
9dec99824e | ||
|
|
d4730e1656 | ||
|
|
c079868224 | ||
|
|
54a59c5e6f | ||
|
|
0804a16314 | ||
|
|
4cc020f0ea | ||
|
|
b49fef78f5 | ||
|
|
2cced53c97 | ||
|
|
f6253ae7ed | ||
|
|
3f50bdb334 | ||
|
|
2a79813460 | ||
|
|
650824574c | ||
|
|
7b0506bbdb | ||
|
|
a3847bcca5 | ||
|
|
a143b21ea1 | ||
|
|
bfdf7b822f | ||
|
|
7dde924fcc | ||
|
|
d1d2067ad5 | ||
|
|
9456370077 | ||
|
|
6d2c5f7fd4 | ||
|
|
1f6ca8bcc3 | ||
|
|
e19edbb27a | ||
|
|
f2fe6a9885 | ||
|
|
0442a31153 | ||
|
|
e03c725b86 | ||
|
|
77d4760945 | ||
|
|
4ec0d0633d |
@@ -14,7 +14,8 @@
|
||||
"nonew": true,
|
||||
"predef": [
|
||||
"define",
|
||||
"Promise"
|
||||
"Promise",
|
||||
"WeakMap"
|
||||
],
|
||||
"shadow": "outer",
|
||||
"strict": "implied",
|
||||
|
||||
69
API.md
69
API.md
@@ -71,9 +71,10 @@ Custom types may be registered via
|
||||
[`openmct.types`]{@link module:openmct.MCT#types}:
|
||||
|
||||
```
|
||||
openmct.types.addType('my-type', new openmct.Type({
|
||||
openmct.types.addType('my-type', {
|
||||
label: "My Type",
|
||||
description: "This is a type that I added!"
|
||||
description: "This is a type that I added!",
|
||||
creatable: true
|
||||
});
|
||||
```
|
||||
|
||||
@@ -128,8 +129,10 @@ provider.
|
||||
|
||||
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 for composition, but there may be cases where you want
|
||||
to provide the composition of a certain object (or type of object) dynamically.
|
||||
[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.
|
||||
|
||||
For instance, you may want to populate a hierarchy under a custom root-level
|
||||
object based on the contents of a telemetry dictionary.
|
||||
To do this, you can add a new CompositionProvider:
|
||||
@@ -145,6 +148,29 @@ openmct.composition.addProvider({
|
||||
});
|
||||
```
|
||||
|
||||
#### 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.:
|
||||
|
||||
```js
|
||||
var domainObject = {
|
||||
name: "My Object",
|
||||
type: 'folder',
|
||||
composition: [
|
||||
{
|
||||
key: '412229c3-922c-444b-8624-736d85516247',
|
||||
namespace: 'foo'
|
||||
},
|
||||
{
|
||||
key: 'd6e0ce02-5b85-4e55-8006-a8a505b64c75',
|
||||
namespace: 'foo'
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
### Adding Telemetry Providers
|
||||
|
||||
When connecting to a new telemetry source, you will want to register a new
|
||||
@@ -264,6 +290,41 @@ openmct.install(myPlugin);
|
||||
|
||||
The plugin will be invoked to configure Open MCT before it is started.
|
||||
|
||||
### Included Plugins
|
||||
|
||||
Open MCT is packaged along with a few general-purpose plugins:
|
||||
|
||||
* `openmct.plugins.CouchDB` is an adapter for using CouchDB for persistence
|
||||
of user-created objects. This is a constructor that takes the URL for the
|
||||
CouchDB database as a parameter, e.g.
|
||||
`openmct.install(new openmct.plugins.CouchDB('http://localhost:5984/openmct'))`
|
||||
* `openmct.plugins.Elasticsearch` is an adapter for using Elasticsearch for
|
||||
persistence of user-created objects. This is a
|
||||
constructor that takes the URL for the Elasticsearch instance as a
|
||||
parameter, e.g.
|
||||
`openmct.install(new openmct.plugins.CouchDB('http://localhost:9200'))`.
|
||||
Domain objects will be indexed at `/mct/domain_object`.
|
||||
* `openmct.plugins.espresso` and `openmct.plugins.snow` are two different
|
||||
themes (dark and light) available for Open MCT. Note that at least one
|
||||
of these themes must be installed for Open MCT to appear correctly.
|
||||
* `openmct.plugins.localStorage` provides persistence of user-created
|
||||
objects in browser-local storage. This is particularly useful in
|
||||
development environments.
|
||||
* `openmct.plugins.myItems` adds a top-level folder named "My Items"
|
||||
when the application is first started, providing a place for a
|
||||
user to store created items.
|
||||
* `openmct.plugins.utcTimeSystem` provides support for using the time
|
||||
conductor with UTC time.
|
||||
|
||||
Generally, you will want to either install these plugins, or install
|
||||
different plugins that provide persistence and an initial folder
|
||||
hierarchy. Installation is as described [above](#installing-plugins):
|
||||
|
||||
```
|
||||
openmct.install(openmct.plugins.localStorage);
|
||||
openmct.install(openmct.plugins.myItems);
|
||||
```
|
||||
|
||||
### Writing Plugins
|
||||
|
||||
Plugins configure Open MCT, and should utilize the
|
||||
|
||||
@@ -1338,55 +1338,6 @@ are supported:
|
||||
|
||||
Open MCT defines several Angular directives that are intended for use both
|
||||
internally within the platform, and by plugins.
|
||||
|
||||
## Before Unload
|
||||
|
||||
The `mct-before-unload` directive is used to listen for (and prompt for user
|
||||
confirmation) of navigation changes in the browser. This includes reloading,
|
||||
following links out of Open MCT, or changing routes. It is used to hook into
|
||||
both `onbeforeunload` event handling as well as route changes from within
|
||||
Angular.
|
||||
|
||||
This directive is useable as an attribute. Its value should be an Angular
|
||||
expression. When an action that would trigger an unload and/or route change
|
||||
occurs, this Angular expression is evaluated. Its result should be a message to
|
||||
display to the user to confirm their navigation change; if this expression
|
||||
evaluates to a falsy value, no message will be displayed.
|
||||
|
||||
## Chart
|
||||
|
||||
The `mct-chart` directive is used to support drawing of simple charts. It is
|
||||
present to support the Plot view, and its functionality is limited to the
|
||||
functionality that is relevant for that view.
|
||||
|
||||
This directive is used at the element level and takes one attribute, `draw`
|
||||
which is an Angular expression which will should evaluate to a drawing object.
|
||||
This drawing object should contain the following properties:
|
||||
|
||||
* `dimensions`: The size, in logical coordinates, of the chart area. A
|
||||
two-element array or numbers.
|
||||
* `origin`: The position, in logical coordinates, of the lower-left corner of
|
||||
the chart area. A two-element array or numbers.
|
||||
* `lines`: An array of lines (e.g. as a plot line) to draw, where each line is
|
||||
expressed as an object containing:
|
||||
* `buffer`: A Float32Array containing points in the line, in logical
|
||||
coordinates, in sequential x,y pairs.
|
||||
* `color`: The color of the line, as a four-element RGBA array, where
|
||||
each element is a number in the range of 0.0-1.0.
|
||||
* `points`: The number of points in the line.
|
||||
* `boxes`: An array of rectangles to draw in the chart area. Each is an object
|
||||
containing:
|
||||
* `start`: The first corner of the rectangle, as a two-element array of
|
||||
numbers, in logical coordinates.
|
||||
* `end`: The opposite corner of the rectangle, as a two-element array of
|
||||
numbers, in logical coordinates. color : The color of the line, as a
|
||||
four-element RGBA array, where each element is a number in the range of
|
||||
0.0-1.0.
|
||||
|
||||
While `mct-chart` is intended to support plots specifically, it does perform
|
||||
some useful management of canvas objects (e.g. choosing between WebGL and Canvas
|
||||
2D APIs for drawing based on browser support) so its usage is recommended when
|
||||
its supported drawing primitives are sufficient for other charting tasks.
|
||||
|
||||
## Container
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,7 @@ define([
|
||||
{
|
||||
"key": "eventGenerator",
|
||||
"name": "Event Message Generator",
|
||||
"cssclass": "icon-folder-new",
|
||||
"cssClass": "icon-folder-new",
|
||||
"description": "For development use. Creates sample event message data that mimics a live data stream.",
|
||||
"priority": 10,
|
||||
"features": "creation",
|
||||
|
||||
@@ -36,7 +36,7 @@ define([
|
||||
"name": "Export Telemetry as CSV",
|
||||
"implementation": ExportTelemetryAsCSVAction,
|
||||
"category": "contextual",
|
||||
"cssclass": "icon-download",
|
||||
"cssClass": "icon-download",
|
||||
"depends": [ "exportService" ]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define([
|
||||
"./src/SinewaveTelemetryProvider",
|
||||
"./src/SinewaveLimitCapability",
|
||||
"./src/SinewaveDeltaFormat",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
SinewaveTelemetryProvider,
|
||||
SinewaveLimitCapability,
|
||||
SinewaveDeltaFormat,
|
||||
legacyRegistry
|
||||
) {
|
||||
"use strict";
|
||||
|
||||
legacyRegistry.register("example/generator", {
|
||||
"name": "Sine Wave Generator",
|
||||
"description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
|
||||
"extensions": {
|
||||
"components": [
|
||||
{
|
||||
"implementation": SinewaveTelemetryProvider,
|
||||
"type": "provider",
|
||||
"provides": "telemetryService",
|
||||
"depends": [
|
||||
"$q",
|
||||
"$timeout"
|
||||
]
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
{
|
||||
"key": "limit",
|
||||
"implementation": SinewaveLimitCapability
|
||||
}
|
||||
],
|
||||
"formats": [
|
||||
{
|
||||
"key": "example.delta",
|
||||
"implementation": SinewaveDeltaFormat
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"key": "TIME_CONDUCTOR_DOMAINS",
|
||||
"value": [
|
||||
{
|
||||
"key": "time",
|
||||
"name": "Time"
|
||||
},
|
||||
{
|
||||
"key": "yesterday",
|
||||
"name": "Yesterday"
|
||||
},
|
||||
{
|
||||
"key": "delta",
|
||||
"name": "Delta",
|
||||
"format": "example.delta"
|
||||
}
|
||||
],
|
||||
"priority": -1
|
||||
}
|
||||
],
|
||||
"types": [
|
||||
{
|
||||
"key": "generator",
|
||||
"name": "Sine Wave Generator",
|
||||
"cssclass": "icon-telemetry",
|
||||
"description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
|
||||
"priority": 10,
|
||||
"features": "creation",
|
||||
"model": {
|
||||
"telemetry": {
|
||||
"period": 10
|
||||
}
|
||||
},
|
||||
"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*)?$"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,119 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* Module defining SinewaveTelemetryProvider. Created by vwoeltje on 11/12/14.
|
||||
*/
|
||||
define(
|
||||
["./SinewaveTelemetrySeries"],
|
||||
function (SinewaveTelemetrySeries) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SinewaveTelemetryProvider($q, $timeout) {
|
||||
var subscriptions = [],
|
||||
generating = false;
|
||||
|
||||
//
|
||||
function matchesSource(request) {
|
||||
return request.source === "generator";
|
||||
}
|
||||
|
||||
// Used internally; this will be repacked by doPackage
|
||||
function generateData(request) {
|
||||
return {
|
||||
key: request.key,
|
||||
telemetry: new SinewaveTelemetrySeries(request)
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
function doPackage(results) {
|
||||
var packaged = {};
|
||||
results.forEach(function (result) {
|
||||
packaged[result.key] = result.telemetry;
|
||||
});
|
||||
// Format as expected (sources -> keys -> telemetry)
|
||||
return { generator: packaged };
|
||||
}
|
||||
|
||||
function requestTelemetry(requests) {
|
||||
return $timeout(function () {
|
||||
return doPackage(requests.filter(matchesSource).map(generateData));
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function handleSubscriptions() {
|
||||
subscriptions.forEach(function (subscription) {
|
||||
var requests = subscription.requests;
|
||||
subscription.callback(doPackage(
|
||||
requests.filter(matchesSource).map(generateData)
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
function startGenerating() {
|
||||
generating = true;
|
||||
$timeout(function () {
|
||||
handleSubscriptions();
|
||||
if (generating && subscriptions.length > 0) {
|
||||
startGenerating();
|
||||
} else {
|
||||
generating = false;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function subscribe(callback, requests) {
|
||||
var subscription = {
|
||||
callback: callback,
|
||||
requests: requests
|
||||
};
|
||||
|
||||
function unsubscribe() {
|
||||
subscriptions = subscriptions.filter(function (s) {
|
||||
return s !== subscription;
|
||||
});
|
||||
}
|
||||
|
||||
subscriptions.push(subscription);
|
||||
|
||||
if (!generating) {
|
||||
startGenerating();
|
||||
}
|
||||
|
||||
return unsubscribe;
|
||||
}
|
||||
|
||||
return {
|
||||
requestTelemetry: requestTelemetry,
|
||||
subscribe: subscribe
|
||||
};
|
||||
}
|
||||
|
||||
return SinewaveTelemetryProvider;
|
||||
}
|
||||
);
|
||||
@@ -1,78 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define,Promise*/
|
||||
|
||||
/**
|
||||
* Module defining SinewaveTelemetry. Created by vwoeltje on 11/12/14.
|
||||
*/
|
||||
define(
|
||||
['./SinewaveConstants'],
|
||||
function (SinewaveConstants) {
|
||||
"use strict";
|
||||
|
||||
var ONE_DAY = 60 * 60 * 24,
|
||||
firstObservedTime = Math.floor(SinewaveConstants.START_TIME / 1000);
|
||||
|
||||
/**
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SinewaveTelemetrySeries(request) {
|
||||
var timeOffset = (request.domain === 'yesterday') ? ONE_DAY : 0,
|
||||
latestTime = Math.floor(Date.now() / 1000) - timeOffset,
|
||||
firstTime = firstObservedTime - timeOffset,
|
||||
endTime = (request.end !== undefined) ?
|
||||
Math.floor(request.end / 1000) : latestTime,
|
||||
count = Math.min(endTime, latestTime) - firstTime,
|
||||
period = +request.period || 30,
|
||||
generatorData = {},
|
||||
requestStart = (request.start === undefined) ? firstTime :
|
||||
Math.max(Math.floor(request.start / 1000), firstTime),
|
||||
offset = requestStart - firstTime;
|
||||
|
||||
if (request.size !== undefined) {
|
||||
offset = Math.max(offset, count - request.size);
|
||||
}
|
||||
|
||||
generatorData.getPointCount = function () {
|
||||
return count - offset;
|
||||
};
|
||||
|
||||
generatorData.getDomainValue = function (i, domain) {
|
||||
// delta uses the same numeric values as the default domain,
|
||||
// so it's not checked for here, just formatted for display
|
||||
// differently.
|
||||
return (i + offset) * 1000 + firstTime * 1000 -
|
||||
(domain === 'yesterday' ? (ONE_DAY * 1000) : 0);
|
||||
};
|
||||
|
||||
generatorData.getRangeValue = function (i, range) {
|
||||
range = range || "sin";
|
||||
return Math[range]((i + offset) * Math.PI * 2 / period);
|
||||
};
|
||||
|
||||
return generatorData;
|
||||
}
|
||||
|
||||
return SinewaveTelemetrySeries;
|
||||
}
|
||||
);
|
||||
@@ -49,7 +49,7 @@ define([
|
||||
{
|
||||
"key": "imagery",
|
||||
"name": "Example Imagery",
|
||||
"cssclass": "icon-image",
|
||||
"cssClass": "icon-image",
|
||||
"features": "creation",
|
||||
"description": "For development use. Creates example imagery data that mimics a live imagery stream.",
|
||||
"priority": 10,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(['../../../platform/features/conductor-v2/conductor/src/timeSystems/LocalClock'], function (LocalClock) {
|
||||
define(['../../../platform/features/conductor/core/src/timeSystems/LocalClock'], function (LocalClock) {
|
||||
/**
|
||||
* @implements TickSource
|
||||
* @constructor
|
||||
@@ -31,7 +31,7 @@ define(['../../../platform/features/conductor-v2/conductor/src/timeSystems/Local
|
||||
this.metadata = {
|
||||
key: 'test-lad',
|
||||
mode: 'lad',
|
||||
cssclass: 'icon-clock',
|
||||
cssClass: 'icon-clock',
|
||||
label: 'Latest Available Data',
|
||||
name: 'Latest available data',
|
||||
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
*****************************************************************************/
|
||||
|
||||
define([
|
||||
'../../../platform/features/conductor-v2/conductor/src/timeSystems/TimeSystem',
|
||||
'../../../platform/features/conductor-v2/conductor/src/timeSystems/LocalClock',
|
||||
'../../../platform/features/conductor/core/src/timeSystems/TimeSystem',
|
||||
'../../../platform/features/conductor/core/src/timeSystems/LocalClock',
|
||||
'./LADTickSource'
|
||||
], function (TimeSystem, LocalClock, LADTickSource) {
|
||||
var THIRTY_MINUTES = 30 * 60 * 1000,
|
||||
|
||||
@@ -23,96 +23,93 @@
|
||||
|
||||
define([
|
||||
"./src/RemsTelemetryServerAdapter",
|
||||
"./src/RemsTelemetryInitializer",
|
||||
"./src/RemsTelemetryModelProvider",
|
||||
"./src/RemsTelemetryProvider",
|
||||
'legacyRegistry',
|
||||
"module"
|
||||
], function (
|
||||
RemsTelemetryServerAdapter,
|
||||
RemsTelemetryInitializer,
|
||||
RemsTelemetryModelProvider,
|
||||
RemsTelemetryProvider,
|
||||
legacyRegistry
|
||||
) {
|
||||
"use strict";
|
||||
legacyRegistry.register("example/msl-adapter", {
|
||||
legacyRegistry.register("example/msl", {
|
||||
"name" : "Mars Science Laboratory Data Adapter",
|
||||
"extensions" : {
|
||||
"types": [
|
||||
{
|
||||
"name":"Mars Science Laboratory",
|
||||
"key": "msl.curiosity",
|
||||
"cssclass": "icon-object"
|
||||
},
|
||||
{
|
||||
"name": "Instrument",
|
||||
"key": "msl.instrument",
|
||||
"cssclass": "icon-object",
|
||||
"model": {"composition": []}
|
||||
},
|
||||
{
|
||||
"name": "Measurement",
|
||||
"key": "msl.measurement",
|
||||
"cssclass": "icon-telemetry",
|
||||
"model": {"telemetry": {}},
|
||||
"telemetry": {
|
||||
"source": "rems.source",
|
||||
"domains": [
|
||||
{
|
||||
"name": "Time",
|
||||
"key": "timestamp",
|
||||
"format": "utc"
|
||||
}
|
||||
]
|
||||
"types": [
|
||||
{
|
||||
"name":"Mars Science Laboratory",
|
||||
"key": "msl.curiosity",
|
||||
"cssClass": "icon-object"
|
||||
},
|
||||
{
|
||||
"name": "Instrument",
|
||||
"key": "msl.instrument",
|
||||
"cssClass": "icon-object",
|
||||
"model": {"composition": []}
|
||||
},
|
||||
{
|
||||
"name": "Measurement",
|
||||
"key": "msl.measurement",
|
||||
"cssClass": "icon-telemetry",
|
||||
"model": {"telemetry": {}},
|
||||
"telemetry": {
|
||||
"source": "rems.source",
|
||||
"domains": [
|
||||
{
|
||||
"name": "Time",
|
||||
"key": "utc",
|
||||
"format": "utc"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"key": "REMS_WS_URL",
|
||||
"value": "/proxyUrl?url=http://cab.inta-csic.es/rems/wp-content/plugins/marsweather-widget/api.php"
|
||||
}
|
||||
],
|
||||
"roots": [
|
||||
{
|
||||
"id": "msl:curiosity",
|
||||
"priority" : "preferred",
|
||||
"model": {
|
||||
"type": "msl.curiosity",
|
||||
"name": "Mars Science Laboratory",
|
||||
"composition": []
|
||||
],
|
||||
"constants": [
|
||||
{
|
||||
"key": "REMS_WS_URL",
|
||||
"value": "/proxyUrl?url=http://cab.inta-csic.es/rems/wp-content/plugins/marsweather-widget/api.php"
|
||||
}
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key":"rems.adapter",
|
||||
"implementation": RemsTelemetryServerAdapter,
|
||||
"depends": ["$q", "$http", "$log", "REMS_WS_URL"]
|
||||
}
|
||||
],
|
||||
"runs": [
|
||||
{
|
||||
"implementation": RemsTelemetryInitializer,
|
||||
"depends": ["rems.adapter", "objectService"]
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"provides": "modelService",
|
||||
"type": "provider",
|
||||
"implementation": RemsTelemetryModelProvider,
|
||||
"depends": ["rems.adapter"]
|
||||
},
|
||||
{
|
||||
"provides": "telemetryService",
|
||||
"type": "provider",
|
||||
"implementation": RemsTelemetryProvider,
|
||||
"depends": ["rems.adapter", "$q"]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"roots": [
|
||||
{
|
||||
"id": "msl:curiosity"
|
||||
}
|
||||
],
|
||||
"models": [
|
||||
{
|
||||
"id": "msl:curiosity",
|
||||
"priority": "preferred",
|
||||
"model": {
|
||||
"type": "msl.curiosity",
|
||||
"name": "Mars Science Laboratory",
|
||||
"composition": ["msl_tlm:rems"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"key":"rems.adapter",
|
||||
"implementation": RemsTelemetryServerAdapter,
|
||||
"depends": ["$q", "$http", "$log", "REMS_WS_URL"]
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"provides": "modelService",
|
||||
"type": "provider",
|
||||
"implementation": RemsTelemetryModelProvider,
|
||||
"depends": ["rems.adapter"]
|
||||
},
|
||||
{
|
||||
"provides": "telemetryService",
|
||||
"type": "provider",
|
||||
"implementation": RemsTelemetryProvider,
|
||||
"depends": ["rems.adapter", "$q"]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*****************************************************************************/
|
||||
/*global define*/
|
||||
|
||||
define(
|
||||
function (){
|
||||
"use strict";
|
||||
|
||||
var TAXONOMY_ID = "msl:curiosity",
|
||||
PREFIX = "msl_tlm:";
|
||||
|
||||
/**
|
||||
* Function that is executed on application startup and populates
|
||||
* the navigation tree with objects representing the MSL REMS
|
||||
* telemetry points. The tree is populated based on the data
|
||||
* dictionary on the provider.
|
||||
*
|
||||
* @param {RemsTelemetryServerAdapter} adapter The server adapter
|
||||
* (necessary in order to retrieve data dictionary)
|
||||
* @param objectService the ObjectService which allows for lookup of
|
||||
* objects by ID
|
||||
* @constructor
|
||||
*/
|
||||
function RemsTelemetryInitializer(adapter, objectService) {
|
||||
function makeId(element) {
|
||||
return PREFIX + element.identifier;
|
||||
}
|
||||
|
||||
function initializeTaxonomy(dictionary) {
|
||||
function getTaxonomyObject(domainObjects) {
|
||||
return domainObjects[TAXONOMY_ID];
|
||||
}
|
||||
|
||||
function populateModel (taxonomyObject) {
|
||||
return taxonomyObject.useCapability(
|
||||
"mutation",
|
||||
function (model) {
|
||||
model.name = dictionary.name;
|
||||
model.composition = dictionary.instruments.map(makeId);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
objectService.getObjects([TAXONOMY_ID])
|
||||
.then(getTaxonomyObject)
|
||||
.then(populateModel);
|
||||
}
|
||||
initializeTaxonomy(adapter.dictionary);
|
||||
}
|
||||
return RemsTelemetryInitializer;
|
||||
}
|
||||
);
|
||||
@@ -81,7 +81,7 @@ define([
|
||||
{
|
||||
"key": "plot",
|
||||
"name": "Example Telemetry Plot",
|
||||
"cssclass": "icon-telemetry-panel",
|
||||
"cssClass": "icon-telemetry-panel",
|
||||
"description": "For development use. A plot for displaying telemetry.",
|
||||
"priority": 10,
|
||||
"delegates": [
|
||||
@@ -129,7 +129,7 @@ define([
|
||||
{
|
||||
"name": "Period",
|
||||
"control": "textfield",
|
||||
"cssclass": "l-input-sm l-numeric",
|
||||
"cssClass": "l-input-sm l-numeric",
|
||||
"key": "period",
|
||||
"required": true,
|
||||
"property": [
|
||||
|
||||
@@ -63,7 +63,7 @@ define(
|
||||
* Get the CSS class that defines the icon
|
||||
* to display in this indicator. This will appear
|
||||
* as a dataflow icon.
|
||||
* @returns {string} the cssclass of the dataflow icon
|
||||
* @returns {string} the cssClass of the dataflow icon
|
||||
*/
|
||||
getCssClass: function () {
|
||||
return "icon-connectivity";
|
||||
|
||||
@@ -33,6 +33,11 @@ define([
|
||||
legacyRegistry.register("example/scratchpad", {
|
||||
"extensions": {
|
||||
"roots": [
|
||||
{
|
||||
"id": "scratch:root"
|
||||
}
|
||||
],
|
||||
"models": [
|
||||
{
|
||||
"id": "scratch:root",
|
||||
"model": {
|
||||
|
||||
@@ -35,6 +35,11 @@ define([
|
||||
"description": "Example illustrating the addition of a static top-level hierarchy",
|
||||
"extensions": {
|
||||
"roots": [
|
||||
{
|
||||
"id": "exampleTaxonomy"
|
||||
}
|
||||
],
|
||||
"models": [
|
||||
{
|
||||
"id": "exampleTaxonomy",
|
||||
"model": {
|
||||
|
||||
14
gulpfile.js
14
gulpfile.js
@@ -69,6 +69,11 @@ var gulp = require('gulp'),
|
||||
}
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
options.requirejsOptimize.optimize = 'none';
|
||||
}
|
||||
|
||||
|
||||
gulp.task('scripts', function () {
|
||||
var requirejsOptimize = require('gulp-requirejs-optimize');
|
||||
var replace = require('gulp-replace-task');
|
||||
@@ -126,10 +131,17 @@ gulp.task('lint', function () {
|
||||
|
||||
gulp.task('checkstyle', function () {
|
||||
var jscs = require('gulp-jscs');
|
||||
var mkdirp = require('mkdirp');
|
||||
var reportName = 'jscs-html-report.html';
|
||||
var reportPath = path.resolve(paths.reports, 'checkstyle', reportName);
|
||||
var moveReport = fs.rename.bind(fs, reportName, reportPath, _.noop);
|
||||
|
||||
mkdirp.sync(path.resolve(paths.reports, 'checkstyle'));
|
||||
|
||||
return gulp.src(paths.scripts)
|
||||
.pipe(jscs())
|
||||
.pipe(jscs.reporter())
|
||||
.pipe(jscs.reporter('jscs-html-reporter')).on('finish', moveReport)
|
||||
.pipe(jscs.reporter('fail'));
|
||||
});
|
||||
|
||||
@@ -155,7 +167,7 @@ gulp.task('serve', function () {
|
||||
var app = require('./app.js');
|
||||
});
|
||||
|
||||
gulp.task('develop', ['serve', 'install', 'watch']);
|
||||
gulp.task('develop', ['serve', 'stylesheets', 'watch']);
|
||||
|
||||
gulp.task('install', [ 'assets', 'scripts' ]);
|
||||
|
||||
|
||||
18
index.html
18
index.html
@@ -28,15 +28,25 @@
|
||||
<script src="bower_components/requirejs/require.js">
|
||||
</script>
|
||||
<script>
|
||||
require(['openmct'], function (openmct) {
|
||||
require(['openmct'], function (openmct, generatorPlugin) {
|
||||
[
|
||||
'example/imagery',
|
||||
'example/eventGenerator',
|
||||
'example/generator',
|
||||
'platform/features/my-items'
|
||||
'example/eventGenerator'
|
||||
].forEach(
|
||||
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
|
||||
);
|
||||
openmct.install(openmct.plugins.myItems);
|
||||
openmct.install(openmct.plugins.localStorage);
|
||||
openmct.install(openmct.plugins.espresso);
|
||||
|
||||
openmct.install(openmct.plugins.Generator());
|
||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||
openmct.install(openmct.plugins.Conductor({
|
||||
defaultTimeSystem: 'utc',
|
||||
defaultTimespan: 30 * 60 * 1000,
|
||||
showConductor: true
|
||||
}));
|
||||
|
||||
openmct.start();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -76,7 +76,7 @@ module.exports = function(config) {
|
||||
// Specify browsers to run tests in.
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: [
|
||||
'PhantomJS'
|
||||
'Chrome'
|
||||
],
|
||||
|
||||
// Code coverage reporting.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openmct",
|
||||
"version": "0.12.0-SNAPSHOT",
|
||||
"version": "0.12.1-SNAPSHOT",
|
||||
"description": "The Open MCT core platform",
|
||||
"dependencies": {
|
||||
"express": "^4.13.1",
|
||||
@@ -21,16 +21,16 @@
|
||||
"gulp-sass": "^2.2.0",
|
||||
"gulp-sourcemaps": "^1.6.0",
|
||||
"jasmine-core": "^2.3.0",
|
||||
"jscs-html-reporter": "^0.1.0",
|
||||
"jsdoc": "^3.3.2",
|
||||
"jshint": "^2.7.0",
|
||||
"karma": "^0.13.3",
|
||||
"karma-chrome-launcher": "^0.1.8",
|
||||
"karma-chrome-launcher": "^0.1.12",
|
||||
"karma-cli": "0.0.4",
|
||||
"karma-coverage": "^0.5.3",
|
||||
"karma-html-reporter": "^0.2.7",
|
||||
"karma-jasmine": "^0.1.5",
|
||||
"karma-junit-reporter": "^0.3.8",
|
||||
"karma-phantomjs-launcher": "^1.0.0",
|
||||
"karma-requirejs": "^0.2.2",
|
||||
"lodash": "^3.10.1",
|
||||
"markdown-toc": "^0.11.7",
|
||||
@@ -39,7 +39,6 @@
|
||||
"mkdirp": "^0.5.1",
|
||||
"moment": "^2.11.1",
|
||||
"node-bourbon": "^4.2.3",
|
||||
"phantomjs-prebuilt": "2.1.11 || >2.1.12 <3.0.0",
|
||||
"requirejs": "2.1.x",
|
||||
"split": "^1.0.0"
|
||||
},
|
||||
|
||||
@@ -41,7 +41,6 @@ define([
|
||||
"text!./res/templates/items/items.html",
|
||||
"text!./res/templates/browse/object-properties.html",
|
||||
"text!./res/templates/browse/inspector-region.html",
|
||||
"text!./res/templates/view-object.html",
|
||||
'legacyRegistry'
|
||||
], function (
|
||||
BrowseController,
|
||||
@@ -64,7 +63,6 @@ define([
|
||||
itemsTemplate,
|
||||
objectPropertiesTemplate,
|
||||
inspectorRegionTemplate,
|
||||
viewObjectTemplate,
|
||||
legacyRegistry
|
||||
) {
|
||||
|
||||
@@ -72,14 +70,13 @@ define([
|
||||
"extensions": {
|
||||
"routes": [
|
||||
{
|
||||
"when": "/browse/:ids*",
|
||||
"when": "/browse/:ids*?",
|
||||
"template": browseTemplate,
|
||||
"reloadOnSearch": false
|
||||
},
|
||||
{
|
||||
"when": "",
|
||||
"template": browseTemplate,
|
||||
"reloadOnSearch": false
|
||||
"redirectTo": "/browse/"
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
@@ -142,10 +139,6 @@ define([
|
||||
}
|
||||
],
|
||||
"representations": [
|
||||
{
|
||||
"key": "view-object",
|
||||
"template": viewObjectTemplate
|
||||
},
|
||||
{
|
||||
"key": "browse-object",
|
||||
"template": browseObjectTemplate,
|
||||
@@ -205,7 +198,10 @@ define([
|
||||
"services": [
|
||||
{
|
||||
"key": "navigationService",
|
||||
"implementation": NavigationService
|
||||
"implementation": NavigationService,
|
||||
"depends": [
|
||||
"$window"
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
@@ -213,10 +209,7 @@ define([
|
||||
"key": "navigate",
|
||||
"implementation": NavigateAction,
|
||||
"depends": [
|
||||
"navigationService",
|
||||
"$q",
|
||||
"policyService",
|
||||
"$window"
|
||||
"navigationService"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -233,7 +226,7 @@ define([
|
||||
"$window"
|
||||
],
|
||||
"group": "windowing",
|
||||
"cssclass": "icon-new-window",
|
||||
"cssClass": "icon-new-window",
|
||||
"priority": "preferred"
|
||||
},
|
||||
{
|
||||
@@ -248,7 +241,7 @@ define([
|
||||
{
|
||||
"key": "items",
|
||||
"name": "Items",
|
||||
"cssclass": "icon-thumbs-strip",
|
||||
"cssClass": "icon-thumbs-strip",
|
||||
"description": "Grid of available items",
|
||||
"template": itemsTemplate,
|
||||
"uses": [
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<mct-split-pane class='l-object-and-inspector contents abs' anchor='right'>
|
||||
<div class='split-pane-component t-object pane primary-pane left'>
|
||||
<mct-representation mct-object="navigatedObject"
|
||||
key="'view-object'"
|
||||
key="navigatedObject.getCapability('status').get('editing') ? 'edit-object' : 'browse-object'"
|
||||
class="abs holder holder-object">
|
||||
</mct-representation>
|
||||
<a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect flush-right"
|
||||
|
||||
@@ -28,8 +28,6 @@ define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
var ROOT_ID = "ROOT";
|
||||
|
||||
/**
|
||||
* The BrowseController is used to populate the initial scope in Browse
|
||||
* mode. It loads the root object from the objectService and makes it
|
||||
@@ -49,74 +47,26 @@ define(
|
||||
urlService,
|
||||
defaultPath
|
||||
) {
|
||||
var path = [ROOT_ID].concat(
|
||||
($route.current.params.ids || defaultPath).split("/")
|
||||
);
|
||||
var initialPath = ($route.current.params.ids || defaultPath).split("/");
|
||||
var currentIds;
|
||||
|
||||
function updateRoute(domainObject) {
|
||||
var priorRoute = $route.current,
|
||||
// Act as if params HADN'T changed to avoid page reload
|
||||
unlisten;
|
||||
|
||||
unlisten = $scope.$on('$locationChangeSuccess', function () {
|
||||
// Checks path to make sure /browse/ is at front
|
||||
// if so, change $route.current
|
||||
if ($location.path().indexOf("/browse/") === 0) {
|
||||
$route.current = priorRoute;
|
||||
}
|
||||
unlisten();
|
||||
});
|
||||
// urlService.urlForLocation used to adjust current
|
||||
// path to new, addressed, path based on
|
||||
// domainObject
|
||||
$location.path(urlService.urlForLocation("browse", domainObject));
|
||||
$scope.treeModel = {
|
||||
selectedObject: undefined,
|
||||
onSelection: function (object) {
|
||||
navigationService.setNavigation(object, true);
|
||||
},
|
||||
allowSelection: function (object) {
|
||||
return navigationService.shouldNavigate();
|
||||
}
|
||||
};
|
||||
|
||||
function idsForObject(domainObject) {
|
||||
return urlService
|
||||
.urlForLocation("", domainObject)
|
||||
.replace('/', '');
|
||||
}
|
||||
|
||||
function setScopeObjects(domainObject, navigationAllowed) {
|
||||
if (navigationAllowed) {
|
||||
$scope.navigatedObject = domainObject;
|
||||
$scope.treeModel.selectedObject = domainObject;
|
||||
updateRoute(domainObject);
|
||||
} else {
|
||||
//If navigation was unsuccessful (ie. blocked), reset
|
||||
// the selected object in the tree to the currently
|
||||
// navigated object
|
||||
$scope.treeModel.selectedObject = $scope.navigatedObject ;
|
||||
}
|
||||
}
|
||||
|
||||
// Callback for updating the in-scope reference to the object
|
||||
// that is currently navigated-to.
|
||||
function setNavigation(domainObject) {
|
||||
if (domainObject === $scope.navigatedObject) {
|
||||
//do nothing;
|
||||
return;
|
||||
}
|
||||
if (domainObject) {
|
||||
domainObject.getCapability("action").perform("navigate").then(setScopeObjects.bind(undefined, domainObject));
|
||||
} else {
|
||||
setScopeObjects(domainObject, true);
|
||||
}
|
||||
}
|
||||
|
||||
function navigateTo(domainObject) {
|
||||
|
||||
// Check if an object has been navigated-to already...
|
||||
// If not, or if an ID path has been explicitly set in the URL,
|
||||
// navigate to the URL-specified object.
|
||||
if (!navigationService.getNavigation() || $route.current.params.ids) {
|
||||
// If not, pick a default as the last
|
||||
// root-level component (usually "mine")
|
||||
navigationService.setNavigation(domainObject);
|
||||
$scope.navigatedObject = domainObject;
|
||||
} else {
|
||||
// Otherwise, just expose the currently navigated object.
|
||||
$scope.navigatedObject = navigationService.getNavigation();
|
||||
updateRoute($scope.navigatedObject);
|
||||
}
|
||||
}
|
||||
|
||||
// Find an object in an array of objects.
|
||||
function findObject(domainObjects, id) {
|
||||
var i;
|
||||
for (i = 0; i < domainObjects.length; i += 1) {
|
||||
@@ -126,63 +76,87 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
// Navigate to the domain object identified by path[index],
|
||||
// which we expect to find in the composition of the passed
|
||||
// domain object.
|
||||
function doNavigate(domainObject, index) {
|
||||
var composition = domainObject.useCapability("composition");
|
||||
if (composition) {
|
||||
composition.then(function (c) {
|
||||
var nextObject = findObject(c, path[index]);
|
||||
if (nextObject) {
|
||||
if (index + 1 >= path.length) {
|
||||
navigateTo(nextObject);
|
||||
} else {
|
||||
doNavigate(nextObject, index + 1);
|
||||
}
|
||||
} else if (index === 1 && c.length > 0) {
|
||||
// Roots are in a top-level container that we don't
|
||||
// want to be selected, so if we couldn't find an
|
||||
// object at the path we wanted, at least select
|
||||
// one of its children.
|
||||
navigateTo(c[c.length - 1]);
|
||||
} else {
|
||||
// Couldn't find the next element of the path
|
||||
// so navigate to the last path object we did find
|
||||
navigateTo(domainObject);
|
||||
}
|
||||
// helper, fetch a single object from the object service.
|
||||
function getObject(id) {
|
||||
return objectService.getObjects([id])
|
||||
.then(function (results) {
|
||||
return results[id];
|
||||
});
|
||||
} else {
|
||||
// Similar to above case; this object has no composition,
|
||||
// so navigate to it instead of subsequent path elements.
|
||||
navigateTo(domainObject);
|
||||
}
|
||||
|
||||
// recursively locate and return an object inside of a container
|
||||
// via a path. If at any point in the recursion it fails to find
|
||||
// the next object, it will return the parent.
|
||||
function findViaComposition(containerObject, path) {
|
||||
var nextId = path.shift();
|
||||
if (!nextId) {
|
||||
return containerObject;
|
||||
}
|
||||
return containerObject.useCapability('composition')
|
||||
.then(function (composees) {
|
||||
var nextObject = findObject(composees, nextId);
|
||||
if (!nextObject) {
|
||||
return containerObject;
|
||||
}
|
||||
if (!nextObject.hasCapability('composition')) {
|
||||
return nextObject;
|
||||
}
|
||||
return findViaComposition(nextObject, path);
|
||||
});
|
||||
}
|
||||
|
||||
function navigateToObject(desiredObject) {
|
||||
$scope.navigatedObject = desiredObject;
|
||||
$scope.treeModel.selectedObject = desiredObject;
|
||||
currentIds = idsForObject(desiredObject);
|
||||
$route.current.pathParams.ids = currentIds;
|
||||
$location.path('/browse/' + currentIds);
|
||||
}
|
||||
|
||||
function navigateToPath(path) {
|
||||
return getObject('ROOT')
|
||||
.then(function (root) {
|
||||
return findViaComposition(root, path);
|
||||
})
|
||||
.then(function (object) {
|
||||
navigationService.setNavigation(object);
|
||||
});
|
||||
}
|
||||
|
||||
getObject('ROOT')
|
||||
.then(function (root) {
|
||||
$scope.domainObject = root;
|
||||
navigateToPath(initialPath);
|
||||
});
|
||||
|
||||
// Handle navigation events from view service. Only navigates
|
||||
// if path has changed.
|
||||
function navigateDirectlyToModel(domainObject) {
|
||||
var newIds = idsForObject(domainObject);
|
||||
if (currentIds !== newIds) {
|
||||
currentIds = newIds;
|
||||
navigateToObject(domainObject);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the root object, put it in the scope.
|
||||
// Also, load its immediate children, and (possibly)
|
||||
// navigate to one of them, so that navigation state has
|
||||
// a useful initial value.
|
||||
objectService.getObjects([path[0]]).then(function (objects) {
|
||||
$scope.domainObject = objects[path[0]];
|
||||
doNavigate($scope.domainObject, 1);
|
||||
});
|
||||
|
||||
// Provide a model for the tree to modify
|
||||
$scope.treeModel = {
|
||||
selectedObject: navigationService.getNavigation()
|
||||
};
|
||||
|
||||
// Listen for changes in navigation state.
|
||||
navigationService.addListener(setNavigation);
|
||||
navigationService.addListener(navigateDirectlyToModel);
|
||||
|
||||
// Also listen for changes which come from the tree. Changes in
|
||||
// the tree will trigger a change in browse navigation state.
|
||||
$scope.$watch("treeModel.selectedObject", setNavigation);
|
||||
// Listen for route changes which are caused by browser events
|
||||
// (e.g. bookmarks to pages in OpenMCT) and prevent them. Instead,
|
||||
// navigate to the path ourselves, which results in it being
|
||||
// properly set.
|
||||
$scope.$on('$routeChangeStart', function (event, route) {
|
||||
if (route.$$route === $route.current.$$route &&
|
||||
route.pathParams.ids !== $route.current.pathParams.ids) {
|
||||
event.preventDefault();
|
||||
navigateToPath(route.pathParams.ids.split('/'));
|
||||
}
|
||||
});
|
||||
|
||||
// Clean up when the scope is destroyed
|
||||
$scope.$on("$destroy", function () {
|
||||
navigationService.removeListener(setNavigation);
|
||||
navigationService.removeListener(navigateDirectlyToModel);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -51,24 +51,16 @@ define(
|
||||
}
|
||||
|
||||
function updateQueryParam(viewKey) {
|
||||
var unlisten,
|
||||
priorRoute = $route.current;
|
||||
|
||||
if (viewKey) {
|
||||
if (viewKey && $location.search().view !== viewKey) {
|
||||
$location.search('view', viewKey);
|
||||
unlisten = $scope.$on('$locationChangeSuccess', function () {
|
||||
// Checks path to make sure /browse/ is at front
|
||||
// if so, change $route.current
|
||||
if ($location.path().indexOf("/browse/") === 0) {
|
||||
$route.current = priorRoute;
|
||||
}
|
||||
unlisten();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$scope.$watch('domainObject', setViewForDomainObject);
|
||||
$scope.$watch('representation.selected.key', updateQueryParam);
|
||||
$scope.$on('$locationChangeSuccess', function () {
|
||||
setViewForDomainObject($scope.domainObject);
|
||||
});
|
||||
|
||||
$scope.doAction = function (action) {
|
||||
return $scope[action] && $scope[action]();
|
||||
|
||||
@@ -64,11 +64,11 @@ define(
|
||||
attachStatusListener(domainObject);
|
||||
}
|
||||
|
||||
var navigationListener = navigationService.addListener(attachStatusListener);
|
||||
navigationService.addListener(attachStatusListener);
|
||||
|
||||
$scope.$on("$destroy", function () {
|
||||
statusListener();
|
||||
navigationListener();
|
||||
navigationService.removeListener(attachStatusListener);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -33,12 +33,9 @@ define(
|
||||
* @constructor
|
||||
* @implements {Action}
|
||||
*/
|
||||
function NavigateAction(navigationService, $q, policyService, $window, context) {
|
||||
function NavigateAction(navigationService, context) {
|
||||
this.domainObject = context.domainObject;
|
||||
this.$q = $q;
|
||||
this.navigationService = navigationService;
|
||||
this.policyService = policyService;
|
||||
this.$window = $window;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,36 +44,12 @@ define(
|
||||
* navigation has been updated
|
||||
*/
|
||||
NavigateAction.prototype.perform = function () {
|
||||
var self = this,
|
||||
navigateTo = this.domainObject,
|
||||
currentObject = self.navigationService.getNavigation();
|
||||
|
||||
function allow() {
|
||||
var navigationAllowed = true;
|
||||
self.policyService.allow("navigation", currentObject, navigateTo, function (message) {
|
||||
navigationAllowed = self.$window.confirm(message + "\r\n\r\n" +
|
||||
" Are you sure you want to continue?");
|
||||
});
|
||||
return navigationAllowed;
|
||||
}
|
||||
|
||||
function cancelIfEditing() {
|
||||
var editing = currentObject.hasCapability('editor') &&
|
||||
currentObject.getCapability('editor').isEditContextRoot();
|
||||
|
||||
return self.$q.when(editing && currentObject.getCapability("editor").finish());
|
||||
}
|
||||
|
||||
function navigate() {
|
||||
return self.navigationService.setNavigation(navigateTo);
|
||||
}
|
||||
|
||||
if (allow()) {
|
||||
return cancelIfEditing().then(navigate);
|
||||
} else {
|
||||
return this.$q.when(false);
|
||||
if (this.navigationService.shouldNavigate()) {
|
||||
this.navigationService.setNavigation(this.domainObject, true);
|
||||
return Promise.resolve({});
|
||||
}
|
||||
|
||||
return Promise.reject('Navigation Prevented by User');
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,16 +30,23 @@ define(
|
||||
/**
|
||||
* The navigation service maintains the application's current
|
||||
* navigation state, and allows listening for changes thereto.
|
||||
*
|
||||
* @memberof platform/commonUI/browse
|
||||
* @constructor
|
||||
*/
|
||||
function NavigationService() {
|
||||
function NavigationService($window) {
|
||||
this.navigated = undefined;
|
||||
this.callbacks = [];
|
||||
this.checks = [];
|
||||
this.$window = $window;
|
||||
|
||||
this.oldUnload = $window.onbeforeunload;
|
||||
$window.onbeforeunload = this.onBeforeUnload.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current navigation state.
|
||||
*
|
||||
* @returns {DomainObject} the object that is navigated-to
|
||||
*/
|
||||
NavigationService.prototype.getNavigation = function () {
|
||||
@@ -47,16 +54,33 @@ define(
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the current navigation state. This will invoke listeners.
|
||||
* Navigate to a specified object. If navigation checks exist and
|
||||
* return reasons to prevent navigation, it will prompt the user before
|
||||
* continuing. Trying to navigate to the currently navigated object will
|
||||
* do nothing.
|
||||
*
|
||||
* If a truthy value is passed for `force`, it will skip navigation
|
||||
* and will not prevent navigation to an already selected object.
|
||||
*
|
||||
* @param {DomainObject} domainObject the domain object to navigate to
|
||||
* @param {Boolean} force if true, force navigation to occur.
|
||||
* @returns {Boolean} true if navigation occured, otherwise false.
|
||||
*/
|
||||
NavigationService.prototype.setNavigation = function (value) {
|
||||
if (this.navigated !== value) {
|
||||
this.navigated = value;
|
||||
this.callbacks.forEach(function (callback) {
|
||||
callback(value);
|
||||
});
|
||||
NavigationService.prototype.setNavigation = function (domainObject, force) {
|
||||
if (force) {
|
||||
this.doNavigation(domainObject);
|
||||
return true;
|
||||
}
|
||||
if (this.navigated === domainObject) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var doNotNavigate = this.shouldWarnBeforeNavigate();
|
||||
if (doNotNavigate && !this.$window.confirm(doNotNavigate)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.doNavigation(domainObject);
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -64,6 +88,7 @@ define(
|
||||
* Listen for changes in navigation. The passed callback will
|
||||
* be invoked with the new domain object of navigation when
|
||||
* this changes.
|
||||
*
|
||||
* @param {function} callback the callback to invoke when
|
||||
* navigation state changes
|
||||
*/
|
||||
@@ -73,6 +98,7 @@ define(
|
||||
|
||||
/**
|
||||
* Stop listening for changes in navigation state.
|
||||
*
|
||||
* @param {function} callback the callback which should
|
||||
* no longer be invoked when navigation state
|
||||
* changes
|
||||
@@ -83,6 +109,89 @@ define(
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if navigation should proceed. May prompt a user for input
|
||||
* if any checkFns return messages. Returns true if the user wishes to
|
||||
* navigate, otherwise false. If using this prior to calling
|
||||
* `setNavigation`, you should call `setNavigation` with `force=true`
|
||||
* to prevent duplicate dialogs being displayed to the user.
|
||||
*
|
||||
* @returns {Boolean} true if the user wishes to navigate, otherwise false.
|
||||
*/
|
||||
NavigationService.prototype.shouldNavigate = function () {
|
||||
var doNotNavigate = this.shouldWarnBeforeNavigate();
|
||||
return !doNotNavigate || this.$window.confirm(doNotNavigate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a check function to be called before any navigation occurs.
|
||||
* Check functions should return a human readable "message" if
|
||||
* there are any reasons to prevent navigation. Otherwise, they should
|
||||
* return falsy. Returns a function which can be called to remove the
|
||||
* check function.
|
||||
*
|
||||
* @param {Function} checkFn a function to call before navigation occurs.
|
||||
* @returns {Function} removeCheck call to remove check
|
||||
*/
|
||||
NavigationService.prototype.checkBeforeNavigation = function (checkFn) {
|
||||
this.checks.push(checkFn);
|
||||
return function removeCheck() {
|
||||
this.checks = this.checks.filter(function (fn) {
|
||||
return checkFn !== fn;
|
||||
});
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Private method to actually perform navigation.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NavigationService.prototype.doNavigation = function (value) {
|
||||
this.navigated = value;
|
||||
this.callbacks.forEach(function (callback) {
|
||||
callback(value);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns either a false value, or a string that should be displayed
|
||||
* to the user before navigation is allowed.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NavigationService.prototype.shouldWarnBeforeNavigate = function () {
|
||||
var reasons = [];
|
||||
|
||||
this.checks.forEach(function (checkFn) {
|
||||
var reason = checkFn();
|
||||
if (reason) {
|
||||
reasons.push(reason);
|
||||
}
|
||||
});
|
||||
|
||||
if (reasons.length) {
|
||||
return reasons.join('\n');
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Listener for window on before unload event-- will warn before
|
||||
* navigation is allowed.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
NavigationService.prototype.onBeforeUnload = function () {
|
||||
var shouldWarnBeforeNavigate = this.shouldWarnBeforeNavigate();
|
||||
if (shouldWarnBeforeNavigate) {
|
||||
return shouldWarnBeforeNavigate;
|
||||
}
|
||||
if (this.oldUnload) {
|
||||
return this.oldUnload.apply(undefined, [].slice.apply(arguments));
|
||||
}
|
||||
};
|
||||
|
||||
return NavigationService;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -43,24 +43,24 @@ define([], function () {
|
||||
return context.getParent();
|
||||
}
|
||||
|
||||
function isOrphan(domainObject) {
|
||||
var parent = getParent(domainObject),
|
||||
composition = parent.getModel().composition,
|
||||
id = domainObject.getId();
|
||||
return !composition || (composition.indexOf(id) === -1);
|
||||
}
|
||||
|
||||
function navigateToParent(domainObject) {
|
||||
function preventOrphanNavigation(domainObject) {
|
||||
var parent = getParent(domainObject);
|
||||
return parent.getCapability('action').perform('navigate');
|
||||
parent.useCapability('composition')
|
||||
.then(function (composees) {
|
||||
var isOrphan = composees.every(function (c) {
|
||||
return c.getId() !== domainObject.getId()
|
||||
});
|
||||
if (isOrphan) {
|
||||
parent.getCapability('action').perform('navigate');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkNavigation() {
|
||||
var navigatedObject = navigationService.getNavigation();
|
||||
if (navigatedObject.hasCapability('context') &&
|
||||
isOrphan(navigatedObject)) {
|
||||
if (navigatedObject.hasCapability('context')) {
|
||||
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
|
||||
navigateToParent(navigatedObject);
|
||||
preventOrphanNavigation(navigatedObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +46,12 @@ define(
|
||||
};
|
||||
|
||||
FullscreenAction.prototype.getMetadata = function () {
|
||||
// We override getMetadata, because the icon cssclass and
|
||||
// We override getMetadata, because the icon cssClass and
|
||||
// description need to be determined at run-time
|
||||
// based on whether or not we are currently
|
||||
// full screen.
|
||||
var metadata = Object.create(FullscreenAction);
|
||||
metadata.cssclass = screenfull.isFullscreen ? "icon-fullscreen-expand" : "icon-fullscreen-collapse";
|
||||
metadata.cssClass = screenfull.isFullscreen ? "icon-fullscreen-expand" : "icon-fullscreen-collapse";
|
||||
metadata.description = screenfull.isFullscreen ?
|
||||
EXIT_FULLSCREEN : ENTER_FULLSCREEN;
|
||||
metadata.group = "windowing";
|
||||
|
||||
@@ -24,8 +24,14 @@
|
||||
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../src/BrowseController"],
|
||||
function (BrowseController) {
|
||||
[
|
||||
"../src/BrowseController",
|
||||
"../src/navigation/NavigationService"
|
||||
],
|
||||
function (
|
||||
BrowseController,
|
||||
NavigationService
|
||||
) {
|
||||
|
||||
describe("The browse controller", function () {
|
||||
var mockScope,
|
||||
@@ -35,18 +41,17 @@ define(
|
||||
mockNavigationService,
|
||||
mockRootObject,
|
||||
mockUrlService,
|
||||
mockDomainObject,
|
||||
mockDefaultRootObject,
|
||||
mockOtherDomainObject,
|
||||
mockNextObject,
|
||||
testDefaultRoot,
|
||||
mockActionCapability,
|
||||
controller;
|
||||
|
||||
function mockPromise(value) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return mockPromise(callback(value));
|
||||
}
|
||||
};
|
||||
function waitsForNavigation() {
|
||||
var calls = mockNavigationService.setNavigation.calls.length;
|
||||
waitsFor(function () {
|
||||
return mockNavigationService.setNavigation.calls.length > calls;
|
||||
});
|
||||
}
|
||||
|
||||
function instantiateController() {
|
||||
@@ -68,85 +73,114 @@ define(
|
||||
"$scope",
|
||||
["$on", "$watch"]
|
||||
);
|
||||
mockRoute = { current: { params: {} } };
|
||||
mockLocation = jasmine.createSpyObj(
|
||||
"$location",
|
||||
["path"]
|
||||
);
|
||||
mockRoute = { current: { params: {}, pathParams: {} } };
|
||||
mockUrlService = jasmine.createSpyObj(
|
||||
"urlService",
|
||||
["urlForLocation"]
|
||||
);
|
||||
mockUrlService.urlForLocation.andCallFake(function (mode, object) {
|
||||
if (object === mockDefaultRootObject) {
|
||||
return [mode, testDefaultRoot].join('/');
|
||||
}
|
||||
if (object === mockOtherDomainObject) {
|
||||
return [mode, 'other'].join('/');
|
||||
}
|
||||
if (object === mockNextObject) {
|
||||
return [mode, testDefaultRoot, 'next'].join('/');
|
||||
}
|
||||
throw new Error('Tried to get url for unexpected object');
|
||||
});
|
||||
mockLocation = jasmine.createSpyObj(
|
||||
"$location",
|
||||
["path"]
|
||||
);
|
||||
mockObjectService = jasmine.createSpyObj(
|
||||
"objectService",
|
||||
["getObjects"]
|
||||
);
|
||||
mockNavigationService = jasmine.createSpyObj(
|
||||
"navigationService",
|
||||
[
|
||||
"getNavigation",
|
||||
"setNavigation",
|
||||
"addListener",
|
||||
"removeListener"
|
||||
]
|
||||
);
|
||||
mockNavigationService = new NavigationService({});
|
||||
[
|
||||
"getNavigation",
|
||||
"setNavigation",
|
||||
"addListener",
|
||||
"removeListener"
|
||||
].forEach(function (method) {
|
||||
spyOn(mockNavigationService, method)
|
||||
.andCallThrough();
|
||||
});
|
||||
mockRootObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getId", "getCapability", "getModel", "useCapability"]
|
||||
"rootObjectContainer",
|
||||
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
|
||||
);
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
["getId", "getCapability", "getModel", "useCapability"]
|
||||
mockDefaultRootObject = jasmine.createSpyObj(
|
||||
"defaultRootObject",
|
||||
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
|
||||
);
|
||||
mockOtherDomainObject = jasmine.createSpyObj(
|
||||
"otherDomainObject",
|
||||
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
|
||||
);
|
||||
mockNextObject = jasmine.createSpyObj(
|
||||
"nextObject",
|
||||
["getId", "getCapability", "getModel", "useCapability"]
|
||||
"nestedDomainObject",
|
||||
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
|
||||
);
|
||||
|
||||
mockObjectService.getObjects.andReturn(mockPromise({
|
||||
mockObjectService.getObjects.andReturn(Promise.resolve({
|
||||
ROOT: mockRootObject
|
||||
}));
|
||||
mockRootObject.useCapability.andReturn(mockPromise([
|
||||
mockDomainObject
|
||||
mockRootObject.useCapability.andReturn(Promise.resolve([
|
||||
mockOtherDomainObject,
|
||||
mockDefaultRootObject
|
||||
]));
|
||||
mockDomainObject.useCapability.andReturn(mockPromise([
|
||||
mockRootObject.hasCapability.andReturn(true);
|
||||
mockDefaultRootObject.useCapability.andReturn(Promise.resolve([
|
||||
mockNextObject
|
||||
]));
|
||||
mockDefaultRootObject.hasCapability.andReturn(true);
|
||||
mockOtherDomainObject.hasCapability.andReturn(false);
|
||||
mockNextObject.useCapability.andReturn(undefined);
|
||||
mockNextObject.hasCapability.andReturn(false);
|
||||
mockNextObject.getId.andReturn("next");
|
||||
mockDomainObject.getId.andReturn(testDefaultRoot);
|
||||
|
||||
mockActionCapability = jasmine.createSpyObj('actionCapability', ['perform']);
|
||||
mockDefaultRootObject.getId.andReturn(testDefaultRoot);
|
||||
|
||||
instantiateController();
|
||||
waitsForNavigation();
|
||||
});
|
||||
|
||||
it("uses composition to set the navigated object, if there is none", function () {
|
||||
instantiateController();
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockDomainObject);
|
||||
waitsForNavigation();
|
||||
runs(function () {
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockDefaultRootObject);
|
||||
});
|
||||
});
|
||||
|
||||
it("navigates to a root-level object, even when default path is not found", function () {
|
||||
mockDomainObject.getId
|
||||
mockDefaultRootObject.getId
|
||||
.andReturn("something-other-than-the-" + testDefaultRoot);
|
||||
instantiateController();
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockDomainObject);
|
||||
});
|
||||
|
||||
waitsForNavigation();
|
||||
runs(function () {
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockDefaultRootObject);
|
||||
});
|
||||
|
||||
});
|
||||
//
|
||||
it("does not try to override navigation", function () {
|
||||
mockNavigationService.getNavigation.andReturn(mockDomainObject);
|
||||
mockNavigationService.getNavigation.andReturn(mockDefaultRootObject);
|
||||
instantiateController();
|
||||
expect(mockScope.navigatedObject).toBe(mockDomainObject);
|
||||
waitsForNavigation();
|
||||
expect(mockScope.navigatedObject).toBe(mockDefaultRootObject);
|
||||
});
|
||||
|
||||
//
|
||||
it("updates scope when navigated object changes", function () {
|
||||
// Should have registered a listener - call it
|
||||
mockNavigationService.addListener.mostRecentCall.args[0](
|
||||
mockDomainObject
|
||||
mockOtherDomainObject
|
||||
);
|
||||
expect(mockScope.navigatedObject).toEqual(mockDomainObject);
|
||||
expect(mockScope.navigatedObject).toEqual(mockOtherDomainObject);
|
||||
});
|
||||
|
||||
|
||||
@@ -166,9 +200,12 @@ define(
|
||||
it("uses route parameters to choose initially-navigated object", function () {
|
||||
mockRoute.current.params.ids = testDefaultRoot + "/next";
|
||||
instantiateController();
|
||||
expect(mockScope.navigatedObject).toBe(mockNextObject);
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockNextObject);
|
||||
waitsForNavigation();
|
||||
runs(function () {
|
||||
expect(mockScope.navigatedObject).toBe(mockNextObject);
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockNextObject);
|
||||
});
|
||||
});
|
||||
|
||||
it("handles invalid IDs by going as far as possible", function () {
|
||||
@@ -177,9 +214,13 @@ define(
|
||||
// it hits an invalid ID.
|
||||
mockRoute.current.params.ids = testDefaultRoot + "/junk";
|
||||
instantiateController();
|
||||
expect(mockScope.navigatedObject).toBe(mockDomainObject);
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockDomainObject);
|
||||
waitsForNavigation();
|
||||
runs(function () {
|
||||
expect(mockScope.navigatedObject).toBe(mockDefaultRootObject);
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockDefaultRootObject);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it("handles compositionless objects by going as far as possible", function () {
|
||||
@@ -188,84 +229,33 @@ define(
|
||||
// should stop at it since remaining IDs cannot be loaded.
|
||||
mockRoute.current.params.ids = testDefaultRoot + "/next/junk";
|
||||
instantiateController();
|
||||
expect(mockScope.navigatedObject).toBe(mockNextObject);
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockNextObject);
|
||||
waitsForNavigation();
|
||||
runs(function () {
|
||||
expect(mockScope.navigatedObject).toBe(mockNextObject);
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockNextObject);
|
||||
});
|
||||
});
|
||||
|
||||
it("updates the displayed route to reflect current navigation", function () {
|
||||
var mockContext = jasmine.createSpyObj('context', ['getPath']),
|
||||
mockUnlisten = jasmine.createSpy('unlisten'),
|
||||
mockMode = "browse";
|
||||
|
||||
mockContext.getPath.andReturn(
|
||||
[mockRootObject, mockDomainObject, mockNextObject]
|
||||
);
|
||||
|
||||
//Return true from navigate action
|
||||
mockActionCapability.perform.andReturn(mockPromise(true));
|
||||
|
||||
mockNextObject.getCapability.andCallFake(function (c) {
|
||||
return (c === 'context' && mockContext) ||
|
||||
(c === 'action' && mockActionCapability);
|
||||
// In order to trigger a route update and not a route change,
|
||||
// the current route must be updated before location.path is
|
||||
// called.
|
||||
expect(mockRoute.current.pathParams.ids)
|
||||
.not
|
||||
.toBe(testDefaultRoot + '/next');
|
||||
mockLocation.path.andCallFake(function () {
|
||||
expect(mockRoute.current.pathParams.ids)
|
||||
.toBe(testDefaultRoot + '/next');
|
||||
});
|
||||
mockScope.$on.andReturn(mockUnlisten);
|
||||
// Provide a navigation change
|
||||
mockNavigationService.addListener.mostRecentCall.args[0](
|
||||
mockNextObject
|
||||
);
|
||||
|
||||
// Allows the path index to be checked
|
||||
// prior to setting $route.current
|
||||
mockLocation.path.andReturn("/browse/");
|
||||
|
||||
mockNavigationService.setNavigation.andReturn(true);
|
||||
mockActionCapability.perform.andReturn(mockPromise(true));
|
||||
|
||||
// Exercise the Angular workaround
|
||||
mockNavigationService.addListener.mostRecentCall.args[0]();
|
||||
mockScope.$on.mostRecentCall.args[1]();
|
||||
expect(mockUnlisten).toHaveBeenCalled();
|
||||
|
||||
// location.path to be called with the urlService's
|
||||
// urlFor function with the next domainObject and mode
|
||||
expect(mockLocation.path).toHaveBeenCalledWith(
|
||||
mockUrlService.urlForLocation(mockMode, mockNextObject)
|
||||
'/browse/' + testDefaultRoot + '/next'
|
||||
);
|
||||
});
|
||||
|
||||
it("after successful navigation event sets the selected tree " +
|
||||
"object", function () {
|
||||
mockScope.navigatedObject = mockDomainObject;
|
||||
mockNavigationService.setNavigation.andReturn(true);
|
||||
|
||||
mockActionCapability.perform.andReturn(mockPromise(true));
|
||||
mockNextObject.getCapability.andReturn(mockActionCapability);
|
||||
|
||||
//Simulate a change in selected tree object
|
||||
mockScope.treeModel = {selectedObject: mockDomainObject};
|
||||
mockScope.$watch.mostRecentCall.args[1](mockNextObject);
|
||||
|
||||
expect(mockScope.treeModel.selectedObject).toBe(mockNextObject);
|
||||
expect(mockScope.treeModel.selectedObject).not.toBe(mockDomainObject);
|
||||
});
|
||||
|
||||
it("after failed navigation event resets the selected tree" +
|
||||
" object", function () {
|
||||
mockScope.navigatedObject = mockDomainObject;
|
||||
|
||||
//Return false from navigation action
|
||||
mockActionCapability.perform.andReturn(mockPromise(false));
|
||||
mockNextObject.getCapability.andReturn(mockActionCapability);
|
||||
|
||||
//Simulate a change in selected tree object
|
||||
mockScope.treeModel = {selectedObject: mockDomainObject};
|
||||
mockScope.$watch.mostRecentCall.args[1](mockNextObject);
|
||||
|
||||
expect(mockScope.treeModel.selectedObject).not.toBe(mockNextObject);
|
||||
expect(mockScope.treeModel.selectedObject).toBe(mockDomainObject);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -29,7 +29,6 @@ define(
|
||||
var mockScope,
|
||||
mockLocation,
|
||||
mockRoute,
|
||||
mockUnlisten,
|
||||
controller;
|
||||
|
||||
// Utility function; look for a $watch on scope and fire it
|
||||
@@ -51,9 +50,7 @@ define(
|
||||
"$location",
|
||||
["path", "search"]
|
||||
);
|
||||
mockUnlisten = jasmine.createSpy("unlisten");
|
||||
|
||||
mockScope.$on.andReturn(mockUnlisten);
|
||||
mockLocation.search.andReturn({});
|
||||
|
||||
controller = new BrowseObjectController(
|
||||
mockScope,
|
||||
@@ -69,10 +66,6 @@ define(
|
||||
// Allows the path index to be checked
|
||||
// prior to setting $route.current
|
||||
mockLocation.path.andReturn("/browse/");
|
||||
|
||||
// Exercise the Angular workaround
|
||||
mockScope.$on.mostRecentCall.args[1]();
|
||||
expect(mockUnlisten).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("sets the active view from query parameters", function () {
|
||||
|
||||
@@ -23,145 +23,74 @@
|
||||
/**
|
||||
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
|
||||
*/
|
||||
define(
|
||||
["../../src/navigation/NavigateAction"],
|
||||
function (NavigateAction) {
|
||||
define([
|
||||
"../../src/navigation/NavigateAction"
|
||||
], function (
|
||||
NavigateAction
|
||||
) {
|
||||
|
||||
describe("The navigate action", function () {
|
||||
var mockNavigationService,
|
||||
mockQ,
|
||||
mockDomainObject,
|
||||
mockPolicyService,
|
||||
mockNavigatedObject,
|
||||
mockWindow,
|
||||
capabilities,
|
||||
action;
|
||||
describe("The navigate action", function () {
|
||||
var mockNavigationService,
|
||||
mockDomainObject,
|
||||
action;
|
||||
|
||||
function mockPromise(value) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return mockPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
capabilities = {};
|
||||
|
||||
mockQ = { when: mockPromise };
|
||||
mockNavigatedObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[
|
||||
"getId",
|
||||
"getModel",
|
||||
"hasCapability",
|
||||
"getCapability"
|
||||
]
|
||||
);
|
||||
|
||||
capabilities.editor = jasmine.createSpyObj("editorCapability", [
|
||||
"isEditContextRoot",
|
||||
"finish"
|
||||
]);
|
||||
|
||||
mockNavigatedObject.getCapability.andCallFake(function (capability) {
|
||||
return capabilities[capability];
|
||||
});
|
||||
mockNavigatedObject.hasCapability.andReturn(false);
|
||||
|
||||
mockNavigationService = jasmine.createSpyObj(
|
||||
"navigationService",
|
||||
[
|
||||
"setNavigation",
|
||||
"getNavigation"
|
||||
]
|
||||
);
|
||||
mockNavigationService.getNavigation.andReturn(mockNavigatedObject);
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[
|
||||
"getId",
|
||||
"getModel"
|
||||
]
|
||||
);
|
||||
|
||||
mockPolicyService = jasmine.createSpyObj("policyService",
|
||||
[
|
||||
"allow"
|
||||
]);
|
||||
mockWindow = jasmine.createSpyObj("$window",
|
||||
[
|
||||
"confirm"
|
||||
]);
|
||||
|
||||
action = new NavigateAction(
|
||||
mockNavigationService,
|
||||
mockQ,
|
||||
mockPolicyService,
|
||||
mockWindow,
|
||||
{ domainObject: mockDomainObject }
|
||||
);
|
||||
function waitForCall() {
|
||||
var called = false;
|
||||
waitsFor(function () {
|
||||
return called;
|
||||
});
|
||||
return function () {
|
||||
called = true;
|
||||
};
|
||||
}
|
||||
|
||||
it("invokes the policy service to determine if navigation" +
|
||||
" allowed", function () {
|
||||
action.perform();
|
||||
expect(mockPolicyService.allow)
|
||||
.toHaveBeenCalledWith("navigation", jasmine.any(Object), jasmine.any(Object), jasmine.any(Function));
|
||||
});
|
||||
beforeEach(function () {
|
||||
mockNavigationService = jasmine.createSpyObj(
|
||||
"navigationService",
|
||||
[
|
||||
"shouldNavigate",
|
||||
"setNavigation"
|
||||
]
|
||||
);
|
||||
|
||||
it("prompts user if policy rejection", function () {
|
||||
action.perform();
|
||||
expect(mockPolicyService.allow).toHaveBeenCalled();
|
||||
mockPolicyService.allow.mostRecentCall.args[3]();
|
||||
expect(mockWindow.confirm).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("shows a prompt", function () {
|
||||
beforeEach(function () {
|
||||
// Ensure the allow callback is called synchronously
|
||||
mockPolicyService.allow.andCallFake(function () {
|
||||
return arguments[3]();
|
||||
});
|
||||
});
|
||||
it("does not navigate on prompt rejection", function () {
|
||||
mockWindow.confirm.andReturn(false);
|
||||
action.perform();
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does navigate on prompt acceptance", function () {
|
||||
mockWindow.confirm.andReturn(true);
|
||||
action.perform();
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("in edit mode", function () {
|
||||
beforeEach(function () {
|
||||
mockNavigatedObject.hasCapability.andCallFake(function (capability) {
|
||||
return capability === "editor";
|
||||
});
|
||||
capabilities.editor.isEditContextRoot.andReturn(true);
|
||||
});
|
||||
|
||||
it("finishes editing if in edit mode", function () {
|
||||
action.perform();
|
||||
expect(capabilities.editor.finish)
|
||||
.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("is only applicable when a domain object is in context", function () {
|
||||
expect(NavigateAction.appliesTo({})).toBeFalsy();
|
||||
expect(NavigateAction.appliesTo({
|
||||
domainObject: mockDomainObject
|
||||
})).toBeTruthy();
|
||||
});
|
||||
mockDomainObject = {};
|
||||
|
||||
action = new NavigateAction(
|
||||
mockNavigationService,
|
||||
{ domainObject: mockDomainObject }
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it("sets navigation if it is allowed", function () {
|
||||
mockNavigationService.shouldNavigate.andReturn(true);
|
||||
action.perform()
|
||||
.then(waitForCall());
|
||||
runs(function () {
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.toHaveBeenCalledWith(mockDomainObject, true);
|
||||
});
|
||||
});
|
||||
|
||||
it("does not set navigation if it is not allowed", function () {
|
||||
mockNavigationService.shouldNavigate.andReturn(false);
|
||||
var onSuccess = jasmine.createSpy('onSuccess');
|
||||
action.perform()
|
||||
.then(onSuccess, waitForCall());
|
||||
runs(function () {
|
||||
expect(onSuccess).not.toHaveBeenCalled();
|
||||
expect(mockNavigationService.setNavigation)
|
||||
.not
|
||||
.toHaveBeenCalledWith(mockDomainObject);
|
||||
});
|
||||
});
|
||||
|
||||
it("is only applicable when a domain object is in context", function () {
|
||||
expect(NavigateAction.appliesTo({})).toBeFalsy();
|
||||
expect(NavigateAction.appliesTo({
|
||||
domainObject: mockDomainObject
|
||||
})).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,10 +28,12 @@ define(
|
||||
function (NavigationService) {
|
||||
|
||||
describe("The navigation service", function () {
|
||||
var navigationService;
|
||||
var $window,
|
||||
navigationService;
|
||||
|
||||
beforeEach(function () {
|
||||
navigationService = new NavigationService();
|
||||
$window = jasmine.createSpyObj('$window', ['confirm']);
|
||||
navigationService = new NavigationService($window);
|
||||
});
|
||||
|
||||
it("stores navigation state", function () {
|
||||
|
||||
@@ -51,7 +51,7 @@ define(
|
||||
});
|
||||
|
||||
it("provides displayable metadata", function () {
|
||||
expect(action.getMetadata().cssclass).toBeDefined();
|
||||
expect(action.getMetadata().cssClass).toBeDefined();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -53,7 +53,8 @@ define([
|
||||
"depends": [
|
||||
"overlayService",
|
||||
"$q",
|
||||
"$log"
|
||||
"$log",
|
||||
"$document"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -35,11 +35,15 @@ define(
|
||||
* @memberof platform/commonUI/dialog
|
||||
* @constructor
|
||||
*/
|
||||
function DialogService(overlayService, $q, $log) {
|
||||
function DialogService(overlayService, $q, $log, $document) {
|
||||
this.overlayService = overlayService;
|
||||
this.$q = $q;
|
||||
this.$log = $log;
|
||||
this.activeOverlay = undefined;
|
||||
|
||||
this.findBody = function () {
|
||||
return $document.find('body');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +64,8 @@ define(
|
||||
// input is asynchronous.
|
||||
var deferred = this.$q.defer(),
|
||||
self = this,
|
||||
overlay;
|
||||
overlay,
|
||||
handleEscKeydown;
|
||||
|
||||
// Confirm function; this will be passed in to the
|
||||
// overlay-dialog template and associated with a
|
||||
@@ -76,13 +81,22 @@ define(
|
||||
// Cancel or X button click
|
||||
function cancel() {
|
||||
deferred.reject();
|
||||
self.findBody().off('keydown', handleEscKeydown);
|
||||
self.dismissOverlay(overlay);
|
||||
}
|
||||
|
||||
handleEscKeydown = function (event) {
|
||||
if (event.keyCode === 27) {
|
||||
cancel();
|
||||
}
|
||||
};
|
||||
|
||||
// Add confirm/cancel callbacks
|
||||
model.confirm = confirm;
|
||||
model.cancel = cancel;
|
||||
|
||||
this.findBody().on('keydown', handleEscKeydown);
|
||||
|
||||
if (this.canShowDialog(model)) {
|
||||
// Add the overlay using the OverlayService, which
|
||||
// will handle actual insertion into the DOM
|
||||
|
||||
@@ -33,6 +33,8 @@ define(
|
||||
mockLog,
|
||||
mockOverlay,
|
||||
mockDeferred,
|
||||
mockDocument,
|
||||
mockBody,
|
||||
dialogService;
|
||||
|
||||
beforeEach(function () {
|
||||
@@ -56,6 +58,13 @@ define(
|
||||
"deferred",
|
||||
["resolve", "reject"]
|
||||
);
|
||||
mockDocument = jasmine.createSpyObj(
|
||||
"$document",
|
||||
["find"]
|
||||
);
|
||||
mockBody = jasmine.createSpyObj('body', ['on', 'off']);
|
||||
mockDocument.find.andReturn(mockBody);
|
||||
|
||||
mockDeferred.promise = "mock promise";
|
||||
|
||||
mockQ.defer.andReturn(mockDeferred);
|
||||
@@ -64,7 +73,8 @@ define(
|
||||
dialogService = new DialogService(
|
||||
mockOverlayService,
|
||||
mockQ,
|
||||
mockLog
|
||||
mockLog,
|
||||
mockDocument
|
||||
);
|
||||
});
|
||||
|
||||
@@ -130,6 +140,36 @@ define(
|
||||
);
|
||||
});
|
||||
|
||||
it("adds a keydown event listener to the body", function () {
|
||||
dialogService.getUserInput({}, {});
|
||||
expect(mockDocument.find).toHaveBeenCalledWith("body");
|
||||
expect(mockBody.on).toHaveBeenCalledWith("keydown", jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("destroys the event listener when the dialog is cancelled", function () {
|
||||
dialogService.getUserInput({}, {});
|
||||
mockOverlayService.createOverlay.mostRecentCall.args[1].cancel();
|
||||
expect(mockBody.off).toHaveBeenCalledWith("keydown", jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("cancels the dialog when an escape keydown event is triggered", function () {
|
||||
dialogService.getUserInput({}, {});
|
||||
mockBody.on.mostRecentCall.args[1]({
|
||||
keyCode: 27
|
||||
});
|
||||
expect(mockDeferred.reject).toHaveBeenCalled();
|
||||
expect(mockDeferred.resolve).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("ignores non escape keydown events", function () {
|
||||
dialogService.getUserInput({}, {});
|
||||
mockBody.on.mostRecentCall.args[1]({
|
||||
keyCode: 13
|
||||
});
|
||||
expect(mockDeferred.reject).not.toHaveBeenCalled();
|
||||
expect(mockDeferred.resolve).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("the blocking message dialog", function () {
|
||||
var dialogModel = {};
|
||||
var dialogHandle;
|
||||
|
||||
@@ -2,25 +2,6 @@ Contains sources and resources associated with Edit mode.
|
||||
|
||||
# Extensions
|
||||
|
||||
## Directives
|
||||
|
||||
This bundle introduces the `mct-before-unload` directive, primarily for
|
||||
internal use (to prompt the user to confirm navigation away from unsaved
|
||||
changes in Edit mode.)
|
||||
|
||||
The `mct-before-unload` directive is used as an attribute whose value is
|
||||
an Angular expression that is evaluated when navigation changes (either
|
||||
via browser-level changes, such as the refresh button, or changes to
|
||||
the Angular route, which happens when hitting the back button in Edit
|
||||
mode.) The result of this evaluation, when truthy, is shown in a browser
|
||||
dialog to allow the user to confirm navigation. When falsy, no prompt is
|
||||
shown, allowing these dialogs to be shown conditionally. (For instance, in
|
||||
Edit mode, prompts are only shown if user-initiated changes have
|
||||
occurred.)
|
||||
|
||||
This directive may be attached to any element; its behavior will be enforced
|
||||
so long as that element remains within the DOM.
|
||||
|
||||
# Toolbars
|
||||
|
||||
Views may specify the contents of a toolbar through a `toolbar`
|
||||
|
||||
@@ -25,7 +25,6 @@ define([
|
||||
"./src/controllers/EditPanesController",
|
||||
"./src/controllers/ElementsController",
|
||||
"./src/controllers/EditObjectController",
|
||||
"./src/directives/MCTBeforeUnload",
|
||||
"./src/actions/EditAndComposeAction",
|
||||
"./src/actions/EditAction",
|
||||
"./src/actions/PropertiesAction",
|
||||
@@ -37,7 +36,6 @@ define([
|
||||
"./src/policies/EditActionPolicy",
|
||||
"./src/policies/EditableLinkPolicy",
|
||||
"./src/policies/EditableMovePolicy",
|
||||
"./src/policies/EditNavigationPolicy",
|
||||
"./src/policies/EditContextualActionPolicy",
|
||||
"./src/representers/EditRepresenter",
|
||||
"./src/representers/EditToolbarRepresenter",
|
||||
@@ -65,7 +63,6 @@ define([
|
||||
EditPanesController,
|
||||
ElementsController,
|
||||
EditObjectController,
|
||||
MCTBeforeUnload,
|
||||
EditAndComposeAction,
|
||||
EditAction,
|
||||
PropertiesAction,
|
||||
@@ -77,7 +74,6 @@ define([
|
||||
EditActionPolicy,
|
||||
EditableLinkPolicy,
|
||||
EditableMovePolicy,
|
||||
EditNavigationPolicy,
|
||||
EditContextualActionPolicy,
|
||||
EditRepresenter,
|
||||
EditToolbarRepresenter,
|
||||
@@ -132,7 +128,7 @@ define([
|
||||
"depends": [
|
||||
"$scope",
|
||||
"$location",
|
||||
"policyService"
|
||||
"navigationService"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -152,15 +148,6 @@ define([
|
||||
]
|
||||
}
|
||||
],
|
||||
"directives": [
|
||||
{
|
||||
"key": "mctBeforeUnload",
|
||||
"implementation": MCTBeforeUnload,
|
||||
"depends": [
|
||||
"$window"
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"key": "compose",
|
||||
@@ -176,7 +163,7 @@ define([
|
||||
],
|
||||
"description": "Edit",
|
||||
"category": "view-control",
|
||||
"cssclass": "major icon-pencil"
|
||||
"cssClass": "major icon-pencil"
|
||||
},
|
||||
{
|
||||
"key": "properties",
|
||||
@@ -185,7 +172,7 @@ define([
|
||||
"view-control"
|
||||
],
|
||||
"implementation": PropertiesAction,
|
||||
"cssclass": "major icon-pencil",
|
||||
"cssClass": "major icon-pencil",
|
||||
"name": "Edit Properties...",
|
||||
"description": "Edit properties of this object.",
|
||||
"depends": [
|
||||
@@ -196,7 +183,7 @@ define([
|
||||
"key": "remove",
|
||||
"category": "contextual",
|
||||
"implementation": RemoveAction,
|
||||
"cssclass": "icon-trash",
|
||||
"cssClass": "icon-trash",
|
||||
"name": "Remove",
|
||||
"description": "Remove this object from its containing object.",
|
||||
"depends": [
|
||||
@@ -208,10 +195,11 @@ define([
|
||||
"category": "save",
|
||||
"implementation": SaveAndStopEditingAction,
|
||||
"name": "Save and Finish Editing",
|
||||
"cssclass": "icon-save labeled",
|
||||
"cssClass": "icon-save labeled",
|
||||
"description": "Save changes made to these objects.",
|
||||
"depends": [
|
||||
"dialogService"
|
||||
"dialogService",
|
||||
"notificationService"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -219,10 +207,11 @@ define([
|
||||
"category": "save",
|
||||
"implementation": SaveAction,
|
||||
"name": "Save and Continue Editing",
|
||||
"cssclass": "icon-save labeled",
|
||||
"cssClass": "icon-save labeled",
|
||||
"description": "Save changes made to these objects.",
|
||||
"depends": [
|
||||
"dialogService"
|
||||
"dialogService",
|
||||
"notificationService"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -230,13 +219,14 @@ define([
|
||||
"category": "save",
|
||||
"implementation": SaveAsAction,
|
||||
"name": "Save As...",
|
||||
"cssclass": "icon-save labeled",
|
||||
"cssClass": "icon-save labeled",
|
||||
"description": "Save changes made to these objects.",
|
||||
"depends": [
|
||||
"$injector",
|
||||
"policyService",
|
||||
"dialogService",
|
||||
"copyService"
|
||||
"copyService",
|
||||
"notificationService"
|
||||
],
|
||||
"priority": "mandatory"
|
||||
},
|
||||
@@ -247,7 +237,7 @@ define([
|
||||
// Because we use the name as label for edit buttons and mct-control buttons need
|
||||
// the label to be set to undefined in order to not apply the labeled CSS rule.
|
||||
"name": undefined,
|
||||
"cssclass": "icon-x no-label",
|
||||
"cssClass": "icon-x no-label",
|
||||
"description": "Discard changes made to these objects.",
|
||||
"depends": []
|
||||
}
|
||||
@@ -270,11 +260,6 @@ define([
|
||||
"category": "action",
|
||||
"implementation": EditableLinkPolicy
|
||||
},
|
||||
{
|
||||
"category": "navigation",
|
||||
"message": "Continuing will cause the loss of any unsaved changes.",
|
||||
"implementation": EditNavigationPolicy
|
||||
},
|
||||
{
|
||||
"implementation": CreationPolicy,
|
||||
"category": "creation"
|
||||
@@ -347,7 +332,8 @@ define([
|
||||
"implementation": TransactionService,
|
||||
"depends": [
|
||||
"$q",
|
||||
"$log"
|
||||
"$log",
|
||||
"cacheService"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -388,7 +374,6 @@ define([
|
||||
{
|
||||
"implementation": EditRepresenter,
|
||||
"depends": [
|
||||
"$q",
|
||||
"$log"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -25,14 +25,14 @@
|
||||
<li ng-repeat="createAction in createActions" ng-click="createAction.perform()">
|
||||
<a ng-mouseover="representation.activeMetadata = createAction.getMetadata()"
|
||||
ng-mouseleave="representation.activeMetadata = undefined"
|
||||
class="menu-item-a {{ createAction.getMetadata().cssclass }}">
|
||||
class="menu-item-a {{ createAction.getMetadata().cssClass }}">
|
||||
{{createAction.getMetadata().name}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pane right menu-item-description">
|
||||
<div class="desc-area icon {{ representation.activeMetadata.cssclass }}"></div>
|
||||
<div class="desc-area icon {{ representation.activeMetadata.cssClass }}"></div>
|
||||
<div class="desc-area title">
|
||||
{{representation.activeMetadata.name}}
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
structure="{
|
||||
text: saveActions[0].getMetadata().name,
|
||||
click: actionPerformer(saveActions[0]),
|
||||
cssclass: 'major ' + saveActions[0].getMetadata().cssclass
|
||||
cssClass: 'major ' + saveActions[0].getMetadata().cssClass
|
||||
}">
|
||||
</mct-control>
|
||||
</span>
|
||||
@@ -36,7 +36,7 @@
|
||||
structure="{
|
||||
options: saveActionsAsMenuOptions,
|
||||
click: saveActionMenuClickHandler,
|
||||
cssclass: 'btn-bar right icon-save no-label major'
|
||||
cssClass: 'btn-bar right icon-save no-label major'
|
||||
}">
|
||||
</mct-control>
|
||||
</span>
|
||||
@@ -46,7 +46,7 @@
|
||||
structure="{
|
||||
text: currentAction.getMetadata().name,
|
||||
click: actionPerformer(currentAction),
|
||||
cssclass: currentAction.getMetadata().cssclass
|
||||
cssClass: currentAction.getMetadata().cssClass
|
||||
}">
|
||||
</mct-control>
|
||||
</span>
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<div class="abs l-flex-col" ng-controller="EditObjectController as EditObjectController">
|
||||
<div mct-before-unload="EditObjectController.getUnloadWarning()"
|
||||
class="holder flex-elem l-flex-row object-browse-bar ">
|
||||
<div class="holder flex-elem l-flex-row object-browse-bar ">
|
||||
<div class="items-select left flex-elem l-flex-row grows">
|
||||
<mct-representation key="'back-arrow'"
|
||||
mct-object="domainObject"
|
||||
|
||||
@@ -56,13 +56,13 @@ define(
|
||||
//navigate back to parent because nothing to show.
|
||||
return domainObject.getCapability("location").getOriginal().then(function (original) {
|
||||
parent = original.getCapability("context").getParent();
|
||||
parent.getCapability("action").perform("navigate");
|
||||
return parent.getCapability("action").perform("navigate");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function cancel(allowed) {
|
||||
return allowed && domainObject.getCapability("editor").finish();
|
||||
function cancel() {
|
||||
return domainObject.getCapability("editor").finish();
|
||||
}
|
||||
|
||||
//Do navigation first in order to trigger unsaved changes dialog
|
||||
|
||||
@@ -33,10 +33,12 @@ define(
|
||||
*/
|
||||
function SaveAction(
|
||||
dialogService,
|
||||
notificationService,
|
||||
context
|
||||
) {
|
||||
this.domainObject = (context || {}).domainObject;
|
||||
this.dialogService = dialogService;
|
||||
this.notificationService = notificationService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +49,8 @@ define(
|
||||
* @memberof platform/commonUI/edit.SaveAction#
|
||||
*/
|
||||
SaveAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject,
|
||||
var self = this,
|
||||
domainObject = this.domainObject,
|
||||
dialog = new SaveInProgressDialog(this.dialogService);
|
||||
|
||||
// Invoke any save behavior introduced by the editor capability;
|
||||
@@ -58,15 +61,21 @@ define(
|
||||
return domainObject.getCapability("editor").save();
|
||||
}
|
||||
|
||||
function hideBlockingDialog() {
|
||||
function onSuccess() {
|
||||
dialog.hide();
|
||||
self.notificationService.info("Save Succeeded");
|
||||
}
|
||||
|
||||
function onFailure() {
|
||||
dialog.hide();
|
||||
self.notificationService.error("Save Failed");
|
||||
}
|
||||
|
||||
dialog.show();
|
||||
|
||||
return doSave()
|
||||
.then(hideBlockingDialog)
|
||||
.catch(hideBlockingDialog);
|
||||
.then(onSuccess)
|
||||
.catch(onFailure);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,11 +33,13 @@ define(
|
||||
*/
|
||||
function SaveAndStopEditingAction(
|
||||
dialogService,
|
||||
notificationService,
|
||||
context
|
||||
) {
|
||||
this.context = context;
|
||||
this.domainObject = (context || {}).domainObject;
|
||||
this.dialogService = dialogService;
|
||||
this.notificationService = notificationService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,7 +51,7 @@ define(
|
||||
*/
|
||||
SaveAndStopEditingAction.prototype.perform = function () {
|
||||
var domainObject = this.domainObject,
|
||||
saveAction = new SaveAction(this.dialogService, this.context);
|
||||
saveAction = new SaveAction(this.dialogService, this.notificationService, this.context);
|
||||
|
||||
function closeEditor() {
|
||||
return domainObject.getCapability("editor").finish();
|
||||
|
||||
@@ -43,6 +43,7 @@ define([
|
||||
policyService,
|
||||
dialogService,
|
||||
copyService,
|
||||
notificationService,
|
||||
context
|
||||
) {
|
||||
this.domainObject = (context || {}).domainObject;
|
||||
@@ -52,6 +53,7 @@ define([
|
||||
this.policyService = policyService;
|
||||
this.dialogService = dialogService;
|
||||
this.copyService = copyService;
|
||||
this.notificationService = notificationService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,8 +119,10 @@ define([
|
||||
|
||||
return self.dialogService
|
||||
.getUserInput(wizard.getFormStructure(true),
|
||||
wizard.getInitialFormValue()
|
||||
).then(wizard.populateObjectFromInput.bind(wizard));
|
||||
wizard.getInitialFormValue())
|
||||
.then(wizard.populateObjectFromInput.bind(wizard), function (failureReason) {
|
||||
return Promise.reject("user canceled");
|
||||
});
|
||||
}
|
||||
|
||||
function showBlockingDialog(object) {
|
||||
@@ -171,11 +175,21 @@ define([
|
||||
|
||||
function finishEditing(clonedObject) {
|
||||
return domainObject.getCapability("editor").finish()
|
||||
.then(resolveWith(clonedObject));
|
||||
.then(function () {
|
||||
return fetchObject(clonedObject.getId());
|
||||
});
|
||||
}
|
||||
|
||||
function onFailure() {
|
||||
function onSuccess(object) {
|
||||
self.notificationService.info("Save Succeeded");
|
||||
return object;
|
||||
}
|
||||
|
||||
function onFailure(reason) {
|
||||
hideBlockingDialog();
|
||||
if (reason !== "user canceled") {
|
||||
self.notificationService.error("Save Failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -188,6 +202,7 @@ define([
|
||||
.then(saveAfterClone)
|
||||
.then(finishEditing)
|
||||
.then(hideBlockingDialog)
|
||||
.then(onSuccess)
|
||||
.catch(onFailure);
|
||||
};
|
||||
|
||||
|
||||
@@ -48,9 +48,10 @@ define(
|
||||
* Decorate PersistenceCapability to queue persistence calls when a
|
||||
* transaction is in progress.
|
||||
*/
|
||||
TransactionCapabilityDecorator.prototype.getCapabilities = function (model) {
|
||||
TransactionCapabilityDecorator.prototype.getCapabilities = function () {
|
||||
var self = this,
|
||||
capabilities = this.capabilityService.getCapabilities(model),
|
||||
capabilities = this.capabilityService.getCapabilities
|
||||
.apply(this.capabilityService, arguments),
|
||||
persistenceCapability = capabilities.persistence;
|
||||
|
||||
capabilities.persistence = function (domainObject) {
|
||||
|
||||
@@ -41,7 +41,7 @@ define(
|
||||
return {
|
||||
key: action,
|
||||
name: action.getMetadata().name,
|
||||
cssclass: action.getMetadata().cssclass
|
||||
cssClass: action.getMetadata().cssClass
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -28,18 +28,49 @@ define(
|
||||
[],
|
||||
function () {
|
||||
|
||||
function isDirty(domainObject) {
|
||||
var navigatedObject = domainObject,
|
||||
editorCapability = navigatedObject &&
|
||||
navigatedObject.getCapability("editor");
|
||||
|
||||
return editorCapability &&
|
||||
editorCapability.isEditContextRoot() &&
|
||||
editorCapability.dirty();
|
||||
}
|
||||
|
||||
function cancelEditing(domainObject) {
|
||||
var navigatedObject = domainObject,
|
||||
editorCapability = navigatedObject &&
|
||||
navigatedObject.getCapability("editor");
|
||||
|
||||
return editorCapability &&
|
||||
editorCapability.finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller which is responsible for populating the scope for
|
||||
* Edit mode
|
||||
* @memberof platform/commonUI/edit
|
||||
* @constructor
|
||||
*/
|
||||
function EditObjectController($scope, $location, policyService) {
|
||||
function EditObjectController($scope, $location, navigationService) {
|
||||
this.scope = $scope;
|
||||
this.policyService = policyService;
|
||||
var domainObject = $scope.domainObject;
|
||||
|
||||
var navigatedObject;
|
||||
function setViewForDomainObject(domainObject) {
|
||||
var removeCheck = navigationService
|
||||
.checkBeforeNavigation(function () {
|
||||
if (isDirty(domainObject)) {
|
||||
return "Continuing will cause the loss of any unsaved changes.";
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
removeCheck();
|
||||
cancelEditing(domainObject);
|
||||
});
|
||||
|
||||
function setViewForDomainObject() {
|
||||
|
||||
var locationViewKey = $location.search().view;
|
||||
|
||||
@@ -54,34 +85,15 @@ define(
|
||||
((domainObject && domainObject.useCapability('view')) || [])
|
||||
.forEach(selectViewIfMatching);
|
||||
}
|
||||
navigatedObject = domainObject;
|
||||
}
|
||||
|
||||
$scope.$watch('domainObject', setViewForDomainObject);
|
||||
setViewForDomainObject();
|
||||
|
||||
$scope.doAction = function (action) {
|
||||
return $scope[action] && $scope[action]();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the warning to show if the user attempts to navigate
|
||||
* away from Edit mode while unsaved changes are present.
|
||||
* @returns {string} the warning to show, or undefined if
|
||||
* there are no unsaved changes
|
||||
*/
|
||||
EditObjectController.prototype.getUnloadWarning = function () {
|
||||
var navigatedObject = this.scope.domainObject,
|
||||
policyMessage;
|
||||
|
||||
this.policyService.allow("navigation", navigatedObject, undefined, function (message) {
|
||||
policyMessage = message;
|
||||
});
|
||||
|
||||
return policyMessage;
|
||||
|
||||
};
|
||||
|
||||
return EditObjectController;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@ define(
|
||||
function AddAction(type, parent, context, $q, dialogService, policyService) {
|
||||
this.metadata = {
|
||||
key: 'add',
|
||||
cssclass: type.getCssClass(),
|
||||
cssClass: type.getCssClass(),
|
||||
name: type.getName(),
|
||||
type: type.getKey(),
|
||||
description: type.getDescription(),
|
||||
|
||||
@@ -66,9 +66,7 @@ define(
|
||||
}
|
||||
|
||||
// Introduce one create action per type
|
||||
return this.typeService.listTypes().filter(function (type) {
|
||||
return self.policyService.allow("creation", type) && self.policyService.allow("composition", destination.getCapability('type'), type);
|
||||
}).map(function (type) {
|
||||
['timeline', 'activity'].map(function (type) {
|
||||
return new AddAction(
|
||||
type,
|
||||
destination,
|
||||
|
||||
@@ -47,7 +47,7 @@ define(
|
||||
function CreateAction(type, parent, context) {
|
||||
this.metadata = {
|
||||
key: 'create',
|
||||
cssclass: type.getCssClass(),
|
||||
cssClass: type.getCssClass(),
|
||||
name: type.getName(),
|
||||
type: type.getKey(),
|
||||
description: type.getDescription(),
|
||||
|
||||
@@ -56,7 +56,7 @@ define(
|
||||
*/
|
||||
CreateWizard.prototype.getFormStructure = function (includeLocation) {
|
||||
var sections = [],
|
||||
type = this.type,
|
||||
domainObject = this.domainObject,
|
||||
policyService = this.policyService;
|
||||
|
||||
function validateLocation(locatingObject) {
|
||||
@@ -65,7 +65,7 @@ define(
|
||||
return locatingType && policyService.allow(
|
||||
"composition",
|
||||
locatingType,
|
||||
type
|
||||
domainObject
|
||||
);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ define(
|
||||
if (includeLocation) {
|
||||
sections.push({
|
||||
name: 'Location',
|
||||
cssclass: "grows",
|
||||
cssClass: "grows",
|
||||
rows: [{
|
||||
name: "Save In",
|
||||
control: "locator",
|
||||
@@ -118,7 +118,7 @@ define(
|
||||
formModel = this.createModel(formValue);
|
||||
|
||||
formModel.location = parent.getId();
|
||||
this.domainObject.useCapability("mutation", function () {
|
||||
this.domainObject.useCapability("mutation", function (model) {
|
||||
return formModel;
|
||||
});
|
||||
return this.domainObject;
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Defines the `mct-before-unload` directive. The expression bound
|
||||
* to this attribute will be evaluated during page navigation events
|
||||
* and, if it returns a truthy value, will be used to populate a
|
||||
* prompt to the user to confirm this navigation.
|
||||
* @memberof platform/commonUI/edit
|
||||
* @constructor
|
||||
* @param $window the window
|
||||
*/
|
||||
function MCTBeforeUnload($window) {
|
||||
var unloads = [],
|
||||
oldBeforeUnload = $window.onbeforeunload;
|
||||
|
||||
// Run all unload functions, returning the first returns truthily.
|
||||
function checkUnloads() {
|
||||
var result;
|
||||
unloads.forEach(function (unload) {
|
||||
result = result || unload();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// Link function for an mct-before-unload directive usage
|
||||
function link(scope, element, attrs) {
|
||||
// Invoke the
|
||||
function unload() {
|
||||
return scope.$eval(attrs.mctBeforeUnload);
|
||||
}
|
||||
|
||||
// Stop using this unload expression
|
||||
function removeUnload() {
|
||||
unloads = unloads.filter(function (callback) {
|
||||
return callback !== unload;
|
||||
});
|
||||
if (unloads.length === 0) {
|
||||
$window.onbeforeunload = oldBeforeUnload;
|
||||
}
|
||||
}
|
||||
|
||||
// Show a dialog before allowing a location change
|
||||
function checkLocationChange(event) {
|
||||
// Get an unload message (if any)
|
||||
var warning = unload();
|
||||
// Prompt the user if there's an unload message
|
||||
if (warning && !$window.confirm(warning)) {
|
||||
// ...and prevent the route change if it was confirmed
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the first active instance of this directive,
|
||||
// register as the window's beforeunload handler
|
||||
if (unloads.length === 0) {
|
||||
$window.onbeforeunload = checkUnloads;
|
||||
}
|
||||
|
||||
// Include this instance of the directive's unload function
|
||||
unloads.push(unload);
|
||||
|
||||
// Remove it when the scope is destroyed
|
||||
scope.$on("$destroy", removeUnload);
|
||||
|
||||
// Also handle route changes
|
||||
scope.$on("$locationChangeStart", checkLocationChange);
|
||||
}
|
||||
|
||||
return {
|
||||
// Applicable as an attribute
|
||||
restrict: "A",
|
||||
// Link with the provided function
|
||||
link: link
|
||||
};
|
||||
}
|
||||
|
||||
return MCTBeforeUnload;
|
||||
|
||||
}
|
||||
);
|
||||
@@ -1,64 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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(
|
||||
[],
|
||||
function () {
|
||||
|
||||
/**
|
||||
* Policy controlling whether navigation events should proceed
|
||||
* when object is being edited.
|
||||
* @memberof platform/commonUI/edit
|
||||
* @constructor
|
||||
* @implements {Policy.<Action, ActionContext>}
|
||||
*/
|
||||
function EditNavigationPolicy(policyService) {
|
||||
this.policyService = policyService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
EditNavigationPolicy.prototype.isDirty = function (domainObject) {
|
||||
var navigatedObject = domainObject,
|
||||
editorCapability = navigatedObject &&
|
||||
navigatedObject.getCapability("editor");
|
||||
|
||||
return editorCapability &&
|
||||
editorCapability.isEditContextRoot() &&
|
||||
editorCapability.dirty();
|
||||
};
|
||||
|
||||
/**
|
||||
* Allow navigation if an object is not dirty, or if the user elects
|
||||
* to proceed anyway.
|
||||
* @param currentNavigation
|
||||
* @returns {boolean|*} true if the object model is clean; or if
|
||||
* it's dirty and the user wishes to proceed anyway.
|
||||
*/
|
||||
EditNavigationPolicy.prototype.allow = function (currentNavigation) {
|
||||
return !this.isDirty(currentNavigation);
|
||||
};
|
||||
|
||||
return EditNavigationPolicy;
|
||||
}
|
||||
);
|
||||
@@ -43,98 +43,55 @@ define(
|
||||
* @implements {Representer}
|
||||
* @constructor
|
||||
*/
|
||||
function EditRepresenter($q, $log, scope) {
|
||||
var self = this;
|
||||
|
||||
this.scope = scope;
|
||||
this.listenHandle = undefined;
|
||||
|
||||
// Mutate and persist a new version of a domain object's model.
|
||||
function doMutate(model) {
|
||||
var domainObject = self.domainObject;
|
||||
|
||||
// First, mutate; then, persist.
|
||||
return $q.when(domainObject.useCapability("mutation", function () {
|
||||
return model;
|
||||
}));
|
||||
}
|
||||
|
||||
// Handle changes to model and/or view configuration
|
||||
function commit(message) {
|
||||
// Look up from scope; these will have been populated by
|
||||
// mct-representation.
|
||||
var model = scope.model,
|
||||
configuration = scope.configuration,
|
||||
domainObject = self.domainObject;
|
||||
|
||||
// Log the commit message
|
||||
$log.debug([
|
||||
"Committing ",
|
||||
domainObject && domainObject.getModel().name,
|
||||
"(" + (domainObject && domainObject.getId()) + "):",
|
||||
message
|
||||
].join(" "));
|
||||
|
||||
// Update the configuration stored in the model, and persist.
|
||||
if (domainObject) {
|
||||
// Configurations for specific views are stored by
|
||||
// key in the "configuration" field of the model.
|
||||
if (self.key && configuration) {
|
||||
model.configuration = model.configuration || {};
|
||||
model.configuration[self.key] = configuration;
|
||||
}
|
||||
doMutate(model);
|
||||
}
|
||||
}
|
||||
|
||||
// Place the "commit" method in the scope
|
||||
scope.commit = commit;
|
||||
|
||||
// Clean up when the scope is destroyed
|
||||
scope.$on("$destroy", function () {
|
||||
self.destroy();
|
||||
});
|
||||
function EditRepresenter($log, $scope) {
|
||||
this.$log = $log;
|
||||
this.$scope = $scope;
|
||||
|
||||
this.$scope.commit = this.commit.bind(this);
|
||||
}
|
||||
|
||||
// Handle a specific representation of a specific domain object
|
||||
EditRepresenter.prototype.represent = function represent(representation, representedObject) {
|
||||
var scope = this.scope;
|
||||
/**
|
||||
* Commit any changes made to the in-scope model to the domain object.
|
||||
* Also commits any changes made to $scope.configuration to the proper
|
||||
* configuration value for the current representation.
|
||||
*
|
||||
* @param {String} message a message to log with the commit message.
|
||||
*/
|
||||
EditRepresenter.prototype.commit = function (message) {
|
||||
var model = this.$scope.model,
|
||||
configuration = this.$scope.configuration,
|
||||
domainObject = this.domainObject;
|
||||
|
||||
// Track the key, to know which view configuration to save to.
|
||||
this.key = (representation || {}).key;
|
||||
// Track the represented object
|
||||
this.domainObject = representedObject;
|
||||
this.$log.debug([
|
||||
"Committing ",
|
||||
domainObject && domainObject.getModel().name,
|
||||
"(" + (domainObject && domainObject.getId()) + "):",
|
||||
message
|
||||
].join(" "));
|
||||
|
||||
// Ensure existing watches are released
|
||||
this.destroy();
|
||||
|
||||
function setEditing() {
|
||||
scope.viewObjectTemplate = 'edit-object';
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for changes in object state. If the object becomes
|
||||
* editable then change the view and inspector regions
|
||||
* object representation accordingly
|
||||
*/
|
||||
this.listenHandle = this.domainObject.getCapability('status').listen(function (statuses) {
|
||||
if (statuses.indexOf('editing') !== -1) {
|
||||
setEditing();
|
||||
} else {
|
||||
delete scope.viewObjectTemplate;
|
||||
if (this.domainObject) {
|
||||
if (this.key && configuration) {
|
||||
model.configuration = model.configuration || {};
|
||||
model.configuration[this.key] = configuration;
|
||||
}
|
||||
});
|
||||
domainObject.useCapability('mutation', function () {
|
||||
return model;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (representedObject.hasCapability('editor') && representedObject.getCapability('editor').isEditContextRoot()) {
|
||||
setEditing();
|
||||
// Handle a specific representation of a specific domain object
|
||||
EditRepresenter.prototype.represent = function (representation, representedObject) {
|
||||
this.domainObject = representedObject;
|
||||
if (representation) {
|
||||
this.key = representation.key;
|
||||
} else {
|
||||
delete this.key;
|
||||
}
|
||||
};
|
||||
|
||||
// Respond to the destruction of the current representation.
|
||||
EditRepresenter.prototype.destroy = function destroy() {
|
||||
return this.listenHandle && this.listenHandle();
|
||||
};
|
||||
EditRepresenter.prototype.destroy = function () {};
|
||||
|
||||
return EditRepresenter;
|
||||
}
|
||||
|
||||
@@ -34,9 +34,10 @@ define(
|
||||
* @param $q
|
||||
* @constructor
|
||||
*/
|
||||
function TransactionService($q, $log) {
|
||||
function TransactionService($q, $log, cacheService) {
|
||||
this.$q = $q;
|
||||
this.$log = $log;
|
||||
this.cacheService = cacheService;
|
||||
this.transactions = [];
|
||||
}
|
||||
|
||||
@@ -87,14 +88,25 @@ define(
|
||||
|
||||
/**
|
||||
* All persist calls deferred since the beginning of the transaction
|
||||
* will be committed.
|
||||
* will be committed. If this is the last transaction, clears the
|
||||
* cache.
|
||||
*
|
||||
* @returns {Promise} resolved when all persist operations have
|
||||
* completed. Will reject if any commit operations fail
|
||||
*/
|
||||
TransactionService.prototype.commit = function () {
|
||||
var transaction = this.transactions.pop();
|
||||
return transaction ? transaction.commit() : Promise.reject();
|
||||
if (!transaction) {
|
||||
return Promise.reject();
|
||||
}
|
||||
if (!this.isActive()) {
|
||||
return transaction.commit()
|
||||
.then(function (r) {
|
||||
this.cacheService.flush();
|
||||
return r;
|
||||
}.bind(this));
|
||||
}
|
||||
return transaction.commit();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global describe,it,expect,beforeEach,jasmine,waitsFor,runs*/
|
||||
|
||||
define(
|
||||
["../../src/actions/SaveAction"],
|
||||
@@ -28,7 +29,8 @@ define(
|
||||
var mockDomainObject,
|
||||
mockEditorCapability,
|
||||
actionContext,
|
||||
dialogService,
|
||||
mockDialogService,
|
||||
mockNotificationService,
|
||||
mockActionCapability,
|
||||
capabilities = {},
|
||||
action;
|
||||
@@ -68,11 +70,17 @@ define(
|
||||
actionContext = {
|
||||
domainObject: mockDomainObject
|
||||
};
|
||||
dialogService = jasmine.createSpyObj(
|
||||
|
||||
mockDialogService = jasmine.createSpyObj(
|
||||
"dialogService",
|
||||
["showBlockingMessage"]
|
||||
);
|
||||
|
||||
mockNotificationService = jasmine.createSpyObj(
|
||||
"notificationService",
|
||||
["info", "error"]
|
||||
);
|
||||
|
||||
mockDomainObject.hasCapability.andReturn(true);
|
||||
mockDomainObject.getCapability.andCallFake(function (capability) {
|
||||
return capabilities[capability];
|
||||
@@ -81,7 +89,7 @@ define(
|
||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||
|
||||
action = new SaveAction(dialogService, actionContext);
|
||||
action = new SaveAction(mockDialogService, mockNotificationService, actionContext);
|
||||
});
|
||||
|
||||
it("only applies to domain object with an editor capability", function () {
|
||||
@@ -105,30 +113,54 @@ define(
|
||||
expect(mockEditorCapability.save).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("a blocking dialog", function () {
|
||||
describe("in order to keep the user in the loop", function () {
|
||||
var mockDialogHandle;
|
||||
|
||||
beforeEach(function () {
|
||||
mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]);
|
||||
dialogService.showBlockingMessage.andReturn(mockDialogHandle);
|
||||
mockDialogService.showBlockingMessage.andReturn(mockDialogHandle);
|
||||
});
|
||||
|
||||
|
||||
it("shows a dialog while saving", function () {
|
||||
mockEditorCapability.save.andReturn(new Promise(function () {
|
||||
}));
|
||||
action.perform();
|
||||
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
|
||||
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
||||
expect(mockDialogHandle.dismiss).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("hides a dialog when saving is complete", function () {
|
||||
it("hides the dialog when saving is complete", function () {
|
||||
action.perform();
|
||||
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
|
||||
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
||||
expect(mockDialogHandle.dismiss).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("notifies if saving succeeded", function () {
|
||||
var mockCallback = jasmine.createSpy("callback");
|
||||
mockEditorCapability.save.andReturn(Promise.resolve("success"));
|
||||
action.perform().then(mockCallback);
|
||||
waitsFor(function () {
|
||||
return mockCallback.calls.length > 0;
|
||||
});
|
||||
runs(function () {
|
||||
expect(mockNotificationService.info).toHaveBeenCalled();
|
||||
expect(mockNotificationService.error).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("notifies if saving failed", function () {
|
||||
var mockCallback = jasmine.createSpy("callback");
|
||||
mockEditorCapability.save.andReturn(Promise.reject("some failure reason"));
|
||||
action.perform().then(mockCallback);
|
||||
waitsFor(function () {
|
||||
return mockCallback.calls.length > 0;
|
||||
});
|
||||
runs(function () {
|
||||
expect(mockNotificationService.error).toHaveBeenCalled();
|
||||
expect(mockNotificationService.info).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global describe,it,expect,beforeEach,jasmine*/
|
||||
|
||||
define(
|
||||
["../../src/actions/SaveAndStopEditingAction"],
|
||||
@@ -35,6 +36,7 @@ define(
|
||||
mockEditorCapability,
|
||||
actionContext,
|
||||
dialogService,
|
||||
notificationService,
|
||||
mockActionCapability,
|
||||
capabilities = {},
|
||||
action;
|
||||
@@ -79,6 +81,11 @@ define(
|
||||
["showBlockingMessage"]
|
||||
);
|
||||
|
||||
notificationService = jasmine.createSpyObj(
|
||||
"notificationService",
|
||||
["info", "error"]
|
||||
);
|
||||
|
||||
mockDomainObject.hasCapability.andReturn(true);
|
||||
mockDomainObject.getCapability.andCallFake(function (capability) {
|
||||
return capabilities[capability];
|
||||
@@ -87,7 +94,7 @@ define(
|
||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||
|
||||
action = new SaveAndStopEditingAction(dialogService, actionContext);
|
||||
action = new SaveAndStopEditingAction(dialogService, notificationService, actionContext);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -27,11 +27,13 @@ define(
|
||||
|
||||
describe("The Save As action", function () {
|
||||
var mockDomainObject,
|
||||
mockClonedObject,
|
||||
mockEditorCapability,
|
||||
mockActionCapability,
|
||||
mockObjectService,
|
||||
mockDialogService,
|
||||
mockCopyService,
|
||||
mockNotificationService,
|
||||
mockParent,
|
||||
actionContext,
|
||||
capabilities = {},
|
||||
@@ -57,7 +59,8 @@ define(
|
||||
[
|
||||
"getCapability",
|
||||
"hasCapability",
|
||||
"getModel"
|
||||
"getModel",
|
||||
"getId"
|
||||
]
|
||||
);
|
||||
mockDomainObject.hasCapability.andReturn(true);
|
||||
@@ -65,6 +68,15 @@ define(
|
||||
return capabilities[capability];
|
||||
});
|
||||
mockDomainObject.getModel.andReturn({location: 'a', persisted: undefined});
|
||||
mockDomainObject.getId.andReturn(0);
|
||||
|
||||
mockClonedObject = jasmine.createSpyObj(
|
||||
"clonedObject",
|
||||
[
|
||||
"getId"
|
||||
]
|
||||
);
|
||||
mockClonedObject.getId.andReturn(1);
|
||||
|
||||
mockParent = jasmine.createSpyObj(
|
||||
"parentObject",
|
||||
@@ -111,12 +123,27 @@ define(
|
||||
"perform"
|
||||
]
|
||||
);
|
||||
mockCopyService.perform.andReturn(mockPromise(mockClonedObject));
|
||||
|
||||
mockNotificationService = jasmine.createSpyObj(
|
||||
"notificationService",
|
||||
[
|
||||
"info",
|
||||
"error"
|
||||
]
|
||||
);
|
||||
|
||||
actionContext = {
|
||||
domainObject: mockDomainObject
|
||||
};
|
||||
|
||||
action = new SaveAsAction(undefined, undefined, mockDialogService, mockCopyService, actionContext);
|
||||
action = new SaveAsAction(
|
||||
undefined,
|
||||
undefined,
|
||||
mockDialogService,
|
||||
mockCopyService,
|
||||
mockNotificationService,
|
||||
actionContext);
|
||||
|
||||
spyOn(action, "getObjectService");
|
||||
action.getObjectService.andReturn(mockObjectService);
|
||||
@@ -186,7 +213,7 @@ define(
|
||||
expect(mockDialogService.getUserInput).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("a blocking dialog", function () {
|
||||
describe("in order to keep the user in the loop", function () {
|
||||
var mockDialogHandle;
|
||||
|
||||
beforeEach(function () {
|
||||
@@ -194,14 +221,14 @@ define(
|
||||
mockDialogService.showBlockingMessage.andReturn(mockDialogHandle);
|
||||
});
|
||||
|
||||
it("indicates that a save is taking place", function () {
|
||||
it("shows a blocking dialog indicating that saving is in progress", function () {
|
||||
mockEditorCapability.save.andReturn(new Promise(function () {}));
|
||||
action.perform();
|
||||
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
||||
expect(mockDialogHandle.dismiss).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("is hidden after saving", function () {
|
||||
it("hides the blocking dialog after saving finishes", function () {
|
||||
var mockCallback = jasmine.createSpy();
|
||||
action.perform().then(mockCallback);
|
||||
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
||||
@@ -212,6 +239,31 @@ define(
|
||||
expect(mockDialogHandle.dismiss).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("notifies if saving succeeded", function () {
|
||||
var mockCallback = jasmine.createSpy();
|
||||
action.perform().then(mockCallback);
|
||||
waitsFor(function () {
|
||||
return mockCallback.calls.length > 0;
|
||||
});
|
||||
runs(function () {
|
||||
expect(mockNotificationService.info).toHaveBeenCalled();
|
||||
expect(mockNotificationService.error).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("notifies if saving failed", function () {
|
||||
mockCopyService.perform.andReturn(Promise.reject("some failure reason"));
|
||||
var mockCallback = jasmine.createSpy();
|
||||
action.perform().then(mockCallback);
|
||||
waitsFor(function () {
|
||||
return mockCallback.calls.length > 0;
|
||||
});
|
||||
runs(function () {
|
||||
expect(mockNotificationService.error).toHaveBeenCalled();
|
||||
expect(mockNotificationService.info).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ define(
|
||||
describe("The Edit Action controller", function () {
|
||||
var mockSaveActionMetadata = {
|
||||
name: "mocked-save-action",
|
||||
cssclass: "mocked-save-action-css"
|
||||
cssClass: "mocked-save-action-css"
|
||||
};
|
||||
|
||||
function fakeGetActions(actionContext) {
|
||||
@@ -86,7 +86,7 @@ define(
|
||||
expect(menuOptions[1].key).toEqual(mockScope.saveActions[1]);
|
||||
menuOptions.forEach(function (option) {
|
||||
expect(option.name).toEqual(mockSaveActionMetadata.name);
|
||||
expect(option.cssclass).toEqual(mockSaveActionMetadata.cssclass);
|
||||
expect(option.cssClass).toEqual(mockSaveActionMetadata.cssClass);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -24,32 +24,19 @@ define(
|
||||
["../../src/controllers/EditObjectController"],
|
||||
function (EditObjectController) {
|
||||
|
||||
describe("The Edit mode controller", function () {
|
||||
describe("The Edit Object controller", function () {
|
||||
var mockScope,
|
||||
mockObject,
|
||||
mockType,
|
||||
testViews,
|
||||
mockEditorCapability,
|
||||
mockLocation,
|
||||
mockNavigationService,
|
||||
removeCheck,
|
||||
mockStatusCapability,
|
||||
mockCapabilities,
|
||||
mockPolicyService,
|
||||
controller;
|
||||
|
||||
// Utility function; look for a $watch on scope and fire it
|
||||
function fireWatch(expr, value) {
|
||||
mockScope.$watch.calls.forEach(function (call) {
|
||||
if (call.args[0] === expr) {
|
||||
call.args[1](value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockPolicyService = jasmine.createSpyObj(
|
||||
"policyService",
|
||||
[
|
||||
"allow"
|
||||
]
|
||||
);
|
||||
mockScope = jasmine.createSpyObj(
|
||||
"$scope",
|
||||
["$on", "$watch"]
|
||||
@@ -58,16 +45,16 @@ define(
|
||||
"domainObject",
|
||||
["getId", "getModel", "getCapability", "hasCapability", "useCapability"]
|
||||
);
|
||||
mockType = jasmine.createSpyObj(
|
||||
"type",
|
||||
["hasFeature"]
|
||||
mockEditorCapability = jasmine.createSpyObj(
|
||||
"mockEditorCapability",
|
||||
["isEditContextRoot", "dirty", "finish"]
|
||||
);
|
||||
mockStatusCapability = jasmine.createSpyObj('statusCapability',
|
||||
["get"]
|
||||
);
|
||||
|
||||
mockCapabilities = {
|
||||
"type" : mockType,
|
||||
"editor" : mockEditorCapability,
|
||||
"status": mockStatusCapability
|
||||
};
|
||||
|
||||
@@ -75,52 +62,70 @@ define(
|
||||
["search"]
|
||||
);
|
||||
mockLocation.search.andReturn({"view": "fixed"});
|
||||
mockNavigationService = jasmine.createSpyObj('navigationService',
|
||||
["checkBeforeNavigation"]
|
||||
);
|
||||
|
||||
removeCheck = jasmine.createSpy('removeCheck');
|
||||
mockNavigationService.checkBeforeNavigation.andReturn(removeCheck);
|
||||
|
||||
mockObject.getId.andReturn("test");
|
||||
mockObject.getModel.andReturn({ name: "Test object" });
|
||||
mockObject.getCapability.andCallFake(function (key) {
|
||||
return mockCapabilities[key];
|
||||
});
|
||||
mockType.hasFeature.andReturn(true);
|
||||
|
||||
mockScope.domainObject = mockObject;
|
||||
|
||||
controller = new EditObjectController(
|
||||
mockScope,
|
||||
mockLocation,
|
||||
mockPolicyService
|
||||
);
|
||||
});
|
||||
|
||||
it("exposes a warning message for unload", function () {
|
||||
var errorMessage = "Unsaved changes";
|
||||
|
||||
// Normally, should be undefined
|
||||
expect(controller.getUnloadWarning()).toBeUndefined();
|
||||
|
||||
// Override the policy service to prevent navigation
|
||||
mockPolicyService.allow.andCallFake(function (category, object, context, callback) {
|
||||
callback(errorMessage);
|
||||
});
|
||||
|
||||
// Should have some warning message here now
|
||||
expect(controller.getUnloadWarning()).toEqual(errorMessage);
|
||||
});
|
||||
|
||||
|
||||
it("sets the active view from query parameters", function () {
|
||||
var testViews = [
|
||||
{ key: 'abc' },
|
||||
{ key: 'def', someKey: 'some value' },
|
||||
{ key: 'xyz' }
|
||||
];
|
||||
testViews = [
|
||||
{ key: 'abc' },
|
||||
{ key: 'def', someKey: 'some value' },
|
||||
{ key: 'xyz' }
|
||||
];
|
||||
|
||||
mockObject.useCapability.andCallFake(function (c) {
|
||||
return (c === 'view') && testViews;
|
||||
});
|
||||
mockLocation.search.andReturn({ view: 'def' });
|
||||
|
||||
fireWatch('domainObject', mockObject);
|
||||
mockScope.domainObject = mockObject;
|
||||
|
||||
controller = new EditObjectController(
|
||||
mockScope,
|
||||
mockLocation,
|
||||
mockNavigationService
|
||||
);
|
||||
});
|
||||
|
||||
it("adds a check before navigation", function () {
|
||||
expect(mockNavigationService.checkBeforeNavigation)
|
||||
.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
|
||||
var checkFn = mockNavigationService.checkBeforeNavigation.mostRecentCall.args[0];
|
||||
|
||||
mockEditorCapability.isEditContextRoot.andReturn(false);
|
||||
mockEditorCapability.dirty.andReturn(false);
|
||||
|
||||
expect(checkFn()).toBe(false);
|
||||
|
||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||
expect(checkFn()).toBe(false);
|
||||
|
||||
mockEditorCapability.dirty.andReturn(true);
|
||||
expect(checkFn())
|
||||
.toBe("Continuing will cause the loss of any unsaved changes.");
|
||||
|
||||
});
|
||||
|
||||
it("cleans up on destroy", function () {
|
||||
expect(mockScope.$on)
|
||||
.toHaveBeenCalledWith("$destroy", jasmine.any(Function));
|
||||
|
||||
mockScope.$on.mostRecentCall.args[1]();
|
||||
|
||||
expect(mockEditorCapability.finish).toHaveBeenCalled();
|
||||
expect(removeCheck).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("sets the active view from query parameters", function () {
|
||||
expect(mockScope.representation.selected)
|
||||
.toEqual(testViews[1]);
|
||||
});
|
||||
@@ -138,7 +138,7 @@ define(
|
||||
|
||||
expect(metadata.name).toEqual("Test");
|
||||
expect(metadata.description).toEqual("a test type");
|
||||
expect(metadata.cssclass).toEqual("icon-telemetry");
|
||||
expect(metadata.cssClass).toEqual("icon-telemetry");
|
||||
});
|
||||
|
||||
describe("the perform function", function () {
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
/*****************************************************************************
|
||||
* 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(
|
||||
["../../src/directives/MCTBeforeUnload"],
|
||||
function (MCTBeforeUnload) {
|
||||
|
||||
describe("The mct-before-unload directive", function () {
|
||||
var mockWindow,
|
||||
mockScope,
|
||||
testAttrs,
|
||||
mockEvent,
|
||||
directive;
|
||||
|
||||
function fireListener(eventType, value) {
|
||||
mockScope.$on.calls.forEach(function (call) {
|
||||
if (call.args[0] === eventType) {
|
||||
call.args[1](value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockWindow = jasmine.createSpyObj("$window", ['confirm']);
|
||||
mockScope = jasmine.createSpyObj("$scope", ['$eval', '$on']);
|
||||
testAttrs = { mctBeforeUnload: "someExpression" };
|
||||
mockEvent = jasmine.createSpyObj("event", ["preventDefault"]);
|
||||
directive = new MCTBeforeUnload(mockWindow);
|
||||
directive.link(mockScope, {}, testAttrs);
|
||||
});
|
||||
|
||||
it("can be used only as an attribute", function () {
|
||||
expect(directive.restrict).toEqual('A');
|
||||
});
|
||||
|
||||
it("listens for beforeunload", function () {
|
||||
expect(mockWindow.onbeforeunload).toEqual(jasmine.any(Function));
|
||||
});
|
||||
|
||||
it("listens for route changes", function () {
|
||||
expect(mockScope.$on).toHaveBeenCalledWith(
|
||||
"$locationChangeStart",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("listens for its scope's destroy event", function () {
|
||||
expect(mockScope.$on).toHaveBeenCalledWith(
|
||||
"$destroy",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("uses result of evaluated expression as a warning", function () {
|
||||
mockScope.$eval.andReturn(undefined);
|
||||
expect(mockWindow.onbeforeunload(mockEvent)).toBeUndefined();
|
||||
mockScope.$eval.andReturn("some message");
|
||||
expect(mockWindow.onbeforeunload(mockEvent)).toEqual("some message");
|
||||
// Verify that the right expression was evaluated
|
||||
expect(mockScope.$eval).toHaveBeenCalledWith(testAttrs.mctBeforeUnload);
|
||||
});
|
||||
|
||||
it("confirms route changes", function () {
|
||||
// First, try with no unsaved changes;
|
||||
// should not confirm or preventDefault
|
||||
mockScope.$eval.andReturn(undefined);
|
||||
fireListener("$locationChangeStart", mockEvent);
|
||||
expect(mockWindow.confirm).not.toHaveBeenCalled();
|
||||
expect(mockEvent.preventDefault).not.toHaveBeenCalled();
|
||||
|
||||
// Next, try with unsaved changes that the user confirms;
|
||||
// should prompt, but not preventDefault
|
||||
mockScope.$eval.andReturn("some message");
|
||||
mockWindow.confirm.andReturn(true);
|
||||
fireListener("$locationChangeStart", mockEvent);
|
||||
expect(mockWindow.confirm).toHaveBeenCalledWith("some message");
|
||||
expect(mockEvent.preventDefault).not.toHaveBeenCalled();
|
||||
|
||||
// Finally, act as if the user said no to this dialog;
|
||||
// this should preventDefault on the location change.
|
||||
mockWindow.confirm.andReturn(false);
|
||||
fireListener("$locationChangeStart", mockEvent);
|
||||
expect(mockWindow.confirm).toHaveBeenCalledWith("some message");
|
||||
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("cleans up listeners when destroyed", function () {
|
||||
fireListener("$destroy", mockEvent);
|
||||
expect(mockWindow.onbeforeunload).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -20,103 +20,70 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
define(
|
||||
["../../src/representers/EditRepresenter"],
|
||||
function (EditRepresenter) {
|
||||
define([
|
||||
'../../src/representers/EditRepresenter'
|
||||
], function (
|
||||
EditRepresenter
|
||||
) {
|
||||
describe('EditRepresenter', function () {
|
||||
var $log,
|
||||
$scope,
|
||||
representer;
|
||||
|
||||
describe("The Edit mode representer", function () {
|
||||
var mockQ,
|
||||
mockLog,
|
||||
mockScope,
|
||||
testRepresentation,
|
||||
mockDomainObject,
|
||||
mockStatusCapability,
|
||||
mockEditorCapability,
|
||||
mockCapabilities,
|
||||
representer;
|
||||
|
||||
function mockPromise(value) {
|
||||
return {
|
||||
then: function (callback) {
|
||||
return mockPromise(callback(value));
|
||||
}
|
||||
};
|
||||
}
|
||||
beforeEach(function () {
|
||||
$log = jasmine.createSpyObj('$log', ['debug']);
|
||||
$scope = {};
|
||||
representer = new EditRepresenter($log, $scope);
|
||||
});
|
||||
|
||||
it('injects a commit function in scope', function () {
|
||||
expect($scope.commit).toEqual(jasmine.any(Function));
|
||||
});
|
||||
|
||||
describe('representation', function () {
|
||||
var domainObject,
|
||||
representation;
|
||||
|
||||
beforeEach(function () {
|
||||
mockQ = { when: mockPromise };
|
||||
mockLog = jasmine.createSpyObj("$log", ["info", "debug"]);
|
||||
mockScope = jasmine.createSpyObj("$scope", ["$watch", "$on"]);
|
||||
testRepresentation = { key: "test" };
|
||||
mockDomainObject = jasmine.createSpyObj("domainObject", [
|
||||
"getId",
|
||||
"getModel",
|
||||
"getCapability",
|
||||
"useCapability",
|
||||
"hasCapability"
|
||||
domainObject = jasmine.createSpyObj('domainObject', [
|
||||
'getId',
|
||||
'getModel',
|
||||
'useCapability'
|
||||
]);
|
||||
mockStatusCapability =
|
||||
jasmine.createSpyObj("statusCapability", ["listen"]);
|
||||
mockEditorCapability =
|
||||
jasmine.createSpyObj("editorCapability", ["isEditContextRoot"]);
|
||||
|
||||
mockCapabilities = {
|
||||
'status': mockStatusCapability,
|
||||
'editor': mockEditorCapability
|
||||
domainObject.getId.andReturn('anId');
|
||||
domainObject.getModel.andReturn({name: 'anObject'});
|
||||
|
||||
representation = {
|
||||
key: 'someRepresentation'
|
||||
};
|
||||
|
||||
mockDomainObject.getModel.andReturn({});
|
||||
mockDomainObject.hasCapability.andReturn(true);
|
||||
mockDomainObject.useCapability.andReturn(true);
|
||||
mockDomainObject.getCapability.andCallFake(function (capability) {
|
||||
return mockCapabilities[capability];
|
||||
});
|
||||
|
||||
representer = new EditRepresenter(mockQ, mockLog, mockScope);
|
||||
representer.represent(testRepresentation, mockDomainObject);
|
||||
$scope.model = {name: 'anotherName'};
|
||||
$scope.configuration = {some: 'config'};
|
||||
representer.represent(representation, domainObject);
|
||||
});
|
||||
|
||||
it("provides a commit method in scope", function () {
|
||||
expect(mockScope.commit).toEqual(jasmine.any(Function));
|
||||
it('logs a message when commiting', function () {
|
||||
$scope.commit('Test Message');
|
||||
expect($log.debug)
|
||||
.toHaveBeenCalledWith('Committing anObject (anId): Test Message');
|
||||
});
|
||||
|
||||
it("Sets edit view template on edit mode", function () {
|
||||
mockStatusCapability.listen.mostRecentCall.args[0](['editing']);
|
||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||
expect(mockScope.viewObjectTemplate).toEqual('edit-object');
|
||||
it('mutates the object when committing', function () {
|
||||
$scope.commit('Test Message');
|
||||
|
||||
expect(domainObject.useCapability)
|
||||
.toHaveBeenCalledWith('mutation', jasmine.any(Function));
|
||||
|
||||
var mutateValue = domainObject.useCapability.calls[0].args[1]();
|
||||
|
||||
expect(mutateValue.configuration.someRepresentation)
|
||||
.toEqual({some: 'config'});
|
||||
expect(mutateValue.name).toEqual('anotherName');
|
||||
});
|
||||
|
||||
it("Cleans up listeners on scope destroy", function () {
|
||||
representer.listenHandle = jasmine.createSpy('listen');
|
||||
mockScope.$on.mostRecentCall.args[1]();
|
||||
expect(representer.listenHandle).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("mutates upon observed changes", function () {
|
||||
mockScope.model = { someKey: "some value" };
|
||||
mockScope.configuration = { someConfiguration: "something" };
|
||||
|
||||
mockScope.commit("Some message");
|
||||
|
||||
// Should have mutated the object...
|
||||
expect(mockDomainObject.useCapability).toHaveBeenCalledWith(
|
||||
"mutation",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
|
||||
// Finally, check that the provided mutation function
|
||||
// includes both model and configuration
|
||||
expect(
|
||||
mockDomainObject.useCapability.mostRecentCall.args[1]()
|
||||
).toEqual({
|
||||
someKey: "some value",
|
||||
configuration: {
|
||||
test: { someConfiguration: "something" }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,6 +95,45 @@ define([
|
||||
})[0][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of the current range of the time conductor's
|
||||
* bounds.
|
||||
* @param timeRange
|
||||
* @returns {*}
|
||||
*/
|
||||
UTCTimeFormat.prototype.timeUnits = function (timeRange) {
|
||||
var momentified = moment.duration(timeRange);
|
||||
|
||||
return [
|
||||
["Decades", function (r) {
|
||||
return r.years() > 15;
|
||||
}],
|
||||
["Years", function (r) {
|
||||
return r.years() > 1;
|
||||
}],
|
||||
["Months", function (r) {
|
||||
return r.years() === 1 || r.months() > 1;
|
||||
}],
|
||||
["Days", function (r) {
|
||||
return r.months() === 1 || r.days() > 1;
|
||||
}],
|
||||
["Hours", function (r) {
|
||||
return r.days() === 1 || r.hours() > 1;
|
||||
}],
|
||||
["Minutes", function (r) {
|
||||
return r.hours() === 1 || r.minutes() > 1;
|
||||
}],
|
||||
["Seconds", function (r) {
|
||||
return r.minutes() === 1 || r.seconds() > 1;
|
||||
}],
|
||||
["Milliseconds", function (r) {
|
||||
return true;
|
||||
}]
|
||||
].filter(function (row) {
|
||||
return row[1](momentified);
|
||||
})[0][0];
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value
|
||||
|
||||
@@ -58,5 +58,26 @@ define([
|
||||
expect(format.format(APRIL, scale)).toBe("April");
|
||||
expect(format.format(TWENTY_SIXTEEN, scale)).toBe("2016");
|
||||
});
|
||||
|
||||
it("Returns appropriate time units for a given time span", function () {
|
||||
var ONE_DAY = 1000 * 60 * 60 * 24;
|
||||
var FIVE_DAYS = 5 * ONE_DAY;
|
||||
var FIVE_MONTHS = 60 * ONE_DAY;
|
||||
|
||||
var ONE_YEAR = 365 * ONE_DAY;
|
||||
var SEVEN_YEARS = 7 * ONE_YEAR;
|
||||
var TWO_DECADES = 20 * ONE_YEAR;
|
||||
|
||||
//A span of one day should show a zoom label of "Hours"
|
||||
expect(format.timeUnits(ONE_DAY)).toEqual("Hours");
|
||||
//Multiple days should display "Days"
|
||||
expect(format.timeUnits(FIVE_DAYS)).toEqual("Days");
|
||||
expect(format.timeUnits(FIVE_MONTHS)).toEqual("Days");
|
||||
//A span of one year should show a zoom level of "Months".
|
||||
// Multiple years will show "Years"
|
||||
expect(format.timeUnits(ONE_YEAR)).toEqual("Months");
|
||||
expect(format.timeUnits(SEVEN_YEARS)).toEqual("Years");
|
||||
expect(format.timeUnits(TWO_DECADES)).toEqual("Decades");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"metadata": {
|
||||
"name": "openmct-symbols-16px",
|
||||
"lastOpened": 1469724858940,
|
||||
"created": 1469724856623
|
||||
"lastOpened": 1481575258437,
|
||||
"created": 1481575255265
|
||||
},
|
||||
"iconSets": [
|
||||
{
|
||||
@@ -535,18 +535,50 @@
|
||||
{
|
||||
"order": 21,
|
||||
"prevSize": 24,
|
||||
"name": "icon-x-in-circle",
|
||||
"name": "icon-resync",
|
||||
"id": 16,
|
||||
"code": 921654,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 121,
|
||||
"id": 103,
|
||||
"name": "icon-x-in-circle",
|
||||
"prevSize": 24,
|
||||
"code": 921656,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 118,
|
||||
"id": 102,
|
||||
"name": "icon-brightness",
|
||||
"prevSize": 24,
|
||||
"code": 921657,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 119,
|
||||
"id": 104,
|
||||
"name": "icon-contrast",
|
||||
"prevSize": 24,
|
||||
"code": 921664,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 120,
|
||||
"id": 105,
|
||||
"name": "icon-reset",
|
||||
"prevSize": 24,
|
||||
"code": 921655,
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 37,
|
||||
"prevSize": 24,
|
||||
"name": "icon-activity",
|
||||
"id": 32,
|
||||
"code": 921856,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 36,
|
||||
@@ -554,7 +586,7 @@
|
||||
"name": "icon-activity-mode",
|
||||
"id": 31,
|
||||
"code": 921857,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 52,
|
||||
@@ -562,7 +594,7 @@
|
||||
"name": "icon-autoflow-tabular",
|
||||
"id": 47,
|
||||
"code": 921858,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 55,
|
||||
@@ -570,7 +602,7 @@
|
||||
"name": "icon-clock",
|
||||
"id": 50,
|
||||
"code": 921859,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 58,
|
||||
@@ -578,7 +610,7 @@
|
||||
"name": "icon-database",
|
||||
"id": 53,
|
||||
"code": 921860,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 57,
|
||||
@@ -586,7 +618,7 @@
|
||||
"name": "icon-database-query",
|
||||
"id": 52,
|
||||
"code": 921861,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 17,
|
||||
@@ -594,7 +626,7 @@
|
||||
"name": "icon-dataset",
|
||||
"id": 12,
|
||||
"code": 921862,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 22,
|
||||
@@ -602,7 +634,7 @@
|
||||
"name": "icon-datatable",
|
||||
"id": 17,
|
||||
"code": 921863,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 59,
|
||||
@@ -610,7 +642,7 @@
|
||||
"name": "icon-dictionary",
|
||||
"id": 54,
|
||||
"code": 921864,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 62,
|
||||
@@ -618,7 +650,7 @@
|
||||
"name": "icon-folder",
|
||||
"id": 57,
|
||||
"code": 921865,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 66,
|
||||
@@ -626,7 +658,7 @@
|
||||
"name": "icon-image",
|
||||
"id": 61,
|
||||
"code": 921872,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 68,
|
||||
@@ -634,7 +666,7 @@
|
||||
"name": "icon-layout",
|
||||
"id": 63,
|
||||
"code": 921873,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 77,
|
||||
@@ -642,7 +674,7 @@
|
||||
"name": "icon-object",
|
||||
"id": 72,
|
||||
"code": 921874,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 78,
|
||||
@@ -650,7 +682,7 @@
|
||||
"name": "icon-object-unknown",
|
||||
"id": 73,
|
||||
"code": 921875,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 79,
|
||||
@@ -658,7 +690,7 @@
|
||||
"name": "icon-packet",
|
||||
"id": 74,
|
||||
"code": 921876,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 80,
|
||||
@@ -666,7 +698,7 @@
|
||||
"name": "icon-page",
|
||||
"id": 75,
|
||||
"code": 921877,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 114,
|
||||
@@ -674,7 +706,7 @@
|
||||
"name": "icon-plot-overlay",
|
||||
"prevSize": 24,
|
||||
"code": 921878,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 113,
|
||||
@@ -682,7 +714,7 @@
|
||||
"name": "icon-plot-stacked",
|
||||
"prevSize": 24,
|
||||
"code": 921879,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 10,
|
||||
@@ -690,7 +722,7 @@
|
||||
"name": "icon-session",
|
||||
"id": 5,
|
||||
"code": 921880,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 24,
|
||||
@@ -698,7 +730,7 @@
|
||||
"name": "icon-tabular",
|
||||
"id": 19,
|
||||
"code": 921881,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 7,
|
||||
@@ -706,7 +738,7 @@
|
||||
"name": "icon-tabular-lad",
|
||||
"id": 2,
|
||||
"code": 921888,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 6,
|
||||
@@ -714,7 +746,7 @@
|
||||
"name": "icon-tabular-lad-set",
|
||||
"id": 1,
|
||||
"code": 921889,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 8,
|
||||
@@ -722,7 +754,7 @@
|
||||
"name": "icon-tabular-realtime",
|
||||
"id": 3,
|
||||
"code": 921890,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 23,
|
||||
@@ -730,7 +762,7 @@
|
||||
"name": "icon-tabular-scrolling",
|
||||
"id": 18,
|
||||
"code": 921891,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 112,
|
||||
@@ -738,7 +770,7 @@
|
||||
"name": "icon-telemetry",
|
||||
"id": 86,
|
||||
"code": 921892,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 90,
|
||||
@@ -746,7 +778,7 @@
|
||||
"name": "icon-telemetry-panel",
|
||||
"id": 85,
|
||||
"code": 921893,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 93,
|
||||
@@ -754,7 +786,7 @@
|
||||
"name": "icon-timeline",
|
||||
"id": 88,
|
||||
"code": 921894,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 116,
|
||||
@@ -762,7 +794,7 @@
|
||||
"name": "icon-timer-v1.5",
|
||||
"prevSize": 24,
|
||||
"code": 921895,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 11,
|
||||
@@ -770,7 +802,7 @@
|
||||
"name": "icon-topic",
|
||||
"id": 6,
|
||||
"code": 921896,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
},
|
||||
{
|
||||
"order": 115,
|
||||
@@ -778,13 +810,13 @@
|
||||
"name": "icon-box-with-dashed-lines",
|
||||
"id": 29,
|
||||
"code": 921897,
|
||||
"tempChar": ""
|
||||
"tempChar": ""
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"name": "openmct-symbols-16px",
|
||||
"importSize": {
|
||||
"width": 448,
|
||||
"width": 512,
|
||||
"height": 512
|
||||
},
|
||||
"designer": "Charles Hacskaylo",
|
||||
@@ -1973,7 +2005,7 @@
|
||||
},
|
||||
{
|
||||
"paths": [
|
||||
"M1012.8 414.2v-391.6l-127.6 127.4c-96.6-96.8-225.2-150-362-150s-265.2 53.2-362 150c-96.8 96.8-150 225.2-150 362s53.2 265.4 150 362c96.8 96.8 225.2 150 362 150s265.4-53.2 362-150l-136.6-136.6c-124.2 124.2-326.4 124.2-450.8 0-124.2-124.2-124.2-326.4 0-450.8 124.2-124.2 326.4-124.2 450.8 0l-127.4 127.4h391.6z"
|
||||
"M960 432v-432l-164.8 164.8c-79.8-65.2-178.8-100.8-283.2-100.8-119.6 0-232.2 46.6-316.8 131.2s-131.2 197.2-131.2 316.8 46.6 232.2 131.2 316.8c84.6 84.6 197.2 131.2 316.8 131.2s232.2-46.6 316.8-131.2c69.4-69.4 113.2-157.4 126.6-252.8h-130c-29.8 145.8-159 256-313.6 256-176.4 0-320-143.6-320-320s143.8-320 320.2-320c72 0 138.4 23.8 192 64l-176 176h432z"
|
||||
],
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
@@ -1981,9 +2013,19 @@
|
||||
],
|
||||
"defaultCode": 114,
|
||||
"id": 27,
|
||||
"attrs": [],
|
||||
"attrs": [
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"colorPermutations": {
|
||||
"1161751207457516161751": []
|
||||
"1161751207457516161751": [
|
||||
{
|
||||
"f": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2110,6 +2152,37 @@
|
||||
"1161751207457516161751": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 105,
|
||||
"paths": [
|
||||
"M795.2 164.8c-79.8-65.2-178.8-100.8-283.2-100.8-119.6 0-232.2 46.6-316.8 131.2-69.4 69.4-113.2 157.4-126.6 252.8h130c29.6-145.8 158.8-256 313.4-256 72 0 138.4 23.8 192 64l-176 176h432v-432l-164.8 164.8z",
|
||||
"M512 832c-72 0-138.4-23.8-192-64l176-176h-432v432l164.8-164.8c79.8 65.2 178.8 100.8 283.2 100.8 119.6 0 232.2-46.6 316.8-131.2 69.4-69.4 113.2-157.4 126.6-252.8h-130c-29.6 145.8-158.8 256-313.4 256z"
|
||||
],
|
||||
"attrs": [
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-resync"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"1161751207457516161751": [
|
||||
{
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"f": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"paths": [
|
||||
"M512 0c-282.8 0-512 229.2-512 512s229.2 512 512 512 512-229.2 512-512-229.2-512-512-512zM832 704l-128 128-192-192-192 192-128-128 192-192-192-192 128-128 192 192 192-192 128 128-192 192 192 192z"
|
||||
@@ -2125,6 +2198,134 @@
|
||||
"1161751207457516161751": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 103,
|
||||
"paths": [
|
||||
"M253.414 318.061l-155.172-116.384c-50.233 66.209-85.127 146.713-97.91 234.39l191.586 30.216c8.145-56.552 29.998-106.879 62.068-149.006z",
|
||||
"M191.98 557.717l-191.919 27.434c13.115 90.459 48.009 170.963 99.174 238.453l154.18-117.665c-31.476-41.347-53.309-91.675-61.231-146.504z",
|
||||
"M466.283 191.98l-27.434-191.919c-90.459 13.115-170.963 48.009-238.453 99.174l117.665 154.18c41.347-31.476 91.675-53.309 146.504-61.231z",
|
||||
"M822.323 98.242c-66.209-50.233-146.713-85.127-234.39-97.91l-30.216 191.586c56.552 8.145 106.879 29.998 149.006 62.068z",
|
||||
"M832.020 466.283l191.919-27.434c-13.115-90.459-48.009-170.963-99.174-238.453l-154.18 117.665c31.476 41.347 53.309 91.675 61.231 146.504z",
|
||||
"M201.677 925.758c66.209 50.233 146.713 85.127 234.39 97.91l30.216-191.586c-56.552-8.145-106.879-29.998-149.006-62.068z",
|
||||
"M770.586 705.939l155.131 116.343c50.233-66.209 85.127-146.713 97.91-234.39l-191.586-30.216c-8.125 56.564-29.966 106.906-62.028 149.049z",
|
||||
"M557.717 832.020l27.434 191.919c90.459-13.115 170.963-48.009 238.453-99.174l-117.665-154.18c-41.347 31.476-91.675 53.309-146.504 61.231z",
|
||||
"M770.586 512c0 142.813-115.773 258.586-258.586 258.586s-258.586-115.773-258.586-258.586c0-142.813 115.773-258.586 258.586-258.586s258.586 115.773 258.586 258.586z"
|
||||
],
|
||||
"attrs": [
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
},
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-brightness"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"1161751207457516161751": [
|
||||
{
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"f": 1
|
||||
},
|
||||
{
|
||||
"f": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 102,
|
||||
"paths": [
|
||||
"M512 0c-282.78 0-512 229.24-512 512s229.22 512 512 512 512-229.24 512-512-229.22-512-512-512zM783.52 783.52c-69.111 69.481-164.785 112.481-270.502 112.481-0.358 0-0.716-0-1.074-0.001l0.055-768c212.070 0.010 383.982 171.929 383.982 384 0 106.034-42.977 202.031-112.462 271.52z"
|
||||
],
|
||||
"attrs": [
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-contrast"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"1161751207457516161751": [
|
||||
{
|
||||
"f": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 104,
|
||||
"paths": [
|
||||
"M460.8 460.8l-187.8-187.8c57.2-42.8 128-68.2 204.8-68.2 188.2 0 341.6 153.2 341.6 341.4s-153.2 341.2-341.4 341.2c-165 0-302.8-117.6-334.6-273h-138.4c14.2 101.8 61 195.6 135 269.6 90.2 90.2 210.4 140 338 140s247.6-49.8 338-140 140-210.4 140-338-49.8-247.6-140-338-210.4-140-338-140c-111.4 0-217 38-302 107.6l-176-175.6v460.8h460.8z"
|
||||
],
|
||||
"attrs": [
|
||||
{
|
||||
"fill": "rgb(0, 161, 75)"
|
||||
}
|
||||
],
|
||||
"isMulticolor": false,
|
||||
"isMulticolor2": false,
|
||||
"grid": 16,
|
||||
"tags": [
|
||||
"icon-reset"
|
||||
],
|
||||
"colorPermutations": {
|
||||
"1161751207457516161751": [
|
||||
{
|
||||
"f": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"paths": [
|
||||
"M576 64h-256l320 320h-290.256c-44.264-76.516-126.99-128-221.744-128h-128v512h128c94.754 0 177.48-51.484 221.744-128h290.256l-320 320h256l448-448-448-448z"
|
||||
|
||||
Binary file not shown.
@@ -65,14 +65,18 @@
|
||||
<glyph unicode="󡀦" glyph-name="icon-plot-resource" d="M255.884 256c0.040 0.034 0.082 0.074 0.116 0.116v127.884c0 70.58 57.42 128 128 128h255.884c0.040 0.034 0.082 0.074 0.116 0.116v127.884c0 70.58 57.42 128 128 128h143.658c-93.832 117.038-237.98 192-399.658 192-282.77 0-512-229.23-512-512 0-67.904 13.25-132.704 37.256-192h218.628zM768.116 640c-0.040-0.034-0.082-0.074-0.116-0.116v-127.884c0-70.58-57.42-128-128-128h-255.884c-0.040-0.034-0.082-0.074-0.116-0.116v-127.884c0-70.58-57.42-128-128-128h-143.658c93.832-117.038 237.98-192 399.658-192 282.77 0 512 229.23 512 512 0 67.904-13.25 132.704-37.256 192h-218.628z" />
|
||||
<glyph unicode="󡀧" glyph-name="icon-pointer-left" horiz-adv-x="512" d="M510-64l-256 512 256 512h-256l-256-512 256-512z" />
|
||||
<glyph unicode="󡀨" glyph-name="icon-pointer-right" horiz-adv-x="512" d="M-2 960l256-512-256-512h256l256 512-256 512z" />
|
||||
<glyph unicode="󡀩" glyph-name="icon-refresh" d="M1012.8 545.8v391.6l-127.6-127.4c-96.6 96.8-225.2 150-362 150s-265.2-53.2-362-150c-96.8-96.8-150-225.2-150-362s53.2-265.4 150-362c96.8-96.8 225.2-150 362-150s265.4 53.2 362 150l-136.6 136.6c-124.2-124.2-326.4-124.2-450.8 0-124.2 124.2-124.2 326.4 0 450.8 124.2 124.2 326.4 124.2 450.8 0l-127.4-127.4h391.6z" />
|
||||
<glyph unicode="󡀩" glyph-name="icon-refresh" d="M960 528v432l-164.8-164.8c-79.8 65.2-178.8 100.8-283.2 100.8-119.6 0-232.2-46.6-316.8-131.2s-131.2-197.2-131.2-316.8 46.6-232.2 131.2-316.8c84.6-84.6 197.2-131.2 316.8-131.2s232.2 46.6 316.8 131.2c69.4 69.4 113.2 157.4 126.6 252.8h-130c-29.8-145.8-159-256-313.6-256-176.4 0-320 143.6-320 320s143.8 320 320.2 320c72 0 138.4-23.8 192-64l-176-176h432z" />
|
||||
<glyph unicode="󡀰" glyph-name="icon-save" d="M192.2 384c-0.2 0-0.2 0 0 0l-0.2-448h640v447.8c0 0 0 0-0.2 0.2h-639.6zM978.8 749.2l-165.4 165.4c-25 25-74.2 45.4-109.4 45.4h-576c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128v448c0 35.2 28.8 64 64 64h640c35.2 0 64-28.8 64-64v-448c70.4 0 128 57.6 128 128v576c0 35.2-20.4 84.4-45.2 109.2zM704 704c0-35.2-28.8-64-64-64h-448c-35.2 0-64 28.8-64 64v192h320v-192h128v192h128v-192z" />
|
||||
<glyph unicode="󡀱" glyph-name="icon-sine" d="M1022.294 448c-1.746 7.196-3.476 14.452-5.186 21.786-20.036 85.992-53.302 208.976-98 306.538-22.42 48.938-45.298 86.556-69.946 115.006-48.454 55.93-98.176 67.67-131.356 67.67s-82.902-11.74-131.356-67.672c-24.648-28.45-47.528-66.068-69.948-115.006-44.696-97.558-77.962-220.544-98-306.538-21.646-92.898-46.444-175.138-71.71-237.836-16.308-40.46-30.222-66.358-40.6-82.604-10.378 16.246-24.292 42.142-40.6 82.604-23.272 57.75-46.144 132.088-66.524 216.052h-197.362c1.746-7.196 3.476-14.452 5.186-21.786 20.036-85.992 53.302-208.976 98-306.538 22.42-48.938 45.298-86.556 69.946-115.006 48.454-55.932 98.176-67.672 131.356-67.672s82.902 11.74 131.356 67.672c24.648 28.45 47.528 66.068 69.948 115.006 44.696 97.558 77.962 220.544 98 306.538 21.646 92.898 46.444 175.138 71.71 237.836 16.308 40.46 30.222 66.358 40.6 82.604 10.378-16.246 24.292-42.142 40.6-82.604 23.274-57.748 46.146-132.086 66.526-216.050h197.36z" />
|
||||
<glyph unicode="󡀲" glyph-name="icon-T" d="M0 960v-256h128v64h256v-704h-192v-128h640v128h-192v704h256v-64h128v256z" />
|
||||
<glyph unicode="󡀳" glyph-name="icon-thumbs-strip" d="M448 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 578c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM448 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320zM1024 2c0-35.2-28.8-64-64-64h-320c-35.2 0-64 28.8-64 64v320c0 35.2 28.8 64 64 64h320c35.2 0 64-28.8 64-64v-320z" />
|
||||
<glyph unicode="󡀴" glyph-name="icon-two-parts-both" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM128 832h320v-768h-320v768zM896 64h-320v768h320v-768z" />
|
||||
<glyph unicode="󡀵" glyph-name="icon-two-parts-one-only" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM896 64h-320v768h320v-768z" />
|
||||
<glyph unicode="󡀶" glyph-name="icon-x-in-circle" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM832 256l-128-128-192 192-192-192-128 128 192 192-192 192 128 128 192-192 192 192 128-128-192-192 192-192z" />
|
||||
<glyph unicode="󡀶" glyph-name="icon-resync" d="M795.2 795.2c-79.8 65.2-178.8 100.8-283.2 100.8-119.6 0-232.2-46.6-316.8-131.2-69.4-69.4-113.2-157.4-126.6-252.8h130c29.6 145.8 158.8 256 313.4 256 72 0 138.4-23.8 192-64l-176-176h432v432l-164.8-164.8zM512 128c-72 0-138.4 23.8-192 64l176 176h-432v-432l164.8 164.8c79.8-65.2 178.8-100.8 283.2-100.8 119.6 0 232.2 46.6 316.8 131.2 69.4 69.4 113.2 157.4 126.6 252.8h-130c-29.6-145.8-158.8-256-313.4-256z" />
|
||||
<glyph unicode="󡀷" glyph-name="icon-reset" d="M460.8 499.2l-187.8 187.8c57.2 42.8 128 68.2 204.8 68.2 188.2 0 341.6-153.2 341.6-341.4s-153.2-341.2-341.4-341.2c-165 0-302.8 117.6-334.6 273h-138.4c14.2-101.8 61-195.6 135-269.6 90.2-90.2 210.4-140 338-140s247.6 49.8 338 140 140 210.4 140 338-49.8 247.6-140 338-210.4 140-338 140c-111.4 0-217-38-302-107.6l-176 175.6v-460.8h460.8z" />
|
||||
<glyph unicode="󡀸" glyph-name="icon-x-in-circle" d="M512 960c-282.8 0-512-229.2-512-512s229.2-512 512-512 512 229.2 512 512-229.2 512-512 512zM832 256l-128-128-192 192-192-192-128 128 192 192-192 192 128 128 192-192 192 192 128-128-192-192 192-192z" />
|
||||
<glyph unicode="󡀹" glyph-name="icon-brightness" d="M253.414 641.939l-155.172 116.384c-50.233-66.209-85.127-146.713-97.91-234.39l191.586-30.216c8.145 56.552 29.998 106.879 62.068 149.006zM191.98 402.283l-191.919-27.434c13.115-90.459 48.009-170.963 99.174-238.453l154.18 117.665c-31.476 41.347-53.309 91.675-61.231 146.504zM466.283 768.020l-27.434 191.919c-90.459-13.115-170.963-48.009-238.453-99.174l117.665-154.18c41.347 31.476 91.675 53.309 146.504 61.231zM822.323 861.758c-66.209 50.233-146.713 85.127-234.39 97.91l-30.216-191.586c56.552-8.145 106.879-29.998 149.006-62.068zM832.020 493.717l191.919 27.434c-13.115 90.459-48.009 170.963-99.174 238.453l-154.18-117.665c31.476-41.347 53.309-91.675 61.231-146.504zM201.677 34.242c66.209-50.233 146.713-85.127 234.39-97.91l30.216 191.586c-56.552 8.145-106.879 29.998-149.006 62.068zM770.586 254.061l155.131-116.343c50.233 66.209 85.127 146.713 97.91 234.39l-191.586 30.216c-8.125-56.564-29.966-106.906-62.028-149.049zM557.717 127.98l27.434-191.919c90.459 13.115 170.963 48.009 238.453 99.174l-117.665 154.18c-41.347-31.476-91.675-53.309-146.504-61.231zM770.586 448c0-142.813-115.773-258.586-258.586-258.586s-258.586 115.773-258.586 258.586c0 142.813 115.773 258.586 258.586 258.586s258.586-115.773 258.586-258.586z" />
|
||||
<glyph unicode="󡁀" glyph-name="icon-contrast" d="M512 960c-282.78 0-512-229.24-512-512s229.22-512 512-512 512 229.24 512 512-229.22 512-512 512zM783.52 176.48c-69.111-69.481-164.785-112.481-270.502-112.481-0.358 0-0.716 0-1.074 0.001l0.055 768c212.070-0.010 383.982-171.929 383.982-384 0-106.034-42.977-202.031-112.462-271.52z" />
|
||||
<glyph unicode="󡄀" glyph-name="icon-activity" d="M576 896h-256l320-320h-290.256c-44.264 76.516-126.99 128-221.744 128h-128v-512h128c94.754 0 177.48 51.484 221.744 128h290.256l-320-320h256l448 448-448 448z" />
|
||||
<glyph unicode="󡄁" glyph-name="icon-activity-mode" d="M512 960c-214.866 0-398.786-132.372-474.744-320h90.744c56.86 0 107.938-24.724 143.094-64h240.906l-192 192h256l320-320-320-320h-256l192 192h-240.906c-35.156-39.276-86.234-64-143.094-64h-90.744c75.958-187.628 259.878-320 474.744-320 282.77 0 512 229.23 512 512s-229.23 512-512 512z" />
|
||||
<glyph unicode="󡄂" glyph-name="icon-autoflow-tabular" d="M192 960c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h64v1024h-64zM384 960h256v-1024h-256v1024zM832 960h-64v-704h256v512c0 105.6-86.4 192-192 192z" />
|
||||
|
||||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 39 KiB |
Binary file not shown.
Binary file not shown.
@@ -78,7 +78,7 @@ $treeContextTriggerW: 20px;
|
||||
/*************** Tabular */
|
||||
$tabularHeaderH: 22px;
|
||||
$tabularTdPadLR: $itemPadLR;
|
||||
$tabularTdPadTB: 3px;
|
||||
$tabularTdPadTB: 2px;
|
||||
/*************** Imagery */
|
||||
$imageMainControlBarH: 25px;
|
||||
$imageThumbsD: 120px;
|
||||
@@ -99,7 +99,7 @@ $plotXBarH: 32px;
|
||||
$plotLegendH: 20px;
|
||||
$plotSwatchD: 8px;
|
||||
// 1: Top, 2: right, 3: bottom, 4: left
|
||||
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH + $interiorMargin, $plotYBarW);
|
||||
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH, $plotYBarW);
|
||||
/* min plot height is based on user testing to find minimum useful height */
|
||||
$plotMinH: 95px;
|
||||
/*************** Bubbles */
|
||||
|
||||
@@ -21,18 +21,18 @@
|
||||
*****************************************************************************/
|
||||
/************************** FONTS */
|
||||
@font-face {
|
||||
/*
|
||||
/*
|
||||
* Use https://icomoon.io/app with icomoon-project-openmct-symbols-16px.json
|
||||
* to generate font files
|
||||
*/
|
||||
font-family: 'symbolsfont';
|
||||
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-16px.eot');
|
||||
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-16px.eot?#iefix') format('embedded-opentype'),
|
||||
url($dirCommonRes + 'fonts/symbols/openmct-symbols-16px.woff') format('woff'),
|
||||
url($dirCommonRes + 'fonts/symbols/openmct-symbols-16px.ttf') format('truetype'),
|
||||
url($dirCommonRes + 'fonts/symbols/openmct-symbols-16px.svg') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-family: 'symbolsfont';
|
||||
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-16px.eot');
|
||||
src: url($dirCommonRes + 'fonts/symbols/openmct-symbols-16px.eot?#iefix') format('embedded-opentype'),
|
||||
url($dirCommonRes + 'fonts/symbols/openmct-symbols-16px.woff') format('woff'),
|
||||
url($dirCommonRes + 'fonts/symbols/openmct-symbols-16px.ttf') format('truetype'),
|
||||
url($dirCommonRes + 'fonts/symbols/openmct-symbols-16px.svg') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -52,53 +52,53 @@
|
||||
|
||||
/************************** HTML ENTITIES */
|
||||
a {
|
||||
color: $colorA;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
color: $colorAHov;
|
||||
}
|
||||
color: $colorA;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
&:hover {
|
||||
color: $colorAHov;
|
||||
}
|
||||
}
|
||||
|
||||
body, html {
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
background-color: $colorBodyBg;
|
||||
color: $colorBodyFg;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 100%;
|
||||
font-weight: 200;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
background-color: $colorBodyBg;
|
||||
color: $colorBodyFg;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 100%;
|
||||
font-weight: 200;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
em {
|
||||
font-style: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="search"] {
|
||||
vertical-align: baseline;
|
||||
padding: $inputTextP;
|
||||
vertical-align: baseline;
|
||||
padding: $inputTextP;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.7em;
|
||||
font-weight: normal !important;
|
||||
line-height: 120%;
|
||||
margin-bottom: 20px;
|
||||
margin-top: 0;
|
||||
font-size: 1.7em;
|
||||
font-weight: normal !important;
|
||||
line-height: 120%;
|
||||
margin-bottom: 20px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: $interiorMarginLg;
|
||||
margin-bottom: $interiorMarginLg;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
@@ -108,30 +108,30 @@ ol, ul {
|
||||
}
|
||||
|
||||
mct-container {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.abs {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: auto;
|
||||
width: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.code {
|
||||
font-family: "Lucida Console", monospace;
|
||||
font-size: 0.7em;
|
||||
line-height: 150%;
|
||||
white-space: pre;
|
||||
font-family: "Lucida Console", monospace;
|
||||
font-size: 0.7em;
|
||||
line-height: 150%;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.codehilite {
|
||||
@extend .code;
|
||||
background-color: rgba($colorBodyFg, 0.1);
|
||||
padding: 1em;
|
||||
@extend .code;
|
||||
background-color: rgba($colorBodyFg, 0.1);
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.disabled,
|
||||
@@ -141,12 +141,41 @@ a.disabled {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
.s-status-missing {
|
||||
// Labels. Expects .s-status-missing to be applied to mct-representation that contains
|
||||
// .t-object-label
|
||||
.t-object-label .t-item-icon:before {
|
||||
content: $glyph-icon-object-unknown;
|
||||
}
|
||||
|
||||
// Item, grid item. Expects .s-status-missing to be applied to mct-representation that contains .item.grid-item
|
||||
.item .t-item-icon-glyph:before {
|
||||
content: $glyph-icon-object-unknown;
|
||||
}
|
||||
|
||||
// Object header. Expects .s-status-missing to be applied to mct-representation.object-header
|
||||
&.object-header {
|
||||
.type-icon:before {
|
||||
content: $glyph-icon-object-unknown;
|
||||
}
|
||||
}
|
||||
|
||||
// Tree item. Expects .s-status-missing to be applied to .tree-item,
|
||||
// and mct-representation.search-item
|
||||
&.tree-item,
|
||||
&.search-item {
|
||||
> .rep-object-label .t-item-icon:before {
|
||||
content: $glyph-icon-object-unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.centered {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
@@ -155,24 +184,42 @@ a.disabled {
|
||||
|
||||
.scrolling,
|
||||
.scroll {
|
||||
overflow: auto;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.vscroll {
|
||||
overflow-y: auto;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.slidable {
|
||||
cursor: move; // Fallback
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
&.horz {
|
||||
cursor: col-resize;
|
||||
}
|
||||
&.vert {
|
||||
cursor: row-resize;
|
||||
}
|
||||
}
|
||||
|
||||
.no-margin {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ds {
|
||||
box-shadow: rgba(#000, 0.7) 0 4px 10px 2px;
|
||||
box-shadow: rgba(#000, 0.7) 0 4px 10px 2px;
|
||||
}
|
||||
|
||||
.hide,
|
||||
.hidden {
|
||||
display: none !important;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.hide-nice {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.off {
|
||||
@@ -186,7 +233,7 @@ a.disabled {
|
||||
}
|
||||
|
||||
.sep {
|
||||
color: rgba(#fff, 0.2);
|
||||
color: rgba(#fff, 0.2);
|
||||
}
|
||||
|
||||
.test-stripes {
|
||||
|
||||
@@ -40,7 +40,6 @@ $glyph-icon-brackets: '\e929';
|
||||
$glyph-icon-arrows-out: '\e1000';
|
||||
$glyph-icon-arrows-right-left: '\e1001';
|
||||
$glyph-icon-arrows-up-down: '\e1002';
|
||||
$glyph-icon-box-with-dashed-lines: '';
|
||||
$glyph-icon-bullet: '\e1004';
|
||||
$glyph-icon-calendar: '\e1005';
|
||||
$glyph-icon-chain-links: '\e1006';
|
||||
@@ -73,7 +72,11 @@ $glyph-icon-T: '\e1032';
|
||||
$glyph-icon-thumbs-strip: '\e1033';
|
||||
$glyph-icon-two-parts-both: '\e1034';
|
||||
$glyph-icon-two-parts-one-only: '\e1035';
|
||||
$glyph-icon-x-in-circle: '\e1036';
|
||||
$glyph-icon-resync: '\e1036';
|
||||
$glyph-icon-x-in-circle: '\e1038';
|
||||
$glyph-icon-brightness: '\e1039';
|
||||
$glyph-icon-contrast: '\e1040';
|
||||
$glyph-icon-reset: '\e1037';
|
||||
$glyph-icon-activity: '\e1100';
|
||||
$glyph-icon-activity-mode: '\e1101';
|
||||
$glyph-icon-autoflow-tabular: '\e1102';
|
||||
@@ -173,6 +176,10 @@ $glyph-icon-box-with-dashed-lines: '\e1129';
|
||||
.icon-two-parts-both { @include glyph($glyph-icon-two-parts-both); }
|
||||
.icon-two-parts-one-only { @include glyph($glyph-icon-two-parts-one-only); }
|
||||
.icon-x-in-circle { @include glyph($glyph-icon-x-in-circle); }
|
||||
.icon-brightness { @include glyph($glyph-icon-brightness); }
|
||||
.icon-contrast { @include glyph($glyph-icon-contrast); }
|
||||
.icon-reset { @include glyph($glyph-icon-reset); }
|
||||
.icon-resync { @include glyph($glyph-icon-resync); }
|
||||
.icon-activity { @include glyph($glyph-icon-activity); }
|
||||
.icon-activity-mode { @include glyph($glyph-icon-activity-mode); }
|
||||
.icon-autoflow-tabular { @include glyph($glyph-icon-autoflow-tabular); }
|
||||
@@ -209,4 +216,4 @@ $glyph-icon-box-with-dashed-lines: '\e1129';
|
||||
.icon-eye-open-12px { @include glyph($glyph-icon-eye-open,'symbolsfont-12px'); }
|
||||
.icon-collapse-pane-left-12px { @include glyph($glyph-icon-collapse-pane-left,'symbolsfont-12px'); }
|
||||
.icon-collapse-pane-right-12px { @include glyph($glyph-icon-collapse-pane-right,'symbolsfont-12px'); }
|
||||
.icon-folder-12px { @include glyph($glyph-icon-folder,'symbolsfont-12px'); }
|
||||
.icon-folder-12px { @include glyph($glyph-icon-folder,'symbolsfont-12px'); }
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
@import "effects";
|
||||
@import "global";
|
||||
@import "glyphs";
|
||||
@import "animations";
|
||||
@import "global";
|
||||
@import "archetypes";
|
||||
@import "about";
|
||||
@import "text";
|
||||
|
||||
@@ -363,14 +363,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
@mixin webkitProp($name, $val) {
|
||||
#{$name}: #{$val};
|
||||
-webkit-#{$name}: #{$val};
|
||||
}
|
||||
|
||||
@mixin webkitVal($name, $val) {
|
||||
#{$name}: #{$val};
|
||||
#{$name}: -webkit-#{$val};
|
||||
@mixin cursorGrab() {
|
||||
cursor: grab;
|
||||
cursor: -webkit-grab;
|
||||
&:active {
|
||||
cursor: grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin verticalCenter {
|
||||
@@ -392,6 +391,14 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@mixin reverseEllipsis() {
|
||||
direction: rtl;
|
||||
unicode-bidi:bidi-override;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@mixin scrollH($showBar: auto) {
|
||||
overflow-x: $showBar;
|
||||
overflow-y: hidden;
|
||||
|
||||
@@ -122,13 +122,21 @@
|
||||
// Default position is upper right
|
||||
$p: $interiorMargin;
|
||||
position: absolute;
|
||||
top: $p;
|
||||
right: $p;
|
||||
top: $p; right: $p; bottom: auto;
|
||||
text-align: right;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.s-local-controls {
|
||||
@include trans-prop-nice(opacity);
|
||||
font-size: 0.7rem;
|
||||
&.s-wrapper-transluc {
|
||||
// Semi-opaque wrapper to visually distinguish a control
|
||||
// from the background
|
||||
background: rgba($colorTransLucBg, 0.7);
|
||||
box-sizing: border-box;
|
||||
border-radius: $controlCr;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************** CUSTOM CHECKBOXES */
|
||||
@@ -459,7 +467,7 @@ input[type="search"] {
|
||||
width: $h;
|
||||
height: $h;
|
||||
margin-top: 1 + floor($h/2) * -1;
|
||||
@include btnSubtle(pullForward($colorBtnBg, 10%));
|
||||
@include btnSubtle(pullForward($colorBtnBg, 20%));
|
||||
border-radius: 50% !important;
|
||||
}
|
||||
|
||||
@@ -653,3 +661,4 @@ body.desktop {
|
||||
background: $scrollbarTrackColorBg;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -273,6 +273,7 @@
|
||||
|
||||
.btn-bar.right .menu,
|
||||
.menus-to-left .menu {
|
||||
z-index: 79;
|
||||
left: auto;
|
||||
right: 0;
|
||||
width: auto;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.l-image-main-wrapper,
|
||||
.l-image-thumbs-wrapper {
|
||||
.l-image-thumbs-wrapper,
|
||||
.image-main {
|
||||
@include absPosDefault(0, false);
|
||||
}
|
||||
|
||||
@@ -32,7 +33,7 @@
|
||||
|
||||
/*************************************** MAIN IMAGE */
|
||||
|
||||
.l-image-main,
|
||||
.image-main,
|
||||
.l-image-thumb-item .l-thumb {
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
@@ -134,6 +135,54 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************** LOCAL CONTROLS */
|
||||
.l-local-controls {
|
||||
max-width: 200px;
|
||||
width: 35%;
|
||||
input[type="range"] {
|
||||
display: block;
|
||||
width: 100%;
|
||||
&:not(:first-child) {
|
||||
margin-top: $interiorMarginLg;
|
||||
}
|
||||
|
||||
&:before {
|
||||
margin-right: $interiorMarginSm;
|
||||
}
|
||||
}
|
||||
|
||||
.t-reset-btn-holder {
|
||||
$bc: $scrollbarTrackColorBg;
|
||||
&:before,
|
||||
&:after {
|
||||
border-right: 1px solid $bc;
|
||||
content:'';
|
||||
display: block;
|
||||
width: 5px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
border-top: 1px solid $bc;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
border-bottom: 1px solid $bc;
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
&.s-wrapper-transluc {
|
||||
left: auto;
|
||||
padding: $interiorMargin $interiorMarginLg;
|
||||
}
|
||||
|
||||
&.l-flex-row {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************** WHEN IN FRAME */
|
||||
.frame .t-imagery {
|
||||
.l-image-main-wrapper {
|
||||
|
||||
@@ -51,13 +51,9 @@ table {
|
||||
|
||||
tbody, .tbody {
|
||||
display: table-row-group;
|
||||
tr, .tr {
|
||||
&:hover {
|
||||
background: rgba($colorTabBodyFg, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
tr, .tr {
|
||||
border-top: 1px solid $colorTabBorder;
|
||||
display: table-row;
|
||||
&:first-child .td {
|
||||
border-top: none;
|
||||
@@ -71,11 +67,12 @@ table {
|
||||
}
|
||||
th, .th, td, .td {
|
||||
display: table-cell;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
th, .th {
|
||||
border-left: 1px solid $colorTabHeaderBorder;
|
||||
color: $colorTabHeaderFg;
|
||||
padding: $tabularTdPadLR $tabularTdPadLR;
|
||||
padding: $tabularTdPadTB $tabularTdPadLR;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle; // This is crucial to hiding f**king 4px height injected by browser by default
|
||||
&:first-child {
|
||||
@@ -99,7 +96,6 @@ table {
|
||||
}
|
||||
}
|
||||
td, .td {
|
||||
border-bottom: 1px solid $colorTabBorder;
|
||||
min-width: 20px;
|
||||
color: $colorTelemFresh;
|
||||
padding: $tabularTdPadTB $tabularTdPadLR;
|
||||
|
||||
@@ -55,9 +55,7 @@
|
||||
height: 70%;
|
||||
width: 50%;
|
||||
min-height: 300px;
|
||||
max-height: 800px;
|
||||
min-width: 600px;
|
||||
max-width: 1000px;
|
||||
z-index: 101;
|
||||
> .contents {
|
||||
$m: $overlayMargin;
|
||||
@@ -141,4 +139,4 @@
|
||||
$h: 225px;
|
||||
min-height: $h;
|
||||
height: $h;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,18 +46,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
.gl-plot-wrapper-display-area-and-x-axis {
|
||||
// Holds the plot area and the X-axis only
|
||||
position: absolute;
|
||||
top: nth($plotDisplayArea, 1);
|
||||
right: nth($plotDisplayArea, 2);
|
||||
bottom: 0;
|
||||
left: nth($plotDisplayArea, 4);
|
||||
|
||||
.gl-plot-display-area {
|
||||
//@include test(yellow);
|
||||
@if $colorPlotBg != none {
|
||||
background-color: $colorPlotBg;
|
||||
}
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: nth($plotDisplayArea, 3);
|
||||
left: 0;
|
||||
cursor: crosshair;
|
||||
border: 1px solid $colorPlotAreaBorder;
|
||||
}
|
||||
|
||||
.gl-plot-axis-area.gl-plot-x {
|
||||
//@include test(green);
|
||||
top: auto;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: $plotXBarH;
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.gl-plot-axis-area {
|
||||
//@include test();
|
||||
position: absolute;
|
||||
&.gl-plot-x {
|
||||
top: auto;
|
||||
right: 0;
|
||||
bottom: $interiorMargin;
|
||||
left: $plotYBarW;
|
||||
height: $plotXBarH;
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
&.gl-plot-y {
|
||||
top: $plotLegendH + $interiorMargin;
|
||||
right: auto;
|
||||
@@ -84,19 +108,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.gl-plot-display-area {
|
||||
@if $colorPlotBg != none {
|
||||
background-color: $colorPlotBg;
|
||||
}
|
||||
position: absolute;
|
||||
top: nth($plotDisplayArea, 1);
|
||||
right: nth($plotDisplayArea, 2);
|
||||
bottom: nth($plotDisplayArea, 3);
|
||||
left: nth($plotDisplayArea, 4);
|
||||
cursor: crosshair;
|
||||
border: 1px solid $colorPlotAreaBorder;
|
||||
}
|
||||
|
||||
.gl-plot-label,
|
||||
.l-plot-label {
|
||||
color: $colorPlotLabelFg;
|
||||
@@ -265,13 +276,9 @@
|
||||
|
||||
.gl-plot-tick,
|
||||
.tick-label {
|
||||
direction: rtl;
|
||||
unicode-bidi:bidi-override;
|
||||
@include reverseEllipsis();
|
||||
font-size: 0.7rem;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
&.gl-plot-x-tick-label,
|
||||
&.tick-label-x {
|
||||
right: auto;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<a class="s-button key-{{parameters.action.getMetadata().key}} {{parameters.action.getMetadata().cssclass}}"
|
||||
<a class="s-button key-{{parameters.action.getMetadata().key}} {{parameters.action.getMetadata().cssClass}}"
|
||||
ng-class="{ labeled: parameters.labeled }"
|
||||
title="{{parameters.action.getMetadata().description}}"
|
||||
ng-click="parameters.action.perform()">
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<span ng-controller="ViewSwitcherController">
|
||||
<div class="view-switcher menu-element s-menu-button {{ngModel.selected.cssclass}}"
|
||||
<div class="view-switcher menu-element s-menu-button {{ngModel.selected.cssClass}}"
|
||||
ng-if="view.length > 1"
|
||||
ng-controller="ClickAwayController as toggle">
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<ul>
|
||||
<li ng-repeat="option in view"
|
||||
ng-click="ngModel.selected = option; toggle.setState(false)"
|
||||
class="{{option.cssclass}}">
|
||||
class="{{option.cssClass}}">
|
||||
{{option.name}}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<li ng-repeat="menuAction in menuActions"
|
||||
ng-click="menuAction.perform()"
|
||||
title="{{menuAction.getMetadata().description}}"
|
||||
class="{{menuAction.getMetadata().cssclass}}">
|
||||
class="{{menuAction.getMetadata().cssClass}}">
|
||||
{{menuAction.getMetadata().name}}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
this source code distribution or the Licensing information page available
|
||||
at runtime from the About dialog for additional information.
|
||||
-->
|
||||
<mct-tree mct-object="domainObject" mct-model="ngModel.selectedObject">
|
||||
<mct-tree root-object="domainObject"
|
||||
selected-object="ngModel.selectedObject"
|
||||
on-selection="ngModel.onSelection"
|
||||
allow-selection="ngModel.allowSelection">
|
||||
</mct-tree>
|
||||
|
||||
|
||||
@@ -26,25 +26,54 @@ define([
|
||||
], function (angular, TreeView) {
|
||||
function MCTTree(gestureService) {
|
||||
function link(scope, element) {
|
||||
var treeView = new TreeView(gestureService),
|
||||
unobserve = treeView.observe(function (domainObject) {
|
||||
if (scope.mctModel !== domainObject) {
|
||||
scope.mctModel = domainObject;
|
||||
scope.$apply();
|
||||
}
|
||||
});
|
||||
if (!scope.allowSelection) {
|
||||
scope.allowSelection = function () {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
if (!scope.onSelection) {
|
||||
scope.onSelection = function () {};
|
||||
}
|
||||
var currentSelection = scope.selectedObject;
|
||||
var treeView = new TreeView(gestureService);
|
||||
|
||||
function setSelection(domainObject, event) {
|
||||
if (currentSelection === domainObject) {
|
||||
return;
|
||||
}
|
||||
if (!scope.allowSelection(domainObject)) {
|
||||
treeView.value(currentSelection);
|
||||
return;
|
||||
}
|
||||
currentSelection = domainObject;
|
||||
scope.onSelection(domainObject);
|
||||
scope.selectedObject = domainObject;
|
||||
if (event && event instanceof MouseEvent) {
|
||||
scope.$apply();
|
||||
}
|
||||
}
|
||||
|
||||
var unobserve = treeView.observe(setSelection);
|
||||
|
||||
element.append(angular.element(treeView.elements()));
|
||||
|
||||
scope.$watch('mctModel', treeView.value.bind(treeView));
|
||||
scope.$watch('mctObject', treeView.model.bind(treeView));
|
||||
scope.$watch('selectedObject', function (object) {
|
||||
currentSelection = object;
|
||||
treeView.value(object);
|
||||
});
|
||||
scope.$watch('rootObject', treeView.model.bind(treeView));
|
||||
scope.$on('$destroy', unobserve);
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: "E",
|
||||
link: link,
|
||||
scope: { mctObject: "=", mctModel: "=" }
|
||||
scope: {
|
||||
rootObject: "=",
|
||||
selectedObject: "=",
|
||||
onSelection: "=?",
|
||||
allowSelection: "=?"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ define([
|
||||
|
||||
this.labelView = new TreeLabelView(gestureService);
|
||||
|
||||
$(this.labelView.elements()).on('click', function () {
|
||||
selectFn(this.activeObject);
|
||||
$(this.labelView.elements()).on('click', function (event) {
|
||||
selectFn(this.activeObject, event);
|
||||
}.bind(this));
|
||||
|
||||
this.li.append($(nodeTemplate));
|
||||
|
||||
@@ -109,11 +109,11 @@ define([
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
TreeView.prototype.value = function (domainObject) {
|
||||
TreeView.prototype.value = function (domainObject, event) {
|
||||
this.selectedObject = domainObject;
|
||||
this.updateNodeViewSelection();
|
||||
this.callbacks.forEach(function (callback) {
|
||||
callback(domainObject);
|
||||
callback(domainObject, event);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/* global console*/
|
||||
|
||||
define([
|
||||
'../../src/directives/MCTTree'
|
||||
], function (MCTTree) {
|
||||
'../../src/directives/MCTTree',
|
||||
'../../src/ui/TreeView'
|
||||
], function (MCTTree, TreeView) {
|
||||
describe("The mct-tree directive", function () {
|
||||
var mockParse,
|
||||
mockGestureService,
|
||||
@@ -50,6 +52,7 @@ define([
|
||||
mockExpr = jasmine.createSpy('expr');
|
||||
mockExpr.assign = jasmine.createSpy('assign');
|
||||
mockParse.andReturn(mockExpr);
|
||||
spyOn(TreeView.prototype, 'observe').andCallThrough();
|
||||
|
||||
mctTree = new MCTTree(mockParse, mockGestureService);
|
||||
});
|
||||
@@ -58,8 +61,13 @@ define([
|
||||
expect(mctTree.restrict).toEqual("E");
|
||||
});
|
||||
|
||||
it("two-way binds to mctObject and mctModel", function () {
|
||||
expect(mctTree.scope).toEqual({ mctObject: "=", mctModel: "=" });
|
||||
it("two-way binds", function () {
|
||||
expect(mctTree.scope).toEqual({
|
||||
rootObject: "=",
|
||||
selectedObject: "=",
|
||||
allowSelection: "=?",
|
||||
onSelection: "=?"
|
||||
});
|
||||
});
|
||||
|
||||
describe("link", function () {
|
||||
@@ -81,16 +89,16 @@ define([
|
||||
expect(mockElement.append).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("watches for mct-model's expression in the parent", function () {
|
||||
it("watches for selected-object expression in the parent", function () {
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||
"mctModel",
|
||||
"selectedObject",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it("watches for changes to mct-object", function () {
|
||||
it("watches for changes to root-object", function () {
|
||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||
"mctObject",
|
||||
"rootObject",
|
||||
jasmine.any(Function)
|
||||
);
|
||||
});
|
||||
@@ -102,6 +110,10 @@ define([
|
||||
);
|
||||
});
|
||||
|
||||
it("watches for changes in tree view", function () {
|
||||
|
||||
});
|
||||
|
||||
// https://github.com/nasa/openmct/issues/1114
|
||||
it("does not trigger $apply during $watches", function () {
|
||||
mockScope.mctObject = makeMockDomainObject('root');
|
||||
@@ -111,14 +123,18 @@ define([
|
||||
});
|
||||
expect(mockScope.$apply).not.toHaveBeenCalled();
|
||||
});
|
||||
it("does trigger $apply from other value changes", function () {
|
||||
it("does trigger $apply from tree manipulation", function () {
|
||||
if (/PhantomJS/g.test(window.navigator.userAgent)) {
|
||||
console.log('Unable to run test in PhantomJS due to lack of support for event constructors');
|
||||
return;
|
||||
}
|
||||
// White-boxy; we know this is the setter for the tree's value
|
||||
var treeValueFn = mockScope.$watch.calls[0].args[1];
|
||||
var treeValueFn = TreeView.prototype.observe.calls[0].args[0];
|
||||
|
||||
mockScope.mctObject = makeMockDomainObject('root');
|
||||
mockScope.mctMode = makeMockDomainObject('selection');
|
||||
|
||||
treeValueFn(makeMockDomainObject('other'));
|
||||
treeValueFn(makeMockDomainObject('other'), new MouseEvent("click"));
|
||||
|
||||
expect(mockScope.$apply).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -289,8 +289,9 @@ define([
|
||||
});
|
||||
|
||||
it("notifies listeners when value is changed", function () {
|
||||
treeView.value(mockDomainObject);
|
||||
expect(mockCallback).toHaveBeenCalledWith(mockDomainObject);
|
||||
treeView.value(mockDomainObject, {some: event});
|
||||
expect(mockCallback)
|
||||
.toHaveBeenCalledWith(mockDomainObject, {some: event});
|
||||
});
|
||||
|
||||
it("does not notify listeners when deactivated", function () {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
|
||||
<span ng-show="notifications.length > 0" class="status block"
|
||||
<a ng-click="showNotificationsList()" ng-show="notifications.length > 0" class="status block"
|
||||
ng-class="highest.severity"
|
||||
ng-controller="NotificationIndicatorController">
|
||||
<span class="status-indicator icon-bell"></span><span class="label">
|
||||
<a ng-click="showNotificationsList()">{{notifications.length}}
|
||||
Notifications</a>
|
||||
{{notifications.length}} Notifications
|
||||
</span><span class="count">{{notifications.length}}</span>
|
||||
</span>
|
||||
|
||||
</a>
|
||||
|
||||
@@ -61,7 +61,7 @@ define(
|
||||
* @property {boolean} [unknownProgress] a boolean indicating that the
|
||||
* progress of the underlying task is unknown. This will result in a
|
||||
* visually distinct progress bar.
|
||||
* @property {boolean | number} [autoDismiss] If truthy, dialog will
|
||||
* @property {boolean} [autoDismiss] If truthy, dialog will
|
||||
* be automatically minimized or dismissed (depending on severity).
|
||||
* Additionally, if the provided value is a number, it will be used
|
||||
* as the delay period before being dismissed.
|
||||
@@ -109,18 +109,18 @@ define(
|
||||
* @memberof platform/commonUI/notification
|
||||
* @constructor
|
||||
* @param $timeout the Angular $timeout service
|
||||
* @param DEFAULT_AUTO_DISMISS The period of time that an
|
||||
* @param defaultAutoDismissTimeout The period of time that an
|
||||
* auto-dismissed message will be displayed for.
|
||||
* @param MINIMIZE_TIMEOUT When notifications are minimized, a brief
|
||||
* @param minimizeAnimationTimeout When notifications are minimized, a brief
|
||||
* animation is shown. This animation requires some time to execute,
|
||||
* so a timeout is required before the notification is hidden
|
||||
*/
|
||||
function NotificationService($timeout, topic, DEFAULT_AUTO_DISMISS, MINIMIZE_TIMEOUT) {
|
||||
function NotificationService($timeout, topic, defaultAutoDismissTimeout, minimizeAnimationTimeout) {
|
||||
this.notifications = [];
|
||||
this.$timeout = $timeout;
|
||||
this.highest = { severity: "info" };
|
||||
this.DEFAULT_AUTO_DISMISS = DEFAULT_AUTO_DISMISS;
|
||||
this.MINIMIZE_TIMEOUT = MINIMIZE_TIMEOUT;
|
||||
this.AUTO_DISMISS_TIMEOUT = defaultAutoDismissTimeout;
|
||||
this.MINIMIZE_ANIMATION_TIMEOUT = minimizeAnimationTimeout;
|
||||
this.topic = topic;
|
||||
|
||||
/*
|
||||
@@ -162,7 +162,7 @@ define(
|
||||
// in order to allow the minimize animation to run through.
|
||||
service.$timeout(function () {
|
||||
service.setActiveNotification(service.selectNextNotification());
|
||||
}, service.MINIMIZE_TIMEOUT);
|
||||
}, service.MINIMIZE_ANIMATION_TIMEOUT);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -208,11 +208,16 @@ define(
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.dismissOrMinimize = function (notification) {
|
||||
|
||||
//For now minimize everything, and have discussion around which
|
||||
//kind of messages should or should not be in the minimized
|
||||
//notifications list
|
||||
notification.minimize();
|
||||
var model = notification.model;
|
||||
if (model.severity === "info") {
|
||||
if (model.autoDismiss === false) {
|
||||
notification.minimize();
|
||||
} else {
|
||||
notification.dismiss();
|
||||
}
|
||||
} else {
|
||||
notification.minimize();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -226,7 +231,9 @@ define(
|
||||
/**
|
||||
* A convenience method for info notifications. Notifications
|
||||
* created via this method will be auto-dismissed after a default
|
||||
* wait period
|
||||
* wait period unless explicitly forbidden by the caller through
|
||||
* the {autoDismiss} property on the {NotificationModel}, in which
|
||||
* case the notification will be minimized after the wait.
|
||||
* @param {NotificationModel | string} message either a string for
|
||||
* the title of the notification message, or a {@link NotificationModel}
|
||||
* defining the options notification to display
|
||||
@@ -235,7 +242,6 @@ define(
|
||||
*/
|
||||
NotificationService.prototype.info = function (message) {
|
||||
var notificationModel = typeof message === "string" ? {title: message} : message;
|
||||
notificationModel.autoDismiss = notificationModel.autoDismiss || true;
|
||||
notificationModel.severity = "info";
|
||||
return this.notify(notificationModel);
|
||||
};
|
||||
@@ -306,28 +312,29 @@ define(
|
||||
activeNotification = self.active.notification,
|
||||
topic = this.topic();
|
||||
|
||||
notificationModel.severity = notificationModel.severity || "info";
|
||||
|
||||
notification = {
|
||||
model: notificationModel,
|
||||
|
||||
minimize: function () {
|
||||
self.minimize(self, notification);
|
||||
},
|
||||
|
||||
dismiss: function () {
|
||||
self.dismiss(self, notification);
|
||||
topic.notify();
|
||||
},
|
||||
|
||||
dismissOrMinimize: function () {
|
||||
self.dismissOrMinimize(notification);
|
||||
},
|
||||
|
||||
onDismiss: function (callback) {
|
||||
topic.listen(callback);
|
||||
}
|
||||
};
|
||||
|
||||
notificationModel.severity = notificationModel.severity || "info";
|
||||
if (notificationModel.autoDismiss === true) {
|
||||
notificationModel.autoDismiss = this.DEFAULT_AUTO_DISMISS;
|
||||
}
|
||||
|
||||
//Notifications support a 'dismissable' attribute. This is a
|
||||
// convenience to support adding a 'dismiss' option to the
|
||||
// notification for the common case of dismissing a
|
||||
@@ -366,38 +373,39 @@ define(
|
||||
*/
|
||||
this.active.timeout = this.$timeout(function () {
|
||||
activeNotification.dismissOrMinimize();
|
||||
}, this.DEFAULT_AUTO_DISMISS);
|
||||
}, this.AUTO_DISMISS_TIMEOUT);
|
||||
}
|
||||
|
||||
return notification;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
* @private
|
||||
*/
|
||||
NotificationService.prototype.setActiveNotification =
|
||||
function (notification) {
|
||||
var timeout;
|
||||
NotificationService.prototype.setActiveNotification = function (notification) {
|
||||
var shouldAutoDismiss;
|
||||
this.active.notification = notification;
|
||||
|
||||
this.active.notification = notification;
|
||||
/*
|
||||
If autoDismiss has been specified, OR there are other
|
||||
notifications queued for display, setup a timeout to
|
||||
dismiss the dialog.
|
||||
*/
|
||||
if (notification && (notification.model.autoDismiss ||
|
||||
this.selectNextNotification())) {
|
||||
if (!notification) {
|
||||
delete this.active.timeout;
|
||||
return;
|
||||
}
|
||||
|
||||
timeout = notification.model.autoDismiss || this.DEFAULT_AUTO_DISMISS;
|
||||
this.active.timeout = this.$timeout(function () {
|
||||
notification.dismissOrMinimize();
|
||||
}, timeout);
|
||||
} else {
|
||||
delete this.active.timeout;
|
||||
}
|
||||
};
|
||||
if (notification.model.severity === "info") {
|
||||
shouldAutoDismiss = true;
|
||||
} else {
|
||||
shouldAutoDismiss = notification.model.autoDismiss;
|
||||
}
|
||||
|
||||
if (shouldAutoDismiss || this.selectNextNotification()) {
|
||||
this.active.timeout = this.$timeout(function () {
|
||||
notification.dismissOrMinimize();
|
||||
}, this.AUTO_DISMISS_TIMEOUT);
|
||||
} else {
|
||||
delete this.active.timeout;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Used internally by the NotificationService
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
/*global describe,it,expect,beforeEach,jasmine*/
|
||||
|
||||
define(
|
||||
['../src/NotificationService'],
|
||||
@@ -29,11 +30,16 @@ define(
|
||||
mockTimeout,
|
||||
mockAutoDismiss,
|
||||
mockMinimizeTimeout,
|
||||
successModel,
|
||||
mockTopicFunction,
|
||||
mockTopicObject,
|
||||
infoModel,
|
||||
alertModel,
|
||||
errorModel;
|
||||
|
||||
function elapseTimeout() {
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
mockTimeout = jasmine.createSpy("$timeout");
|
||||
mockTopicFunction = jasmine.createSpy("topic");
|
||||
@@ -41,153 +47,189 @@ define(
|
||||
mockTopicFunction.andReturn(mockTopicObject);
|
||||
|
||||
mockAutoDismiss = mockMinimizeTimeout = 1000;
|
||||
notificationService = new NotificationService(
|
||||
mockTimeout, mockTopicFunction, mockAutoDismiss, mockMinimizeTimeout);
|
||||
successModel = {
|
||||
title: "Mock Success Notification",
|
||||
notificationService = new NotificationService(mockTimeout, mockTopicFunction, mockAutoDismiss, mockMinimizeTimeout);
|
||||
|
||||
infoModel = {
|
||||
title: "Mock Info Notification",
|
||||
severity: "info"
|
||||
};
|
||||
|
||||
alertModel = {
|
||||
title: "Mock Alert Notification",
|
||||
severity: "alert"
|
||||
};
|
||||
|
||||
errorModel = {
|
||||
title: "Mock Error Notification",
|
||||
severity: "error"
|
||||
};
|
||||
});
|
||||
|
||||
it("gets a new success notification, making" +
|
||||
" the notification active", function () {
|
||||
var activeNotification;
|
||||
notificationService.notify(successModel);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(successModel);
|
||||
});
|
||||
|
||||
it("notifies listeners on dismissal of notification", function () {
|
||||
var notification,
|
||||
dismissListener = jasmine.createSpy("ondismiss");
|
||||
notification = notificationService.notify(successModel);
|
||||
var dismissListener = jasmine.createSpy("ondismiss");
|
||||
var notification = notificationService.notify(infoModel);
|
||||
notification.onDismiss(dismissListener);
|
||||
expect(mockTopicObject.listen).toHaveBeenCalled();
|
||||
notification.dismiss();
|
||||
expect(mockTopicObject.notify).toHaveBeenCalled();
|
||||
mockTopicObject.listen.mostRecentCall.args[0]();
|
||||
expect(dismissListener).toHaveBeenCalled();
|
||||
|
||||
});
|
||||
|
||||
it("allows specification of an info notification given just a" +
|
||||
" title, making the notification active", function () {
|
||||
var activeNotification,
|
||||
notificationTitle = "Test info notification";
|
||||
notificationService.info(notificationTitle);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model.title).toBe(notificationTitle);
|
||||
expect(activeNotification.model.severity).toBe("info");
|
||||
});
|
||||
|
||||
it("gets a new success notification with" +
|
||||
" numerical auto-dismiss specified. ", function () {
|
||||
var activeNotification;
|
||||
successModel.autoDismiss = 1000;
|
||||
notificationService.notify(successModel);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(successModel);
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
expect(mockTimeout.calls.length).toBe(2);
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification).toBeUndefined();
|
||||
});
|
||||
|
||||
it("gets a new notification with" +
|
||||
" boolean auto-dismiss specified. ", function () {
|
||||
var activeNotification;
|
||||
successModel.autoDismiss = true;
|
||||
notificationService.notify(successModel);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(successModel);
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
expect(mockTimeout.calls.length).toBe(2);
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification).toBeUndefined();
|
||||
});
|
||||
|
||||
it("allows minimization of notifications", function () {
|
||||
var notification,
|
||||
activeNotification;
|
||||
|
||||
successModel.autoDismiss = false;
|
||||
notification = notificationService.notify(successModel);
|
||||
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(successModel);
|
||||
notification.minimize();
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toBe(1);
|
||||
});
|
||||
|
||||
it("allows dismissal of notifications", function () {
|
||||
var notification,
|
||||
activeNotification;
|
||||
|
||||
successModel.autoDismiss = false;
|
||||
notification = notificationService.notify(successModel);
|
||||
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(successModel);
|
||||
it("dismisses a notification when the notification's dismiss method is used", function () {
|
||||
var notification = notificationService.info(infoModel);
|
||||
notification.dismiss();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toBe(0);
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
|
||||
describe(" gets called with multiple notifications", function () {
|
||||
it("auto-dismisses the previously active notification, making" +
|
||||
" the new notification active", function () {
|
||||
it("minimizes a notification when the notification's minimize method is used", function () {
|
||||
var notification = notificationService.info(infoModel);
|
||||
notification.minimize();
|
||||
elapseTimeout(); // needed for the minimize animation timeout
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
describe("when receiving info notifications", function () {
|
||||
it("minimizes info notifications if the caller disables auto-dismiss", function () {
|
||||
infoModel.autoDismiss = false;
|
||||
var notification = notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
// 2nd elapse for the minimize animation timeout
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("dismisses info notifications if the caller ignores auto-dismiss", function () {
|
||||
notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("dismisses info notifications if the caller requests auto-dismiss", function () {
|
||||
infoModel.autoDismiss = true;
|
||||
notificationService.info(infoModel);
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when receiving alert notifications", function () {
|
||||
it("minimizes alert notifications if the caller enables auto-dismiss", function () {
|
||||
alertModel.autoDismiss = true;
|
||||
var notification = notificationService.alert(alertModel);
|
||||
elapseTimeout();
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps alert notifications active if the caller disables auto-dismiss", function () {
|
||||
mockTimeout.andCallFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
alertModel.autoDismiss = false;
|
||||
var notification = notificationService.alert(alertModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps alert notifications active if the caller ignores auto-dismiss", function () {
|
||||
mockTimeout.andCallFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
var notification = notificationService.alert(alertModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when receiving error notifications", function () {
|
||||
it("minimizes error notifications if the caller enables auto-dismiss", function () {
|
||||
errorModel.autoDismiss = true;
|
||||
var notification = notificationService.error(errorModel);
|
||||
elapseTimeout();
|
||||
elapseTimeout();
|
||||
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps error notifications active if the caller disables auto-dismiss", function () {
|
||||
mockTimeout.andCallFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
errorModel.autoDismiss = false;
|
||||
var notification = notificationService.error(errorModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
|
||||
it("keeps error notifications active if the caller ignores auto-dismiss", function () {
|
||||
mockTimeout.andCallFake(function (callback, time) {
|
||||
callback();
|
||||
});
|
||||
var notification = notificationService.error(errorModel);
|
||||
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||
expect(notificationService.notifications.length).toEqual(1);
|
||||
expect(notificationService.notifications[0]).toEqual(notification);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when called with multiple notifications", function () {
|
||||
it("auto-dismisses the previously active notification, making the new notification active", function () {
|
||||
var activeNotification;
|
||||
infoModel.autoDismiss = false;
|
||||
//First pre-load with a info message
|
||||
notificationService.notify(successModel);
|
||||
activeNotification =
|
||||
notificationService.getActiveNotification();
|
||||
notificationService.notify(infoModel);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
//Initially expect the active notification to be info
|
||||
expect(activeNotification.model).toBe(successModel);
|
||||
expect(activeNotification.model).toBe(infoModel);
|
||||
//Then notify of an error
|
||||
notificationService.notify(errorModel);
|
||||
//But it should be auto-dismissed and replaced with the
|
||||
// error notification
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
elapseTimeout();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(errorModel);
|
||||
});
|
||||
|
||||
it("auto-minimizes an active error notification", function () {
|
||||
var activeNotification;
|
||||
//First pre-load with an error message
|
||||
notificationService.notify(errorModel);
|
||||
//Then notify of info
|
||||
notificationService.notify(successModel);
|
||||
notificationService.notify(infoModel);
|
||||
expect(notificationService.notifications.length).toEqual(2);
|
||||
//Mock the auto-minimize
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
elapseTimeout();
|
||||
//Previous error message should be minimized, not
|
||||
// dismissed
|
||||
expect(notificationService.notifications.length).toEqual(2);
|
||||
activeNotification =
|
||||
notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(successModel);
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(infoModel);
|
||||
expect(errorModel.minimized).toEqual(true);
|
||||
});
|
||||
it("auto-minimizes errors when a number of them arrive in" +
|
||||
" short succession ", function () {
|
||||
|
||||
it("auto-minimizes errors when a number of them arrive in short succession", function () {
|
||||
var activeNotification,
|
||||
error2 = {
|
||||
title: "Second Mock Error Notification",
|
||||
@@ -205,30 +247,27 @@ define(
|
||||
notificationService.notify(error3);
|
||||
expect(notificationService.notifications.length).toEqual(3);
|
||||
//Mock the auto-minimize
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
elapseTimeout();
|
||||
//Previous error message should be minimized, not
|
||||
// dismissed
|
||||
expect(notificationService.notifications.length).toEqual(3);
|
||||
activeNotification =
|
||||
notificationService.getActiveNotification();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(error2);
|
||||
expect(errorModel.minimized).toEqual(true);
|
||||
|
||||
//Mock the second auto-minimize
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
elapseTimeout();
|
||||
//Two timeouts, one is to force minimization after
|
||||
// displaying the message for a minimum period, the
|
||||
// second is to allow minimization animation to take place.
|
||||
mockTimeout.mostRecentCall.args[0]();
|
||||
activeNotification =
|
||||
notificationService.getActiveNotification();
|
||||
elapseTimeout();
|
||||
activeNotification = notificationService.getActiveNotification();
|
||||
expect(activeNotification.model).toBe(error3);
|
||||
expect(error2.minimized).toEqual(true);
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -50,6 +50,7 @@ $sliderKnobW: 15px;
|
||||
$sliderKnobR: 2px;
|
||||
$timeControllerToiLineColor: #00c2ff;
|
||||
$timeControllerToiLineColorHov: #fff;
|
||||
$colorTransLucBg: #666; // Used as a visual blocking element over variable backgrounds, like imagery
|
||||
|
||||
// General Colors
|
||||
$colorAlt1: #ffc700;
|
||||
@@ -61,7 +62,7 @@ $colorCreateBtn: $colorKey;
|
||||
$colorGridLines: rgba(#fff, 0.05);
|
||||
$colorInvokeMenu: #fff;
|
||||
$colorObjHdrTxt: $colorBodyFg;
|
||||
$colorObjHdrIc: pullForward($colorObjHdrTxt, 20%);
|
||||
$colorObjHdrIc: lighten($colorObjHdrTxt, 20%);
|
||||
$colorTick: rgba(white, 0.2);
|
||||
$colorSelectableSelectedPrimary: $colorKey;
|
||||
$colorSelectableSelectedSecondary: pushBack($colorSelectableSelectedPrimary, 20%);
|
||||
@@ -157,11 +158,11 @@ $shdwItemText: rgba(black, 0.1) 0 1px 2px;
|
||||
$colorItemBgSelected: $colorKey;
|
||||
|
||||
// Tabular
|
||||
$colorTabBorder: pullForward($colorBodyBg, 10%);
|
||||
$colorTabBorder: pullForward($colorBodyBg, 5%);
|
||||
$colorTabBodyBg: darken($colorBodyBg, 10%);
|
||||
$colorTabBodyFg: lighten($colorTabBodyBg, 40%);
|
||||
$colorTabHeaderBg: rgba(white, 0.1); // lighten($colorBodyBg, 10%);
|
||||
$colorTabHeaderFg: $colorBodyFg; //lighten($colorTabHeaderBg, 40%);
|
||||
$colorTabHeaderBg: rgba(white, 0.1);
|
||||
$colorTabHeaderFg: $colorBodyFg;
|
||||
$colorTabHeaderBorder: $colorBodyBg;
|
||||
|
||||
// Plot
|
||||
|
||||
@@ -50,6 +50,7 @@ $sliderKnobW: 15px;
|
||||
$sliderKnobR: 2px;
|
||||
$timeControllerToiLineColor: $colorBodyFg;
|
||||
$timeControllerToiLineColorHov: #0052b5;
|
||||
$colorTransLucBg: #666; // Used as a visual blocking element over variable backgrounds, like imagery
|
||||
|
||||
// General Colors
|
||||
$colorAlt1: #776ba2;
|
||||
@@ -61,7 +62,7 @@ $colorCreateBtn: $colorKey;
|
||||
$colorGridLines: rgba(#000, 0.05);
|
||||
$colorInvokeMenu: #fff;
|
||||
$colorObjHdrTxt: $colorBodyFg;
|
||||
$colorObjHdrIc: pushBack($colorObjHdrTxt, 30%);
|
||||
$colorObjHdrIc: lighten($colorObjHdrTxt, 30%);
|
||||
$colorTick: rgba(black, 0.2);
|
||||
$colorSelectableSelectedPrimary: $colorKey;
|
||||
$colorSelectableSelectedSecondary: pushBack($colorSelectableSelectedPrimary, 20%);
|
||||
|
||||
@@ -40,9 +40,6 @@ define([
|
||||
{
|
||||
"category": "composition",
|
||||
"implementation": CompositionPolicy,
|
||||
"depends": [
|
||||
"$injector"
|
||||
],
|
||||
"message": "Objects of this type cannot contain objects of that type."
|
||||
},
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user