mirror of
https://github.com/nasa/openmct.git
synced 2025-06-26 19:12:02 +00:00
Compare commits
236 Commits
subobject1
...
new-tutori
Author | SHA1 | Date | |
---|---|---|---|
9553e0c64f | |||
da40f4c96e | |||
6fa5a31217 | |||
5bc7a701dc | |||
5cd0516048 | |||
48a603ece8 | |||
abfa56464a | |||
c1d6e21c3c | |||
3bd556a406 | |||
347fb6d882 | |||
b3cf7a5d93 | |||
c7cffdeb3b | |||
d45ae7908d | |||
b10fb4533e | |||
c74fdb816f | |||
40985a56c8 | |||
5c01f0be24 | |||
f7ff5af60b | |||
2088fc52f3 | |||
db33ab143e | |||
8c77d4006a | |||
2fa567b98b | |||
e61f04663a | |||
4b905fa7d2 | |||
5e6e7f018a | |||
53f56b430a | |||
50c934820c | |||
2a10a2cae2 | |||
65325b90fd | |||
cfecc36ae6 | |||
d9f8622459 | |||
8e13819e1e | |||
aaedf5d576 | |||
af9ffaf02d | |||
970acbd56e | |||
46c7399867 | |||
9a6745635d | |||
fa962b42bc | |||
34dc457aff | |||
a3311e4c57 | |||
ef8efbd53d | |||
6cd99efbb9 | |||
ae2b73a4f5 | |||
0c3ff82cfe | |||
50f303bbdc | |||
2a4944d6ee | |||
3544caf4be | |||
976333d7f7 | |||
6d5530ba9c | |||
77d0134e2e | |||
d3b4ad41c2 | |||
b28eb049dc | |||
3d3baddd23 | |||
e712edba4e | |||
35d8024aaa | |||
17564aa489 | |||
3ae0fd7bc9 | |||
df7d59bc9c | |||
4f24c46e9b | |||
d262c4428e | |||
9f9d28deef | |||
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 | |||
64ff463200 | |||
bfdf7b822f | |||
7dde924fcc | |||
d1d2067ad5 | |||
9456370077 | |||
6d2c5f7fd4 | |||
1f6ca8bcc3 | |||
e19edbb27a | |||
f2fe6a9885 | |||
0442a31153 | |||
e03c725b86 | |||
77d4760945 | |||
4ec0d0633d |
@ -14,7 +14,8 @@
|
|||||||
"nonew": true,
|
"nonew": true,
|
||||||
"predef": [
|
"predef": [
|
||||||
"define",
|
"define",
|
||||||
"Promise"
|
"Promise",
|
||||||
|
"WeakMap"
|
||||||
],
|
],
|
||||||
"shadow": "outer",
|
"shadow": "outer",
|
||||||
"strict": "implied",
|
"strict": "implied",
|
||||||
|
82
API.md
82
API.md
@ -71,9 +71,10 @@ Custom types may be registered via
|
|||||||
[`openmct.types`]{@link module:openmct.MCT#types}:
|
[`openmct.types`]{@link module:openmct.MCT#types}:
|
||||||
|
|
||||||
```
|
```
|
||||||
openmct.types.addType('my-type', new openmct.Type({
|
openmct.types.addType('my-type', {
|
||||||
label: "My Type",
|
label: "My Type",
|
||||||
description: "This is a type that I added!"
|
description: "This is a type that I added!",
|
||||||
|
creatable: true
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -118,25 +119,20 @@ To do so, use the [`addRoot`]{@link module:openmct.ObjectAPI#addRoot} method
|
|||||||
of the [object API]{@link module:openmct.ObjectAPI}:
|
of the [object API]{@link module:openmct.ObjectAPI}:
|
||||||
|
|
||||||
```
|
```
|
||||||
openmct.objects.addRoot({
|
openmct.objects.addRoot({ key: "my-key", namespace: "my-namespace" });
|
||||||
identifier: { key: "my-key", namespace: "my-namespace" }
|
|
||||||
name: "My Root-level Object",
|
|
||||||
type: "my-type"
|
|
||||||
});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also remove this root-level object via its identifier:
|
Root objects are loaded just like any other objects, i.e. via an object
|
||||||
|
provider.
|
||||||
```
|
|
||||||
openmct.objects.removeRoot({ key: "my-key", namespace: "my-namespace" });
|
|
||||||
```
|
|
||||||
|
|
||||||
### Adding Composition Providers
|
### Adding Composition Providers
|
||||||
|
|
||||||
The "composition" of a domain object is the list of objects it contains,
|
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
|
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
|
[default solution](#default-composition-provider) for composition, but there
|
||||||
to provide the composition of a certain object (or type of object) dynamically.
|
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
|
For instance, you may want to populate a hierarchy under a custom root-level
|
||||||
object based on the contents of a telemetry dictionary.
|
object based on the contents of a telemetry dictionary.
|
||||||
To do this, you can add a new CompositionProvider:
|
To do this, you can add a new CompositionProvider:
|
||||||
@ -152,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
|
### Adding Telemetry Providers
|
||||||
|
|
||||||
When connecting to a new telemetry source, you will want to register a new
|
When connecting to a new telemetry source, you will want to register a new
|
||||||
@ -271,6 +290,41 @@ openmct.install(myPlugin);
|
|||||||
|
|
||||||
The plugin will be invoked to configure Open MCT before it is started.
|
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
|
### Writing Plugins
|
||||||
|
|
||||||
Plugins configure Open MCT, and should utilize the
|
Plugins configure Open MCT, and should utilize the
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0)
|
# Open MCT [](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
||||||
Open MCT is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
|
Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
|
||||||
|
|
||||||
Please visit our [Official Site](https://nasa.github.io/openmct/) and [Getting Started Guide](https://nasa.github.io/openmct/getting-started/)
|
Please visit our [Official Site](https://nasa.github.io/openmct/) and [Getting Started Guide](https://nasa.github.io/openmct/getting-started/)
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ Keeping that in mind, there are a few useful patterns supported by the
|
|||||||
framework that are useful to keep in mind.
|
framework that are useful to keep in mind.
|
||||||
|
|
||||||
The specific service infrastructure provided by the platform is described
|
The specific service infrastructure provided by the platform is described
|
||||||
in the [Platform Architecture](Platform.md).
|
in the [Platform Architecture](platform.md).
|
||||||
|
|
||||||
## Extension Categories
|
## Extension Categories
|
||||||
|
|
||||||
|
@ -1339,55 +1339,6 @@ are supported:
|
|||||||
Open MCT defines several Angular directives that are intended for use both
|
Open MCT defines several Angular directives that are intended for use both
|
||||||
internally within the platform, and by plugins.
|
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
|
## Container
|
||||||
|
|
||||||
The `mct-container` is similar to the `mct-include` directive insofar as it allows
|
The `mct-container` is similar to the `mct-include` directive insofar as it allows
|
||||||
@ -2310,10 +2261,7 @@ The platform understands the following policy categories (specifiable as the
|
|||||||
|
|
||||||
* `action`: Determines whether or not a given action is allowable. The candidate
|
* `action`: Determines whether or not a given action is allowable. The candidate
|
||||||
argument here is an Action; the context is its action context object.
|
argument here is an Action; the context is its action context object.
|
||||||
* `composition`: Determines whether or not domain objects of a given type are
|
* `composition`: Determines whether or not domain objects of a given type (first argument, `parentType`) can contain a given object (second argument, `child`).
|
||||||
allowed to contain domain objects of another type. The candidate argument here
|
|
||||||
is the container's `Type`; the context argument is the `Type` of the object to be
|
|
||||||
contained.
|
|
||||||
* `view`: Determines whether or not a view is applicable for a domain object.
|
* `view`: Determines whether or not a view is applicable for a domain object.
|
||||||
The candidate argument is the view's extension definition; the context argument
|
The candidate argument is the view's extension definition; the context argument
|
||||||
is the `DomainObject` to be viewed.
|
is the `DomainObject` to be viewed.
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -49,7 +49,7 @@ define([
|
|||||||
{
|
{
|
||||||
"key": "eventGenerator",
|
"key": "eventGenerator",
|
||||||
"name": "Event Message Generator",
|
"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.",
|
"description": "For development use. Creates sample event message data that mimics a live data stream.",
|
||||||
"priority": 10,
|
"priority": 10,
|
||||||
"features": "creation",
|
"features": "creation",
|
||||||
|
@ -36,7 +36,7 @@ define([
|
|||||||
"name": "Export Telemetry as CSV",
|
"name": "Export Telemetry as CSV",
|
||||||
"implementation": ExportTelemetryAsCSVAction,
|
"implementation": ExportTelemetryAsCSVAction,
|
||||||
"category": "contextual",
|
"category": "contextual",
|
||||||
"cssclass": "icon-download",
|
"cssClass": "icon-download",
|
||||||
"depends": [ "exportService" ]
|
"depends": [ "exportService" ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
87
example/generator/GeneratorProvider.js
Normal file
87
example/generator/GeneratorProvider.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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([
|
||||||
|
'./WorkerInterface'
|
||||||
|
], function (
|
||||||
|
WorkerInterface
|
||||||
|
) {
|
||||||
|
|
||||||
|
var REQUEST_DEFAULTS = {
|
||||||
|
amplitude: 1,
|
||||||
|
period: 10,
|
||||||
|
offset: 0,
|
||||||
|
dataRateInHz: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
function GeneratorProvider() {
|
||||||
|
this.workerInterface = new WorkerInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
GeneratorProvider.prototype.canProvideTelemetry = function (domainObject) {
|
||||||
|
return domainObject.type === 'generator';
|
||||||
|
};
|
||||||
|
|
||||||
|
GeneratorProvider.prototype.supportsRequest =
|
||||||
|
GeneratorProvider.prototype.supportsSubscribe =
|
||||||
|
GeneratorProvider.prototype.canProvideTelemetry;
|
||||||
|
|
||||||
|
GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) {
|
||||||
|
var props = [
|
||||||
|
'amplitude',
|
||||||
|
'period',
|
||||||
|
'offset',
|
||||||
|
'dataRateInHz'
|
||||||
|
];
|
||||||
|
|
||||||
|
var workerRequest = {};
|
||||||
|
|
||||||
|
props.forEach(function (prop) {
|
||||||
|
if (domainObject.telemetry && domainObject.telemetry.hasOwnProperty(prop)) {
|
||||||
|
workerRequest[prop] = domainObject.telemetry[prop];
|
||||||
|
}
|
||||||
|
if (request.hasOwnProperty(prop)) {
|
||||||
|
workerRequest[prop] = request[prop];
|
||||||
|
}
|
||||||
|
if (!workerRequest[prop]) {
|
||||||
|
workerRequest[prop] = REQUEST_DEFAULTS[prop];
|
||||||
|
}
|
||||||
|
workerRequest[prop] = Number(workerRequest[prop]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return workerRequest;
|
||||||
|
};
|
||||||
|
|
||||||
|
GeneratorProvider.prototype.request = function (domainObject, request) {
|
||||||
|
var workerRequest = this.makeWorkerRequest(domainObject, request);
|
||||||
|
workerRequest.start = request.start;
|
||||||
|
workerRequest.end = request.end;
|
||||||
|
return this.workerInterface.request(workerRequest);
|
||||||
|
};
|
||||||
|
|
||||||
|
GeneratorProvider.prototype.subscribe = function (domainObject, callback, request) {
|
||||||
|
var workerRequest = this.makeWorkerRequest(domainObject, request);
|
||||||
|
return this.workerInterface.subscribe(workerRequest, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
return GeneratorProvider;
|
||||||
|
});
|
@ -19,7 +19,7 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/*global define,Promise*/
|
/*global define*/
|
||||||
|
|
||||||
define({
|
define({
|
||||||
START_TIME: Date.now() - 24 * 60 * 60 * 1000 // Now minus a day.
|
START_TIME: Date.now() - 24 * 60 * 60 * 1000 // Now minus a day.
|
@ -19,12 +19,11 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/*global define,Promise*/
|
/*global define*/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
['./SinewaveConstants', 'moment'],
|
['./SinewaveConstants', 'moment'],
|
||||||
function (SinewaveConstants, moment) {
|
function (SinewaveConstants, moment) {
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var START_TIME = SinewaveConstants.START_TIME,
|
var START_TIME = SinewaveConstants.START_TIME,
|
||||||
FORMAT_REGEX = /^-?\d+:\d+:\d+$/,
|
FORMAT_REGEX = /^-?\d+:\d+:\d+$/,
|
70
example/generator/SinewaveTelemetryProvider.js
Normal file
70
example/generator/SinewaveTelemetryProvider.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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",
|
||||||
|
"./GeneratorProvider"
|
||||||
|
], function (
|
||||||
|
SinewaveTelemetrySeries,
|
||||||
|
GeneratorProvider
|
||||||
|
) {
|
||||||
|
|
||||||
|
function SinewaveTelemetryProvider() {
|
||||||
|
this.provider = new GeneratorProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
SinewaveTelemetryProvider.prototype.requestTelemetry = function (requests) {
|
||||||
|
if (requests[0].source !== 'generator') {
|
||||||
|
return Promise.resolve({});
|
||||||
|
}
|
||||||
|
return this.provider.request({}, requests[0])
|
||||||
|
.then(function (data) {
|
||||||
|
var res = {
|
||||||
|
generator: {}
|
||||||
|
};
|
||||||
|
res.generator[requests[0].key] = new SinewaveTelemetrySeries(data);
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
SinewaveTelemetryProvider.prototype.subscribe = function (callback, requests) {
|
||||||
|
if (requests[0].source !== 'generator') {
|
||||||
|
return function unsubscribe() {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapper(data) {
|
||||||
|
var res = {
|
||||||
|
generator: {}
|
||||||
|
};
|
||||||
|
res.generator[requests[0].key] = new SinewaveTelemetrySeries(data);
|
||||||
|
callback(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.provider.subscribe({}, wrapper, requests[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return SinewaveTelemetryProvider;
|
||||||
|
});
|
75
example/generator/SinewaveTelemetrySeries.js
Normal file
75
example/generator/SinewaveTelemetrySeries.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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([
|
||||||
|
|
||||||
|
], function (
|
||||||
|
|
||||||
|
) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function SinewaveTelemetrySeries(data) {
|
||||||
|
if (!Array.isArray(data)) {
|
||||||
|
data = [data];
|
||||||
|
}
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
SinewaveTelemetrySeries.prototype.getPointCount = function () {
|
||||||
|
return this.data.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
SinewaveTelemetrySeries.prototype.getDomainValue = function (
|
||||||
|
index,
|
||||||
|
domain
|
||||||
|
) {
|
||||||
|
domain = domain || 'time';
|
||||||
|
|
||||||
|
return this.getDatum(index)[domain];
|
||||||
|
};
|
||||||
|
|
||||||
|
SinewaveTelemetrySeries.prototype.getRangeValue = function (
|
||||||
|
index,
|
||||||
|
range
|
||||||
|
) {
|
||||||
|
range = range || 'sin';
|
||||||
|
return this.getDatum(index)[range];
|
||||||
|
};
|
||||||
|
|
||||||
|
SinewaveTelemetrySeries.prototype.getDatum = function (index) {
|
||||||
|
if (index > this.data.length || index < 0) {
|
||||||
|
throw new Error('IndexOutOfRange: index not available in series.');
|
||||||
|
}
|
||||||
|
return this.data[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
SinewaveTelemetrySeries.prototype.getData = function () {
|
||||||
|
return this.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
return SinewaveTelemetrySeries;
|
||||||
|
});
|
115
example/generator/WorkerInterface.js
Normal file
115
example/generator/WorkerInterface.js
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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([
|
||||||
|
'text!./generatorWorker.js',
|
||||||
|
'uuid'
|
||||||
|
], function (
|
||||||
|
workerText,
|
||||||
|
uuid
|
||||||
|
) {
|
||||||
|
|
||||||
|
var workerBlob = new Blob(
|
||||||
|
[workerText],
|
||||||
|
{type: 'application/javascript'}
|
||||||
|
);
|
||||||
|
var workerUrl = URL.createObjectURL(workerBlob);
|
||||||
|
|
||||||
|
function WorkerInterface() {
|
||||||
|
this.worker = new Worker(workerUrl);
|
||||||
|
this.worker.onmessage = this.onMessage.bind(this);
|
||||||
|
this.callbacks = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkerInterface.prototype.onMessage = function (message) {
|
||||||
|
message = message.data;
|
||||||
|
var callback = this.callbacks[message.id];
|
||||||
|
if (callback) {
|
||||||
|
if (callback(message)) {
|
||||||
|
delete this.callbacks[message.id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WorkerInterface.prototype.dispatch = function (request, data, callback) {
|
||||||
|
var message = {
|
||||||
|
request: request,
|
||||||
|
data: data,
|
||||||
|
id: uuid()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
this.callbacks[message.id] = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.worker.postMessage(message);
|
||||||
|
|
||||||
|
return message.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
WorkerInterface.prototype.request = function (request) {
|
||||||
|
var deferred = {};
|
||||||
|
var promise = new Promise(function (resolve, reject) {
|
||||||
|
deferred.resolve = resolve;
|
||||||
|
deferred.reject = reject;
|
||||||
|
});
|
||||||
|
|
||||||
|
function callback(message) {
|
||||||
|
if (message.error) {
|
||||||
|
deferred.reject(message.error);
|
||||||
|
} else {
|
||||||
|
deferred.resolve(message.data);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dispatch('request', request, callback);
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
WorkerInterface.prototype.subscribe = function (request, cb) {
|
||||||
|
var isCancelled = false;
|
||||||
|
|
||||||
|
var callback = function (message) {
|
||||||
|
if (isCancelled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cb(message.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
var messageId = this.dispatch('subscribe', request, callback)
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
isCancelled = true;
|
||||||
|
this.dispatch('unsubscribe', {
|
||||||
|
id: messageId
|
||||||
|
});
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return WorkerInterface;
|
||||||
|
});
|
@ -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*)?$"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
155
example/generator/generatorWorker.js
Normal file
155
example/generator/generatorWorker.js
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 self*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
var FIFTEEN_MINUTES = 15 * 60 * 1000;
|
||||||
|
|
||||||
|
var handlers = {
|
||||||
|
subscribe: onSubscribe,
|
||||||
|
unsubscribe: onUnsubscribe,
|
||||||
|
request: onRequest
|
||||||
|
};
|
||||||
|
|
||||||
|
var subscriptions = {};
|
||||||
|
|
||||||
|
function workSubscriptions(timestamp) {
|
||||||
|
var now = Date.now();
|
||||||
|
var nextWork = Math.min.apply(Math, Object.values(subscriptions).map(function (subscription) {
|
||||||
|
return subscription(now);
|
||||||
|
}));
|
||||||
|
var wait = nextWork - now;
|
||||||
|
if (wait < 0) {
|
||||||
|
wait = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Number.isFinite(wait)) {
|
||||||
|
setTimeout(workSubscriptions, wait);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubscribe(message) {
|
||||||
|
var data = message.data;
|
||||||
|
|
||||||
|
// Keep
|
||||||
|
var start = Date.now();
|
||||||
|
var step = 1000 / data.dataRateInHz;
|
||||||
|
var nextStep = start - (start % step) + step;
|
||||||
|
|
||||||
|
function work(now) {
|
||||||
|
while (nextStep < now) {
|
||||||
|
self.postMessage({
|
||||||
|
id: message.id,
|
||||||
|
data: {
|
||||||
|
utc: nextStep,
|
||||||
|
yesterday: nextStep - 60*60*24*1000,
|
||||||
|
delta: 60*60*24*1000,
|
||||||
|
sin: sin(nextStep, data.period, data.amplitude, data.offset),
|
||||||
|
cos: cos(nextStep, data.period, data.amplitude, data.offset)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
nextStep += step;
|
||||||
|
}
|
||||||
|
return nextStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriptions[message.id] = work;
|
||||||
|
workSubscriptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUnsubscribe(message) {
|
||||||
|
delete subscriptions[message.data.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRequest(message) {
|
||||||
|
var data = message.data;
|
||||||
|
if (data.end == undefined) {
|
||||||
|
data.end = Date.now();
|
||||||
|
}
|
||||||
|
if (data.start == undefined){
|
||||||
|
data.start = data.end - FIFTEEN_MINUTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
var now = Date.now();
|
||||||
|
var start = data.start;
|
||||||
|
var end = data.end > now ? now : data.end;
|
||||||
|
var amplitude = data.amplitude;
|
||||||
|
var period = data.period;
|
||||||
|
var offset = data.offset;
|
||||||
|
var dataRateInHz = data.dataRateInHz;
|
||||||
|
|
||||||
|
var step = 1000 / dataRateInHz;
|
||||||
|
var nextStep = start - (start % step) + step;
|
||||||
|
|
||||||
|
var data = [];
|
||||||
|
|
||||||
|
for (; nextStep < end; nextStep += step) {
|
||||||
|
data.push({
|
||||||
|
utc: nextStep,
|
||||||
|
yesterday: nextStep - 60*60*24*1000,
|
||||||
|
delta: 60*60*24*1000,
|
||||||
|
sin: sin(nextStep, period, amplitude, offset),
|
||||||
|
cos: cos(nextStep, period, amplitude, offset)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.postMessage({
|
||||||
|
id: message.id,
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function cos(timestamp, period, amplitude, offset) {
|
||||||
|
return amplitude *
|
||||||
|
Math.cos(timestamp / period / 1000 * Math.PI * 2) + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sin(timestamp, period, amplitude, offset) {
|
||||||
|
return amplitude *
|
||||||
|
Math.sin(timestamp / period / 1000 * Math.PI * 2) + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendError(error, message) {
|
||||||
|
self.postMessage({
|
||||||
|
error: error.name + ': ' + error.message,
|
||||||
|
message: message,
|
||||||
|
id: message.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onmessage = function handleMessage(event) {
|
||||||
|
var message = event.data;
|
||||||
|
var handler = handlers[message.request];
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
sendError(new Error('unknown message type'), message);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
handler(message);
|
||||||
|
} catch (e) {
|
||||||
|
sendError(e, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}());
|
171
example/generator/plugin.js
Normal file
171
example/generator/plugin.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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([
|
||||||
|
"./GeneratorProvider",
|
||||||
|
"./SinewaveLimitCapability",
|
||||||
|
"./SinewaveDeltaFormat"
|
||||||
|
], function (
|
||||||
|
GeneratorProvider,
|
||||||
|
SinewaveLimitCapability,
|
||||||
|
SinewaveDeltaFormat
|
||||||
|
) {
|
||||||
|
|
||||||
|
var legacyExtensions = {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"priority": -1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return function(openmct){
|
||||||
|
//Register legacy extensions for things not yet supported by the new API
|
||||||
|
Object.keys(legacyExtensions).forEach(function (type){
|
||||||
|
var extensionsOfType = legacyExtensions[type];
|
||||||
|
extensionsOfType.forEach(function (extension) {
|
||||||
|
openmct.legacyExtension(type, extension)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
openmct.types.addType("generator", {
|
||||||
|
label: "Sine Wave Generator",
|
||||||
|
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
|
||||||
|
cssClass: "icon-telemetry",
|
||||||
|
creatable: true,
|
||||||
|
form: [
|
||||||
|
{
|
||||||
|
name: "Period",
|
||||||
|
control: "textfield",
|
||||||
|
cssClass: "l-input-sm l-numeric",
|
||||||
|
key: "period",
|
||||||
|
required: true,
|
||||||
|
property: [
|
||||||
|
"telemetry",
|
||||||
|
"period"
|
||||||
|
],
|
||||||
|
pattern: "^\\d*(\\.\\d*)?$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Amplitude",
|
||||||
|
control: "textfield",
|
||||||
|
cssClass: "l-input-sm l-numeric",
|
||||||
|
key: "amplitude",
|
||||||
|
required: true,
|
||||||
|
property: [
|
||||||
|
"telemetry",
|
||||||
|
"amplitude"
|
||||||
|
],
|
||||||
|
pattern: "^\\d*(\\.\\d*)?$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Offset",
|
||||||
|
control: "textfield",
|
||||||
|
cssClass: "l-input-sm l-numeric",
|
||||||
|
key: "offset",
|
||||||
|
required: true,
|
||||||
|
property: [
|
||||||
|
"telemetry",
|
||||||
|
"offset"
|
||||||
|
],
|
||||||
|
pattern: "^\\d*(\\.\\d*)?$"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Data Rate (hz)",
|
||||||
|
control: "textfield",
|
||||||
|
cssClass: "l-input-sm l-numeric",
|
||||||
|
key: "dataRateInHz",
|
||||||
|
required: true,
|
||||||
|
property: [
|
||||||
|
"telemetry",
|
||||||
|
"dataRateInHz"
|
||||||
|
],
|
||||||
|
pattern: "^\\d*(\\.\\d*)?$"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
initialize: function (object) {
|
||||||
|
object.telemetry = {
|
||||||
|
period: 10,
|
||||||
|
amplitude: 1,
|
||||||
|
offset: 0,
|
||||||
|
dataRateInHz: 1,
|
||||||
|
domains: [
|
||||||
|
{
|
||||||
|
key: "utc",
|
||||||
|
name: "Time",
|
||||||
|
format: "utc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "yesterday",
|
||||||
|
name: "Yesterday",
|
||||||
|
format: "utc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "delta",
|
||||||
|
name: "Delta",
|
||||||
|
format: "example.delta"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
ranges: [
|
||||||
|
{
|
||||||
|
key: "sin",
|
||||||
|
name: "Sine"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "cos",
|
||||||
|
name: "Cosine"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
openmct.telemetry.addProvider(new GeneratorProvider());
|
||||||
|
};
|
||||||
|
|
||||||
|
});
|
@ -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",
|
"key": "imagery",
|
||||||
"name": "Example Imagery",
|
"name": "Example Imagery",
|
||||||
"cssclass": "icon-image",
|
"cssClass": "icon-image",
|
||||||
"features": "creation",
|
"features": "creation",
|
||||||
"description": "For development use. Creates example imagery data that mimics a live imagery stream.",
|
"description": "For development use. Creates example imagery data that mimics a live imagery stream.",
|
||||||
"priority": 10,
|
"priority": 10,
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* 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
|
* @implements TickSource
|
||||||
* @constructor
|
* @constructor
|
||||||
@ -31,7 +31,7 @@ define(['../../../platform/features/conductor-v2/conductor/src/timeSystems/Local
|
|||||||
this.metadata = {
|
this.metadata = {
|
||||||
key: 'test-lad',
|
key: 'test-lad',
|
||||||
mode: 'lad',
|
mode: 'lad',
|
||||||
cssclass: 'icon-clock',
|
cssClass: 'icon-clock',
|
||||||
label: 'Latest Available Data',
|
label: 'Latest Available Data',
|
||||||
name: '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.'
|
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([
|
define([
|
||||||
'../../../platform/features/conductor-v2/conductor/src/timeSystems/TimeSystem',
|
'../../../platform/features/conductor/core/src/timeSystems/TimeSystem',
|
||||||
'../../../platform/features/conductor-v2/conductor/src/timeSystems/LocalClock',
|
'../../../platform/features/conductor/core/src/timeSystems/LocalClock',
|
||||||
'./LADTickSource'
|
'./LADTickSource'
|
||||||
], function (TimeSystem, LocalClock, LADTickSource) {
|
], function (TimeSystem, LocalClock, LADTickSource) {
|
||||||
var THIRTY_MINUTES = 30 * 60 * 1000,
|
var THIRTY_MINUTES = 30 * 60 * 1000,
|
||||||
|
@ -23,45 +23,43 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
"./src/RemsTelemetryServerAdapter",
|
"./src/RemsTelemetryServerAdapter",
|
||||||
"./src/RemsTelemetryInitializer",
|
|
||||||
"./src/RemsTelemetryModelProvider",
|
"./src/RemsTelemetryModelProvider",
|
||||||
"./src/RemsTelemetryProvider",
|
"./src/RemsTelemetryProvider",
|
||||||
'legacyRegistry',
|
'legacyRegistry',
|
||||||
"module"
|
"module"
|
||||||
], function (
|
], function (
|
||||||
RemsTelemetryServerAdapter,
|
RemsTelemetryServerAdapter,
|
||||||
RemsTelemetryInitializer,
|
|
||||||
RemsTelemetryModelProvider,
|
RemsTelemetryModelProvider,
|
||||||
RemsTelemetryProvider,
|
RemsTelemetryProvider,
|
||||||
legacyRegistry
|
legacyRegistry
|
||||||
) {
|
) {
|
||||||
"use strict";
|
"use strict";
|
||||||
legacyRegistry.register("example/msl-adapter", {
|
legacyRegistry.register("example/msl", {
|
||||||
"name" : "Mars Science Laboratory Data Adapter",
|
"name" : "Mars Science Laboratory Data Adapter",
|
||||||
"extensions" : {
|
"extensions" : {
|
||||||
"types": [
|
"types": [
|
||||||
{
|
{
|
||||||
"name":"Mars Science Laboratory",
|
"name":"Mars Science Laboratory",
|
||||||
"key": "msl.curiosity",
|
"key": "msl.curiosity",
|
||||||
"cssclass": "icon-object"
|
"cssClass": "icon-object"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Instrument",
|
"name": "Instrument",
|
||||||
"key": "msl.instrument",
|
"key": "msl.instrument",
|
||||||
"cssclass": "icon-object",
|
"cssClass": "icon-object",
|
||||||
"model": {"composition": []}
|
"model": {"composition": []}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Measurement",
|
"name": "Measurement",
|
||||||
"key": "msl.measurement",
|
"key": "msl.measurement",
|
||||||
"cssclass": "icon-telemetry",
|
"cssClass": "icon-telemetry",
|
||||||
"model": {"telemetry": {}},
|
"model": {"telemetry": {}},
|
||||||
"telemetry": {
|
"telemetry": {
|
||||||
"source": "rems.source",
|
"source": "rems.source",
|
||||||
"domains": [
|
"domains": [
|
||||||
{
|
{
|
||||||
"name": "Time",
|
"name": "Time",
|
||||||
"key": "timestamp",
|
"key": "utc",
|
||||||
"format": "utc"
|
"format": "utc"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -75,13 +73,18 @@ define([
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"roots": [
|
"roots": [
|
||||||
|
{
|
||||||
|
"id": "msl:curiosity"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"models": [
|
||||||
{
|
{
|
||||||
"id": "msl:curiosity",
|
"id": "msl:curiosity",
|
||||||
"priority" : "preferred",
|
"priority": "preferred",
|
||||||
"model": {
|
"model": {
|
||||||
"type": "msl.curiosity",
|
"type": "msl.curiosity",
|
||||||
"name": "Mars Science Laboratory",
|
"name": "Mars Science Laboratory",
|
||||||
"composition": []
|
"composition": ["msl_tlm:rems"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -92,12 +95,6 @@ define([
|
|||||||
"depends": ["$q", "$http", "$log", "REMS_WS_URL"]
|
"depends": ["$q", "$http", "$log", "REMS_WS_URL"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"runs": [
|
|
||||||
{
|
|
||||||
"implementation": RemsTelemetryInitializer,
|
|
||||||
"depends": ["rems.adapter", "objectService"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"components": [
|
"components": [
|
||||||
{
|
{
|
||||||
"provides": "modelService",
|
"provides": "modelService",
|
||||||
|
@ -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",
|
"key": "plot",
|
||||||
"name": "Example Telemetry Plot",
|
"name": "Example Telemetry Plot",
|
||||||
"cssclass": "icon-telemetry-panel",
|
"cssClass": "icon-telemetry-panel",
|
||||||
"description": "For development use. A plot for displaying telemetry.",
|
"description": "For development use. A plot for displaying telemetry.",
|
||||||
"priority": 10,
|
"priority": 10,
|
||||||
"delegates": [
|
"delegates": [
|
||||||
@ -129,7 +129,7 @@ define([
|
|||||||
{
|
{
|
||||||
"name": "Period",
|
"name": "Period",
|
||||||
"control": "textfield",
|
"control": "textfield",
|
||||||
"cssclass": "l-input-sm l-numeric",
|
"cssClass": "l-input-sm l-numeric",
|
||||||
"key": "period",
|
"key": "period",
|
||||||
"required": true,
|
"required": true,
|
||||||
"property": [
|
"property": [
|
||||||
|
@ -63,7 +63,7 @@ define(
|
|||||||
* Get the CSS class that defines the icon
|
* Get the CSS class that defines the icon
|
||||||
* to display in this indicator. This will appear
|
* to display in this indicator. This will appear
|
||||||
* as a dataflow icon.
|
* as a dataflow icon.
|
||||||
* @returns {string} the cssclass of the dataflow icon
|
* @returns {string} the cssClass of the dataflow icon
|
||||||
*/
|
*/
|
||||||
getCssClass: function () {
|
getCssClass: function () {
|
||||||
return "icon-connectivity";
|
return "icon-connectivity";
|
||||||
|
@ -33,6 +33,11 @@ define([
|
|||||||
legacyRegistry.register("example/scratchpad", {
|
legacyRegistry.register("example/scratchpad", {
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"roots": [
|
"roots": [
|
||||||
|
{
|
||||||
|
"id": "scratch:root"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"models": [
|
||||||
{
|
{
|
||||||
"id": "scratch:root",
|
"id": "scratch:root",
|
||||||
"model": {
|
"model": {
|
||||||
|
@ -35,6 +35,11 @@ define([
|
|||||||
"description": "Example illustrating the addition of a static top-level hierarchy",
|
"description": "Example illustrating the addition of a static top-level hierarchy",
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"roots": [
|
"roots": [
|
||||||
|
{
|
||||||
|
"id": "exampleTaxonomy"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"models": [
|
||||||
{
|
{
|
||||||
"id": "exampleTaxonomy",
|
"id": "exampleTaxonomy",
|
||||||
"model": {
|
"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 () {
|
gulp.task('scripts', function () {
|
||||||
var requirejsOptimize = require('gulp-requirejs-optimize');
|
var requirejsOptimize = require('gulp-requirejs-optimize');
|
||||||
var replace = require('gulp-replace-task');
|
var replace = require('gulp-replace-task');
|
||||||
@ -126,10 +131,17 @@ gulp.task('lint', function () {
|
|||||||
|
|
||||||
gulp.task('checkstyle', function () {
|
gulp.task('checkstyle', function () {
|
||||||
var jscs = require('gulp-jscs');
|
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)
|
return gulp.src(paths.scripts)
|
||||||
.pipe(jscs())
|
.pipe(jscs())
|
||||||
.pipe(jscs.reporter())
|
.pipe(jscs.reporter())
|
||||||
|
.pipe(jscs.reporter('jscs-html-reporter')).on('finish', moveReport)
|
||||||
.pipe(jscs.reporter('fail'));
|
.pipe(jscs.reporter('fail'));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -155,7 +167,7 @@ gulp.task('serve', function () {
|
|||||||
var app = require('./app.js');
|
var app = require('./app.js');
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('develop', ['serve', 'install', 'watch']);
|
gulp.task('develop', ['serve', 'stylesheets', 'watch']);
|
||||||
|
|
||||||
gulp.task('install', [ 'assets', 'scripts' ]);
|
gulp.task('install', [ 'assets', 'scripts' ]);
|
||||||
|
|
||||||
|
19
index.html
19
index.html
@ -25,17 +25,32 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
<title></title>
|
<title></title>
|
||||||
|
<script src="openmct-tutorial/lib/http.js">
|
||||||
|
</script>
|
||||||
<script src="bower_components/requirejs/require.js">
|
<script src="bower_components/requirejs/require.js">
|
||||||
</script>
|
</script>
|
||||||
|
<script src="openmct-tutorial/dictionary-plugin.js">
|
||||||
|
</script>
|
||||||
|
<script src="openmct-tutorial/realtime-telemetry-plugin.js">
|
||||||
|
</script>
|
||||||
|
<script src="openmct-tutorial/historical-telemetry-plugin.js">
|
||||||
|
</script>
|
||||||
<script>
|
<script>
|
||||||
require(['openmct'], function (openmct) {
|
require(['openmct'], function (openmct) {
|
||||||
[
|
[
|
||||||
'example/imagery',
|
'example/imagery',
|
||||||
'example/eventGenerator',
|
'example/eventGenerator'
|
||||||
'example/generator'
|
|
||||||
].forEach(
|
].forEach(
|
||||||
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
|
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(DictionaryPlugin());
|
||||||
|
openmct.install(RealtimeTelemetryPlugin());
|
||||||
|
openmct.install(HistoricalTelemetryPlugin());
|
||||||
openmct.start();
|
openmct.start();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -76,7 +76,7 @@ module.exports = function(config) {
|
|||||||
// Specify browsers to run tests in.
|
// Specify browsers to run tests in.
|
||||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||||
browsers: [
|
browsers: [
|
||||||
'PhantomJS'
|
'Chrome'
|
||||||
],
|
],
|
||||||
|
|
||||||
// Code coverage reporting.
|
// Code coverage reporting.
|
||||||
|
1
openmct-tutorial
Submodule
1
openmct-tutorial
Submodule
Submodule openmct-tutorial added at 7076a15d3a
@ -84,5 +84,11 @@ define([
|
|||||||
return new Main().run(defaultRegistry);
|
return new Main().run(defaultRegistry);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// For now, install conductor by default
|
||||||
|
openmct.install(openmct.plugins.Conductor({
|
||||||
|
showConductor: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
return openmct;
|
return openmct;
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "openmct",
|
"name": "openmct",
|
||||||
"version": "0.12.0-SNAPSHOT",
|
"version": "0.12.1-SNAPSHOT",
|
||||||
"description": "The Open MCT core platform",
|
"description": "The Open MCT core platform",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.13.1",
|
"express": "^4.13.1",
|
||||||
@ -21,16 +21,16 @@
|
|||||||
"gulp-sass": "^2.2.0",
|
"gulp-sass": "^2.2.0",
|
||||||
"gulp-sourcemaps": "^1.6.0",
|
"gulp-sourcemaps": "^1.6.0",
|
||||||
"jasmine-core": "^2.3.0",
|
"jasmine-core": "^2.3.0",
|
||||||
|
"jscs-html-reporter": "^0.1.0",
|
||||||
"jsdoc": "^3.3.2",
|
"jsdoc": "^3.3.2",
|
||||||
"jshint": "^2.7.0",
|
"jshint": "^2.7.0",
|
||||||
"karma": "^0.13.3",
|
"karma": "^0.13.3",
|
||||||
"karma-chrome-launcher": "^0.1.8",
|
"karma-chrome-launcher": "^0.1.12",
|
||||||
"karma-cli": "0.0.4",
|
"karma-cli": "0.0.4",
|
||||||
"karma-coverage": "^0.5.3",
|
"karma-coverage": "^0.5.3",
|
||||||
"karma-html-reporter": "^0.2.7",
|
"karma-html-reporter": "^0.2.7",
|
||||||
"karma-jasmine": "^0.1.5",
|
"karma-jasmine": "^0.1.5",
|
||||||
"karma-junit-reporter": "^0.3.8",
|
"karma-junit-reporter": "^0.3.8",
|
||||||
"karma-phantomjs-launcher": "^1.0.0",
|
|
||||||
"karma-requirejs": "^0.2.2",
|
"karma-requirejs": "^0.2.2",
|
||||||
"lodash": "^3.10.1",
|
"lodash": "^3.10.1",
|
||||||
"markdown-toc": "^0.11.7",
|
"markdown-toc": "^0.11.7",
|
||||||
@ -39,7 +39,6 @@
|
|||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"moment": "^2.11.1",
|
"moment": "^2.11.1",
|
||||||
"node-bourbon": "^4.2.3",
|
"node-bourbon": "^4.2.3",
|
||||||
"phantomjs-prebuilt": "2.1.11 || >2.1.12 <3.0.0",
|
|
||||||
"requirejs": "2.1.x",
|
"requirejs": "2.1.x",
|
||||||
"split": "^1.0.0"
|
"split": "^1.0.0"
|
||||||
},
|
},
|
||||||
|
@ -41,7 +41,6 @@ define([
|
|||||||
"text!./res/templates/items/items.html",
|
"text!./res/templates/items/items.html",
|
||||||
"text!./res/templates/browse/object-properties.html",
|
"text!./res/templates/browse/object-properties.html",
|
||||||
"text!./res/templates/browse/inspector-region.html",
|
"text!./res/templates/browse/inspector-region.html",
|
||||||
"text!./res/templates/view-object.html",
|
|
||||||
'legacyRegistry'
|
'legacyRegistry'
|
||||||
], function (
|
], function (
|
||||||
BrowseController,
|
BrowseController,
|
||||||
@ -64,7 +63,6 @@ define([
|
|||||||
itemsTemplate,
|
itemsTemplate,
|
||||||
objectPropertiesTemplate,
|
objectPropertiesTemplate,
|
||||||
inspectorRegionTemplate,
|
inspectorRegionTemplate,
|
||||||
viewObjectTemplate,
|
|
||||||
legacyRegistry
|
legacyRegistry
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -72,14 +70,13 @@ define([
|
|||||||
"extensions": {
|
"extensions": {
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
"when": "/browse/:ids*",
|
"when": "/browse/:ids*?",
|
||||||
"template": browseTemplate,
|
"template": browseTemplate,
|
||||||
"reloadOnSearch": false
|
"reloadOnSearch": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"when": "",
|
"when": "",
|
||||||
"template": browseTemplate,
|
"redirectTo": "/browse/"
|
||||||
"reloadOnSearch": false
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"constants": [
|
"constants": [
|
||||||
@ -142,10 +139,6 @@ define([
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"representations": [
|
"representations": [
|
||||||
{
|
|
||||||
"key": "view-object",
|
|
||||||
"template": viewObjectTemplate
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"key": "browse-object",
|
"key": "browse-object",
|
||||||
"template": browseObjectTemplate,
|
"template": browseObjectTemplate,
|
||||||
@ -205,7 +198,10 @@ define([
|
|||||||
"services": [
|
"services": [
|
||||||
{
|
{
|
||||||
"key": "navigationService",
|
"key": "navigationService",
|
||||||
"implementation": NavigationService
|
"implementation": NavigationService,
|
||||||
|
"depends": [
|
||||||
|
"$window"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"actions": [
|
"actions": [
|
||||||
@ -213,10 +209,7 @@ define([
|
|||||||
"key": "navigate",
|
"key": "navigate",
|
||||||
"implementation": NavigateAction,
|
"implementation": NavigateAction,
|
||||||
"depends": [
|
"depends": [
|
||||||
"navigationService",
|
"navigationService"
|
||||||
"$q",
|
|
||||||
"policyService",
|
|
||||||
"$window"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -233,7 +226,7 @@ define([
|
|||||||
"$window"
|
"$window"
|
||||||
],
|
],
|
||||||
"group": "windowing",
|
"group": "windowing",
|
||||||
"cssclass": "icon-new-window",
|
"cssClass": "icon-new-window",
|
||||||
"priority": "preferred"
|
"priority": "preferred"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -248,7 +241,7 @@ define([
|
|||||||
{
|
{
|
||||||
"key": "items",
|
"key": "items",
|
||||||
"name": "Items",
|
"name": "Items",
|
||||||
"cssclass": "icon-thumbs-strip",
|
"cssClass": "icon-thumbs-strip",
|
||||||
"description": "Grid of available items",
|
"description": "Grid of available items",
|
||||||
"template": itemsTemplate,
|
"template": itemsTemplate,
|
||||||
"uses": [
|
"uses": [
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
<mct-split-pane class='l-object-and-inspector contents abs' anchor='right'>
|
<mct-split-pane class='l-object-and-inspector contents abs' anchor='right'>
|
||||||
<div class='split-pane-component t-object pane primary-pane left'>
|
<div class='split-pane-component t-object pane primary-pane left'>
|
||||||
<mct-representation mct-object="navigatedObject"
|
<mct-representation mct-object="navigatedObject"
|
||||||
key="'view-object'"
|
key="navigatedObject.getCapability('status').get('editing') ? 'edit-object' : 'browse-object'"
|
||||||
class="abs holder holder-object">
|
class="abs holder holder-object">
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
<a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect flush-right"
|
<a class="mini-tab-icon anchor-right mobile-hide toggle-pane toggle-inspect flush-right"
|
||||||
|
@ -28,8 +28,6 @@ define(
|
|||||||
[],
|
[],
|
||||||
function () {
|
function () {
|
||||||
|
|
||||||
var ROOT_ID = "ROOT";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The BrowseController is used to populate the initial scope in Browse
|
* The BrowseController is used to populate the initial scope in Browse
|
||||||
* mode. It loads the root object from the objectService and makes it
|
* mode. It loads the root object from the objectService and makes it
|
||||||
@ -49,74 +47,26 @@ define(
|
|||||||
urlService,
|
urlService,
|
||||||
defaultPath
|
defaultPath
|
||||||
) {
|
) {
|
||||||
var path = [ROOT_ID].concat(
|
var initialPath = ($route.current.params.ids || defaultPath).split("/");
|
||||||
($route.current.params.ids || defaultPath).split("/")
|
var currentIds;
|
||||||
);
|
|
||||||
|
|
||||||
function updateRoute(domainObject) {
|
$scope.treeModel = {
|
||||||
var priorRoute = $route.current,
|
selectedObject: undefined,
|
||||||
// Act as if params HADN'T changed to avoid page reload
|
onSelection: function (object) {
|
||||||
unlisten;
|
navigationService.setNavigation(object, true);
|
||||||
|
},
|
||||||
unlisten = $scope.$on('$locationChangeSuccess', function () {
|
allowSelection: function (object) {
|
||||||
// Checks path to make sure /browse/ is at front
|
return navigationService.shouldNavigate();
|
||||||
// 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));
|
|
||||||
|
|
||||||
|
function idsForObject(domainObject) {
|
||||||
|
return urlService
|
||||||
|
.urlForLocation("", domainObject)
|
||||||
|
.replace('/', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
function setScopeObjects(domainObject, navigationAllowed) {
|
// Find an object in an array of objects.
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function findObject(domainObjects, id) {
|
function findObject(domainObjects, id) {
|
||||||
var i;
|
var i;
|
||||||
for (i = 0; i < domainObjects.length; i += 1) {
|
for (i = 0; i < domainObjects.length; i += 1) {
|
||||||
@ -126,63 +76,87 @@ define(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigate to the domain object identified by path[index],
|
// helper, fetch a single object from the object service.
|
||||||
// which we expect to find in the composition of the passed
|
function getObject(id) {
|
||||||
// domain object.
|
return objectService.getObjects([id])
|
||||||
function doNavigate(domainObject, index) {
|
.then(function (results) {
|
||||||
var composition = domainObject.useCapability("composition");
|
return results[id];
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// Similar to above case; this object has no composition,
|
|
||||||
// so navigate to it instead of subsequent path elements.
|
|
||||||
navigateTo(domainObject);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the root object, put it in the scope.
|
// recursively locate and return an object inside of a container
|
||||||
// Also, load its immediate children, and (possibly)
|
// via a path. If at any point in the recursion it fails to find
|
||||||
// navigate to one of them, so that navigation state has
|
// the next object, it will return the parent.
|
||||||
// a useful initial value.
|
function findViaComposition(containerObject, path) {
|
||||||
objectService.getObjects([path[0]]).then(function (objects) {
|
var nextId = path.shift();
|
||||||
$scope.domainObject = objects[path[0]];
|
if (!nextId) {
|
||||||
doNavigate($scope.domainObject, 1);
|
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Provide a model for the tree to modify
|
// Handle navigation events from view service. Only navigates
|
||||||
$scope.treeModel = {
|
// if path has changed.
|
||||||
selectedObject: navigationService.getNavigation()
|
function navigateDirectlyToModel(domainObject) {
|
||||||
};
|
var newIds = idsForObject(domainObject);
|
||||||
|
if (currentIds !== newIds) {
|
||||||
|
currentIds = newIds;
|
||||||
|
navigateToObject(domainObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Listen for changes in navigation state.
|
// Listen for changes in navigation state.
|
||||||
navigationService.addListener(setNavigation);
|
navigationService.addListener(navigateDirectlyToModel);
|
||||||
|
|
||||||
// Also listen for changes which come from the tree. Changes in
|
// Listen for route changes which are caused by browser events
|
||||||
// the tree will trigger a change in browse navigation state.
|
// (e.g. bookmarks to pages in OpenMCT) and prevent them. Instead,
|
||||||
$scope.$watch("treeModel.selectedObject", setNavigation);
|
// 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
|
// Clean up when the scope is destroyed
|
||||||
$scope.$on("$destroy", function () {
|
$scope.$on("$destroy", function () {
|
||||||
navigationService.removeListener(setNavigation);
|
navigationService.removeListener(navigateDirectlyToModel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,24 +51,16 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateQueryParam(viewKey) {
|
function updateQueryParam(viewKey) {
|
||||||
var unlisten,
|
if (viewKey && $location.search().view !== viewKey) {
|
||||||
priorRoute = $route.current;
|
|
||||||
|
|
||||||
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('domainObject', setViewForDomainObject);
|
||||||
$scope.$watch('representation.selected.key', updateQueryParam);
|
$scope.$watch('representation.selected.key', updateQueryParam);
|
||||||
|
$scope.$on('$locationChangeSuccess', function () {
|
||||||
|
setViewForDomainObject($scope.domainObject);
|
||||||
|
});
|
||||||
|
|
||||||
$scope.doAction = function (action) {
|
$scope.doAction = function (action) {
|
||||||
return $scope[action] && $scope[action]();
|
return $scope[action] && $scope[action]();
|
||||||
|
@ -64,11 +64,11 @@ define(
|
|||||||
attachStatusListener(domainObject);
|
attachStatusListener(domainObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
var navigationListener = navigationService.addListener(attachStatusListener);
|
navigationService.addListener(attachStatusListener);
|
||||||
|
|
||||||
$scope.$on("$destroy", function () {
|
$scope.$on("$destroy", function () {
|
||||||
statusListener();
|
statusListener();
|
||||||
navigationListener();
|
navigationService.removeListener(attachStatusListener);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,12 +33,9 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @implements {Action}
|
* @implements {Action}
|
||||||
*/
|
*/
|
||||||
function NavigateAction(navigationService, $q, policyService, $window, context) {
|
function NavigateAction(navigationService, context) {
|
||||||
this.domainObject = context.domainObject;
|
this.domainObject = context.domainObject;
|
||||||
this.$q = $q;
|
|
||||||
this.navigationService = navigationService;
|
this.navigationService = navigationService;
|
||||||
this.policyService = policyService;
|
|
||||||
this.$window = $window;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,36 +44,12 @@ define(
|
|||||||
* navigation has been updated
|
* navigation has been updated
|
||||||
*/
|
*/
|
||||||
NavigateAction.prototype.perform = function () {
|
NavigateAction.prototype.perform = function () {
|
||||||
var self = this,
|
if (this.navigationService.shouldNavigate()) {
|
||||||
navigateTo = this.domainObject,
|
this.navigationService.setNavigation(this.domainObject, true);
|
||||||
currentObject = self.navigationService.getNavigation();
|
return Promise.resolve({});
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Promise.reject('Navigation Prevented by User');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,16 +30,23 @@ define(
|
|||||||
/**
|
/**
|
||||||
* The navigation service maintains the application's current
|
* The navigation service maintains the application's current
|
||||||
* navigation state, and allows listening for changes thereto.
|
* navigation state, and allows listening for changes thereto.
|
||||||
|
*
|
||||||
* @memberof platform/commonUI/browse
|
* @memberof platform/commonUI/browse
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function NavigationService() {
|
function NavigationService($window) {
|
||||||
this.navigated = undefined;
|
this.navigated = undefined;
|
||||||
this.callbacks = [];
|
this.callbacks = [];
|
||||||
|
this.checks = [];
|
||||||
|
this.$window = $window;
|
||||||
|
|
||||||
|
this.oldUnload = $window.onbeforeunload;
|
||||||
|
$window.onbeforeunload = this.onBeforeUnload.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current navigation state.
|
* Get the current navigation state.
|
||||||
|
*
|
||||||
* @returns {DomainObject} the object that is navigated-to
|
* @returns {DomainObject} the object that is navigated-to
|
||||||
*/
|
*/
|
||||||
NavigationService.prototype.getNavigation = function () {
|
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 {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) {
|
NavigationService.prototype.setNavigation = function (domainObject, force) {
|
||||||
if (this.navigated !== value) {
|
if (force) {
|
||||||
this.navigated = value;
|
this.doNavigation(domainObject);
|
||||||
this.callbacks.forEach(function (callback) {
|
return true;
|
||||||
callback(value);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
if (this.navigated === domainObject) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var doNotNavigate = this.shouldWarnBeforeNavigate();
|
||||||
|
if (doNotNavigate && !this.$window.confirm(doNotNavigate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.doNavigation(domainObject);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,6 +88,7 @@ define(
|
|||||||
* Listen for changes in navigation. The passed callback will
|
* Listen for changes in navigation. The passed callback will
|
||||||
* be invoked with the new domain object of navigation when
|
* be invoked with the new domain object of navigation when
|
||||||
* this changes.
|
* this changes.
|
||||||
|
*
|
||||||
* @param {function} callback the callback to invoke when
|
* @param {function} callback the callback to invoke when
|
||||||
* navigation state changes
|
* navigation state changes
|
||||||
*/
|
*/
|
||||||
@ -73,6 +98,7 @@ define(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop listening for changes in navigation state.
|
* Stop listening for changes in navigation state.
|
||||||
|
*
|
||||||
* @param {function} callback the callback which should
|
* @param {function} callback the callback which should
|
||||||
* no longer be invoked when navigation state
|
* no longer be invoked when navigation state
|
||||||
* changes
|
* 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;
|
return NavigationService;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -43,24 +43,24 @@ define([], function () {
|
|||||||
return context.getParent();
|
return context.getParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
function isOrphan(domainObject) {
|
function preventOrphanNavigation(domainObject) {
|
||||||
var parent = getParent(domainObject),
|
|
||||||
composition = parent.getModel().composition,
|
|
||||||
id = domainObject.getId();
|
|
||||||
return !composition || (composition.indexOf(id) === -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function navigateToParent(domainObject) {
|
|
||||||
var parent = getParent(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() {
|
function checkNavigation() {
|
||||||
var navigatedObject = navigationService.getNavigation();
|
var navigatedObject = navigationService.getNavigation();
|
||||||
if (navigatedObject.hasCapability('context') &&
|
if (navigatedObject.hasCapability('context')) {
|
||||||
isOrphan(navigatedObject)) {
|
|
||||||
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
|
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
|
||||||
navigateToParent(navigatedObject);
|
preventOrphanNavigation(navigatedObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,12 +46,12 @@ define(
|
|||||||
};
|
};
|
||||||
|
|
||||||
FullscreenAction.prototype.getMetadata = function () {
|
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
|
// description need to be determined at run-time
|
||||||
// based on whether or not we are currently
|
// based on whether or not we are currently
|
||||||
// full screen.
|
// full screen.
|
||||||
var metadata = Object.create(FullscreenAction);
|
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 ?
|
metadata.description = screenfull.isFullscreen ?
|
||||||
EXIT_FULLSCREEN : ENTER_FULLSCREEN;
|
EXIT_FULLSCREEN : ENTER_FULLSCREEN;
|
||||||
metadata.group = "windowing";
|
metadata.group = "windowing";
|
||||||
|
@ -24,8 +24,14 @@
|
|||||||
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
|
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
|
||||||
*/
|
*/
|
||||||
define(
|
define(
|
||||||
["../src/BrowseController"],
|
[
|
||||||
function (BrowseController) {
|
"../src/BrowseController",
|
||||||
|
"../src/navigation/NavigationService"
|
||||||
|
],
|
||||||
|
function (
|
||||||
|
BrowseController,
|
||||||
|
NavigationService
|
||||||
|
) {
|
||||||
|
|
||||||
describe("The browse controller", function () {
|
describe("The browse controller", function () {
|
||||||
var mockScope,
|
var mockScope,
|
||||||
@ -35,18 +41,17 @@ define(
|
|||||||
mockNavigationService,
|
mockNavigationService,
|
||||||
mockRootObject,
|
mockRootObject,
|
||||||
mockUrlService,
|
mockUrlService,
|
||||||
mockDomainObject,
|
mockDefaultRootObject,
|
||||||
|
mockOtherDomainObject,
|
||||||
mockNextObject,
|
mockNextObject,
|
||||||
testDefaultRoot,
|
testDefaultRoot,
|
||||||
mockActionCapability,
|
|
||||||
controller;
|
controller;
|
||||||
|
|
||||||
function mockPromise(value) {
|
function waitsForNavigation() {
|
||||||
return {
|
var calls = mockNavigationService.setNavigation.calls.length;
|
||||||
then: function (callback) {
|
waitsFor(function () {
|
||||||
return mockPromise(callback(value));
|
return mockNavigationService.setNavigation.calls.length > calls;
|
||||||
}
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function instantiateController() {
|
function instantiateController() {
|
||||||
@ -68,85 +73,114 @@ define(
|
|||||||
"$scope",
|
"$scope",
|
||||||
["$on", "$watch"]
|
["$on", "$watch"]
|
||||||
);
|
);
|
||||||
mockRoute = { current: { params: {} } };
|
mockRoute = { current: { params: {}, pathParams: {} } };
|
||||||
mockLocation = jasmine.createSpyObj(
|
|
||||||
"$location",
|
|
||||||
["path"]
|
|
||||||
);
|
|
||||||
mockUrlService = jasmine.createSpyObj(
|
mockUrlService = jasmine.createSpyObj(
|
||||||
"urlService",
|
"urlService",
|
||||||
["urlForLocation"]
|
["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(
|
mockObjectService = jasmine.createSpyObj(
|
||||||
"objectService",
|
"objectService",
|
||||||
["getObjects"]
|
["getObjects"]
|
||||||
);
|
);
|
||||||
mockNavigationService = jasmine.createSpyObj(
|
mockNavigationService = new NavigationService({});
|
||||||
"navigationService",
|
|
||||||
[
|
[
|
||||||
"getNavigation",
|
"getNavigation",
|
||||||
"setNavigation",
|
"setNavigation",
|
||||||
"addListener",
|
"addListener",
|
||||||
"removeListener"
|
"removeListener"
|
||||||
]
|
].forEach(function (method) {
|
||||||
);
|
spyOn(mockNavigationService, method)
|
||||||
|
.andCallThrough();
|
||||||
|
});
|
||||||
mockRootObject = jasmine.createSpyObj(
|
mockRootObject = jasmine.createSpyObj(
|
||||||
"domainObject",
|
"rootObjectContainer",
|
||||||
["getId", "getCapability", "getModel", "useCapability"]
|
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
|
||||||
);
|
);
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDefaultRootObject = jasmine.createSpyObj(
|
||||||
"domainObject",
|
"defaultRootObject",
|
||||||
["getId", "getCapability", "getModel", "useCapability"]
|
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
|
||||||
|
);
|
||||||
|
mockOtherDomainObject = jasmine.createSpyObj(
|
||||||
|
"otherDomainObject",
|
||||||
|
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
|
||||||
);
|
);
|
||||||
mockNextObject = jasmine.createSpyObj(
|
mockNextObject = jasmine.createSpyObj(
|
||||||
"nextObject",
|
"nestedDomainObject",
|
||||||
["getId", "getCapability", "getModel", "useCapability"]
|
["getId", "getCapability", "getModel", "useCapability", "hasCapability"]
|
||||||
);
|
);
|
||||||
|
mockObjectService.getObjects.andReturn(Promise.resolve({
|
||||||
mockObjectService.getObjects.andReturn(mockPromise({
|
|
||||||
ROOT: mockRootObject
|
ROOT: mockRootObject
|
||||||
}));
|
}));
|
||||||
mockRootObject.useCapability.andReturn(mockPromise([
|
mockRootObject.useCapability.andReturn(Promise.resolve([
|
||||||
mockDomainObject
|
mockOtherDomainObject,
|
||||||
|
mockDefaultRootObject
|
||||||
]));
|
]));
|
||||||
mockDomainObject.useCapability.andReturn(mockPromise([
|
mockRootObject.hasCapability.andReturn(true);
|
||||||
|
mockDefaultRootObject.useCapability.andReturn(Promise.resolve([
|
||||||
mockNextObject
|
mockNextObject
|
||||||
]));
|
]));
|
||||||
|
mockDefaultRootObject.hasCapability.andReturn(true);
|
||||||
|
mockOtherDomainObject.hasCapability.andReturn(false);
|
||||||
mockNextObject.useCapability.andReturn(undefined);
|
mockNextObject.useCapability.andReturn(undefined);
|
||||||
|
mockNextObject.hasCapability.andReturn(false);
|
||||||
mockNextObject.getId.andReturn("next");
|
mockNextObject.getId.andReturn("next");
|
||||||
mockDomainObject.getId.andReturn(testDefaultRoot);
|
mockDefaultRootObject.getId.andReturn(testDefaultRoot);
|
||||||
|
|
||||||
mockActionCapability = jasmine.createSpyObj('actionCapability', ['perform']);
|
|
||||||
|
|
||||||
instantiateController();
|
instantiateController();
|
||||||
|
waitsForNavigation();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses composition to set the navigated object, if there is none", function () {
|
it("uses composition to set the navigated object, if there is none", function () {
|
||||||
instantiateController();
|
instantiateController();
|
||||||
|
waitsForNavigation();
|
||||||
|
runs(function () {
|
||||||
expect(mockNavigationService.setNavigation)
|
expect(mockNavigationService.setNavigation)
|
||||||
.toHaveBeenCalledWith(mockDomainObject);
|
.toHaveBeenCalledWith(mockDefaultRootObject);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("navigates to a root-level object, even when default path is not found", function () {
|
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);
|
.andReturn("something-other-than-the-" + testDefaultRoot);
|
||||||
instantiateController();
|
instantiateController();
|
||||||
|
|
||||||
|
waitsForNavigation();
|
||||||
|
runs(function () {
|
||||||
expect(mockNavigationService.setNavigation)
|
expect(mockNavigationService.setNavigation)
|
||||||
.toHaveBeenCalledWith(mockDomainObject);
|
.toHaveBeenCalledWith(mockDefaultRootObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
//
|
||||||
it("does not try to override navigation", function () {
|
it("does not try to override navigation", function () {
|
||||||
mockNavigationService.getNavigation.andReturn(mockDomainObject);
|
mockNavigationService.getNavigation.andReturn(mockDefaultRootObject);
|
||||||
instantiateController();
|
instantiateController();
|
||||||
expect(mockScope.navigatedObject).toBe(mockDomainObject);
|
waitsForNavigation();
|
||||||
|
expect(mockScope.navigatedObject).toBe(mockDefaultRootObject);
|
||||||
});
|
});
|
||||||
|
//
|
||||||
it("updates scope when navigated object changes", function () {
|
it("updates scope when navigated object changes", function () {
|
||||||
// Should have registered a listener - call it
|
// Should have registered a listener - call it
|
||||||
mockNavigationService.addListener.mostRecentCall.args[0](
|
mockNavigationService.addListener.mostRecentCall.args[0](
|
||||||
mockDomainObject
|
mockOtherDomainObject
|
||||||
);
|
);
|
||||||
expect(mockScope.navigatedObject).toEqual(mockDomainObject);
|
expect(mockScope.navigatedObject).toEqual(mockOtherDomainObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -166,10 +200,13 @@ define(
|
|||||||
it("uses route parameters to choose initially-navigated object", function () {
|
it("uses route parameters to choose initially-navigated object", function () {
|
||||||
mockRoute.current.params.ids = testDefaultRoot + "/next";
|
mockRoute.current.params.ids = testDefaultRoot + "/next";
|
||||||
instantiateController();
|
instantiateController();
|
||||||
|
waitsForNavigation();
|
||||||
|
runs(function () {
|
||||||
expect(mockScope.navigatedObject).toBe(mockNextObject);
|
expect(mockScope.navigatedObject).toBe(mockNextObject);
|
||||||
expect(mockNavigationService.setNavigation)
|
expect(mockNavigationService.setNavigation)
|
||||||
.toHaveBeenCalledWith(mockNextObject);
|
.toHaveBeenCalledWith(mockNextObject);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("handles invalid IDs by going as far as possible", function () {
|
it("handles invalid IDs by going as far as possible", function () {
|
||||||
// Idea here is that if we get a bad path of IDs,
|
// Idea here is that if we get a bad path of IDs,
|
||||||
@ -177,9 +214,13 @@ define(
|
|||||||
// it hits an invalid ID.
|
// it hits an invalid ID.
|
||||||
mockRoute.current.params.ids = testDefaultRoot + "/junk";
|
mockRoute.current.params.ids = testDefaultRoot + "/junk";
|
||||||
instantiateController();
|
instantiateController();
|
||||||
expect(mockScope.navigatedObject).toBe(mockDomainObject);
|
waitsForNavigation();
|
||||||
|
runs(function () {
|
||||||
|
expect(mockScope.navigatedObject).toBe(mockDefaultRootObject);
|
||||||
expect(mockNavigationService.setNavigation)
|
expect(mockNavigationService.setNavigation)
|
||||||
.toHaveBeenCalledWith(mockDomainObject);
|
.toHaveBeenCalledWith(mockDefaultRootObject);
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles compositionless objects by going as far as possible", function () {
|
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.
|
// should stop at it since remaining IDs cannot be loaded.
|
||||||
mockRoute.current.params.ids = testDefaultRoot + "/next/junk";
|
mockRoute.current.params.ids = testDefaultRoot + "/next/junk";
|
||||||
instantiateController();
|
instantiateController();
|
||||||
|
waitsForNavigation();
|
||||||
|
runs(function () {
|
||||||
expect(mockScope.navigatedObject).toBe(mockNextObject);
|
expect(mockScope.navigatedObject).toBe(mockNextObject);
|
||||||
expect(mockNavigationService.setNavigation)
|
expect(mockNavigationService.setNavigation)
|
||||||
.toHaveBeenCalledWith(mockNextObject);
|
.toHaveBeenCalledWith(mockNextObject);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("updates the displayed route to reflect current navigation", function () {
|
it("updates the displayed route to reflect current navigation", function () {
|
||||||
var mockContext = jasmine.createSpyObj('context', ['getPath']),
|
// In order to trigger a route update and not a route change,
|
||||||
mockUnlisten = jasmine.createSpy('unlisten'),
|
// the current route must be updated before location.path is
|
||||||
mockMode = "browse";
|
// called.
|
||||||
|
expect(mockRoute.current.pathParams.ids)
|
||||||
mockContext.getPath.andReturn(
|
.not
|
||||||
[mockRootObject, mockDomainObject, mockNextObject]
|
.toBe(testDefaultRoot + '/next');
|
||||||
);
|
mockLocation.path.andCallFake(function () {
|
||||||
|
expect(mockRoute.current.pathParams.ids)
|
||||||
//Return true from navigate action
|
.toBe(testDefaultRoot + '/next');
|
||||||
mockActionCapability.perform.andReturn(mockPromise(true));
|
|
||||||
|
|
||||||
mockNextObject.getCapability.andCallFake(function (c) {
|
|
||||||
return (c === 'context' && mockContext) ||
|
|
||||||
(c === 'action' && mockActionCapability);
|
|
||||||
});
|
});
|
||||||
mockScope.$on.andReturn(mockUnlisten);
|
|
||||||
// Provide a navigation change
|
|
||||||
mockNavigationService.addListener.mostRecentCall.args[0](
|
mockNavigationService.addListener.mostRecentCall.args[0](
|
||||||
mockNextObject
|
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(
|
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,
|
var mockScope,
|
||||||
mockLocation,
|
mockLocation,
|
||||||
mockRoute,
|
mockRoute,
|
||||||
mockUnlisten,
|
|
||||||
controller;
|
controller;
|
||||||
|
|
||||||
// Utility function; look for a $watch on scope and fire it
|
// Utility function; look for a $watch on scope and fire it
|
||||||
@ -51,9 +50,7 @@ define(
|
|||||||
"$location",
|
"$location",
|
||||||
["path", "search"]
|
["path", "search"]
|
||||||
);
|
);
|
||||||
mockUnlisten = jasmine.createSpy("unlisten");
|
mockLocation.search.andReturn({});
|
||||||
|
|
||||||
mockScope.$on.andReturn(mockUnlisten);
|
|
||||||
|
|
||||||
controller = new BrowseObjectController(
|
controller = new BrowseObjectController(
|
||||||
mockScope,
|
mockScope,
|
||||||
@ -69,10 +66,6 @@ define(
|
|||||||
// Allows the path index to be checked
|
// Allows the path index to be checked
|
||||||
// prior to setting $route.current
|
// prior to setting $route.current
|
||||||
mockLocation.path.andReturn("/browse/");
|
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 () {
|
it("sets the active view from query parameters", function () {
|
||||||
|
@ -23,135 +23,65 @@
|
|||||||
/**
|
/**
|
||||||
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
|
* MCTRepresentationSpec. Created by vwoeltje on 11/6/14.
|
||||||
*/
|
*/
|
||||||
define(
|
define([
|
||||||
["../../src/navigation/NavigateAction"],
|
"../../src/navigation/NavigateAction"
|
||||||
function (NavigateAction) {
|
], function (
|
||||||
|
NavigateAction
|
||||||
|
) {
|
||||||
|
|
||||||
describe("The navigate action", function () {
|
describe("The navigate action", function () {
|
||||||
var mockNavigationService,
|
var mockNavigationService,
|
||||||
mockQ,
|
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockPolicyService,
|
|
||||||
mockNavigatedObject,
|
|
||||||
mockWindow,
|
|
||||||
capabilities,
|
|
||||||
action;
|
action;
|
||||||
|
|
||||||
function mockPromise(value) {
|
|
||||||
return {
|
function waitForCall() {
|
||||||
then: function (callback) {
|
var called = false;
|
||||||
return mockPromise(callback(value));
|
waitsFor(function () {
|
||||||
}
|
return called;
|
||||||
|
});
|
||||||
|
return function () {
|
||||||
|
called = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
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(
|
mockNavigationService = jasmine.createSpyObj(
|
||||||
"navigationService",
|
"navigationService",
|
||||||
[
|
[
|
||||||
"setNavigation",
|
"shouldNavigate",
|
||||||
"getNavigation"
|
"setNavigation"
|
||||||
]
|
|
||||||
);
|
|
||||||
mockNavigationService.getNavigation.andReturn(mockNavigatedObject);
|
|
||||||
|
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
|
||||||
"domainObject",
|
|
||||||
[
|
|
||||||
"getId",
|
|
||||||
"getModel"
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
mockPolicyService = jasmine.createSpyObj("policyService",
|
mockDomainObject = {};
|
||||||
[
|
|
||||||
"allow"
|
|
||||||
]);
|
|
||||||
mockWindow = jasmine.createSpyObj("$window",
|
|
||||||
[
|
|
||||||
"confirm"
|
|
||||||
]);
|
|
||||||
|
|
||||||
action = new NavigateAction(
|
action = new NavigateAction(
|
||||||
mockNavigationService,
|
mockNavigationService,
|
||||||
mockQ,
|
|
||||||
mockPolicyService,
|
|
||||||
mockWindow,
|
|
||||||
{ domainObject: mockDomainObject }
|
{ domainObject: mockDomainObject }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("invokes the policy service to determine if navigation" +
|
it("sets navigation if it is allowed", function () {
|
||||||
" allowed", function () {
|
mockNavigationService.shouldNavigate.andReturn(true);
|
||||||
action.perform();
|
action.perform()
|
||||||
expect(mockPolicyService.allow)
|
.then(waitForCall());
|
||||||
.toHaveBeenCalledWith("navigation", jasmine.any(Object), jasmine.any(Object), jasmine.any(Function));
|
runs(function () {
|
||||||
});
|
|
||||||
|
|
||||||
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)
|
expect(mockNavigationService.setNavigation)
|
||||||
.not.toHaveBeenCalled();
|
.toHaveBeenCalledWith(mockDomainObject, true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does navigate on prompt acceptance", function () {
|
it("does not set navigation if it is not allowed", function () {
|
||||||
mockWindow.confirm.andReturn(true);
|
mockNavigationService.shouldNavigate.andReturn(false);
|
||||||
action.perform();
|
var onSuccess = jasmine.createSpy('onSuccess');
|
||||||
|
action.perform()
|
||||||
|
.then(onSuccess, waitForCall());
|
||||||
|
runs(function () {
|
||||||
|
expect(onSuccess).not.toHaveBeenCalled();
|
||||||
expect(mockNavigationService.setNavigation)
|
expect(mockNavigationService.setNavigation)
|
||||||
.toHaveBeenCalled();
|
.not
|
||||||
});
|
.toHaveBeenCalledWith(mockDomainObject);
|
||||||
});
|
|
||||||
|
|
||||||
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();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -163,5 +93,4 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
@ -28,10 +28,12 @@ define(
|
|||||||
function (NavigationService) {
|
function (NavigationService) {
|
||||||
|
|
||||||
describe("The navigation service", function () {
|
describe("The navigation service", function () {
|
||||||
var navigationService;
|
var $window,
|
||||||
|
navigationService;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
navigationService = new NavigationService();
|
$window = jasmine.createSpyObj('$window', ['confirm']);
|
||||||
|
navigationService = new NavigationService($window);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("stores navigation state", function () {
|
it("stores navigation state", function () {
|
||||||
|
@ -33,7 +33,7 @@ define([
|
|||||||
mockContext,
|
mockContext,
|
||||||
mockActionCapability,
|
mockActionCapability,
|
||||||
mockEditor,
|
mockEditor,
|
||||||
testParentModel,
|
testParentComposition,
|
||||||
testId,
|
testId,
|
||||||
mockThrottledFns;
|
mockThrottledFns;
|
||||||
|
|
||||||
@ -41,7 +41,6 @@ define([
|
|||||||
testId = 'some-identifier';
|
testId = 'some-identifier';
|
||||||
|
|
||||||
mockThrottledFns = [];
|
mockThrottledFns = [];
|
||||||
testParentModel = {};
|
|
||||||
|
|
||||||
mockTopic = jasmine.createSpy('topic');
|
mockTopic = jasmine.createSpy('topic');
|
||||||
mockThrottle = jasmine.createSpy('throttle');
|
mockThrottle = jasmine.createSpy('throttle');
|
||||||
@ -55,14 +54,12 @@ define([
|
|||||||
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
||||||
'getId',
|
'getId',
|
||||||
'getCapability',
|
'getCapability',
|
||||||
'getModel',
|
|
||||||
'hasCapability'
|
'hasCapability'
|
||||||
]);
|
]);
|
||||||
mockParentObject = jasmine.createSpyObj('domainObject', [
|
mockParentObject = jasmine.createSpyObj('domainObject', [
|
||||||
'getId',
|
'getId',
|
||||||
'getCapability',
|
'getCapability',
|
||||||
'getModel',
|
'useCapability'
|
||||||
'hasCapability'
|
|
||||||
]);
|
]);
|
||||||
mockContext = jasmine.createSpyObj('context', ['getParent']);
|
mockContext = jasmine.createSpyObj('context', ['getParent']);
|
||||||
mockActionCapability = jasmine.createSpyObj('action', ['perform']);
|
mockActionCapability = jasmine.createSpyObj('action', ['perform']);
|
||||||
@ -75,9 +72,7 @@ define([
|
|||||||
mockThrottledFns.push(mockThrottledFn);
|
mockThrottledFns.push(mockThrottledFn);
|
||||||
return mockThrottledFn;
|
return mockThrottledFn;
|
||||||
});
|
});
|
||||||
mockTopic.andCallFake(function (k) {
|
mockTopic.andReturn(mockMutationTopic);
|
||||||
return k === 'mutation' && mockMutationTopic;
|
|
||||||
});
|
|
||||||
mockDomainObject.getId.andReturn(testId);
|
mockDomainObject.getId.andReturn(testId);
|
||||||
mockDomainObject.getCapability.andCallFake(function (c) {
|
mockDomainObject.getCapability.andCallFake(function (c) {
|
||||||
return {
|
return {
|
||||||
@ -88,12 +83,13 @@ define([
|
|||||||
mockDomainObject.hasCapability.andCallFake(function (c) {
|
mockDomainObject.hasCapability.andCallFake(function (c) {
|
||||||
return !!mockDomainObject.getCapability(c);
|
return !!mockDomainObject.getCapability(c);
|
||||||
});
|
});
|
||||||
mockParentObject.getModel.andReturn(testParentModel);
|
|
||||||
mockParentObject.getCapability.andCallFake(function (c) {
|
mockParentObject.getCapability.andCallFake(function (c) {
|
||||||
return {
|
return {
|
||||||
action: mockActionCapability
|
action: mockActionCapability
|
||||||
}[c];
|
}[c];
|
||||||
});
|
});
|
||||||
|
testParentComposition = [];
|
||||||
|
mockParentObject.useCapability.andReturn(Promise.resolve(testParentComposition));
|
||||||
mockContext.getParent.andReturn(mockParentObject);
|
mockContext.getParent.andReturn(mockParentObject);
|
||||||
mockNavigationService.getNavigation.andReturn(mockDomainObject);
|
mockNavigationService.getNavigation.andReturn(mockDomainObject);
|
||||||
mockEditor.isEditContextRoot.andReturn(false);
|
mockEditor.isEditContextRoot.andReturn(false);
|
||||||
@ -126,7 +122,9 @@ define([
|
|||||||
var prefix = isOrphan ? "" : "non-";
|
var prefix = isOrphan ? "" : "non-";
|
||||||
describe("for " + prefix + "orphan objects", function () {
|
describe("for " + prefix + "orphan objects", function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
testParentModel.composition = isOrphan ? [] : [testId];
|
if (!isOrphan) {
|
||||||
|
testParentComposition.push(mockDomainObject);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
[false, true].forEach(function (isEditRoot) {
|
[false, true].forEach(function (isEditRoot) {
|
||||||
@ -136,14 +134,32 @@ define([
|
|||||||
function itNavigatesAsExpected() {
|
function itNavigatesAsExpected() {
|
||||||
if (isOrphan && !isEditRoot) {
|
if (isOrphan && !isEditRoot) {
|
||||||
it("navigates to the parent", function () {
|
it("navigates to the parent", function () {
|
||||||
|
var done = false;
|
||||||
|
waitsFor(function () {
|
||||||
|
return done;
|
||||||
|
});
|
||||||
|
setTimeout(function () {
|
||||||
|
done = true;
|
||||||
|
}, 5);
|
||||||
|
runs(function () {
|
||||||
expect(mockActionCapability.perform)
|
expect(mockActionCapability.perform)
|
||||||
.toHaveBeenCalledWith('navigate');
|
.toHaveBeenCalledWith('navigate');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
it("does nothing", function () {
|
it("does nothing", function () {
|
||||||
|
var done = false;
|
||||||
|
waitsFor(function () {
|
||||||
|
return done;
|
||||||
|
});
|
||||||
|
setTimeout(function () {
|
||||||
|
done = true;
|
||||||
|
}, 5);
|
||||||
|
runs(function () {
|
||||||
expect(mockActionCapability.perform)
|
expect(mockActionCapability.perform)
|
||||||
.not.toHaveBeenCalled();
|
.not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +173,6 @@ define([
|
|||||||
mockNavigationService.addListener.mostRecentCall
|
mockNavigationService.addListener.mostRecentCall
|
||||||
.args[0](mockDomainObject);
|
.args[0](mockDomainObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
itNavigatesAsExpected();
|
itNavigatesAsExpected();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("provides displayable metadata", function () {
|
it("provides displayable metadata", function () {
|
||||||
expect(action.getMetadata().cssclass).toBeDefined();
|
expect(action.getMetadata().cssClass).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -53,7 +53,8 @@ define([
|
|||||||
"depends": [
|
"depends": [
|
||||||
"overlayService",
|
"overlayService",
|
||||||
"$q",
|
"$q",
|
||||||
"$log"
|
"$log",
|
||||||
|
"$document"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -35,11 +35,15 @@ define(
|
|||||||
* @memberof platform/commonUI/dialog
|
* @memberof platform/commonUI/dialog
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function DialogService(overlayService, $q, $log) {
|
function DialogService(overlayService, $q, $log, $document) {
|
||||||
this.overlayService = overlayService;
|
this.overlayService = overlayService;
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
this.$log = $log;
|
this.$log = $log;
|
||||||
this.activeOverlay = undefined;
|
this.activeOverlay = undefined;
|
||||||
|
|
||||||
|
this.findBody = function () {
|
||||||
|
return $document.find('body');
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,7 +64,8 @@ define(
|
|||||||
// input is asynchronous.
|
// input is asynchronous.
|
||||||
var deferred = this.$q.defer(),
|
var deferred = this.$q.defer(),
|
||||||
self = this,
|
self = this,
|
||||||
overlay;
|
overlay,
|
||||||
|
handleEscKeydown;
|
||||||
|
|
||||||
// Confirm function; this will be passed in to the
|
// Confirm function; this will be passed in to the
|
||||||
// overlay-dialog template and associated with a
|
// overlay-dialog template and associated with a
|
||||||
@ -76,13 +81,22 @@ define(
|
|||||||
// Cancel or X button click
|
// Cancel or X button click
|
||||||
function cancel() {
|
function cancel() {
|
||||||
deferred.reject();
|
deferred.reject();
|
||||||
|
self.findBody().off('keydown', handleEscKeydown);
|
||||||
self.dismissOverlay(overlay);
|
self.dismissOverlay(overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleEscKeydown = function (event) {
|
||||||
|
if (event.keyCode === 27) {
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Add confirm/cancel callbacks
|
// Add confirm/cancel callbacks
|
||||||
model.confirm = confirm;
|
model.confirm = confirm;
|
||||||
model.cancel = cancel;
|
model.cancel = cancel;
|
||||||
|
|
||||||
|
this.findBody().on('keydown', handleEscKeydown);
|
||||||
|
|
||||||
if (this.canShowDialog(model)) {
|
if (this.canShowDialog(model)) {
|
||||||
// Add the overlay using the OverlayService, which
|
// Add the overlay using the OverlayService, which
|
||||||
// will handle actual insertion into the DOM
|
// will handle actual insertion into the DOM
|
||||||
|
@ -33,6 +33,8 @@ define(
|
|||||||
mockLog,
|
mockLog,
|
||||||
mockOverlay,
|
mockOverlay,
|
||||||
mockDeferred,
|
mockDeferred,
|
||||||
|
mockDocument,
|
||||||
|
mockBody,
|
||||||
dialogService;
|
dialogService;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
@ -56,6 +58,13 @@ define(
|
|||||||
"deferred",
|
"deferred",
|
||||||
["resolve", "reject"]
|
["resolve", "reject"]
|
||||||
);
|
);
|
||||||
|
mockDocument = jasmine.createSpyObj(
|
||||||
|
"$document",
|
||||||
|
["find"]
|
||||||
|
);
|
||||||
|
mockBody = jasmine.createSpyObj('body', ['on', 'off']);
|
||||||
|
mockDocument.find.andReturn(mockBody);
|
||||||
|
|
||||||
mockDeferred.promise = "mock promise";
|
mockDeferred.promise = "mock promise";
|
||||||
|
|
||||||
mockQ.defer.andReturn(mockDeferred);
|
mockQ.defer.andReturn(mockDeferred);
|
||||||
@ -64,7 +73,8 @@ define(
|
|||||||
dialogService = new DialogService(
|
dialogService = new DialogService(
|
||||||
mockOverlayService,
|
mockOverlayService,
|
||||||
mockQ,
|
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 () {
|
describe("the blocking message dialog", function () {
|
||||||
var dialogModel = {};
|
var dialogModel = {};
|
||||||
var dialogHandle;
|
var dialogHandle;
|
||||||
|
@ -2,25 +2,6 @@ Contains sources and resources associated with Edit mode.
|
|||||||
|
|
||||||
# Extensions
|
# 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
|
# Toolbars
|
||||||
|
|
||||||
Views may specify the contents of a toolbar through a `toolbar`
|
Views may specify the contents of a toolbar through a `toolbar`
|
||||||
|
@ -25,7 +25,6 @@ define([
|
|||||||
"./src/controllers/EditPanesController",
|
"./src/controllers/EditPanesController",
|
||||||
"./src/controllers/ElementsController",
|
"./src/controllers/ElementsController",
|
||||||
"./src/controllers/EditObjectController",
|
"./src/controllers/EditObjectController",
|
||||||
"./src/directives/MCTBeforeUnload",
|
|
||||||
"./src/actions/EditAndComposeAction",
|
"./src/actions/EditAndComposeAction",
|
||||||
"./src/actions/EditAction",
|
"./src/actions/EditAction",
|
||||||
"./src/actions/PropertiesAction",
|
"./src/actions/PropertiesAction",
|
||||||
@ -37,7 +36,6 @@ define([
|
|||||||
"./src/policies/EditActionPolicy",
|
"./src/policies/EditActionPolicy",
|
||||||
"./src/policies/EditableLinkPolicy",
|
"./src/policies/EditableLinkPolicy",
|
||||||
"./src/policies/EditableMovePolicy",
|
"./src/policies/EditableMovePolicy",
|
||||||
"./src/policies/EditNavigationPolicy",
|
|
||||||
"./src/policies/EditContextualActionPolicy",
|
"./src/policies/EditContextualActionPolicy",
|
||||||
"./src/representers/EditRepresenter",
|
"./src/representers/EditRepresenter",
|
||||||
"./src/representers/EditToolbarRepresenter",
|
"./src/representers/EditToolbarRepresenter",
|
||||||
@ -65,7 +63,6 @@ define([
|
|||||||
EditPanesController,
|
EditPanesController,
|
||||||
ElementsController,
|
ElementsController,
|
||||||
EditObjectController,
|
EditObjectController,
|
||||||
MCTBeforeUnload,
|
|
||||||
EditAndComposeAction,
|
EditAndComposeAction,
|
||||||
EditAction,
|
EditAction,
|
||||||
PropertiesAction,
|
PropertiesAction,
|
||||||
@ -77,7 +74,6 @@ define([
|
|||||||
EditActionPolicy,
|
EditActionPolicy,
|
||||||
EditableLinkPolicy,
|
EditableLinkPolicy,
|
||||||
EditableMovePolicy,
|
EditableMovePolicy,
|
||||||
EditNavigationPolicy,
|
|
||||||
EditContextualActionPolicy,
|
EditContextualActionPolicy,
|
||||||
EditRepresenter,
|
EditRepresenter,
|
||||||
EditToolbarRepresenter,
|
EditToolbarRepresenter,
|
||||||
@ -132,7 +128,7 @@ define([
|
|||||||
"depends": [
|
"depends": [
|
||||||
"$scope",
|
"$scope",
|
||||||
"$location",
|
"$location",
|
||||||
"policyService"
|
"navigationService"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -152,15 +148,6 @@ define([
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"directives": [
|
|
||||||
{
|
|
||||||
"key": "mctBeforeUnload",
|
|
||||||
"implementation": MCTBeforeUnload,
|
|
||||||
"depends": [
|
|
||||||
"$window"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"actions": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"key": "compose",
|
"key": "compose",
|
||||||
@ -176,7 +163,7 @@ define([
|
|||||||
],
|
],
|
||||||
"description": "Edit",
|
"description": "Edit",
|
||||||
"category": "view-control",
|
"category": "view-control",
|
||||||
"cssclass": "major icon-pencil"
|
"cssClass": "major icon-pencil"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "properties",
|
"key": "properties",
|
||||||
@ -185,7 +172,7 @@ define([
|
|||||||
"view-control"
|
"view-control"
|
||||||
],
|
],
|
||||||
"implementation": PropertiesAction,
|
"implementation": PropertiesAction,
|
||||||
"cssclass": "major icon-pencil",
|
"cssClass": "major icon-pencil",
|
||||||
"name": "Edit Properties...",
|
"name": "Edit Properties...",
|
||||||
"description": "Edit properties of this object.",
|
"description": "Edit properties of this object.",
|
||||||
"depends": [
|
"depends": [
|
||||||
@ -196,7 +183,7 @@ define([
|
|||||||
"key": "remove",
|
"key": "remove",
|
||||||
"category": "contextual",
|
"category": "contextual",
|
||||||
"implementation": RemoveAction,
|
"implementation": RemoveAction,
|
||||||
"cssclass": "icon-trash",
|
"cssClass": "icon-trash",
|
||||||
"name": "Remove",
|
"name": "Remove",
|
||||||
"description": "Remove this object from its containing object.",
|
"description": "Remove this object from its containing object.",
|
||||||
"depends": [
|
"depends": [
|
||||||
@ -208,10 +195,11 @@ define([
|
|||||||
"category": "save",
|
"category": "save",
|
||||||
"implementation": SaveAndStopEditingAction,
|
"implementation": SaveAndStopEditingAction,
|
||||||
"name": "Save and Finish Editing",
|
"name": "Save and Finish Editing",
|
||||||
"cssclass": "icon-save labeled",
|
"cssClass": "icon-save labeled",
|
||||||
"description": "Save changes made to these objects.",
|
"description": "Save changes made to these objects.",
|
||||||
"depends": [
|
"depends": [
|
||||||
"dialogService"
|
"dialogService",
|
||||||
|
"notificationService"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -219,10 +207,11 @@ define([
|
|||||||
"category": "save",
|
"category": "save",
|
||||||
"implementation": SaveAction,
|
"implementation": SaveAction,
|
||||||
"name": "Save and Continue Editing",
|
"name": "Save and Continue Editing",
|
||||||
"cssclass": "icon-save labeled",
|
"cssClass": "icon-save labeled",
|
||||||
"description": "Save changes made to these objects.",
|
"description": "Save changes made to these objects.",
|
||||||
"depends": [
|
"depends": [
|
||||||
"dialogService"
|
"dialogService",
|
||||||
|
"notificationService"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -230,13 +219,14 @@ define([
|
|||||||
"category": "save",
|
"category": "save",
|
||||||
"implementation": SaveAsAction,
|
"implementation": SaveAsAction,
|
||||||
"name": "Save As...",
|
"name": "Save As...",
|
||||||
"cssclass": "icon-save labeled",
|
"cssClass": "icon-save labeled",
|
||||||
"description": "Save changes made to these objects.",
|
"description": "Save changes made to these objects.",
|
||||||
"depends": [
|
"depends": [
|
||||||
"$injector",
|
"$injector",
|
||||||
"policyService",
|
"policyService",
|
||||||
"dialogService",
|
"dialogService",
|
||||||
"copyService"
|
"copyService",
|
||||||
|
"notificationService"
|
||||||
],
|
],
|
||||||
"priority": "mandatory"
|
"priority": "mandatory"
|
||||||
},
|
},
|
||||||
@ -247,7 +237,7 @@ define([
|
|||||||
// Because we use the name as label for edit buttons and mct-control buttons need
|
// 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.
|
// the label to be set to undefined in order to not apply the labeled CSS rule.
|
||||||
"name": undefined,
|
"name": undefined,
|
||||||
"cssclass": "icon-x no-label",
|
"cssClass": "icon-x no-label",
|
||||||
"description": "Discard changes made to these objects.",
|
"description": "Discard changes made to these objects.",
|
||||||
"depends": []
|
"depends": []
|
||||||
}
|
}
|
||||||
@ -270,11 +260,6 @@ define([
|
|||||||
"category": "action",
|
"category": "action",
|
||||||
"implementation": EditableLinkPolicy
|
"implementation": EditableLinkPolicy
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"category": "navigation",
|
|
||||||
"message": "Continuing will cause the loss of any unsaved changes.",
|
|
||||||
"implementation": EditNavigationPolicy
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"implementation": CreationPolicy,
|
"implementation": CreationPolicy,
|
||||||
"category": "creation"
|
"category": "creation"
|
||||||
@ -347,7 +332,8 @@ define([
|
|||||||
"implementation": TransactionService,
|
"implementation": TransactionService,
|
||||||
"depends": [
|
"depends": [
|
||||||
"$q",
|
"$q",
|
||||||
"$log"
|
"$log",
|
||||||
|
"cacheService"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -388,7 +374,6 @@ define([
|
|||||||
{
|
{
|
||||||
"implementation": EditRepresenter,
|
"implementation": EditRepresenter,
|
||||||
"depends": [
|
"depends": [
|
||||||
"$q",
|
|
||||||
"$log"
|
"$log"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -25,14 +25,14 @@
|
|||||||
<li ng-repeat="createAction in createActions" ng-click="createAction.perform()">
|
<li ng-repeat="createAction in createActions" ng-click="createAction.perform()">
|
||||||
<a ng-mouseover="representation.activeMetadata = createAction.getMetadata()"
|
<a ng-mouseover="representation.activeMetadata = createAction.getMetadata()"
|
||||||
ng-mouseleave="representation.activeMetadata = undefined"
|
ng-mouseleave="representation.activeMetadata = undefined"
|
||||||
class="menu-item-a {{ createAction.getMetadata().cssclass }}">
|
class="menu-item-a {{ createAction.getMetadata().cssClass }}">
|
||||||
{{createAction.getMetadata().name}}
|
{{createAction.getMetadata().name}}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="pane right menu-item-description">
|
<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">
|
<div class="desc-area title">
|
||||||
{{representation.activeMetadata.name}}
|
{{representation.activeMetadata.name}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
structure="{
|
structure="{
|
||||||
text: saveActions[0].getMetadata().name,
|
text: saveActions[0].getMetadata().name,
|
||||||
click: actionPerformer(saveActions[0]),
|
click: actionPerformer(saveActions[0]),
|
||||||
cssclass: 'major ' + saveActions[0].getMetadata().cssclass
|
cssClass: 'major ' + saveActions[0].getMetadata().cssClass
|
||||||
}">
|
}">
|
||||||
</mct-control>
|
</mct-control>
|
||||||
</span>
|
</span>
|
||||||
@ -36,7 +36,7 @@
|
|||||||
structure="{
|
structure="{
|
||||||
options: saveActionsAsMenuOptions,
|
options: saveActionsAsMenuOptions,
|
||||||
click: saveActionMenuClickHandler,
|
click: saveActionMenuClickHandler,
|
||||||
cssclass: 'btn-bar right icon-save no-label major'
|
cssClass: 'btn-bar right icon-save no-label major'
|
||||||
}">
|
}">
|
||||||
</mct-control>
|
</mct-control>
|
||||||
</span>
|
</span>
|
||||||
@ -46,7 +46,7 @@
|
|||||||
structure="{
|
structure="{
|
||||||
text: currentAction.getMetadata().name,
|
text: currentAction.getMetadata().name,
|
||||||
click: actionPerformer(currentAction),
|
click: actionPerformer(currentAction),
|
||||||
cssclass: currentAction.getMetadata().cssclass
|
cssClass: currentAction.getMetadata().cssClass
|
||||||
}">
|
}">
|
||||||
</mct-control>
|
</mct-control>
|
||||||
</span>
|
</span>
|
||||||
|
@ -20,8 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="abs l-flex-col" ng-controller="EditObjectController as EditObjectController">
|
<div class="abs l-flex-col" ng-controller="EditObjectController as EditObjectController">
|
||||||
<div mct-before-unload="EditObjectController.getUnloadWarning()"
|
<div class="holder flex-elem l-flex-row object-browse-bar ">
|
||||||
class="holder flex-elem l-flex-row object-browse-bar ">
|
|
||||||
<div class="items-select left flex-elem l-flex-row grows">
|
<div class="items-select left flex-elem l-flex-row grows">
|
||||||
<mct-representation key="'back-arrow'"
|
<mct-representation key="'back-arrow'"
|
||||||
mct-object="domainObject"
|
mct-object="domainObject"
|
||||||
|
@ -56,13 +56,13 @@ define(
|
|||||||
//navigate back to parent because nothing to show.
|
//navigate back to parent because nothing to show.
|
||||||
return domainObject.getCapability("location").getOriginal().then(function (original) {
|
return domainObject.getCapability("location").getOriginal().then(function (original) {
|
||||||
parent = original.getCapability("context").getParent();
|
parent = original.getCapability("context").getParent();
|
||||||
parent.getCapability("action").perform("navigate");
|
return parent.getCapability("action").perform("navigate");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel(allowed) {
|
function cancel() {
|
||||||
return allowed && domainObject.getCapability("editor").finish();
|
return domainObject.getCapability("editor").finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Do navigation first in order to trigger unsaved changes dialog
|
//Do navigation first in order to trigger unsaved changes dialog
|
||||||
|
@ -33,10 +33,12 @@ define(
|
|||||||
*/
|
*/
|
||||||
function SaveAction(
|
function SaveAction(
|
||||||
dialogService,
|
dialogService,
|
||||||
|
notificationService,
|
||||||
context
|
context
|
||||||
) {
|
) {
|
||||||
this.domainObject = (context || {}).domainObject;
|
this.domainObject = (context || {}).domainObject;
|
||||||
this.dialogService = dialogService;
|
this.dialogService = dialogService;
|
||||||
|
this.notificationService = notificationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +49,8 @@ define(
|
|||||||
* @memberof platform/commonUI/edit.SaveAction#
|
* @memberof platform/commonUI/edit.SaveAction#
|
||||||
*/
|
*/
|
||||||
SaveAction.prototype.perform = function () {
|
SaveAction.prototype.perform = function () {
|
||||||
var domainObject = this.domainObject,
|
var self = this,
|
||||||
|
domainObject = this.domainObject,
|
||||||
dialog = new SaveInProgressDialog(this.dialogService);
|
dialog = new SaveInProgressDialog(this.dialogService);
|
||||||
|
|
||||||
// Invoke any save behavior introduced by the editor capability;
|
// Invoke any save behavior introduced by the editor capability;
|
||||||
@ -58,15 +61,21 @@ define(
|
|||||||
return domainObject.getCapability("editor").save();
|
return domainObject.getCapability("editor").save();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideBlockingDialog() {
|
function onSuccess() {
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
|
self.notificationService.info("Save Succeeded");
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFailure() {
|
||||||
|
dialog.hide();
|
||||||
|
self.notificationService.error("Save Failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
|
||||||
return doSave()
|
return doSave()
|
||||||
.then(hideBlockingDialog)
|
.then(onSuccess)
|
||||||
.catch(hideBlockingDialog);
|
.catch(onFailure);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,11 +33,13 @@ define(
|
|||||||
*/
|
*/
|
||||||
function SaveAndStopEditingAction(
|
function SaveAndStopEditingAction(
|
||||||
dialogService,
|
dialogService,
|
||||||
|
notificationService,
|
||||||
context
|
context
|
||||||
) {
|
) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.domainObject = (context || {}).domainObject;
|
this.domainObject = (context || {}).domainObject;
|
||||||
this.dialogService = dialogService;
|
this.dialogService = dialogService;
|
||||||
|
this.notificationService = notificationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,7 +51,7 @@ define(
|
|||||||
*/
|
*/
|
||||||
SaveAndStopEditingAction.prototype.perform = function () {
|
SaveAndStopEditingAction.prototype.perform = function () {
|
||||||
var domainObject = this.domainObject,
|
var domainObject = this.domainObject,
|
||||||
saveAction = new SaveAction(this.dialogService, this.context);
|
saveAction = new SaveAction(this.dialogService, this.notificationService, this.context);
|
||||||
|
|
||||||
function closeEditor() {
|
function closeEditor() {
|
||||||
return domainObject.getCapability("editor").finish();
|
return domainObject.getCapability("editor").finish();
|
||||||
|
@ -43,6 +43,7 @@ define([
|
|||||||
policyService,
|
policyService,
|
||||||
dialogService,
|
dialogService,
|
||||||
copyService,
|
copyService,
|
||||||
|
notificationService,
|
||||||
context
|
context
|
||||||
) {
|
) {
|
||||||
this.domainObject = (context || {}).domainObject;
|
this.domainObject = (context || {}).domainObject;
|
||||||
@ -52,6 +53,7 @@ define([
|
|||||||
this.policyService = policyService;
|
this.policyService = policyService;
|
||||||
this.dialogService = dialogService;
|
this.dialogService = dialogService;
|
||||||
this.copyService = copyService;
|
this.copyService = copyService;
|
||||||
|
this.notificationService = notificationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,8 +119,10 @@ define([
|
|||||||
|
|
||||||
return self.dialogService
|
return self.dialogService
|
||||||
.getUserInput(wizard.getFormStructure(true),
|
.getUserInput(wizard.getFormStructure(true),
|
||||||
wizard.getInitialFormValue()
|
wizard.getInitialFormValue())
|
||||||
).then(wizard.populateObjectFromInput.bind(wizard));
|
.then(wizard.populateObjectFromInput.bind(wizard), function (failureReason) {
|
||||||
|
return Promise.reject("user canceled");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showBlockingDialog(object) {
|
function showBlockingDialog(object) {
|
||||||
@ -171,11 +175,21 @@ define([
|
|||||||
|
|
||||||
function finishEditing(clonedObject) {
|
function finishEditing(clonedObject) {
|
||||||
return domainObject.getCapability("editor").finish()
|
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();
|
hideBlockingDialog();
|
||||||
|
if (reason !== "user canceled") {
|
||||||
|
self.notificationService.error("Save Failed");
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,6 +202,7 @@ define([
|
|||||||
.then(saveAfterClone)
|
.then(saveAfterClone)
|
||||||
.then(finishEditing)
|
.then(finishEditing)
|
||||||
.then(hideBlockingDialog)
|
.then(hideBlockingDialog)
|
||||||
|
.then(onSuccess)
|
||||||
.catch(onFailure);
|
.catch(onFailure);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,9 +48,10 @@ define(
|
|||||||
* Decorate PersistenceCapability to queue persistence calls when a
|
* Decorate PersistenceCapability to queue persistence calls when a
|
||||||
* transaction is in progress.
|
* transaction is in progress.
|
||||||
*/
|
*/
|
||||||
TransactionCapabilityDecorator.prototype.getCapabilities = function (model) {
|
TransactionCapabilityDecorator.prototype.getCapabilities = function () {
|
||||||
var self = this,
|
var self = this,
|
||||||
capabilities = this.capabilityService.getCapabilities(model),
|
capabilities = this.capabilityService.getCapabilities
|
||||||
|
.apply(this.capabilityService, arguments),
|
||||||
persistenceCapability = capabilities.persistence;
|
persistenceCapability = capabilities.persistence;
|
||||||
|
|
||||||
capabilities.persistence = function (domainObject) {
|
capabilities.persistence = function (domainObject) {
|
||||||
|
@ -41,7 +41,7 @@ define(
|
|||||||
return {
|
return {
|
||||||
key: action,
|
key: action,
|
||||||
name: action.getMetadata().name,
|
name: action.getMetadata().name,
|
||||||
cssclass: action.getMetadata().cssclass
|
cssClass: action.getMetadata().cssClass
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,18 +28,49 @@ define(
|
|||||||
[],
|
[],
|
||||||
function () {
|
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
|
* Controller which is responsible for populating the scope for
|
||||||
* Edit mode
|
* Edit mode
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EditObjectController($scope, $location, policyService) {
|
function EditObjectController($scope, $location, navigationService) {
|
||||||
this.scope = $scope;
|
this.scope = $scope;
|
||||||
this.policyService = policyService;
|
var domainObject = $scope.domainObject;
|
||||||
|
|
||||||
var navigatedObject;
|
var removeCheck = navigationService
|
||||||
function setViewForDomainObject(domainObject) {
|
.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;
|
var locationViewKey = $location.search().view;
|
||||||
|
|
||||||
@ -54,34 +85,15 @@ define(
|
|||||||
((domainObject && domainObject.useCapability('view')) || [])
|
((domainObject && domainObject.useCapability('view')) || [])
|
||||||
.forEach(selectViewIfMatching);
|
.forEach(selectViewIfMatching);
|
||||||
}
|
}
|
||||||
navigatedObject = domainObject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.$watch('domainObject', setViewForDomainObject);
|
setViewForDomainObject();
|
||||||
|
|
||||||
$scope.doAction = function (action) {
|
$scope.doAction = function (action) {
|
||||||
return $scope[action] && $scope[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;
|
return EditObjectController;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -51,7 +51,7 @@ define(
|
|||||||
function AddAction(type, parent, context, $q, dialogService, policyService) {
|
function AddAction(type, parent, context, $q, dialogService, policyService) {
|
||||||
this.metadata = {
|
this.metadata = {
|
||||||
key: 'add',
|
key: 'add',
|
||||||
cssclass: type.getCssClass(),
|
cssClass: type.getCssClass(),
|
||||||
name: type.getName(),
|
name: type.getName(),
|
||||||
type: type.getKey(),
|
type: type.getKey(),
|
||||||
description: type.getDescription(),
|
description: type.getDescription(),
|
||||||
|
@ -54,8 +54,7 @@ define(
|
|||||||
AddActionProvider.prototype.getActions = function (actionContext) {
|
AddActionProvider.prototype.getActions = function (actionContext) {
|
||||||
var context = actionContext || {},
|
var context = actionContext || {},
|
||||||
key = context.key,
|
key = context.key,
|
||||||
destination = context.domainObject,
|
destination = context.domainObject;
|
||||||
self = this;
|
|
||||||
|
|
||||||
// We only provide Add actions, and we need a
|
// We only provide Add actions, and we need a
|
||||||
// domain object to serve as the container for the
|
// domain object to serve as the container for the
|
||||||
@ -66,18 +65,16 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Introduce one create action per type
|
// Introduce one create action per type
|
||||||
return this.typeService.listTypes().filter(function (type) {
|
return ['timeline', 'activity'].map(function (type) {
|
||||||
return self.policyService.allow("creation", type) && self.policyService.allow("composition", destination.getCapability('type'), type);
|
|
||||||
}).map(function (type) {
|
|
||||||
return new AddAction(
|
return new AddAction(
|
||||||
type,
|
this.typeService.getType(type),
|
||||||
destination,
|
destination,
|
||||||
context,
|
context,
|
||||||
self.$q,
|
this.$q,
|
||||||
self.dialogService,
|
this.dialogService,
|
||||||
self.policyService
|
this.policyService
|
||||||
);
|
);
|
||||||
});
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
return AddActionProvider;
|
return AddActionProvider;
|
||||||
|
@ -47,7 +47,7 @@ define(
|
|||||||
function CreateAction(type, parent, context) {
|
function CreateAction(type, parent, context) {
|
||||||
this.metadata = {
|
this.metadata = {
|
||||||
key: 'create',
|
key: 'create',
|
||||||
cssclass: type.getCssClass(),
|
cssClass: type.getCssClass(),
|
||||||
name: type.getName(),
|
name: type.getName(),
|
||||||
type: type.getKey(),
|
type: type.getKey(),
|
||||||
description: type.getDescription(),
|
description: type.getDescription(),
|
||||||
|
@ -56,16 +56,16 @@ define(
|
|||||||
*/
|
*/
|
||||||
CreateWizard.prototype.getFormStructure = function (includeLocation) {
|
CreateWizard.prototype.getFormStructure = function (includeLocation) {
|
||||||
var sections = [],
|
var sections = [],
|
||||||
type = this.type,
|
domainObject = this.domainObject,
|
||||||
policyService = this.policyService;
|
policyService = this.policyService;
|
||||||
|
|
||||||
function validateLocation(locatingObject) {
|
function validateLocation(parent) {
|
||||||
var locatingType = locatingObject &&
|
var parentType = parent &&
|
||||||
locatingObject.getCapability('type');
|
parent.getCapability('type');
|
||||||
return locatingType && policyService.allow(
|
return parentType && policyService.allow(
|
||||||
"composition",
|
"composition",
|
||||||
locatingType,
|
parentType,
|
||||||
type
|
domainObject
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ define(
|
|||||||
if (includeLocation) {
|
if (includeLocation) {
|
||||||
sections.push({
|
sections.push({
|
||||||
name: 'Location',
|
name: 'Location',
|
||||||
cssclass: "grows",
|
cssClass: "grows",
|
||||||
rows: [{
|
rows: [{
|
||||||
name: "Save In",
|
name: "Save In",
|
||||||
control: "locator",
|
control: "locator",
|
||||||
|
@ -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}
|
* @implements {Representer}
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EditRepresenter($q, $log, scope) {
|
function EditRepresenter($log, $scope) {
|
||||||
var self = this;
|
this.$log = $log;
|
||||||
|
this.$scope = $scope;
|
||||||
|
|
||||||
this.scope = scope;
|
this.$scope.commit = this.commit.bind(this);
|
||||||
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) {
|
* Commit any changes made to the in-scope model to the domain object.
|
||||||
// Look up from scope; these will have been populated by
|
* Also commits any changes made to $scope.configuration to the proper
|
||||||
// mct-representation.
|
* configuration value for the current representation.
|
||||||
var model = scope.model,
|
*
|
||||||
configuration = scope.configuration,
|
* @param {String} message a message to log with the commit message.
|
||||||
domainObject = self.domainObject;
|
*/
|
||||||
|
EditRepresenter.prototype.commit = function (message) {
|
||||||
|
var model = this.$scope.model,
|
||||||
|
configuration = this.$scope.configuration,
|
||||||
|
domainObject = this.domainObject;
|
||||||
|
|
||||||
// Log the commit message
|
this.$log.debug([
|
||||||
$log.debug([
|
|
||||||
"Committing ",
|
"Committing ",
|
||||||
domainObject && domainObject.getModel().name,
|
domainObject && domainObject.getModel().name,
|
||||||
"(" + (domainObject && domainObject.getId()) + "):",
|
"(" + (domainObject && domainObject.getId()) + "):",
|
||||||
message
|
message
|
||||||
].join(" "));
|
].join(" "));
|
||||||
|
|
||||||
// Update the configuration stored in the model, and persist.
|
if (this.domainObject) {
|
||||||
if (domainObject) {
|
if (this.key && configuration) {
|
||||||
// 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 = model.configuration || {};
|
||||||
model.configuration[self.key] = configuration;
|
model.configuration[this.key] = configuration;
|
||||||
}
|
}
|
||||||
doMutate(model);
|
domainObject.useCapability('mutation', function () {
|
||||||
}
|
return model;
|
||||||
}
|
|
||||||
|
|
||||||
// Place the "commit" method in the scope
|
|
||||||
scope.commit = commit;
|
|
||||||
|
|
||||||
// Clean up when the scope is destroyed
|
|
||||||
scope.$on("$destroy", function () {
|
|
||||||
self.destroy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Handle a specific representation of a specific domain object
|
// Handle a specific representation of a specific domain object
|
||||||
EditRepresenter.prototype.represent = function represent(representation, representedObject) {
|
EditRepresenter.prototype.represent = function (representation, representedObject) {
|
||||||
var scope = this.scope;
|
|
||||||
|
|
||||||
// Track the key, to know which view configuration to save to.
|
|
||||||
this.key = (representation || {}).key;
|
|
||||||
// Track the represented object
|
|
||||||
this.domainObject = representedObject;
|
this.domainObject = representedObject;
|
||||||
|
if (representation) {
|
||||||
// Ensure existing watches are released
|
this.key = representation.key;
|
||||||
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 {
|
} else {
|
||||||
delete scope.viewObjectTemplate;
|
delete this.key;
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (representedObject.hasCapability('editor') && representedObject.getCapability('editor').isEditContextRoot()) {
|
|
||||||
setEditing();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Respond to the destruction of the current representation.
|
// Respond to the destruction of the current representation.
|
||||||
EditRepresenter.prototype.destroy = function destroy() {
|
EditRepresenter.prototype.destroy = function () {};
|
||||||
return this.listenHandle && this.listenHandle();
|
|
||||||
};
|
|
||||||
|
|
||||||
return EditRepresenter;
|
return EditRepresenter;
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,10 @@ define(
|
|||||||
* @param $q
|
* @param $q
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function TransactionService($q, $log) {
|
function TransactionService($q, $log, cacheService) {
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
this.$log = $log;
|
this.$log = $log;
|
||||||
|
this.cacheService = cacheService;
|
||||||
this.transactions = [];
|
this.transactions = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,14 +88,25 @@ define(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* All persist calls deferred since the beginning of the transaction
|
* 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
|
* @returns {Promise} resolved when all persist operations have
|
||||||
* completed. Will reject if any commit operations fail
|
* completed. Will reject if any commit operations fail
|
||||||
*/
|
*/
|
||||||
TransactionService.prototype.commit = function () {
|
TransactionService.prototype.commit = function () {
|
||||||
var transaction = this.transactions.pop();
|
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
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
/*global describe,it,expect,beforeEach,jasmine,waitsFor,runs*/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
["../../src/actions/SaveAction"],
|
["../../src/actions/SaveAction"],
|
||||||
@ -28,7 +29,8 @@ define(
|
|||||||
var mockDomainObject,
|
var mockDomainObject,
|
||||||
mockEditorCapability,
|
mockEditorCapability,
|
||||||
actionContext,
|
actionContext,
|
||||||
dialogService,
|
mockDialogService,
|
||||||
|
mockNotificationService,
|
||||||
mockActionCapability,
|
mockActionCapability,
|
||||||
capabilities = {},
|
capabilities = {},
|
||||||
action;
|
action;
|
||||||
@ -68,11 +70,17 @@ define(
|
|||||||
actionContext = {
|
actionContext = {
|
||||||
domainObject: mockDomainObject
|
domainObject: mockDomainObject
|
||||||
};
|
};
|
||||||
dialogService = jasmine.createSpyObj(
|
|
||||||
|
mockDialogService = jasmine.createSpyObj(
|
||||||
"dialogService",
|
"dialogService",
|
||||||
["showBlockingMessage"]
|
["showBlockingMessage"]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
mockNotificationService = jasmine.createSpyObj(
|
||||||
|
"notificationService",
|
||||||
|
["info", "error"]
|
||||||
|
);
|
||||||
|
|
||||||
mockDomainObject.hasCapability.andReturn(true);
|
mockDomainObject.hasCapability.andReturn(true);
|
||||||
mockDomainObject.getCapability.andCallFake(function (capability) {
|
mockDomainObject.getCapability.andCallFake(function (capability) {
|
||||||
return capabilities[capability];
|
return capabilities[capability];
|
||||||
@ -81,7 +89,7 @@ define(
|
|||||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||||
mockEditorCapability.isEditContextRoot.andReturn(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 () {
|
it("only applies to domain object with an editor capability", function () {
|
||||||
@ -105,30 +113,54 @@ define(
|
|||||||
expect(mockEditorCapability.save).toHaveBeenCalled();
|
expect(mockEditorCapability.save).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("a blocking dialog", function () {
|
describe("in order to keep the user in the loop", function () {
|
||||||
var mockDialogHandle;
|
var mockDialogHandle;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]);
|
mockDialogHandle = jasmine.createSpyObj("dialogHandle", ["dismiss"]);
|
||||||
dialogService.showBlockingMessage.andReturn(mockDialogHandle);
|
mockDialogService.showBlockingMessage.andReturn(mockDialogHandle);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it("shows a dialog while saving", function () {
|
it("shows a dialog while saving", function () {
|
||||||
mockEditorCapability.save.andReturn(new Promise(function () {
|
mockEditorCapability.save.andReturn(new Promise(function () {
|
||||||
}));
|
}));
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
|
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
||||||
expect(mockDialogHandle.dismiss).not.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();
|
action.perform();
|
||||||
expect(dialogService.showBlockingMessage).toHaveBeenCalled();
|
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
||||||
expect(mockDialogHandle.dismiss).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
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
/*global describe,it,expect,beforeEach,jasmine*/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
["../../src/actions/SaveAndStopEditingAction"],
|
["../../src/actions/SaveAndStopEditingAction"],
|
||||||
@ -35,6 +36,7 @@ define(
|
|||||||
mockEditorCapability,
|
mockEditorCapability,
|
||||||
actionContext,
|
actionContext,
|
||||||
dialogService,
|
dialogService,
|
||||||
|
notificationService,
|
||||||
mockActionCapability,
|
mockActionCapability,
|
||||||
capabilities = {},
|
capabilities = {},
|
||||||
action;
|
action;
|
||||||
@ -79,6 +81,11 @@ define(
|
|||||||
["showBlockingMessage"]
|
["showBlockingMessage"]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
notificationService = jasmine.createSpyObj(
|
||||||
|
"notificationService",
|
||||||
|
["info", "error"]
|
||||||
|
);
|
||||||
|
|
||||||
mockDomainObject.hasCapability.andReturn(true);
|
mockDomainObject.hasCapability.andReturn(true);
|
||||||
mockDomainObject.getCapability.andCallFake(function (capability) {
|
mockDomainObject.getCapability.andCallFake(function (capability) {
|
||||||
return capabilities[capability];
|
return capabilities[capability];
|
||||||
@ -87,7 +94,7 @@ define(
|
|||||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||||
mockEditorCapability.isEditContextRoot.andReturn(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 () {
|
describe("The Save As action", function () {
|
||||||
var mockDomainObject,
|
var mockDomainObject,
|
||||||
|
mockClonedObject,
|
||||||
mockEditorCapability,
|
mockEditorCapability,
|
||||||
mockActionCapability,
|
mockActionCapability,
|
||||||
mockObjectService,
|
mockObjectService,
|
||||||
mockDialogService,
|
mockDialogService,
|
||||||
mockCopyService,
|
mockCopyService,
|
||||||
|
mockNotificationService,
|
||||||
mockParent,
|
mockParent,
|
||||||
actionContext,
|
actionContext,
|
||||||
capabilities = {},
|
capabilities = {},
|
||||||
@ -57,7 +59,8 @@ define(
|
|||||||
[
|
[
|
||||||
"getCapability",
|
"getCapability",
|
||||||
"hasCapability",
|
"hasCapability",
|
||||||
"getModel"
|
"getModel",
|
||||||
|
"getId"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
mockDomainObject.hasCapability.andReturn(true);
|
mockDomainObject.hasCapability.andReturn(true);
|
||||||
@ -65,6 +68,15 @@ define(
|
|||||||
return capabilities[capability];
|
return capabilities[capability];
|
||||||
});
|
});
|
||||||
mockDomainObject.getModel.andReturn({location: 'a', persisted: undefined});
|
mockDomainObject.getModel.andReturn({location: 'a', persisted: undefined});
|
||||||
|
mockDomainObject.getId.andReturn(0);
|
||||||
|
|
||||||
|
mockClonedObject = jasmine.createSpyObj(
|
||||||
|
"clonedObject",
|
||||||
|
[
|
||||||
|
"getId"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
mockClonedObject.getId.andReturn(1);
|
||||||
|
|
||||||
mockParent = jasmine.createSpyObj(
|
mockParent = jasmine.createSpyObj(
|
||||||
"parentObject",
|
"parentObject",
|
||||||
@ -111,12 +123,27 @@ define(
|
|||||||
"perform"
|
"perform"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
mockCopyService.perform.andReturn(mockPromise(mockClonedObject));
|
||||||
|
|
||||||
|
mockNotificationService = jasmine.createSpyObj(
|
||||||
|
"notificationService",
|
||||||
|
[
|
||||||
|
"info",
|
||||||
|
"error"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
actionContext = {
|
actionContext = {
|
||||||
domainObject: mockDomainObject
|
domainObject: mockDomainObject
|
||||||
};
|
};
|
||||||
|
|
||||||
action = new SaveAsAction(undefined, undefined, mockDialogService, mockCopyService, actionContext);
|
action = new SaveAsAction(
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
mockDialogService,
|
||||||
|
mockCopyService,
|
||||||
|
mockNotificationService,
|
||||||
|
actionContext);
|
||||||
|
|
||||||
spyOn(action, "getObjectService");
|
spyOn(action, "getObjectService");
|
||||||
action.getObjectService.andReturn(mockObjectService);
|
action.getObjectService.andReturn(mockObjectService);
|
||||||
@ -186,7 +213,7 @@ define(
|
|||||||
expect(mockDialogService.getUserInput).toHaveBeenCalled();
|
expect(mockDialogService.getUserInput).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("a blocking dialog", function () {
|
describe("in order to keep the user in the loop", function () {
|
||||||
var mockDialogHandle;
|
var mockDialogHandle;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
@ -194,14 +221,14 @@ define(
|
|||||||
mockDialogService.showBlockingMessage.andReturn(mockDialogHandle);
|
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 () {}));
|
mockEditorCapability.save.andReturn(new Promise(function () {}));
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
||||||
expect(mockDialogHandle.dismiss).not.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();
|
var mockCallback = jasmine.createSpy();
|
||||||
action.perform().then(mockCallback);
|
action.perform().then(mockCallback);
|
||||||
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
expect(mockDialogService.showBlockingMessage).toHaveBeenCalled();
|
||||||
@ -212,6 +239,31 @@ define(
|
|||||||
expect(mockDialogHandle.dismiss).toHaveBeenCalled();
|
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 () {
|
describe("The Edit Action controller", function () {
|
||||||
var mockSaveActionMetadata = {
|
var mockSaveActionMetadata = {
|
||||||
name: "mocked-save-action",
|
name: "mocked-save-action",
|
||||||
cssclass: "mocked-save-action-css"
|
cssClass: "mocked-save-action-css"
|
||||||
};
|
};
|
||||||
|
|
||||||
function fakeGetActions(actionContext) {
|
function fakeGetActions(actionContext) {
|
||||||
@ -86,7 +86,7 @@ define(
|
|||||||
expect(menuOptions[1].key).toEqual(mockScope.saveActions[1]);
|
expect(menuOptions[1].key).toEqual(mockScope.saveActions[1]);
|
||||||
menuOptions.forEach(function (option) {
|
menuOptions.forEach(function (option) {
|
||||||
expect(option.name).toEqual(mockSaveActionMetadata.name);
|
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"],
|
["../../src/controllers/EditObjectController"],
|
||||||
function (EditObjectController) {
|
function (EditObjectController) {
|
||||||
|
|
||||||
describe("The Edit mode controller", function () {
|
describe("The Edit Object controller", function () {
|
||||||
var mockScope,
|
var mockScope,
|
||||||
mockObject,
|
mockObject,
|
||||||
mockType,
|
testViews,
|
||||||
|
mockEditorCapability,
|
||||||
mockLocation,
|
mockLocation,
|
||||||
|
mockNavigationService,
|
||||||
|
removeCheck,
|
||||||
mockStatusCapability,
|
mockStatusCapability,
|
||||||
mockCapabilities,
|
mockCapabilities,
|
||||||
mockPolicyService,
|
|
||||||
controller;
|
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 () {
|
beforeEach(function () {
|
||||||
mockPolicyService = jasmine.createSpyObj(
|
|
||||||
"policyService",
|
|
||||||
[
|
|
||||||
"allow"
|
|
||||||
]
|
|
||||||
);
|
|
||||||
mockScope = jasmine.createSpyObj(
|
mockScope = jasmine.createSpyObj(
|
||||||
"$scope",
|
"$scope",
|
||||||
["$on", "$watch"]
|
["$on", "$watch"]
|
||||||
@ -58,16 +45,16 @@ define(
|
|||||||
"domainObject",
|
"domainObject",
|
||||||
["getId", "getModel", "getCapability", "hasCapability", "useCapability"]
|
["getId", "getModel", "getCapability", "hasCapability", "useCapability"]
|
||||||
);
|
);
|
||||||
mockType = jasmine.createSpyObj(
|
mockEditorCapability = jasmine.createSpyObj(
|
||||||
"type",
|
"mockEditorCapability",
|
||||||
["hasFeature"]
|
["isEditContextRoot", "dirty", "finish"]
|
||||||
);
|
);
|
||||||
mockStatusCapability = jasmine.createSpyObj('statusCapability',
|
mockStatusCapability = jasmine.createSpyObj('statusCapability',
|
||||||
["get"]
|
["get"]
|
||||||
);
|
);
|
||||||
|
|
||||||
mockCapabilities = {
|
mockCapabilities = {
|
||||||
"type" : mockType,
|
"editor" : mockEditorCapability,
|
||||||
"status": mockStatusCapability
|
"status": mockStatusCapability
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -75,41 +62,20 @@ define(
|
|||||||
["search"]
|
["search"]
|
||||||
);
|
);
|
||||||
mockLocation.search.andReturn({"view": "fixed"});
|
mockLocation.search.andReturn({"view": "fixed"});
|
||||||
|
mockNavigationService = jasmine.createSpyObj('navigationService',
|
||||||
|
["checkBeforeNavigation"]
|
||||||
|
);
|
||||||
|
|
||||||
|
removeCheck = jasmine.createSpy('removeCheck');
|
||||||
|
mockNavigationService.checkBeforeNavigation.andReturn(removeCheck);
|
||||||
|
|
||||||
mockObject.getId.andReturn("test");
|
mockObject.getId.andReturn("test");
|
||||||
mockObject.getModel.andReturn({ name: "Test object" });
|
mockObject.getModel.andReturn({ name: "Test object" });
|
||||||
mockObject.getCapability.andCallFake(function (key) {
|
mockObject.getCapability.andCallFake(function (key) {
|
||||||
return mockCapabilities[key];
|
return mockCapabilities[key];
|
||||||
});
|
});
|
||||||
mockType.hasFeature.andReturn(true);
|
|
||||||
|
|
||||||
mockScope.domainObject = mockObject;
|
testViews = [
|
||||||
|
|
||||||
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: 'abc' },
|
||||||
{ key: 'def', someKey: 'some value' },
|
{ key: 'def', someKey: 'some value' },
|
||||||
{ key: 'xyz' }
|
{ key: 'xyz' }
|
||||||
@ -120,7 +86,46 @@ define(
|
|||||||
});
|
});
|
||||||
mockLocation.search.andReturn({ view: 'def' });
|
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)
|
expect(mockScope.representation.selected)
|
||||||
.toEqual(testViews[1]);
|
.toEqual(testViews[1]);
|
||||||
});
|
});
|
@ -31,9 +31,7 @@ define(
|
|||||||
var mockTypeService,
|
var mockTypeService,
|
||||||
mockDialogService,
|
mockDialogService,
|
||||||
mockPolicyService,
|
mockPolicyService,
|
||||||
mockCreationPolicy,
|
mockTypeMap,
|
||||||
mockCompositionPolicy,
|
|
||||||
mockPolicyMap = {},
|
|
||||||
mockTypes,
|
mockTypes,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockQ,
|
mockQ,
|
||||||
@ -55,49 +53,33 @@ define(
|
|||||||
);
|
);
|
||||||
mockType.hasFeature.andReturn(true);
|
mockType.hasFeature.andReturn(true);
|
||||||
mockType.getName.andReturn(name);
|
mockType.getName.andReturn(name);
|
||||||
|
mockType.getKey.andReturn(name);
|
||||||
return mockType;
|
return mockType;
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockTypeService = jasmine.createSpyObj(
|
mockTypeService = jasmine.createSpyObj(
|
||||||
"typeService",
|
"typeService",
|
||||||
["listTypes"]
|
["getType"]
|
||||||
);
|
|
||||||
mockDialogService = jasmine.createSpyObj(
|
|
||||||
"dialogService",
|
|
||||||
["getUserInput"]
|
|
||||||
);
|
|
||||||
mockPolicyService = jasmine.createSpyObj(
|
|
||||||
"policyService",
|
|
||||||
["allow"]
|
|
||||||
);
|
);
|
||||||
|
mockDialogService = {};
|
||||||
|
mockPolicyService = {};
|
||||||
|
mockDomainObject = {};
|
||||||
|
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockTypes = [
|
||||||
"domainObject",
|
"timeline",
|
||||||
["getCapability"]
|
"activity",
|
||||||
);
|
"other"
|
||||||
|
].map(createMockType);
|
||||||
//Mocking getCapability because AddActionProvider uses the
|
mockTypeMap = {};
|
||||||
// type capability of the destination object.
|
|
||||||
mockDomainObject.getCapability.andReturn({});
|
|
||||||
|
|
||||||
mockTypes = ["A", "B", "C"].map(createMockType);
|
|
||||||
|
|
||||||
mockTypes.forEach(function (type) {
|
mockTypes.forEach(function (type) {
|
||||||
mockPolicyMap[type.getName()] = true;
|
mockTypeMap[type.getKey()] = type;
|
||||||
});
|
});
|
||||||
|
|
||||||
mockCreationPolicy = function (type) {
|
mockTypeService.getType.andCallFake(function (key) {
|
||||||
return mockPolicyMap[type.getName()];
|
return mockTypeMap[key];
|
||||||
};
|
});
|
||||||
|
|
||||||
mockCompositionPolicy = function () {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
mockPolicyService.allow.andReturn(true);
|
|
||||||
|
|
||||||
mockTypeService.listTypes.andReturn(mockTypes);
|
|
||||||
|
|
||||||
provider = new AddActionProvider(
|
provider = new AddActionProvider(
|
||||||
mockQ,
|
mockQ,
|
||||||
@ -107,29 +89,16 @@ define(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("checks for creatability", function () {
|
it("provides actions for timeline and activity", function () {
|
||||||
provider.getActions({
|
var actions = provider.getActions({
|
||||||
key: "add",
|
key: "add",
|
||||||
domainObject: mockDomainObject
|
domainObject: mockDomainObject
|
||||||
});
|
});
|
||||||
|
expect(actions.length).toBe(2);
|
||||||
|
expect(actions[0].metadata.type).toBe('timeline');
|
||||||
|
expect(actions[1].metadata.type).toBe('activity');
|
||||||
|
|
||||||
// Make sure it was creation which was used to check
|
// Make sure it was creation which was used to check
|
||||||
expect(mockPolicyService.allow)
|
|
||||||
.toHaveBeenCalledWith("creation", mockTypes[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("checks for composability of type", function () {
|
|
||||||
provider.getActions({
|
|
||||||
key: "add",
|
|
||||||
domainObject: mockDomainObject
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mockPolicyService.allow).toHaveBeenCalledWith(
|
|
||||||
"composition",
|
|
||||||
jasmine.any(Object),
|
|
||||||
jasmine.any(Object)
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(mockDomainObject.getCapability).toHaveBeenCalledWith('type');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ define(
|
|||||||
|
|
||||||
expect(metadata.name).toEqual("Test");
|
expect(metadata.name).toEqual("Test");
|
||||||
expect(metadata.description).toEqual("a test type");
|
expect(metadata.description).toEqual("a test type");
|
||||||
expect(metadata.cssclass).toEqual("icon-telemetry");
|
expect(metadata.cssClass).toEqual("icon-telemetry");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("the perform function", function () {
|
describe("the perform function", function () {
|
||||||
|
@ -175,7 +175,7 @@ define(
|
|||||||
expect(mockPolicyService.allow).toHaveBeenCalledWith(
|
expect(mockPolicyService.allow).toHaveBeenCalledWith(
|
||||||
'composition',
|
'composition',
|
||||||
mockOtherType,
|
mockOtherType,
|
||||||
mockType
|
mockDomainObject
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define([
|
||||||
["../../src/representers/EditRepresenter"],
|
'../../src/representers/EditRepresenter'
|
||||||
function (EditRepresenter) {
|
], function (
|
||||||
|
EditRepresenter
|
||||||
describe("The Edit mode representer", function () {
|
) {
|
||||||
var mockQ,
|
describe('EditRepresenter', function () {
|
||||||
mockLog,
|
var $log,
|
||||||
mockScope,
|
$scope,
|
||||||
testRepresentation,
|
|
||||||
mockDomainObject,
|
|
||||||
mockStatusCapability,
|
|
||||||
mockEditorCapability,
|
|
||||||
mockCapabilities,
|
|
||||||
representer;
|
representer;
|
||||||
|
|
||||||
function mockPromise(value) {
|
|
||||||
return {
|
|
||||||
then: function (callback) {
|
|
||||||
return mockPromise(callback(value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockQ = { when: mockPromise };
|
$log = jasmine.createSpyObj('$log', ['debug']);
|
||||||
mockLog = jasmine.createSpyObj("$log", ["info", "debug"]);
|
$scope = {};
|
||||||
mockScope = jasmine.createSpyObj("$scope", ["$watch", "$on"]);
|
representer = new EditRepresenter($log, $scope);
|
||||||
testRepresentation = { key: "test" };
|
});
|
||||||
mockDomainObject = jasmine.createSpyObj("domainObject", [
|
|
||||||
"getId",
|
it('injects a commit function in scope', function () {
|
||||||
"getModel",
|
expect($scope.commit).toEqual(jasmine.any(Function));
|
||||||
"getCapability",
|
});
|
||||||
"useCapability",
|
|
||||||
"hasCapability"
|
describe('representation', function () {
|
||||||
|
var domainObject,
|
||||||
|
representation;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
domainObject = jasmine.createSpyObj('domainObject', [
|
||||||
|
'getId',
|
||||||
|
'getModel',
|
||||||
|
'useCapability'
|
||||||
]);
|
]);
|
||||||
mockStatusCapability =
|
|
||||||
jasmine.createSpyObj("statusCapability", ["listen"]);
|
|
||||||
mockEditorCapability =
|
|
||||||
jasmine.createSpyObj("editorCapability", ["isEditContextRoot"]);
|
|
||||||
|
|
||||||
mockCapabilities = {
|
domainObject.getId.andReturn('anId');
|
||||||
'status': mockStatusCapability,
|
domainObject.getModel.andReturn({name: 'anObject'});
|
||||||
'editor': mockEditorCapability
|
|
||||||
|
representation = {
|
||||||
|
key: 'someRepresentation'
|
||||||
};
|
};
|
||||||
|
$scope.model = {name: 'anotherName'};
|
||||||
mockDomainObject.getModel.andReturn({});
|
$scope.configuration = {some: 'config'};
|
||||||
mockDomainObject.hasCapability.andReturn(true);
|
representer.represent(representation, domainObject);
|
||||||
mockDomainObject.useCapability.andReturn(true);
|
|
||||||
mockDomainObject.getCapability.andCallFake(function (capability) {
|
|
||||||
return mockCapabilities[capability];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
representer = new EditRepresenter(mockQ, mockLog, mockScope);
|
it('logs a message when commiting', function () {
|
||||||
representer.represent(testRepresentation, mockDomainObject);
|
$scope.commit('Test Message');
|
||||||
|
expect($log.debug)
|
||||||
|
.toHaveBeenCalledWith('Committing anObject (anId): Test Message');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("provides a commit method in scope", function () {
|
it('mutates the object when committing', function () {
|
||||||
expect(mockScope.commit).toEqual(jasmine.any(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("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("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];
|
})[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
|
* @param value
|
||||||
|
@ -58,5 +58,26 @@ define([
|
|||||||
expect(format.format(APRIL, scale)).toBe("April");
|
expect(format.format(APRIL, scale)).toBe("April");
|
||||||
expect(format.format(TWENTY_SIXTEEN, scale)).toBe("2016");
|
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");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -498,7 +498,7 @@ define([
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "legacy-object-inspector",
|
"key": "object-inspector",
|
||||||
"template": objectInspectorTemplate
|
"template": objectInspectorTemplate
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "openmct-symbols-16px",
|
"name": "openmct-symbols-16px",
|
||||||
"lastOpened": 1469724858940,
|
"lastOpened": 1481575258437,
|
||||||
"created": 1469724856623
|
"created": 1481575255265
|
||||||
},
|
},
|
||||||
"iconSets": [
|
"iconSets": [
|
||||||
{
|
{
|
||||||
@ -535,18 +535,50 @@
|
|||||||
{
|
{
|
||||||
"order": 21,
|
"order": 21,
|
||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"name": "icon-x-in-circle",
|
"name": "icon-resync",
|
||||||
"id": 16,
|
"id": 16,
|
||||||
"code": 921654,
|
"code": 921654,
|
||||||
"tempChar": ""
|
"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,
|
"order": 37,
|
||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"name": "icon-activity",
|
"name": "icon-activity",
|
||||||
"id": 32,
|
"id": 32,
|
||||||
"code": 921856,
|
"code": 921856,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 36,
|
"order": 36,
|
||||||
@ -554,7 +586,7 @@
|
|||||||
"name": "icon-activity-mode",
|
"name": "icon-activity-mode",
|
||||||
"id": 31,
|
"id": 31,
|
||||||
"code": 921857,
|
"code": 921857,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 52,
|
"order": 52,
|
||||||
@ -562,7 +594,7 @@
|
|||||||
"name": "icon-autoflow-tabular",
|
"name": "icon-autoflow-tabular",
|
||||||
"id": 47,
|
"id": 47,
|
||||||
"code": 921858,
|
"code": 921858,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 55,
|
"order": 55,
|
||||||
@ -570,7 +602,7 @@
|
|||||||
"name": "icon-clock",
|
"name": "icon-clock",
|
||||||
"id": 50,
|
"id": 50,
|
||||||
"code": 921859,
|
"code": 921859,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 58,
|
"order": 58,
|
||||||
@ -578,7 +610,7 @@
|
|||||||
"name": "icon-database",
|
"name": "icon-database",
|
||||||
"id": 53,
|
"id": 53,
|
||||||
"code": 921860,
|
"code": 921860,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 57,
|
"order": 57,
|
||||||
@ -586,7 +618,7 @@
|
|||||||
"name": "icon-database-query",
|
"name": "icon-database-query",
|
||||||
"id": 52,
|
"id": 52,
|
||||||
"code": 921861,
|
"code": 921861,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 17,
|
"order": 17,
|
||||||
@ -594,7 +626,7 @@
|
|||||||
"name": "icon-dataset",
|
"name": "icon-dataset",
|
||||||
"id": 12,
|
"id": 12,
|
||||||
"code": 921862,
|
"code": 921862,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 22,
|
"order": 22,
|
||||||
@ -602,7 +634,7 @@
|
|||||||
"name": "icon-datatable",
|
"name": "icon-datatable",
|
||||||
"id": 17,
|
"id": 17,
|
||||||
"code": 921863,
|
"code": 921863,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 59,
|
"order": 59,
|
||||||
@ -610,7 +642,7 @@
|
|||||||
"name": "icon-dictionary",
|
"name": "icon-dictionary",
|
||||||
"id": 54,
|
"id": 54,
|
||||||
"code": 921864,
|
"code": 921864,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 62,
|
"order": 62,
|
||||||
@ -618,7 +650,7 @@
|
|||||||
"name": "icon-folder",
|
"name": "icon-folder",
|
||||||
"id": 57,
|
"id": 57,
|
||||||
"code": 921865,
|
"code": 921865,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 66,
|
"order": 66,
|
||||||
@ -626,7 +658,7 @@
|
|||||||
"name": "icon-image",
|
"name": "icon-image",
|
||||||
"id": 61,
|
"id": 61,
|
||||||
"code": 921872,
|
"code": 921872,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 68,
|
"order": 68,
|
||||||
@ -634,7 +666,7 @@
|
|||||||
"name": "icon-layout",
|
"name": "icon-layout",
|
||||||
"id": 63,
|
"id": 63,
|
||||||
"code": 921873,
|
"code": 921873,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 77,
|
"order": 77,
|
||||||
@ -642,7 +674,7 @@
|
|||||||
"name": "icon-object",
|
"name": "icon-object",
|
||||||
"id": 72,
|
"id": 72,
|
||||||
"code": 921874,
|
"code": 921874,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 78,
|
"order": 78,
|
||||||
@ -650,7 +682,7 @@
|
|||||||
"name": "icon-object-unknown",
|
"name": "icon-object-unknown",
|
||||||
"id": 73,
|
"id": 73,
|
||||||
"code": 921875,
|
"code": 921875,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 79,
|
"order": 79,
|
||||||
@ -658,7 +690,7 @@
|
|||||||
"name": "icon-packet",
|
"name": "icon-packet",
|
||||||
"id": 74,
|
"id": 74,
|
||||||
"code": 921876,
|
"code": 921876,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 80,
|
"order": 80,
|
||||||
@ -666,7 +698,7 @@
|
|||||||
"name": "icon-page",
|
"name": "icon-page",
|
||||||
"id": 75,
|
"id": 75,
|
||||||
"code": 921877,
|
"code": 921877,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 114,
|
"order": 114,
|
||||||
@ -674,7 +706,7 @@
|
|||||||
"name": "icon-plot-overlay",
|
"name": "icon-plot-overlay",
|
||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"code": 921878,
|
"code": 921878,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 113,
|
"order": 113,
|
||||||
@ -682,7 +714,7 @@
|
|||||||
"name": "icon-plot-stacked",
|
"name": "icon-plot-stacked",
|
||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"code": 921879,
|
"code": 921879,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 10,
|
"order": 10,
|
||||||
@ -690,7 +722,7 @@
|
|||||||
"name": "icon-session",
|
"name": "icon-session",
|
||||||
"id": 5,
|
"id": 5,
|
||||||
"code": 921880,
|
"code": 921880,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 24,
|
"order": 24,
|
||||||
@ -698,7 +730,7 @@
|
|||||||
"name": "icon-tabular",
|
"name": "icon-tabular",
|
||||||
"id": 19,
|
"id": 19,
|
||||||
"code": 921881,
|
"code": 921881,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 7,
|
"order": 7,
|
||||||
@ -706,7 +738,7 @@
|
|||||||
"name": "icon-tabular-lad",
|
"name": "icon-tabular-lad",
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"code": 921888,
|
"code": 921888,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 6,
|
"order": 6,
|
||||||
@ -714,7 +746,7 @@
|
|||||||
"name": "icon-tabular-lad-set",
|
"name": "icon-tabular-lad-set",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"code": 921889,
|
"code": 921889,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 8,
|
"order": 8,
|
||||||
@ -722,7 +754,7 @@
|
|||||||
"name": "icon-tabular-realtime",
|
"name": "icon-tabular-realtime",
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"code": 921890,
|
"code": 921890,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 23,
|
"order": 23,
|
||||||
@ -730,7 +762,7 @@
|
|||||||
"name": "icon-tabular-scrolling",
|
"name": "icon-tabular-scrolling",
|
||||||
"id": 18,
|
"id": 18,
|
||||||
"code": 921891,
|
"code": 921891,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 112,
|
"order": 112,
|
||||||
@ -738,7 +770,7 @@
|
|||||||
"name": "icon-telemetry",
|
"name": "icon-telemetry",
|
||||||
"id": 86,
|
"id": 86,
|
||||||
"code": 921892,
|
"code": 921892,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 90,
|
"order": 90,
|
||||||
@ -746,7 +778,7 @@
|
|||||||
"name": "icon-telemetry-panel",
|
"name": "icon-telemetry-panel",
|
||||||
"id": 85,
|
"id": 85,
|
||||||
"code": 921893,
|
"code": 921893,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 93,
|
"order": 93,
|
||||||
@ -754,7 +786,7 @@
|
|||||||
"name": "icon-timeline",
|
"name": "icon-timeline",
|
||||||
"id": 88,
|
"id": 88,
|
||||||
"code": 921894,
|
"code": 921894,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 116,
|
"order": 116,
|
||||||
@ -762,7 +794,7 @@
|
|||||||
"name": "icon-timer-v1.5",
|
"name": "icon-timer-v1.5",
|
||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"code": 921895,
|
"code": 921895,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 11,
|
"order": 11,
|
||||||
@ -770,7 +802,7 @@
|
|||||||
"name": "icon-topic",
|
"name": "icon-topic",
|
||||||
"id": 6,
|
"id": 6,
|
||||||
"code": 921896,
|
"code": 921896,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 115,
|
"order": 115,
|
||||||
@ -778,13 +810,13 @@
|
|||||||
"name": "icon-box-with-dashed-lines",
|
"name": "icon-box-with-dashed-lines",
|
||||||
"id": 29,
|
"id": 29,
|
||||||
"code": 921897,
|
"code": 921897,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "openmct-symbols-16px",
|
"name": "openmct-symbols-16px",
|
||||||
"importSize": {
|
"importSize": {
|
||||||
"width": 448,
|
"width": 512,
|
||||||
"height": 512
|
"height": 512
|
||||||
},
|
},
|
||||||
"designer": "Charles Hacskaylo",
|
"designer": "Charles Hacskaylo",
|
||||||
@ -1973,7 +2005,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"paths": [
|
"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,
|
"grid": 16,
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -1981,9 +2013,19 @@
|
|||||||
],
|
],
|
||||||
"defaultCode": 114,
|
"defaultCode": 114,
|
||||||
"id": 27,
|
"id": 27,
|
||||||
"attrs": [],
|
"attrs": [
|
||||||
|
{
|
||||||
|
"fill": "rgb(0, 161, 75)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isMulticolor": false,
|
||||||
|
"isMulticolor2": false,
|
||||||
"colorPermutations": {
|
"colorPermutations": {
|
||||||
"1161751207457516161751": []
|
"1161751207457516161751": [
|
||||||
|
{
|
||||||
|
"f": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -2110,6 +2152,37 @@
|
|||||||
"1161751207457516161751": []
|
"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": [
|
"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"
|
"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": []
|
"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": [
|
"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"
|
"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-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-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-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-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-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-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-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-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-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" 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-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" />
|
<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 */
|
/*************** Tabular */
|
||||||
$tabularHeaderH: 22px;
|
$tabularHeaderH: 22px;
|
||||||
$tabularTdPadLR: $itemPadLR;
|
$tabularTdPadLR: $itemPadLR;
|
||||||
$tabularTdPadTB: 3px;
|
$tabularTdPadTB: 2px;
|
||||||
/*************** Imagery */
|
/*************** Imagery */
|
||||||
$imageMainControlBarH: 25px;
|
$imageMainControlBarH: 25px;
|
||||||
$imageThumbsD: 120px;
|
$imageThumbsD: 120px;
|
||||||
@ -99,7 +99,7 @@ $plotXBarH: 32px;
|
|||||||
$plotLegendH: 20px;
|
$plotLegendH: 20px;
|
||||||
$plotSwatchD: 8px;
|
$plotSwatchD: 8px;
|
||||||
// 1: Top, 2: right, 3: bottom, 4: left
|
// 1: Top, 2: right, 3: bottom, 4: left
|
||||||
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH + $interiorMargin, $plotYBarW);
|
$plotDisplayArea: ($plotLegendH + $interiorMargin, 0, $plotXBarH, $plotYBarW);
|
||||||
/* min plot height is based on user testing to find minimum useful height */
|
/* min plot height is based on user testing to find minimum useful height */
|
||||||
$plotMinH: 95px;
|
$plotMinH: 95px;
|
||||||
/*************** Bubbles */
|
/*************** Bubbles */
|
||||||
|
@ -141,6 +141,35 @@ a.disabled {
|
|||||||
cursor: default !important;
|
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 {
|
.align-right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
@ -162,6 +191,19 @@ a.disabled {
|
|||||||
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 {
|
.no-margin {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@ -175,6 +217,11 @@ a.disabled {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hide-nice {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.off {
|
.off {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
@ -40,7 +40,6 @@ $glyph-icon-brackets: '\e929';
|
|||||||
$glyph-icon-arrows-out: '\e1000';
|
$glyph-icon-arrows-out: '\e1000';
|
||||||
$glyph-icon-arrows-right-left: '\e1001';
|
$glyph-icon-arrows-right-left: '\e1001';
|
||||||
$glyph-icon-arrows-up-down: '\e1002';
|
$glyph-icon-arrows-up-down: '\e1002';
|
||||||
$glyph-icon-box-with-dashed-lines: '';
|
|
||||||
$glyph-icon-bullet: '\e1004';
|
$glyph-icon-bullet: '\e1004';
|
||||||
$glyph-icon-calendar: '\e1005';
|
$glyph-icon-calendar: '\e1005';
|
||||||
$glyph-icon-chain-links: '\e1006';
|
$glyph-icon-chain-links: '\e1006';
|
||||||
@ -73,7 +72,11 @@ $glyph-icon-T: '\e1032';
|
|||||||
$glyph-icon-thumbs-strip: '\e1033';
|
$glyph-icon-thumbs-strip: '\e1033';
|
||||||
$glyph-icon-two-parts-both: '\e1034';
|
$glyph-icon-two-parts-both: '\e1034';
|
||||||
$glyph-icon-two-parts-one-only: '\e1035';
|
$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: '\e1100';
|
||||||
$glyph-icon-activity-mode: '\e1101';
|
$glyph-icon-activity-mode: '\e1101';
|
||||||
$glyph-icon-autoflow-tabular: '\e1102';
|
$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-both { @include glyph($glyph-icon-two-parts-both); }
|
||||||
.icon-two-parts-one-only { @include glyph($glyph-icon-two-parts-one-only); }
|
.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-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 { @include glyph($glyph-icon-activity); }
|
||||||
.icon-activity-mode { @include glyph($glyph-icon-activity-mode); }
|
.icon-activity-mode { @include glyph($glyph-icon-activity-mode); }
|
||||||
.icon-autoflow-tabular { @include glyph($glyph-icon-autoflow-tabular); }
|
.icon-autoflow-tabular { @include glyph($glyph-icon-autoflow-tabular); }
|
||||||
|
@ -20,9 +20,9 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
@import "effects";
|
@import "effects";
|
||||||
@import "global";
|
|
||||||
@import "glyphs";
|
@import "glyphs";
|
||||||
@import "animations";
|
@import "animations";
|
||||||
|
@import "global";
|
||||||
@import "archetypes";
|
@import "archetypes";
|
||||||
@import "about";
|
@import "about";
|
||||||
@import "text";
|
@import "text";
|
||||||
|
@ -363,14 +363,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin webkitProp($name, $val) {
|
@mixin cursorGrab() {
|
||||||
#{$name}: #{$val};
|
cursor: grab;
|
||||||
-webkit-#{$name}: #{$val};
|
cursor: -webkit-grab;
|
||||||
}
|
&:active {
|
||||||
|
cursor: grabbing;
|
||||||
@mixin webkitVal($name, $val) {
|
cursor: -webkit-grabbing;
|
||||||
#{$name}: #{$val};
|
}
|
||||||
#{$name}: -webkit-#{$val};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin verticalCenter {
|
@mixin verticalCenter {
|
||||||
@ -392,6 +391,14 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin reverseEllipsis() {
|
||||||
|
direction: rtl;
|
||||||
|
unicode-bidi:bidi-override;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
@mixin scrollH($showBar: auto) {
|
@mixin scrollH($showBar: auto) {
|
||||||
overflow-x: $showBar;
|
overflow-x: $showBar;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
@ -122,13 +122,21 @@
|
|||||||
// Default position is upper right
|
// Default position is upper right
|
||||||
$p: $interiorMargin;
|
$p: $interiorMargin;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: $p;
|
top: $p; right: $p; bottom: auto;
|
||||||
right: $p;
|
text-align: right;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-local-controls {
|
.s-local-controls {
|
||||||
|
@include trans-prop-nice(opacity);
|
||||||
font-size: 0.7rem;
|
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 */
|
/******************************************************** CUSTOM CHECKBOXES */
|
||||||
@ -459,7 +467,7 @@ input[type="search"] {
|
|||||||
width: $h;
|
width: $h;
|
||||||
height: $h;
|
height: $h;
|
||||||
margin-top: 1 + floor($h/2) * -1;
|
margin-top: 1 + floor($h/2) * -1;
|
||||||
@include btnSubtle(pullForward($colorBtnBg, 10%));
|
@include btnSubtle(pullForward($colorBtnBg, 20%));
|
||||||
border-radius: 50% !important;
|
border-radius: 50% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,3 +661,4 @@ body.desktop {
|
|||||||
background: $scrollbarTrackColorBg;
|
background: $scrollbarTrackColorBg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,6 +273,7 @@
|
|||||||
|
|
||||||
.btn-bar.right .menu,
|
.btn-bar.right .menu,
|
||||||
.menus-to-left .menu {
|
.menus-to-left .menu {
|
||||||
|
z-index: 79;
|
||||||
left: auto;
|
left: auto;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
.l-image-main-wrapper,
|
.l-image-main-wrapper,
|
||||||
.l-image-thumbs-wrapper {
|
.l-image-thumbs-wrapper,
|
||||||
|
.image-main {
|
||||||
@include absPosDefault(0, false);
|
@include absPosDefault(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +33,7 @@
|
|||||||
|
|
||||||
/*************************************** MAIN IMAGE */
|
/*************************************** MAIN IMAGE */
|
||||||
|
|
||||||
.l-image-main,
|
.image-main,
|
||||||
.l-image-thumb-item .l-thumb {
|
.l-image-thumb-item .l-thumb {
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
background-position: center;
|
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 */
|
/*************************************** WHEN IN FRAME */
|
||||||
.frame .t-imagery {
|
.frame .t-imagery {
|
||||||
.l-image-main-wrapper {
|
.l-image-main-wrapper {
|
||||||
|
@ -51,13 +51,9 @@ table {
|
|||||||
|
|
||||||
tbody, .tbody {
|
tbody, .tbody {
|
||||||
display: table-row-group;
|
display: table-row-group;
|
||||||
tr, .tr {
|
|
||||||
&:hover {
|
|
||||||
background: rgba($colorTabBodyFg, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tr, .tr {
|
tr, .tr {
|
||||||
|
border-top: 1px solid $colorTabBorder;
|
||||||
display: table-row;
|
display: table-row;
|
||||||
&:first-child .td {
|
&:first-child .td {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
@ -71,11 +67,12 @@ table {
|
|||||||
}
|
}
|
||||||
th, .th, td, .td {
|
th, .th, td, .td {
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
|
font-size: 0.7rem;
|
||||||
}
|
}
|
||||||
th, .th {
|
th, .th {
|
||||||
border-left: 1px solid $colorTabHeaderBorder;
|
border-left: 1px solid $colorTabHeaderBorder;
|
||||||
color: $colorTabHeaderFg;
|
color: $colorTabHeaderFg;
|
||||||
padding: $tabularTdPadLR $tabularTdPadLR;
|
padding: $tabularTdPadTB $tabularTdPadLR;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
vertical-align: middle; // This is crucial to hiding f**king 4px height injected by browser by default
|
vertical-align: middle; // This is crucial to hiding f**king 4px height injected by browser by default
|
||||||
&:first-child {
|
&:first-child {
|
||||||
@ -99,7 +96,6 @@ table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
td, .td {
|
td, .td {
|
||||||
border-bottom: 1px solid $colorTabBorder;
|
|
||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
color: $colorTelemFresh;
|
color: $colorTelemFresh;
|
||||||
padding: $tabularTdPadTB $tabularTdPadLR;
|
padding: $tabularTdPadTB $tabularTdPadLR;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user