Compare commits
228 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18e51aaff7 | ||
|
|
dfa4591834 | ||
|
|
4d3ec398c9 | ||
|
|
b169089156 | ||
|
|
5b6f95bd4a | ||
|
|
66a6b6d89b | ||
|
|
d74eba1922 | ||
|
|
9a7f69a614 | ||
|
|
0578a651da | ||
|
|
f991dcfb76 | ||
|
|
42c48cb93b | ||
|
|
67b763c4c0 | ||
|
|
2708562872 | ||
|
|
9578fb0cd8 | ||
|
|
a728f2368c | ||
|
|
547696d797 | ||
|
|
025b69541e | ||
|
|
5aa95c0415 | ||
|
|
d63c401e44 | ||
|
|
c9ac85089a | ||
|
|
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 | ||
|
|
08ca7659e7 | ||
|
|
d7edfb4cc6 | ||
|
|
4eca80a770 | ||
|
|
56a662841e | ||
|
|
8878ea4cf7 | ||
|
|
eb32a798b8 | ||
|
|
0759ba6722 | ||
|
|
bfdf7b822f | ||
|
|
7dde924fcc | ||
|
|
b8cb41b1da | ||
|
|
971b92acbb | ||
|
|
d643efa9bb | ||
|
|
08c0aeb2d5 | ||
|
|
b0940eb33e | ||
|
|
6ec858b237 | ||
|
|
891412bdb9 | ||
|
|
1947802a35 | ||
|
|
3b06e32b40 | ||
|
|
a2711c2c08 | ||
|
|
3e6c9fa318 | ||
|
|
2e49c5932a | ||
|
|
abf750f894 | ||
|
|
137434af1b | ||
|
|
ac4d21b252 | ||
|
|
db362a0efc | ||
|
|
887631500b | ||
|
|
65043d0ff3 | ||
|
|
1bf7c06b1e | ||
|
|
7b218c7f02 | ||
|
|
3572877a2e | ||
|
|
f2d44114fa | ||
|
|
d1d2067ad5 | ||
|
|
9456370077 | ||
|
|
0833674b91 | ||
|
|
947b54555a | ||
|
|
1a88c9a651 | ||
|
|
567bb6fa2a | ||
|
|
00d0b71080 | ||
|
|
580e10b024 | ||
|
|
fbe9621387 | ||
|
|
13b5e7c00e | ||
|
|
09d59f00e7 | ||
|
|
772d24b173 | ||
|
|
cbd001e280 | ||
|
|
6d2c5f7fd4 | ||
|
|
1f6ca8bcc3 | ||
|
|
bff4e120a7 | ||
|
|
8a0018177a | ||
|
|
c9c8998fa2 | ||
|
|
e19edbb27a | ||
|
|
f2fe6a9885 | ||
|
|
0442a31153 | ||
|
|
e03c725b86 | ||
|
|
b0e842863d | ||
|
|
5b0fa90e39 | ||
|
|
ae6f6565fa | ||
|
|
c874ae7afd | ||
|
|
c27c0c5b8c | ||
|
|
ed102492e5 | ||
|
|
e077f9ee18 | ||
|
|
e9f4db7719 | ||
|
|
34e07b938d | ||
|
|
230230aa94 | ||
|
|
901846e6c1 | ||
|
|
a93f41f1c3 | ||
|
|
7439d949c4 | ||
|
|
77d4760945 | ||
|
|
e1276e464d | ||
|
|
7d950d067c | ||
|
|
a34e89d56a | ||
|
|
4ec0d0633d | ||
|
|
4b51e604a7 | ||
|
|
e3e7bd27e9 | ||
|
|
f6ed0e8ab6 | ||
|
|
c179d9e759 | ||
|
|
98f387b605 | ||
|
|
275dda820b | ||
|
|
43c32c2c7b | ||
|
|
3f5388c2e8 | ||
|
|
7a94efccbf | ||
|
|
3c72eea9ef | ||
|
|
3548cde9c4 | ||
|
|
3d17435438 | ||
|
|
f185254114 | ||
|
|
943e2ebe14 | ||
|
|
0c27a5f361 | ||
|
|
abfabc85b5 | ||
|
|
e08704e6d3 | ||
|
|
f60fc2ebad | ||
|
|
b50278e92f | ||
|
|
d913798d5b | ||
|
|
56267095cb | ||
|
|
f9ce27def3 | ||
|
|
127a7a62c1 | ||
|
|
4429e847e8 | ||
|
|
56e321f6d9 | ||
|
|
e135ed26f0 | ||
|
|
48b25fe842 | ||
|
|
85e57286ae | ||
|
|
5ef6617062 | ||
|
|
ae89dcd62d | ||
|
|
2a2e9ef99d | ||
|
|
1446b16e77 | ||
|
|
f03003b366 | ||
|
|
d3db26499c | ||
|
|
d1f67fd8b9 | ||
|
|
31ee92b711 | ||
|
|
1ea7fa3084 | ||
|
|
d8dc3c8445 | ||
|
|
ea1855fc26 | ||
|
|
0be84a4e51 | ||
|
|
d87ed1414e | ||
|
|
4382745012 | ||
|
|
79b16ddda6 | ||
|
|
6c9bd0b510 | ||
|
|
2740b6f957 | ||
|
|
ea35395d7e | ||
|
|
24cb72e5b5 | ||
|
|
3dee082141 | ||
|
|
7d52d348b2 | ||
|
|
ded52b8d19 | ||
|
|
ab4ce0caba | ||
|
|
90c13a3959 | ||
|
|
9847c40e34 | ||
|
|
385d90b056 | ||
|
|
ecf526aa03 | ||
|
|
a35b158fd1 | ||
|
|
2ebf05ae5b | ||
|
|
850da9de52 | ||
|
|
353c24c70e | ||
|
|
73d10ab093 | ||
|
|
668b7b6a39 | ||
|
|
70c072be0b | ||
|
|
eb2fbcd8d0 | ||
|
|
02f34d1c04 | ||
|
|
7265b241a2 | ||
|
|
771542ee5f | ||
|
|
b60eff2f5e | ||
|
|
f6d6cb929f | ||
|
|
6f2c80bc2e | ||
|
|
0fe0b21eda | ||
|
|
0bedc227f4 | ||
|
|
6c4419fb72 | ||
|
|
766e94ed62 | ||
|
|
fb8fe93529 | ||
|
|
26839c7fd1 | ||
|
|
264896c264 | ||
|
|
e3208187bf | ||
|
|
bcf85db9c4 | ||
|
|
f1e81c359e | ||
|
|
8279acc9a5 | ||
|
|
63438ad9ba | ||
|
|
467f4d2257 | ||
|
|
a5aaa6f103 | ||
|
|
9b273bc148 | ||
|
|
0aba9122a1 | ||
|
|
ac3706dfb6 | ||
|
|
3905171457 | ||
|
|
1e03f22086 | ||
|
|
360fdeaec8 | ||
|
|
d5914dfde9 | ||
|
|
d3f2a24267 | ||
|
|
f85ac79b4a | ||
|
|
bea9f22a0c | ||
|
|
3f330dccf1 | ||
|
|
f7f934f0e8 | ||
|
|
0e683acde1 | ||
|
|
dd6414daf0 | ||
|
|
8756b7213f | ||
|
|
b0cbb5a2b4 | ||
|
|
6418b0c634 | ||
|
|
433d26e703 | ||
|
|
c99ffcb1f4 | ||
|
|
ef815cf060 | ||
|
|
7fb7a5452f | ||
|
|
4b641f8723 | ||
|
|
0d312def27 |
279
API.md
Normal file
279
API.md
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
# Open MCT API
|
||||||
|
|
||||||
|
The Open MCT framework public api can be utilized by building the application
|
||||||
|
(`gulp install`) and then copying the file from `dist/main.js` to your
|
||||||
|
directory of choice.
|
||||||
|
|
||||||
|
Open MCT supports AMD, CommonJS, and loading via a script tag; it's easy to use
|
||||||
|
in your project. The [`openmct`]{@link module:openmct} module is exported
|
||||||
|
via AMD and CommonJS, and is also exposed as `openmct` in the global scope
|
||||||
|
if loaded via a script tag.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Open MCT's goal is to allow you to browse, create, edit, and visualize all of
|
||||||
|
the domain knowledge you need on a daily basis.
|
||||||
|
|
||||||
|
To do this, the main building block provided by Open MCT is the _domain object_.
|
||||||
|
The temperature sensor on the starboard solar panel,
|
||||||
|
an overlay plot comparing the results of all temperature sensor,
|
||||||
|
the command dictionary for a spacecraft,
|
||||||
|
the individual commands in that dictionary, your "my documents" folder:
|
||||||
|
All of these things are domain objects.
|
||||||
|
|
||||||
|
Domain objects have Types, so a specific instrument temperature sensor is a
|
||||||
|
"Telemetry Point," and turning on a drill for a certain duration of time is
|
||||||
|
an "Activity". Types allow you to form an ontology of knowledge and provide
|
||||||
|
an abstraction for grouping, visualizing, and interpreting data.
|
||||||
|
|
||||||
|
And then we have Views. Views allow you to visualize domain objects. Views can
|
||||||
|
apply to specific domain objects; they may also apply to certain types of
|
||||||
|
domain objects, or they may apply to everything. Views are simply a method
|
||||||
|
of visualizing domain objects.
|
||||||
|
|
||||||
|
Regions allow you to specify what views are displayed for specific types of
|
||||||
|
domain objects in response to different user actions. For instance, you may
|
||||||
|
want to display a different view while editing, or you may want to update the
|
||||||
|
toolbar display when objects are selected. Regions allow you to map views to
|
||||||
|
specific user actions.
|
||||||
|
|
||||||
|
Domain objects can be mutated and persisted, developers can create custom
|
||||||
|
actions and apply them to domain objects, and many more things can be done.
|
||||||
|
For more information, read on!
|
||||||
|
|
||||||
|
## Running Open MCT
|
||||||
|
|
||||||
|
Once the [`openmct`](@link module:openmct) module has been loaded, you can
|
||||||
|
simply invoke [`start`]{@link module:openmct.MCT#start} to run Open MCT:
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.start();
|
||||||
|
```
|
||||||
|
|
||||||
|
Generally, however, you will want to configure Open MCT by adding plugins
|
||||||
|
before starting it. It is important to install plugins and configure Open MCT
|
||||||
|
_before_ calling [`start`]{@link module:openmct.MCT#start}; Open MCT is not
|
||||||
|
designed to be reconfigured once started.
|
||||||
|
|
||||||
|
## Configuring Open MCT
|
||||||
|
|
||||||
|
The [`openmct`]{@link module:openmct} module (more specifically, the
|
||||||
|
[`MCT`]{@link module:openmct.MCT} class, of which `openmct` is an instance)
|
||||||
|
exposes a variety of methods to allow the application to be configured,
|
||||||
|
extended, and customized before running.
|
||||||
|
|
||||||
|
Short examples follow; see the linked documentation for further details.
|
||||||
|
|
||||||
|
### Adding Domain Object Types
|
||||||
|
|
||||||
|
Custom types may be registered via
|
||||||
|
[`openmct.types`]{@link module:openmct.MCT#types}:
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.types.addType('my-type', {
|
||||||
|
label: "My Type",
|
||||||
|
description: "This is a type that I added!",
|
||||||
|
creatable: true
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding Views
|
||||||
|
|
||||||
|
Custom views may be registered based on the region in the application
|
||||||
|
where they should appear:
|
||||||
|
|
||||||
|
* [`openmct.mainViews`]{@link module:openmct.MCT#mainViews} is a registry
|
||||||
|
of views of domain objects which should appear in the main viewing area.
|
||||||
|
* [`openmct.inspectors`]{@link module:openmct.MCT#inspectors} is a registry
|
||||||
|
of views of domain objects and/or active selections, which should appear in
|
||||||
|
the Inspector.
|
||||||
|
* [`openmct.toolbars`]{@link module:openmct.MCT#toolbars} is a registry
|
||||||
|
of views of domain objects and/or active selections, which should appear in
|
||||||
|
the toolbar area while editing.
|
||||||
|
* [`openmct.indicators`]{@link module:openmct.MCT#inspectors} is a registry
|
||||||
|
of views which should appear in the status area of the application.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.mainViews.addProvider({
|
||||||
|
canView: function (domainObject) {
|
||||||
|
return domainObject.type === 'my-type';
|
||||||
|
},
|
||||||
|
view: function (domainObject) {
|
||||||
|
return new MyView(domainObject);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding a Root-level Object
|
||||||
|
|
||||||
|
In many cases, you'd like a certain object (or a certain hierarchy of
|
||||||
|
objects) to be accessible from the top level of the application (the
|
||||||
|
tree on the left-hand side of Open MCT.) It is typical to expose a telemetry
|
||||||
|
dictionary as a hierarchy of telemetry-providing domain objects in this
|
||||||
|
fashion.
|
||||||
|
|
||||||
|
To do so, use the [`addRoot`]{@link module:openmct.ObjectAPI#addRoot} method
|
||||||
|
of the [object API]{@link module:openmct.ObjectAPI}:
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.objects.addRoot({ key: "my-key", namespace: "my-namespace" });
|
||||||
|
```
|
||||||
|
|
||||||
|
Root objects are loaded just like any other objects, i.e. via an object
|
||||||
|
provider.
|
||||||
|
|
||||||
|
### Adding Composition Providers
|
||||||
|
|
||||||
|
The "composition" of a domain object is the list of objects it contains,
|
||||||
|
as shown (for example) in the tree for browsing. Open MCT provides a
|
||||||
|
default solution for composition, but there may be cases where you want
|
||||||
|
to provide the composition of a certain object (or type of object) dynamically.
|
||||||
|
For instance, you may want to populate a hierarchy under a custom root-level
|
||||||
|
object based on the contents of a telemetry dictionary.
|
||||||
|
To do this, you can add a new CompositionProvider:
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.composition.addProvider({
|
||||||
|
appliesTo: function (domainObject) {
|
||||||
|
return domainObject.type === 'my-type';
|
||||||
|
},
|
||||||
|
load: function (domainObject) {
|
||||||
|
return Promise.resolve(myDomainObjects);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding Telemetry Providers
|
||||||
|
|
||||||
|
When connecting to a new telemetry source, you will want to register a new
|
||||||
|
[telemetry provider]{@link module:openmct.TelemetryAPI~TelemetryProvider}
|
||||||
|
with the [telemetry API]{@link module:openmct.TelemetryAPI#addProvider}:
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.telemetry.addProvider({
|
||||||
|
canProvideTelemetry: function (domainObject) {
|
||||||
|
return domainObject.type === 'my-type';
|
||||||
|
},
|
||||||
|
properties: function (domainObject) {
|
||||||
|
return [
|
||||||
|
{ key: 'value', name: "Temperature", units: "degC" },
|
||||||
|
{ key: 'time', name: "UTC" }
|
||||||
|
];
|
||||||
|
},
|
||||||
|
request: function (domainObject, options) {
|
||||||
|
var telemetryId = domainObject.myTelemetryId;
|
||||||
|
return myAdapter.request(telemetryId, options.start, options.end);
|
||||||
|
},
|
||||||
|
subscribe: function (domainObject, callback) {
|
||||||
|
var telemetryId = domainObject.myTelemetryId;
|
||||||
|
myAdapter.subscribe(telemetryId, callback);
|
||||||
|
return myAdapter.unsubscribe.bind(myAdapter, telemetryId, callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The implementations for `request` and `subscribe` can vary depending on the
|
||||||
|
nature of the endpoint which will provide telemetry. In the example above,
|
||||||
|
it is assumed that `myAdapter` contains the specific implementations
|
||||||
|
(HTTP requests, WebSocket connections, etc.) associated with some telemetry
|
||||||
|
source.
|
||||||
|
|
||||||
|
## Using Open MCT
|
||||||
|
|
||||||
|
When implementing new features, it is useful and sometimes necessary to
|
||||||
|
utilize functionality exposed by Open MCT.
|
||||||
|
|
||||||
|
### Retrieving Composition
|
||||||
|
|
||||||
|
To limit which objects are loaded at any given time, the composition of
|
||||||
|
a domain object must be requested asynchronously:
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.composition(myObject).load().then(function (childObjects) {
|
||||||
|
childObjects.forEach(doSomething);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Support Common Gestures
|
||||||
|
|
||||||
|
Custom views may also want to support common gestures using the
|
||||||
|
[gesture API]{@link module:openmct.GestureAPI}. For instance, to make
|
||||||
|
a view (or part of a view) selectable:
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.gestures.selectable(myHtmlElement, myDomainObject);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Working with Domain Objects
|
||||||
|
|
||||||
|
The [object API]{@link module:openmct.ObjectAPI} provides useful methods
|
||||||
|
for working with domain objects.
|
||||||
|
|
||||||
|
To make changes to a domain object, use the
|
||||||
|
[`mutate`]{@link module:openmct.ObjectAPI#mutate} method:
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.objects.mutate(myDomainObject, "name", "New name!");
|
||||||
|
```
|
||||||
|
|
||||||
|
Making modifications in this fashion allows other usages of the domain
|
||||||
|
object to remain up to date using the
|
||||||
|
[`observe`]{@link module:openmct.ObjectAPI#observe} method:
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.objects.observe(myDomainObject, "name", function (newName) {
|
||||||
|
myLabel.textContent = newName;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Telemetry
|
||||||
|
|
||||||
|
Very often in Open MCT, you wish to work with telemetry data (for instance,
|
||||||
|
to display it in a custom visualization.)
|
||||||
|
|
||||||
|
|
||||||
|
### Synchronizing with the Time Conductor
|
||||||
|
|
||||||
|
Views which wish to remain synchronized with the state of Open MCT's
|
||||||
|
time conductor should utilize
|
||||||
|
[`openmct.conductor`]{@link module:openmct.TimeConductor}:
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.conductor.on('bounds', function (newBounds) {
|
||||||
|
requestTelemetry(newBounds.start, newBounds.end).then(displayTelemetry);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
|
||||||
|
While you can register new features with Open MCT directly, it is generally
|
||||||
|
more useful to package these as a plugin. A plugin is a function that takes
|
||||||
|
[`openmct`]{@link module:openmct} as an argument, and performs configuration
|
||||||
|
upon `openmct` when invoked.
|
||||||
|
|
||||||
|
### Installing Plugins
|
||||||
|
|
||||||
|
To install plugins, use the [`install`]{@link module:openmct.MCT#install}
|
||||||
|
method:
|
||||||
|
|
||||||
|
```
|
||||||
|
openmct.install(myPlugin);
|
||||||
|
```
|
||||||
|
|
||||||
|
The plugin will be invoked to configure Open MCT before it is started.
|
||||||
|
|
||||||
|
### Writing Plugins
|
||||||
|
|
||||||
|
Plugins configure Open MCT, and should utilize the
|
||||||
|
[`openmct`]{@link module:openmct} module to do so, as summarized above in
|
||||||
|
"Configuring Open MCT" and "Using Open MCT" above.
|
||||||
|
|
||||||
|
### Distributing Plugins
|
||||||
|
|
||||||
|
Hosting or downloading plugins is outside of the scope of this documentation.
|
||||||
|
We recommend distributing plugins as UMD modules which export a single
|
||||||
|
function.
|
||||||
|
|
||||||
129
LICENSES.md
129
LICENSES.md
@@ -560,3 +560,132 @@ The above copyright notice and this permission notice shall be included in all c
|
|||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Almond
|
||||||
|
|
||||||
|
* Link: https://github.com/requirejs/almond
|
||||||
|
|
||||||
|
* Version: 0.3.3
|
||||||
|
|
||||||
|
* Author: jQuery Foundation
|
||||||
|
|
||||||
|
* Description: Lightweight RequireJS replacement for builds
|
||||||
|
|
||||||
|
#### License
|
||||||
|
|
||||||
|
Copyright jQuery Foundation and other contributors, https://jquery.org/
|
||||||
|
|
||||||
|
This software consists of voluntary contributions made by many
|
||||||
|
individuals. For exact contribution history, see the revision history
|
||||||
|
available at https://github.com/requirejs/almond
|
||||||
|
|
||||||
|
The following license applies to all parts of this software except as
|
||||||
|
documented below:
|
||||||
|
|
||||||
|
====
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
====
|
||||||
|
|
||||||
|
Copyright and related rights for sample code are waived via CC0. Sample
|
||||||
|
code is defined as all source code displayed within the prose of the
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
CC0: http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
|
||||||
|
====
|
||||||
|
|
||||||
|
Files located in the node_modules directory, and certain utilities used
|
||||||
|
to build or test the software in the test and dist directories, are
|
||||||
|
externally maintained libraries used by this software which have their own
|
||||||
|
licenses; we recommend you read them, as their terms may differ from the
|
||||||
|
terms above.
|
||||||
|
|
||||||
|
|
||||||
|
### Lodash
|
||||||
|
|
||||||
|
* Link: https://lodash.com
|
||||||
|
|
||||||
|
* Version: 3.10.1
|
||||||
|
|
||||||
|
* Author: Dojo Foundation
|
||||||
|
|
||||||
|
* Description: Utility functions
|
||||||
|
|
||||||
|
#### License
|
||||||
|
|
||||||
|
Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>
|
||||||
|
Based on Underscore.js, copyright 2009-2015 Jeremy Ashkenas,
|
||||||
|
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
### EventEmitter3
|
||||||
|
|
||||||
|
* Link: https://github.com/primus/eventemitter3
|
||||||
|
|
||||||
|
* Version: 1.2.0
|
||||||
|
|
||||||
|
* Author: Arnout Kazemier
|
||||||
|
|
||||||
|
* Description: Event-driven programming support
|
||||||
|
|
||||||
|
#### License
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Arnout Kazemier
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -10,9 +10,24 @@ Try Open MCT now with our [live demo](https://openmct-demo.herokuapp.com/).
|
|||||||

|

|
||||||
|
|
||||||
## New API
|
## New API
|
||||||
A new API is currently under development that will deprecate a lot of the documentation currently in the docs directory, however Open MCT will remain compatible with the currently documented API. An updated set of tutorials is being developed with the new API, and progress on this task can be followed in the [associated pull request](https://github.com/nasa/openmct/pull/999). Any code in this branch should be considered experimental, and we welcome any feedback.
|
|
||||||
|
|
||||||
Differences between the two APIs include a move away from a declarative system of JSON configuration files towards an imperative system based on function calls. Developers will be able to extend and build on Open MCT by making direct function calls to a public API. Open MCT is also being refactored to minimize the dependencies that using Open MCT imposes on developers, such as the current requirement to use Angular JS.
|
A simpler, [easier-to-use API](https://nasa.github.io/openmct/docs/api/)
|
||||||
|
has been added to Open MCT. Changes in this
|
||||||
|
API include a move away from a declarative system of JSON configuration files
|
||||||
|
towards an imperative system based on function calls. Developers will be able
|
||||||
|
to extend and build on Open MCT by making direct function calls to a public
|
||||||
|
API. Open MCT is also being refactored to minimize the dependencies that using
|
||||||
|
Open MCT imposes on developers, such as the current requirement to use
|
||||||
|
AngularJS.
|
||||||
|
|
||||||
|
This new API has not yet been heavily used and is likely to contain defects.
|
||||||
|
You can help by trying it out, and reporting any issues you encounter
|
||||||
|
using our GitHub issue tracker. Such issues may include bugs, suggestions,
|
||||||
|
missing documentation, or even just requests for help if you're having
|
||||||
|
trouble.
|
||||||
|
|
||||||
|
We want Open MCT to be as easy to use, install, run, and develop for as
|
||||||
|
possible, and your feedback will help us get there!
|
||||||
|
|
||||||
## Building and Running Open MCT Locally
|
## Building and Running Open MCT Locally
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,10 @@
|
|||||||
"comma-separated-values": "^3.6.4",
|
"comma-separated-values": "^3.6.4",
|
||||||
"FileSaver.js": "^0.0.2",
|
"FileSaver.js": "^0.0.2",
|
||||||
"zepto": "^1.1.6",
|
"zepto": "^1.1.6",
|
||||||
"html2canvas": "^0.4.1",
|
"eventemitter3": "^1.2.0",
|
||||||
"jspdf": "^1.2.61"
|
"lodash": "3.10.1",
|
||||||
|
"almond": "~0.3.2",
|
||||||
|
"d3": "~4.1.0",
|
||||||
|
"html2canvas": "^0.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
# Script to build and deploy docs.
|
# Script to build and deploy docs.
|
||||||
|
|
||||||
OUTPUT_DIRECTORY="target/docs"
|
OUTPUT_DIRECTORY="dist/docs"
|
||||||
# Docs, once built, are pushed to the private website repo
|
# Docs, once built, are pushed to the private website repo
|
||||||
REPOSITORY_URL="git@github.com:nasa/openmct-website.git"
|
REPOSITORY_URL="git@github.com:nasa/openmct-website.git"
|
||||||
WEBSITE_DIRECTORY="website"
|
WEBSITE_DIRECTORY="website"
|
||||||
|
|||||||
@@ -9,26 +9,29 @@
|
|||||||
|
|
||||||
Open MCT provides functionality out of the box, but it's also a platform for
|
Open MCT provides functionality out of the box, but it's also a platform for
|
||||||
building rich mission operations applications based on modern web technology.
|
building rich mission operations applications based on modern web technology.
|
||||||
The platform is configured declaratively, and defines conventions for
|
The platform is configured by plugins which extend the platform at a variety
|
||||||
building on the provided capabilities by creating modular 'bundles' that
|
of extension points. The details of how to
|
||||||
extend the platform at a variety of extension points. The details of how to
|
|
||||||
extend the platform are provided in the following documentation.
|
extend the platform are provided in the following documentation.
|
||||||
|
|
||||||
## Sections
|
## Sections
|
||||||
|
|
||||||
* The [Architecture Overview](architecture/) describes the concepts used
|
|
||||||
throughout Open MCT, and gives a high level overview of the platform's design.
|
|
||||||
|
|
||||||
* The [Developer's Guide](guide/) goes into more detail about how to use the
|
|
||||||
platform and the functionality that it provides.
|
|
||||||
|
|
||||||
* The [Tutorials](tutorials/) give examples of extending the platform to add
|
|
||||||
functionality,
|
|
||||||
and integrate with data sources.
|
|
||||||
|
|
||||||
* The [API](api/) document is generated from inline documentation
|
* The [API](api/) document is generated from inline documentation
|
||||||
using [JSDoc](http://usejsdoc.org/), and describes the JavaScript objects and
|
using [JSDoc](http://usejsdoc.org/), and describes the JavaScript objects and
|
||||||
functions that make up the software platform.
|
functions that make up the software platform.
|
||||||
|
|
||||||
* Finally, the [Development Process](process/) document describes the
|
* The [Development Process](process/) document describes the
|
||||||
Open MCT software development cycle.
|
Open MCT software development cycle.
|
||||||
|
|
||||||
|
## Legacy Documentation
|
||||||
|
|
||||||
|
As we transition to a new API, the following documentation for the old API
|
||||||
|
(which is supported during the transtion) may be useful as well:
|
||||||
|
|
||||||
|
* The [Architecture Overview](architecture/) describes the concepts used
|
||||||
|
throughout Open MCT, and gives a high level overview of the platform's design.
|
||||||
|
|
||||||
|
* The [Developer's Guide](guide/) goes into more detail about how to use the
|
||||||
|
platform and the functionality that it provides.
|
||||||
|
|
||||||
|
* The [Tutorials](tutorials/) give examples of extending the platform to add
|
||||||
|
functionality, and integrate with data sources.
|
||||||
|
|||||||
@@ -675,10 +675,10 @@ it in our bundle definition, as an extension of category `controllers`:
|
|||||||
```diff
|
```diff
|
||||||
define([
|
define([
|
||||||
'legacyRegistry',
|
'legacyRegistry',
|
||||||
'./src/controllers/TodoController'
|
+ './src/controllers/TodoController'
|
||||||
], function (
|
], function (
|
||||||
legacyRegistry,
|
legacyRegistry,
|
||||||
TodoController
|
+ TodoController
|
||||||
) {
|
) {
|
||||||
legacyRegistry.register("tutorials/todo", {
|
legacyRegistry.register("tutorials/todo", {
|
||||||
"name": "To-do Plugin",
|
"name": "To-do Plugin",
|
||||||
@@ -2948,7 +2948,7 @@ will implement:
|
|||||||
/*global define*/
|
/*global define*/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
['./src/ExampleTelemetrySeries'],
|
['./ExampleTelemetrySeries'],
|
||||||
function (ExampleTelemetrySeries) {
|
function (ExampleTelemetrySeries) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|||||||
48
example/localTimeSystem/bundle.js
Normal file
48
example/localTimeSystem/bundle.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web 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 Web 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/LocalTimeSystem",
|
||||||
|
"./src/LocalTimeFormat",
|
||||||
|
'legacyRegistry'
|
||||||
|
], function (
|
||||||
|
LocalTimeSystem,
|
||||||
|
LocalTimeFormat,
|
||||||
|
legacyRegistry
|
||||||
|
) {
|
||||||
|
legacyRegistry.register("example/localTimeSystem", {
|
||||||
|
"extensions": {
|
||||||
|
"formats": [
|
||||||
|
{
|
||||||
|
"key": "local-format",
|
||||||
|
"implementation": LocalTimeFormat
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeSystems": [
|
||||||
|
{
|
||||||
|
"implementation": LocalTimeSystem,
|
||||||
|
"depends": ["$timeout"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
43
example/localTimeSystem/src/LADTickSource.js
Normal file
43
example/localTimeSystem/src/LADTickSource.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web 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 Web 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(['../../../platform/features/conductor-v2/conductor/src/timeSystems/LocalClock'], function (LocalClock) {
|
||||||
|
/**
|
||||||
|
* @implements TickSource
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function LADTickSource ($timeout, period) {
|
||||||
|
LocalClock.call(this, $timeout, period);
|
||||||
|
|
||||||
|
this.metadata = {
|
||||||
|
key: 'test-lad',
|
||||||
|
mode: 'lad',
|
||||||
|
cssclass: 'icon-clock',
|
||||||
|
label: 'Latest Available Data',
|
||||||
|
name: 'Latest available data',
|
||||||
|
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
LADTickSource.prototype = Object.create(LocalClock.prototype);
|
||||||
|
|
||||||
|
return LADTickSource;
|
||||||
|
});
|
||||||
112
example/localTimeSystem/src/LocalTimeFormat.js
Normal file
112
example/localTimeSystem/src/LocalTimeFormat.js
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web 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 Web 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([
|
||||||
|
'moment'
|
||||||
|
], function (
|
||||||
|
moment
|
||||||
|
) {
|
||||||
|
|
||||||
|
var DATE_FORMAT = "YYYY-MM-DD h:mm:ss.SSS a",
|
||||||
|
DATE_FORMATS = [
|
||||||
|
DATE_FORMAT,
|
||||||
|
"YYYY-MM-DD h:mm:ss a",
|
||||||
|
"YYYY-MM-DD h:mm a",
|
||||||
|
"YYYY-MM-DD"
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Scale
|
||||||
|
* @property {number} min the minimum scale value, in ms
|
||||||
|
* @property {number} max the maximum scale value, in ms
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatter for UTC timestamps. Interprets numeric values as
|
||||||
|
* milliseconds since the start of 1970.
|
||||||
|
*
|
||||||
|
* @implements {Format}
|
||||||
|
* @constructor
|
||||||
|
* @memberof platform/commonUI/formats
|
||||||
|
*/
|
||||||
|
function LocalTimeFormat() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an appropriate time format based on the provided value and
|
||||||
|
* the threshold required.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function getScaledFormat (d) {
|
||||||
|
var m = moment.utc(d);
|
||||||
|
/**
|
||||||
|
* Uses logic from d3 Time-Scales, v3 of the API. See
|
||||||
|
* https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Scales.md
|
||||||
|
*
|
||||||
|
* Licensed
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
[".SSS", function(m) { return m.milliseconds(); }],
|
||||||
|
[":ss", function(m) { return m.seconds(); }],
|
||||||
|
["hh:mma", function(m) { return m.minutes(); }],
|
||||||
|
["hha", function(m) { return m.hours(); }],
|
||||||
|
["ddd DD", function(m) {
|
||||||
|
return m.days() &&
|
||||||
|
m.date() != 1;
|
||||||
|
}],
|
||||||
|
["MMM DD", function(m) { return m.date() != 1; }],
|
||||||
|
["MMMM", function(m) {
|
||||||
|
return m.month();
|
||||||
|
}],
|
||||||
|
["YYYY", function() { return true; }]
|
||||||
|
].filter(function (row){
|
||||||
|
return row[1](m);
|
||||||
|
})[0][0];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* @param {Scale} [scale] Optionally provides context to the
|
||||||
|
* format request, allowing for scale-appropriate formatting.
|
||||||
|
* @returns {string} the formatted date
|
||||||
|
*/
|
||||||
|
LocalTimeFormat.prototype.format = function (value, scale) {
|
||||||
|
if (scale !== undefined){
|
||||||
|
var scaledFormat = getScaledFormat(value, scale);
|
||||||
|
if (scaledFormat) {
|
||||||
|
return moment.utc(value).format(scaledFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return moment(value).format(DATE_FORMAT);
|
||||||
|
};
|
||||||
|
|
||||||
|
LocalTimeFormat.prototype.parse = function (text) {
|
||||||
|
return moment(text, DATE_FORMATS).valueOf();
|
||||||
|
};
|
||||||
|
|
||||||
|
LocalTimeFormat.prototype.validate = function (text) {
|
||||||
|
return moment(text, DATE_FORMATS).isValid();
|
||||||
|
};
|
||||||
|
|
||||||
|
return LocalTimeFormat;
|
||||||
|
});
|
||||||
79
example/localTimeSystem/src/LocalTimeSystem.js
Normal file
79
example/localTimeSystem/src/LocalTimeSystem.js
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web 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 Web 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([
|
||||||
|
'../../../platform/features/conductor-v2/conductor/src/timeSystems/TimeSystem',
|
||||||
|
'../../../platform/features/conductor-v2/conductor/src/timeSystems/LocalClock',
|
||||||
|
'./LADTickSource'
|
||||||
|
], function (TimeSystem, LocalClock, LADTickSource) {
|
||||||
|
var THIRTY_MINUTES = 30 * 60 * 1000,
|
||||||
|
DEFAULT_PERIOD = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This time system supports UTC dates and provides a ticking clock source.
|
||||||
|
* @implements TimeSystem
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function LocalTimeSystem ($timeout) {
|
||||||
|
TimeSystem.call(this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some metadata, which will be used to identify the time system in
|
||||||
|
* the UI
|
||||||
|
* @type {{key: string, name: string, glyph: string}}
|
||||||
|
*/
|
||||||
|
this.metadata = {
|
||||||
|
'key': 'local',
|
||||||
|
'name': 'Local',
|
||||||
|
'glyph': '\u0043'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.fmts = ['local-format'];
|
||||||
|
this.sources = [new LocalClock($timeout, DEFAULT_PERIOD), new LADTickSource($timeout, DEFAULT_PERIOD)];
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalTimeSystem.prototype = Object.create(TimeSystem.prototype);
|
||||||
|
|
||||||
|
LocalTimeSystem.prototype.formats = function () {
|
||||||
|
return this.fmts;
|
||||||
|
};
|
||||||
|
|
||||||
|
LocalTimeSystem.prototype.deltaFormat = function () {
|
||||||
|
return 'duration';
|
||||||
|
};
|
||||||
|
|
||||||
|
LocalTimeSystem.prototype.tickSources = function () {
|
||||||
|
return this.sources;
|
||||||
|
};
|
||||||
|
|
||||||
|
LocalTimeSystem.prototype.defaults = function (key) {
|
||||||
|
var now = Math.ceil(Date.now() / 1000) * 1000;
|
||||||
|
return {
|
||||||
|
key: 'local-default',
|
||||||
|
name: 'Local 12 hour time system defaults',
|
||||||
|
deltas: {start: THIRTY_MINUTES, end: 0},
|
||||||
|
bounds: {start: now - THIRTY_MINUTES, end: now}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return LocalTimeSystem;
|
||||||
|
});
|
||||||
@@ -23,20 +23,18 @@
|
|||||||
|
|
||||||
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/notifications", {
|
legacyRegistry.register("example/msl", {
|
||||||
"name" : "Mars Science Laboratory Data Adapter",
|
"name" : "Mars Science Laboratory Data Adapter",
|
||||||
"extensions" : {
|
"extensions" : {
|
||||||
"types": [
|
"types": [
|
||||||
@@ -81,7 +79,7 @@ define([
|
|||||||
"model": {
|
"model": {
|
||||||
"type": "msl.curiosity",
|
"type": "msl.curiosity",
|
||||||
"name": "Mars Science Laboratory",
|
"name": "Mars Science Laboratory",
|
||||||
"composition": []
|
"composition": ["msl_tlm:rems"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,12 +90,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;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
55
gulpfile.js
55
gulpfile.js
@@ -21,41 +21,34 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/*global require,__dirname*/
|
/*global require,__dirname*/
|
||||||
|
|
||||||
var gulp = require('gulp'),
|
var gulp = require('gulp'),
|
||||||
requirejsOptimize = require('gulp-requirejs-optimize'),
|
|
||||||
sourcemaps = require('gulp-sourcemaps'),
|
sourcemaps = require('gulp-sourcemaps'),
|
||||||
rename = require('gulp-rename'),
|
|
||||||
sass = require('gulp-sass'),
|
|
||||||
bourbon = require('node-bourbon'),
|
|
||||||
jshint = require('gulp-jshint'),
|
|
||||||
jscs = require('gulp-jscs'),
|
|
||||||
replace = require('gulp-replace-task'),
|
|
||||||
karma = require('karma'),
|
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
git = require('git-rev-sync'),
|
git = require('git-rev-sync'),
|
||||||
moment = require('moment'),
|
moment = require('moment'),
|
||||||
merge = require('merge-stream'),
|
|
||||||
project = require('./package.json'),
|
project = require('./package.json'),
|
||||||
_ = require('lodash'),
|
_ = require('lodash'),
|
||||||
paths = {
|
paths = {
|
||||||
main: 'main.js',
|
main: 'openmct.js',
|
||||||
dist: 'dist',
|
dist: 'dist',
|
||||||
assets: 'dist/assets',
|
|
||||||
reports: 'dist/reports',
|
reports: 'dist/reports',
|
||||||
scss: ['./platform/**/*.scss', './example/**/*.scss'],
|
scss: ['./platform/**/*.scss', './example/**/*.scss'],
|
||||||
scripts: [ 'main.js', 'platform/**/*.js', 'src/**/*.js' ],
|
assets: [
|
||||||
|
'./{example,platform}/**/*.{css,css.map,png,svg,ico,woff,eot,ttf}'
|
||||||
|
],
|
||||||
|
scripts: [ 'openmct.js', 'platform/**/*.js', 'src/**/*.js' ],
|
||||||
specs: [ 'platform/**/*Spec.js', 'src/**/*Spec.js' ],
|
specs: [ 'platform/**/*Spec.js', 'src/**/*Spec.js' ],
|
||||||
static: [
|
|
||||||
'index.html',
|
|
||||||
'platform/**/*',
|
|
||||||
'example/**/*',
|
|
||||||
'bower_components/**/*'
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
options = {
|
options = {
|
||||||
requirejsOptimize: {
|
requirejsOptimize: {
|
||||||
name: paths.main.replace(/\.js$/, ''),
|
name: 'bower_components/almond/almond.js',
|
||||||
|
include: paths.main.replace('.js', ''),
|
||||||
|
wrap: {
|
||||||
|
startFile: "src/start.frag",
|
||||||
|
endFile: "src/end.frag"
|
||||||
|
},
|
||||||
mainConfigFile: paths.main,
|
mainConfigFile: paths.main,
|
||||||
wrapShim: true
|
wrapShim: true
|
||||||
},
|
},
|
||||||
@@ -64,7 +57,6 @@ var gulp = require('gulp'),
|
|||||||
singleRun: true
|
singleRun: true
|
||||||
},
|
},
|
||||||
sass: {
|
sass: {
|
||||||
includePaths: bourbon.includePaths,
|
|
||||||
sourceComments: true
|
sourceComments: true
|
||||||
},
|
},
|
||||||
replace: {
|
replace: {
|
||||||
@@ -78,6 +70,8 @@ var gulp = require('gulp'),
|
|||||||
};
|
};
|
||||||
|
|
||||||
gulp.task('scripts', function () {
|
gulp.task('scripts', function () {
|
||||||
|
var requirejsOptimize = require('gulp-requirejs-optimize');
|
||||||
|
var replace = require('gulp-replace-task');
|
||||||
return gulp.src(paths.main)
|
return gulp.src(paths.main)
|
||||||
.pipe(sourcemaps.init())
|
.pipe(sourcemaps.init())
|
||||||
.pipe(requirejsOptimize(options.requirejsOptimize))
|
.pipe(requirejsOptimize(options.requirejsOptimize))
|
||||||
@@ -87,10 +81,16 @@ gulp.task('scripts', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('test', function (done) {
|
gulp.task('test', function (done) {
|
||||||
|
var karma = require('karma');
|
||||||
new karma.Server(options.karma, done).start();
|
new karma.Server(options.karma, done).start();
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('stylesheets', function () {
|
gulp.task('stylesheets', function () {
|
||||||
|
var sass = require('gulp-sass');
|
||||||
|
var rename = require('gulp-rename');
|
||||||
|
var bourbon = require('node-bourbon');
|
||||||
|
options.sass.includePaths = bourbon.includePaths;
|
||||||
|
|
||||||
return gulp.src(paths.scss, {base: '.'})
|
return gulp.src(paths.scss, {base: '.'})
|
||||||
.pipe(sourcemaps.init())
|
.pipe(sourcemaps.init())
|
||||||
.pipe(sass(options.sass).on('error', sass.logError))
|
.pipe(sass(options.sass).on('error', sass.logError))
|
||||||
@@ -104,6 +104,9 @@ gulp.task('stylesheets', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('lint', function () {
|
gulp.task('lint', function () {
|
||||||
|
var jshint = require('gulp-jshint');
|
||||||
|
var merge = require('merge-stream');
|
||||||
|
|
||||||
var nonspecs = paths.specs.map(function (glob) {
|
var nonspecs = paths.specs.map(function (glob) {
|
||||||
return "!" + glob;
|
return "!" + glob;
|
||||||
}),
|
}),
|
||||||
@@ -122,6 +125,8 @@ gulp.task('lint', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('checkstyle', function () {
|
gulp.task('checkstyle', function () {
|
||||||
|
var jscs = require('gulp-jscs');
|
||||||
|
|
||||||
return gulp.src(paths.scripts)
|
return gulp.src(paths.scripts)
|
||||||
.pipe(jscs())
|
.pipe(jscs())
|
||||||
.pipe(jscs.reporter())
|
.pipe(jscs.reporter())
|
||||||
@@ -129,18 +134,20 @@ gulp.task('checkstyle', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('fixstyle', function () {
|
gulp.task('fixstyle', function () {
|
||||||
|
var jscs = require('gulp-jscs');
|
||||||
|
|
||||||
return gulp.src(paths.scripts, { base: '.' })
|
return gulp.src(paths.scripts, { base: '.' })
|
||||||
.pipe(jscs({ fix: true }))
|
.pipe(jscs({ fix: true }))
|
||||||
.pipe(gulp.dest('.'));
|
.pipe(gulp.dest('.'));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('static', ['stylesheets'], function () {
|
gulp.task('assets', ['stylesheets'], function () {
|
||||||
return gulp.src(paths.static, { base: '.' })
|
return gulp.src(paths.assets)
|
||||||
.pipe(gulp.dest(paths.dist));
|
.pipe(gulp.dest(paths.dist));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('watch', function () {
|
gulp.task('watch', function () {
|
||||||
gulp.watch(paths.scss, ['stylesheets']);
|
return gulp.watch(paths.scss, ['stylesheets', 'assets']);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('serve', function () {
|
gulp.task('serve', function () {
|
||||||
@@ -150,7 +157,7 @@ gulp.task('serve', function () {
|
|||||||
|
|
||||||
gulp.task('develop', ['serve', 'stylesheets', 'watch']);
|
gulp.task('develop', ['serve', 'stylesheets', 'watch']);
|
||||||
|
|
||||||
gulp.task('install', [ 'static', 'scripts' ]);
|
gulp.task('install', [ 'assets', 'scripts' ]);
|
||||||
|
|
||||||
gulp.task('verify', [ 'lint', 'test', 'checkstyle' ]);
|
gulp.task('verify', [ 'lint', 'test', 'checkstyle' ]);
|
||||||
|
|
||||||
|
|||||||
19
index.html
19
index.html
@@ -28,12 +28,17 @@
|
|||||||
<script src="bower_components/requirejs/require.js">
|
<script src="bower_components/requirejs/require.js">
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
require(['main'], function (mct) {
|
require(['openmct'], function (openmct) {
|
||||||
require([
|
[
|
||||||
'./example/imagery/bundle',
|
'example/imagery',
|
||||||
'./example/eventGenerator/bundle',
|
'example/eventGenerator',
|
||||||
'./example/generator/bundle'
|
'example/generator',
|
||||||
], mct.run.bind(mct));
|
'platform/features/my-items',
|
||||||
|
'platform/persistence/local'
|
||||||
|
].forEach(
|
||||||
|
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
|
||||||
|
);
|
||||||
|
openmct.start();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<link rel="stylesheet" href="platform/commonUI/general/res/css/startup-base.css">
|
<link rel="stylesheet" href="platform/commonUI/general/res/css/startup-base.css">
|
||||||
@@ -47,7 +52,5 @@
|
|||||||
<div class="l-splash-holder s-splash-holder">
|
<div class="l-splash-holder s-splash-holder">
|
||||||
<div class="l-splash s-splash"></div>
|
<div class="l-splash s-splash"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-view></div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"source": {
|
"source": {
|
||||||
"include": [
|
"include": [
|
||||||
"platform/"
|
"src/"
|
||||||
],
|
],
|
||||||
"includePattern": "platform/.+\\.js$",
|
"includePattern": "src/.+\\.js$",
|
||||||
"excludePattern": ".+\\Spec\\.js$|lib/.+"
|
"excludePattern": ".+\\Spec\\.js$|lib/.+"
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
|
|||||||
@@ -37,9 +37,11 @@ module.exports = function(config) {
|
|||||||
{pattern: 'bower_components/**/*.js', included: false},
|
{pattern: 'bower_components/**/*.js', included: false},
|
||||||
{pattern: 'src/**/*.js', included: false},
|
{pattern: 'src/**/*.js', included: false},
|
||||||
{pattern: 'example/**/*.js', included: false},
|
{pattern: 'example/**/*.js', included: false},
|
||||||
|
{pattern: 'example/**/*.json', included: false},
|
||||||
{pattern: 'platform/**/*.js', included: false},
|
{pattern: 'platform/**/*.js', included: false},
|
||||||
{pattern: 'warp/**/*.js', included: false},
|
{pattern: 'warp/**/*.js', included: false},
|
||||||
{pattern: 'platform/**/*.html', included: false},
|
{pattern: 'platform/**/*.html', included: false},
|
||||||
|
{pattern: 'src/**/*.html', included: false},
|
||||||
'test-main.js'
|
'test-main.js'
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -27,16 +27,18 @@ requirejs.config({
|
|||||||
"angular": "bower_components/angular/angular.min",
|
"angular": "bower_components/angular/angular.min",
|
||||||
"angular-route": "bower_components/angular-route/angular-route.min",
|
"angular-route": "bower_components/angular-route/angular-route.min",
|
||||||
"csv": "bower_components/comma-separated-values/csv.min",
|
"csv": "bower_components/comma-separated-values/csv.min",
|
||||||
|
"EventEmitter": "bower_components/eventemitter3/index",
|
||||||
"es6-promise": "bower_components/es6-promise/es6-promise.min",
|
"es6-promise": "bower_components/es6-promise/es6-promise.min",
|
||||||
"html2canvas": "bower_components/html2canvas/build/html2canvas.min",
|
"html2canvas": "bower_components/html2canvas/build/html2canvas.min",
|
||||||
"jsPDF": "bower_components/jspdf/dist/jspdf.debug",
|
|
||||||
"moment": "bower_components/moment/moment",
|
"moment": "bower_components/moment/moment",
|
||||||
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
||||||
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
|
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
|
||||||
"screenfull": "bower_components/screenfull/dist/screenfull.min",
|
"screenfull": "bower_components/screenfull/dist/screenfull.min",
|
||||||
"text": "bower_components/text/text",
|
"text": "bower_components/text/text",
|
||||||
"uuid": "bower_components/node-uuid/uuid",
|
"uuid": "bower_components/node-uuid/uuid",
|
||||||
"zepto": "bower_components/zepto/zepto.min"
|
"zepto": "bower_components/zepto/zepto.min",
|
||||||
|
"lodash": "bower_components/lodash/lodash",
|
||||||
|
"d3": "bower_components/d3/d3.min"
|
||||||
},
|
},
|
||||||
"shim": {
|
"shim": {
|
||||||
"angular": {
|
"angular": {
|
||||||
@@ -45,12 +47,12 @@ requirejs.config({
|
|||||||
"angular-route": {
|
"angular-route": {
|
||||||
"deps": ["angular"]
|
"deps": ["angular"]
|
||||||
},
|
},
|
||||||
|
"EventEmitter": {
|
||||||
|
"exports": "EventEmitter"
|
||||||
|
},
|
||||||
"html2canvas": {
|
"html2canvas": {
|
||||||
"exports": "html2canvas"
|
"exports": "html2canvas"
|
||||||
},
|
},
|
||||||
"jsPDF": {
|
|
||||||
"exports": "jsPDF"
|
|
||||||
},
|
|
||||||
"moment-duration-format": {
|
"moment-duration-format": {
|
||||||
"deps": ["moment"]
|
"deps": ["moment"]
|
||||||
},
|
},
|
||||||
@@ -59,54 +61,28 @@ requirejs.config({
|
|||||||
},
|
},
|
||||||
"zepto": {
|
"zepto": {
|
||||||
"exports": "Zepto"
|
"exports": "Zepto"
|
||||||
|
},
|
||||||
|
"lodash": {
|
||||||
|
"exports": "lodash"
|
||||||
|
},
|
||||||
|
"d3": {
|
||||||
|
"exports": "d3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'./platform/framework/src/Main',
|
'./platform/framework/src/Main',
|
||||||
'legacyRegistry',
|
'./src/defaultRegistry',
|
||||||
|
'./src/MCT'
|
||||||
|
], function (Main, defaultRegistry, MCT) {
|
||||||
|
var openmct = new MCT();
|
||||||
|
|
||||||
'./platform/framework/bundle',
|
openmct.legacyRegistry = defaultRegistry;
|
||||||
'./platform/core/bundle',
|
|
||||||
'./platform/representation/bundle',
|
openmct.on('start', function () {
|
||||||
'./platform/commonUI/about/bundle',
|
return new Main().run(defaultRegistry);
|
||||||
'./platform/commonUI/browse/bundle',
|
});
|
||||||
'./platform/commonUI/edit/bundle',
|
|
||||||
'./platform/commonUI/dialog/bundle',
|
return openmct;
|
||||||
'./platform/commonUI/formats/bundle',
|
|
||||||
'./platform/commonUI/general/bundle',
|
|
||||||
'./platform/commonUI/inspect/bundle',
|
|
||||||
'./platform/commonUI/mobile/bundle',
|
|
||||||
'./platform/commonUI/themes/espresso/bundle',
|
|
||||||
'./platform/commonUI/notification/bundle',
|
|
||||||
'./platform/containment/bundle',
|
|
||||||
'./platform/execution/bundle',
|
|
||||||
'./platform/exporters/bundle',
|
|
||||||
'./platform/telemetry/bundle',
|
|
||||||
'./platform/features/clock/bundle',
|
|
||||||
'./platform/features/fixed/bundle',
|
|
||||||
'./platform/features/imagery/bundle',
|
|
||||||
'./platform/features/layout/bundle',
|
|
||||||
'./platform/features/pages/bundle',
|
|
||||||
'./platform/features/plot/bundle',
|
|
||||||
'./platform/features/timeline/bundle',
|
|
||||||
'./platform/features/table/bundle',
|
|
||||||
'./platform/forms/bundle',
|
|
||||||
'./platform/identity/bundle',
|
|
||||||
'./platform/persistence/aggregator/bundle',
|
|
||||||
'./platform/persistence/local/bundle',
|
|
||||||
'./platform/persistence/queue/bundle',
|
|
||||||
'./platform/policy/bundle',
|
|
||||||
'./platform/entanglement/bundle',
|
|
||||||
'./platform/search/bundle',
|
|
||||||
'./platform/status/bundle',
|
|
||||||
'./platform/commonUI/regions/bundle'
|
|
||||||
], function (Main, legacyRegistry) {
|
|
||||||
return {
|
|
||||||
legacyRegistry: legacyRegistry,
|
|
||||||
run: function () {
|
|
||||||
return new Main().run(legacyRegistry);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "openmct",
|
"name": "openmct",
|
||||||
"version": "0.11.4",
|
"version": "0.12.0",
|
||||||
"description": "The Open MCT core platform",
|
"description": "The Open MCT core platform",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.13.1",
|
"express": "^4.13.1",
|
||||||
@@ -48,8 +48,8 @@
|
|||||||
"test": "karma start --single-run",
|
"test": "karma start --single-run",
|
||||||
"jshint": "jshint platform example",
|
"jshint": "jshint platform example",
|
||||||
"watch": "karma start",
|
"watch": "karma start",
|
||||||
"jsdoc": "jsdoc -c jsdoc.json -r -d target/docs/api",
|
"jsdoc": "jsdoc -c jsdoc.json -R API.md -r -d dist/docs/api",
|
||||||
"otherdoc": "node docs/gendocs.js --in docs/src --out target/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",
|
"otherdoc": "node docs/gendocs.js --in docs/src --out dist/docs --suppress-toc 'docs/src/index.md|docs/src/process/index.md'",
|
||||||
"docs": "npm run jsdoc ; npm run otherdoc",
|
"docs": "npm run jsdoc ; npm run otherdoc",
|
||||||
"prepublish": "node ./node_modules/bower/bin/bower install && node ./node_modules/gulp/bin/gulp.js install"
|
"prepublish": "node ./node_modules/bower/bin/bower install && node ./node_modules/gulp/bin/gulp.js install"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ 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,
|
||||||
@@ -63,6 +64,7 @@ define([
|
|||||||
itemsTemplate,
|
itemsTemplate,
|
||||||
objectPropertiesTemplate,
|
objectPropertiesTemplate,
|
||||||
inspectorRegionTemplate,
|
inspectorRegionTemplate,
|
||||||
|
viewObjectTemplate,
|
||||||
legacyRegistry
|
legacyRegistry
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -142,7 +144,7 @@ define([
|
|||||||
"representations": [
|
"representations": [
|
||||||
{
|
{
|
||||||
"key": "view-object",
|
"key": "view-object",
|
||||||
"templateUrl": "templates/view-object.html"
|
"template": viewObjectTemplate
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "browse-object",
|
"key": "browse-object",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="holder l-flex-col flex-elem grows l-object-wrapper">
|
<div class="holder l-flex-col flex-elem grows l-object-wrapper l-controls-visible l-time-controller-visible">
|
||||||
<div class="holder l-flex-col flex-elem grows l-object-wrapper-inner">
|
<div class="holder l-flex-col flex-elem grows l-object-wrapper-inner">
|
||||||
<!-- Toolbar and Save/Cancel buttons -->
|
<!-- Toolbar and Save/Cancel buttons -->
|
||||||
<div class="l-edit-controls flex-elem l-flex-row flex-align-end">
|
<div class="l-edit-controls flex-elem l-flex-row flex-align-end">
|
||||||
@@ -59,4 +59,5 @@
|
|||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<mct-include key="'conductor'" class="abs holder flex-elem flex-fixed l-flex-row l-time-conductor-holder"></mct-include>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
<span class='type-icon flex-elem {{type.getCssClass()}}'></span>
|
<span class='type-icon flex-elem {{type.getCssClass()}}'></span>
|
||||||
<span class="l-elem-wrapper l-flex-row flex-elem grows">
|
<span class="l-elem-wrapper l-flex-row flex-elem grows">
|
||||||
<span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
|
<span ng-if="parameters.mode" class='action flex-elem'>{{parameters.mode}}</span>
|
||||||
<span class='title-label flex-elem flex-can-shrink'>{{model.name}}</span>
|
<span class='title-label flex-elem holder flex-can-shrink'>{{model.name}}</span>
|
||||||
|
<span class='t-object-alert t-alert-unsynced flex-elem holder' title='This object is not currently displaying real-time data'></span>
|
||||||
<mct-representation
|
<mct-representation
|
||||||
key="'menu-arrow'"
|
key="'menu-arrow'"
|
||||||
mct-object='domainObject'
|
mct-object='domainObject'
|
||||||
|
|||||||
@@ -48,20 +48,35 @@ define(
|
|||||||
*/
|
*/
|
||||||
NavigateAction.prototype.perform = function () {
|
NavigateAction.prototype.perform = function () {
|
||||||
var self = this,
|
var self = this,
|
||||||
navigationAllowed = true;
|
navigateTo = this.domainObject,
|
||||||
|
currentObject = self.navigationService.getNavigation();
|
||||||
|
|
||||||
function allow() {
|
function allow() {
|
||||||
self.policyService.allow("navigation", self.navigationService.getNavigation(), self.domainObject, function (message) {
|
var navigationAllowed = true;
|
||||||
|
self.policyService.allow("navigation", currentObject, navigateTo, function (message) {
|
||||||
navigationAllowed = self.$window.confirm(message + "\r\n\r\n" +
|
navigationAllowed = self.$window.confirm(message + "\r\n\r\n" +
|
||||||
" Are you sure you want to continue?");
|
" Are you sure you want to continue?");
|
||||||
});
|
});
|
||||||
return navigationAllowed;
|
return navigationAllowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set navigation, and wrap like a promise
|
function cancelIfEditing() {
|
||||||
return this.$q.when(
|
var editing = currentObject.hasCapability('editor') &&
|
||||||
allow() && this.navigationService.setNavigation(this.domainObject)
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ define(
|
|||||||
mockQ,
|
mockQ,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockPolicyService,
|
mockPolicyService,
|
||||||
|
mockNavigatedObject,
|
||||||
mockWindow,
|
mockWindow,
|
||||||
|
capabilities,
|
||||||
action;
|
action;
|
||||||
|
|
||||||
function mockPromise(value) {
|
function mockPromise(value) {
|
||||||
@@ -44,6 +46,29 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
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",
|
||||||
[
|
[
|
||||||
@@ -51,11 +76,14 @@ define(
|
|||||||
"getNavigation"
|
"getNavigation"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
mockNavigationService.getNavigation.andReturn({});
|
mockNavigationService.getNavigation.andReturn(mockNavigatedObject);
|
||||||
mockQ = { when: mockPromise };
|
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
"domainObject",
|
"domainObject",
|
||||||
["getId", "getModel", "getCapability"]
|
[
|
||||||
|
"getId",
|
||||||
|
"getModel"
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
mockPolicyService = jasmine.createSpyObj("policyService",
|
mockPolicyService = jasmine.createSpyObj("policyService",
|
||||||
@@ -112,6 +140,21 @@ define(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("in edit mode", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
mockNavigatedObject.hasCapability.andCallFake(function (capability) {
|
||||||
|
return capability === "editor";
|
||||||
|
});
|
||||||
|
capabilities.editor.isEditContextRoot.andReturn(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("finishes editing if in edit mode", function () {
|
||||||
|
action.perform();
|
||||||
|
expect(capabilities.editor.finish)
|
||||||
|
.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("is only applicable when a domain object is in context", function () {
|
it("is only applicable when a domain object is in context", function () {
|
||||||
expect(NavigateAction.appliesTo({})).toBeFalsy();
|
expect(NavigateAction.appliesTo({})).toBeFalsy();
|
||||||
expect(NavigateAction.appliesTo({
|
expect(NavigateAction.appliesTo({
|
||||||
|
|||||||
@@ -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.
|
||||||
-->
|
-->
|
||||||
<div class="abs overlay">
|
<div class="abs overlay" ng-class="{'delayEntry100ms' : ngModel.delay}">
|
||||||
<div class="abs blocker"></div>
|
<div class="abs blocker"></div>
|
||||||
<div class="abs holder">
|
<div class="abs holder">
|
||||||
<a ng-click="ngModel.cancel()"
|
<a ng-click="ngModel.cancel()"
|
||||||
|
|||||||
@@ -200,6 +200,9 @@ define(
|
|||||||
* shown above a progress bar to indicate what's happening.
|
* shown above a progress bar to indicate what's happening.
|
||||||
* @property {number} progress a percentage value (1-100)
|
* @property {number} progress a percentage value (1-100)
|
||||||
* indicating the completion of the blocking task
|
* indicating the completion of the blocking task
|
||||||
|
* @property {boolean} delay adds a brief delay before loading
|
||||||
|
* the dialog. Useful for removing the dialog flicker when the
|
||||||
|
* conditions for displaying the dialog change rapidly.
|
||||||
* @property {string} progressText the message to show below a
|
* @property {string} progressText the message to show below a
|
||||||
* progress bar to indicate progress. For example, this might be
|
* progress bar to indicate progress. For example, this might be
|
||||||
* used to indicate time remaining, or items still to process.
|
* used to indicate time remaining, or items still to process.
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ define([
|
|||||||
"./src/actions/PropertiesAction",
|
"./src/actions/PropertiesAction",
|
||||||
"./src/actions/RemoveAction",
|
"./src/actions/RemoveAction",
|
||||||
"./src/actions/SaveAction",
|
"./src/actions/SaveAction",
|
||||||
|
"./src/actions/SaveAndStopEditingAction",
|
||||||
"./src/actions/SaveAsAction",
|
"./src/actions/SaveAsAction",
|
||||||
"./src/actions/CancelAction",
|
"./src/actions/CancelAction",
|
||||||
"./src/policies/EditActionPolicy",
|
"./src/policies/EditActionPolicy",
|
||||||
@@ -70,6 +71,7 @@ define([
|
|||||||
PropertiesAction,
|
PropertiesAction,
|
||||||
RemoveAction,
|
RemoveAction,
|
||||||
SaveAction,
|
SaveAction,
|
||||||
|
SaveAndStopEditingAction,
|
||||||
SaveAsAction,
|
SaveAsAction,
|
||||||
CancelAction,
|
CancelAction,
|
||||||
EditActionPolicy,
|
EditActionPolicy,
|
||||||
@@ -198,25 +200,34 @@ define([
|
|||||||
"name": "Remove",
|
"name": "Remove",
|
||||||
"description": "Remove this object from its containing object.",
|
"description": "Remove this object from its containing object.",
|
||||||
"depends": [
|
"depends": [
|
||||||
"$q",
|
|
||||||
"navigationService"
|
"navigationService"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "save",
|
"key": "save-and-stop-editing",
|
||||||
"category": "conclude-editing",
|
"category": "save",
|
||||||
"implementation": SaveAction,
|
"implementation": SaveAndStopEditingAction,
|
||||||
"name": "Save",
|
"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"
|
||||||
],
|
]
|
||||||
"priority": "mandatory"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"key": "save",
|
"key": "save",
|
||||||
"category": "conclude-editing",
|
"category": "save",
|
||||||
|
"implementation": SaveAction,
|
||||||
|
"name": "Save and Continue Editing",
|
||||||
|
"cssclass": "icon-save labeled",
|
||||||
|
"description": "Save changes made to these objects.",
|
||||||
|
"depends": [
|
||||||
|
"dialogService"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "save-as",
|
||||||
|
"category": "save",
|
||||||
"implementation": SaveAsAction,
|
"implementation": SaveAsAction,
|
||||||
"name": "Save As...",
|
"name": "Save As...",
|
||||||
"cssclass": "icon-save labeled",
|
"cssclass": "icon-save labeled",
|
||||||
@@ -225,7 +236,6 @@ define([
|
|||||||
"$injector",
|
"$injector",
|
||||||
"policyService",
|
"policyService",
|
||||||
"dialogService",
|
"dialogService",
|
||||||
"creationService",
|
|
||||||
"copyService"
|
"copyService"
|
||||||
],
|
],
|
||||||
"priority": "mandatory"
|
"priority": "mandatory"
|
||||||
@@ -234,7 +244,9 @@ define([
|
|||||||
"key": "cancel",
|
"key": "cancel",
|
||||||
"category": "conclude-editing",
|
"category": "conclude-editing",
|
||||||
"implementation": CancelAction,
|
"implementation": CancelAction,
|
||||||
"name": "Cancel",
|
// Because we use the name as label for edit buttons and mct-control buttons need
|
||||||
|
// the label to be set to undefined in order to not apply the labeled CSS rule.
|
||||||
|
"name": undefined,
|
||||||
"cssclass": "icon-x no-label",
|
"cssclass": "icon-x no-label",
|
||||||
"description": "Discard changes made to these objects.",
|
"description": "Discard changes made to these objects.",
|
||||||
"depends": []
|
"depends": []
|
||||||
@@ -335,7 +347,8 @@ define([
|
|||||||
"implementation": TransactionService,
|
"implementation": TransactionService,
|
||||||
"depends": [
|
"depends": [
|
||||||
"$q",
|
"$q",
|
||||||
"$log"
|
"$log",
|
||||||
|
"cacheService"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -20,12 +20,34 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<span ng-controller="EditActionController">
|
<span ng-controller="EditActionController">
|
||||||
<span ng-repeat="currentAction in editActions">
|
<!-- If there's a single save action show a button, otherwise show a dropdown with all save actions. -->
|
||||||
<a class='s-button {{currentAction.getMetadata().cssclass}}'
|
<span ng-if="saveActions.length === 1">
|
||||||
title='{{currentAction.getMetadata().name}}'
|
<mct-control key="'button'"
|
||||||
ng-click="currentAction.perform()"
|
structure="{
|
||||||
ng-class="{ major: $index === 0 }">
|
text: saveActions[0].getMetadata().name,
|
||||||
<span class="title-label">{{currentAction.getMetadata().name}}</span>
|
click: actionPerformer(saveActions[0]),
|
||||||
</a>
|
cssclass: 'major ' + saveActions[0].getMetadata().cssclass
|
||||||
|
}">
|
||||||
|
</mct-control>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span ng-if="saveActions.length > 1">
|
||||||
|
<mct-control key="'menu-button'"
|
||||||
|
structure="{
|
||||||
|
options: saveActionsAsMenuOptions,
|
||||||
|
click: saveActionMenuClickHandler,
|
||||||
|
cssclass: 'btn-bar right icon-save no-label major'
|
||||||
|
}">
|
||||||
|
</mct-control>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span ng-repeat="currentAction in otherEditActions">
|
||||||
|
<mct-control key="'button'"
|
||||||
|
structure="{
|
||||||
|
text: currentAction.getMetadata().name,
|
||||||
|
click: actionPerformer(currentAction),
|
||||||
|
cssclass: currentAction.getMetadata().cssclass
|
||||||
|
}">
|
||||||
|
</mct-control>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -66,4 +66,9 @@
|
|||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div><!--/ l-object-wrapper-inner -->
|
</div><!--/ l-object-wrapper-inner -->
|
||||||
</div>
|
</div>
|
||||||
|
<mct-representation mct-object="domainObject"
|
||||||
|
key="'time-conductor'"
|
||||||
|
class="abs holder flex-elem flex-fixed l-flex-row l-time-conductor-holder">
|
||||||
|
</mct-representation>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function cancel(allowed) {
|
function cancel(allowed) {
|
||||||
return allowed && domainObject.getCapability("editor").cancel();
|
return allowed && domainObject.getCapability("editor").finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Do navigation first in order to trigger unsaved changes dialog
|
//Do navigation first in order to trigger unsaved changes dialog
|
||||||
|
|||||||
@@ -69,18 +69,13 @@ define(
|
|||||||
* Enter edit mode.
|
* Enter edit mode.
|
||||||
*/
|
*/
|
||||||
EditAction.prototype.perform = function () {
|
EditAction.prototype.perform = function () {
|
||||||
var self = this;
|
|
||||||
function cancelEditing() {
|
|
||||||
self.domainObject.getCapability('editor').cancel();
|
|
||||||
self.navigationService.removeListener(cancelEditing);
|
|
||||||
}
|
|
||||||
//If this is not the currently navigated object, then navigate
|
//If this is not the currently navigated object, then navigate
|
||||||
// to it.
|
// to it.
|
||||||
if (this.navigationService.getNavigation() !== this.domainObject) {
|
if (this.navigationService.getNavigation() !== this.domainObject) {
|
||||||
this.navigationService.setNavigation(this.domainObject);
|
this.navigationService.setNavigation(this.domainObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.navigationService.addListener(cancelEditing);
|
|
||||||
this.domainObject.useCapability("editor");
|
this.domainObject.useCapability("editor");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -40,19 +40,11 @@ define(
|
|||||||
var self = this,
|
var self = this,
|
||||||
editAction = this.domainObject.getCapability('action').getActions("edit")[0];
|
editAction = this.domainObject.getCapability('action').getActions("edit")[0];
|
||||||
|
|
||||||
// Persist changes to the domain object
|
|
||||||
function doPersist() {
|
|
||||||
var persistence =
|
|
||||||
self.domainObject.getCapability('persistence');
|
|
||||||
return persistence.persist();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Link these objects
|
// Link these objects
|
||||||
function doLink() {
|
function doLink() {
|
||||||
var composition = self.domainObject &&
|
var composition = self.domainObject &&
|
||||||
self.domainObject.getCapability('composition');
|
self.domainObject.getCapability('composition');
|
||||||
return composition && composition.add(self.selectedObject)
|
return composition && composition.add(self.selectedObject);
|
||||||
.then(doPersist);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editAction) {
|
if (editAction) {
|
||||||
|
|||||||
@@ -50,12 +50,6 @@ define(
|
|||||||
domainObject = this.domainObject,
|
domainObject = this.domainObject,
|
||||||
dialogService = this.dialogService;
|
dialogService = this.dialogService;
|
||||||
|
|
||||||
// Persist modifications to this domain object
|
|
||||||
function doPersist() {
|
|
||||||
var persistence = domainObject.getCapability('persistence');
|
|
||||||
return persistence && persistence.persist();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the domain object model based on user input
|
// Update the domain object model based on user input
|
||||||
function updateModel(userInput, dialog) {
|
function updateModel(userInput, dialog) {
|
||||||
return domainObject.useCapability('mutation', function (model) {
|
return domainObject.useCapability('mutation', function (model) {
|
||||||
@@ -73,11 +67,9 @@ define(
|
|||||||
dialog.getFormStructure(),
|
dialog.getFormStructure(),
|
||||||
dialog.getInitialFormValue()
|
dialog.getInitialFormValue()
|
||||||
).then(function (userInput) {
|
).then(function (userInput) {
|
||||||
// Update the model, if user input was provided
|
// Update the model, if user input was provided
|
||||||
return userInput && updateModel(userInput, dialog);
|
return userInput && updateModel(userInput, dialog);
|
||||||
}).then(function (result) {
|
});
|
||||||
return result && doPersist();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return type && showDialog(type);
|
return type && showDialog(type);
|
||||||
@@ -94,9 +86,7 @@ define(
|
|||||||
creatable = type && type.hasFeature('creation');
|
creatable = type && type.hasFeature('creation');
|
||||||
|
|
||||||
// Only allow creatable types to be edited
|
// Only allow creatable types to be edited
|
||||||
return domainObject &&
|
return domainObject && creatable;
|
||||||
domainObject.hasCapability("persistence") &&
|
|
||||||
creatable;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return PropertiesAction;
|
return PropertiesAction;
|
||||||
|
|||||||
@@ -39,9 +39,8 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @implements {Action}
|
* @implements {Action}
|
||||||
*/
|
*/
|
||||||
function RemoveAction($q, navigationService, context) {
|
function RemoveAction(navigationService, context) {
|
||||||
this.domainObject = (context || {}).domainObject;
|
this.domainObject = (context || {}).domainObject;
|
||||||
this.$q = $q;
|
|
||||||
this.navigationService = navigationService;
|
this.navigationService = navigationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,8 +50,7 @@ define(
|
|||||||
* fulfilled when the action has completed.
|
* fulfilled when the action has completed.
|
||||||
*/
|
*/
|
||||||
RemoveAction.prototype.perform = function () {
|
RemoveAction.prototype.perform = function () {
|
||||||
var $q = this.$q,
|
var navigationService = this.navigationService,
|
||||||
navigationService = this.navigationService,
|
|
||||||
domainObject = this.domainObject;
|
domainObject = this.domainObject;
|
||||||
/*
|
/*
|
||||||
* Check whether an object ID matches the ID of the object being
|
* Check whether an object ID matches the ID of the object being
|
||||||
@@ -71,15 +69,6 @@ define(
|
|||||||
model.composition = model.composition.filter(isNotObject);
|
model.composition = model.composition.filter(isNotObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Invoke persistence on a domain object. This will be called upon
|
|
||||||
* the removed object's parent (as its composition will have changed.)
|
|
||||||
*/
|
|
||||||
function doPersist(domainObj) {
|
|
||||||
var persistence = domainObj.getCapability('persistence');
|
|
||||||
return persistence && persistence.persist();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks current object and ascendants of current
|
* Checks current object and ascendants of current
|
||||||
* object with object being removed, if the current
|
* object with object being removed, if the current
|
||||||
@@ -119,15 +108,10 @@ define(
|
|||||||
// navigates to existing object up tree
|
// navigates to existing object up tree
|
||||||
checkObjectNavigation(object, parent);
|
checkObjectNavigation(object, parent);
|
||||||
|
|
||||||
return $q.when(
|
return parent.useCapability('mutation', doMutate);
|
||||||
parent.useCapability('mutation', doMutate)
|
|
||||||
).then(function () {
|
|
||||||
return doPersist(parent);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $q.when(domainObject)
|
return removeFromContext(domainObject);
|
||||||
.then(removeFromContext);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Object needs to have a parent for Remove to be applicable
|
// Object needs to have a parent for Remove to be applicable
|
||||||
|
|||||||
@@ -25,9 +25,8 @@ define(
|
|||||||
function (SaveInProgressDialog) {
|
function (SaveInProgressDialog) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "Save" action; the action triggered by clicking Save from
|
* The "Save" action; it invokes object capabilities to persist
|
||||||
* Edit Mode. Exits the editing user interface and invokes object
|
* the changes that have been made.
|
||||||
* capabilities to persist the changes that have been made.
|
|
||||||
* @constructor
|
* @constructor
|
||||||
* @implements {Action}
|
* @implements {Action}
|
||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
@@ -41,7 +40,7 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save changes and conclude editing.
|
* Save changes.
|
||||||
*
|
*
|
||||||
* @returns {Promise} a promise that will be fulfilled when
|
* @returns {Promise} a promise that will be fulfilled when
|
||||||
* cancellation has completed
|
* cancellation has completed
|
||||||
@@ -51,40 +50,22 @@ define(
|
|||||||
var domainObject = this.domainObject,
|
var domainObject = this.domainObject,
|
||||||
dialog = new SaveInProgressDialog(this.dialogService);
|
dialog = new SaveInProgressDialog(this.dialogService);
|
||||||
|
|
||||||
function resolveWith(object) {
|
|
||||||
return function () {
|
|
||||||
return object;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invoke any save behavior introduced by the editor capability;
|
// Invoke any save behavior introduced by the editor capability;
|
||||||
// this is introduced by EditableDomainObject which is
|
// this is introduced by EditableDomainObject which is
|
||||||
// used to insulate underlying objects from changes made
|
// used to insulate underlying objects from changes made
|
||||||
// during editing.
|
// during editing.
|
||||||
function doSave() {
|
function doSave() {
|
||||||
return domainObject.getCapability("editor").save()
|
return domainObject.getCapability("editor").save();
|
||||||
.then(resolveWith(domainObject));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discard the current root view (which will be the editing
|
function hideBlockingDialog() {
|
||||||
// UI, which will have been pushed atop the Browse UI.)
|
|
||||||
function returnToBrowse(object) {
|
|
||||||
if (object) {
|
|
||||||
object.getCapability("action").perform("navigate");
|
|
||||||
}
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideBlockingDialog(object) {
|
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
return object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
|
||||||
return doSave()
|
return doSave()
|
||||||
.then(hideBlockingDialog)
|
.then(hideBlockingDialog)
|
||||||
.then(returnToBrowse)
|
|
||||||
.catch(hideBlockingDialog);
|
.catch(hideBlockingDialog);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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(
|
||||||
|
["./SaveAction"],
|
||||||
|
function (SaveAction) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "Save and Stop Editing" action performs a [Save action]{@link SaveAction}
|
||||||
|
* on the object under edit followed by exiting the edit user interface.
|
||||||
|
* @constructor
|
||||||
|
* @implements {Action}
|
||||||
|
* @memberof platform/commonUI/edit
|
||||||
|
*/
|
||||||
|
function SaveAndStopEditingAction(
|
||||||
|
dialogService,
|
||||||
|
context
|
||||||
|
) {
|
||||||
|
this.context = context;
|
||||||
|
this.domainObject = (context || {}).domainObject;
|
||||||
|
this.dialogService = dialogService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger a save operation and exit edit mode.
|
||||||
|
*
|
||||||
|
* @returns {Promise} a promise that will be fulfilled when
|
||||||
|
* cancellation has completed
|
||||||
|
* @memberof platform/commonUI/edit.SaveAndStopEditingAction#
|
||||||
|
*/
|
||||||
|
SaveAndStopEditingAction.prototype.perform = function () {
|
||||||
|
var domainObject = this.domainObject,
|
||||||
|
saveAction = new SaveAction(this.dialogService, this.context);
|
||||||
|
|
||||||
|
function closeEditor() {
|
||||||
|
return domainObject.getCapability("editor").finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
return saveAction.perform()
|
||||||
|
.then(closeEditor)
|
||||||
|
.catch(closeEditor);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this action is applicable in a given context.
|
||||||
|
* This will ensure that a domain object is present in the context,
|
||||||
|
* and that this domain object is in Edit mode.
|
||||||
|
* @returns true if applicable
|
||||||
|
*/
|
||||||
|
SaveAndStopEditingAction.appliesTo = SaveAction.appliesTo;
|
||||||
|
|
||||||
|
return SaveAndStopEditingAction;
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -42,7 +42,6 @@ define([
|
|||||||
$injector,
|
$injector,
|
||||||
policyService,
|
policyService,
|
||||||
dialogService,
|
dialogService,
|
||||||
creationService,
|
|
||||||
copyService,
|
copyService,
|
||||||
context
|
context
|
||||||
) {
|
) {
|
||||||
@@ -52,7 +51,6 @@ define([
|
|||||||
};
|
};
|
||||||
this.policyService = policyService;
|
this.policyService = policyService;
|
||||||
this.dialogService = dialogService;
|
this.dialogService = dialogService;
|
||||||
this.creationService = creationService;
|
|
||||||
this.copyService = copyService;
|
this.copyService = copyService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,11 +164,18 @@ define([
|
|||||||
.then(resolveWith(object));
|
.then(resolveWith(object));
|
||||||
}
|
}
|
||||||
|
|
||||||
function commitEditingAfterClone(clonedObject) {
|
function saveAfterClone(clonedObject) {
|
||||||
return domainObject.getCapability("editor").save()
|
return domainObject.getCapability("editor").save()
|
||||||
.then(resolveWith(clonedObject));
|
.then(resolveWith(clonedObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function finishEditing(clonedObject) {
|
||||||
|
return domainObject.getCapability("editor").finish()
|
||||||
|
.then(function () {
|
||||||
|
return fetchObject(clonedObject.getId());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function onFailure() {
|
function onFailure() {
|
||||||
hideBlockingDialog();
|
hideBlockingDialog();
|
||||||
return false;
|
return false;
|
||||||
@@ -182,7 +187,8 @@ define([
|
|||||||
.then(getParent)
|
.then(getParent)
|
||||||
.then(cloneIntoParent)
|
.then(cloneIntoParent)
|
||||||
.then(undirtyOriginals)
|
.then(undirtyOriginals)
|
||||||
.then(commitEditingAfterClone)
|
.then(saveAfterClone)
|
||||||
|
.then(finishEditing)
|
||||||
.then(hideBlockingDialog)
|
.then(hideBlockingDialog)
|
||||||
.catch(onFailure);
|
.catch(onFailure);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ define([], function () {
|
|||||||
title: "Saving...",
|
title: "Saving...",
|
||||||
hint: "Do not navigate away from this page or close this browser tab while this message is displayed.",
|
hint: "Do not navigate away from this page or close this browser tab while this message is displayed.",
|
||||||
unknownProgress: true,
|
unknownProgress: true,
|
||||||
severity: "info"
|
severity: "info",
|
||||||
|
delay: true
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ define(
|
|||||||
* A capability that implements an editing 'session' for a domain
|
* A capability that implements an editing 'session' for a domain
|
||||||
* object. An editing session is initiated via a call to .edit().
|
* object. An editing session is initiated via a call to .edit().
|
||||||
* Once initiated, any persist operations will be queued pending a
|
* Once initiated, any persist operations will be queued pending a
|
||||||
* subsequent call to [.save()](@link #save) or [.cancel()](@link
|
* subsequent call to [.save()](@link #save) or [.finish()](@link
|
||||||
* #cancel).
|
* #finish).
|
||||||
* @param transactionService
|
* @param transactionService
|
||||||
* @param domainObject
|
* @param domainObject
|
||||||
* @constructor
|
* @constructor
|
||||||
@@ -45,7 +45,7 @@ define(
|
|||||||
/**
|
/**
|
||||||
* Initiate an editing session. This will start a transaction during
|
* Initiate an editing session. This will start a transaction during
|
||||||
* which any persist operations will be deferred until either save()
|
* which any persist operations will be deferred until either save()
|
||||||
* or cancel() are called.
|
* or finish() are called.
|
||||||
*/
|
*/
|
||||||
EditorCapability.prototype.edit = function () {
|
EditorCapability.prototype.edit = function () {
|
||||||
this.transactionService.startTransaction();
|
this.transactionService.startTransaction();
|
||||||
@@ -81,25 +81,25 @@ define(
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save any changes from this editing session. This will flush all
|
* Save any unsaved changes from this editing session. This will
|
||||||
* pending persists and end the current transaction
|
* end the current transaction and continue with a new one.
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
EditorCapability.prototype.save = function () {
|
EditorCapability.prototype.save = function () {
|
||||||
var domainObject = this.domainObject;
|
var transactionService = this.transactionService;
|
||||||
return this.transactionService.commit().then(function () {
|
return transactionService.commit().then(function () {
|
||||||
domainObject.getCapability('status').set('editing', false);
|
transactionService.startTransaction();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
EditorCapability.prototype.invoke = EditorCapability.prototype.edit;
|
EditorCapability.prototype.invoke = EditorCapability.prototype.edit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel the current editing session. This will discard any pending
|
* Finish the current editing session. This will discard any pending
|
||||||
* persist operations
|
* persist operations
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
EditorCapability.prototype.cancel = function () {
|
EditorCapability.prototype.finish = function () {
|
||||||
var domainObject = this.domainObject;
|
var domainObject = this.domainObject;
|
||||||
return this.transactionService.cancel().then(function () {
|
return this.transactionService.cancel().then(function () {
|
||||||
domainObject.getCapability("status").set("editing", false);
|
domainObject.getCapability("status").set("editing", false);
|
||||||
|
|||||||
@@ -81,6 +81,10 @@ define(
|
|||||||
return this.persistenceCapability.getSpace();
|
return this.persistenceCapability.getSpace();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TransactionalPersistenceCapability.prototype.persisted = function () {
|
||||||
|
return this.persistenceCapability.persisted();
|
||||||
|
};
|
||||||
|
|
||||||
return TransactionalPersistenceCapability;
|
return TransactionalPersistenceCapability;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ define(
|
|||||||
[],
|
[],
|
||||||
function () {
|
function () {
|
||||||
|
|
||||||
var ACTION_CONTEXT = { category: 'conclude-editing' };
|
var SAVE_ACTION_CONTEXT = { category: 'save' };
|
||||||
|
var OTHERS_ACTION_CONTEXT = { category: 'conclude-editing' };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller which supplies action instances for Save/Cancel.
|
* Controller which supplies action instances for Save/Cancel.
|
||||||
@@ -35,12 +36,37 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EditActionController($scope) {
|
function EditActionController($scope) {
|
||||||
// Maintain all "conclude-editing" actions in the present
|
|
||||||
// context.
|
function actionToMenuOption(action) {
|
||||||
|
return {
|
||||||
|
key: action,
|
||||||
|
name: action.getMetadata().name,
|
||||||
|
cssclass: action.getMetadata().cssclass
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maintain all "conclude-editing" and "save" actions in the
|
||||||
|
// present context.
|
||||||
function updateActions() {
|
function updateActions() {
|
||||||
$scope.editActions = $scope.action ?
|
$scope.saveActions = $scope.action ?
|
||||||
$scope.action.getActions(ACTION_CONTEXT) :
|
$scope.action.getActions(SAVE_ACTION_CONTEXT) :
|
||||||
[];
|
[];
|
||||||
|
|
||||||
|
$scope.saveActionsAsMenuOptions = $scope.saveActions.map(actionToMenuOption);
|
||||||
|
|
||||||
|
$scope.saveActionMenuClickHandler = function (clickedAction) {
|
||||||
|
clickedAction.perform();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.otherEditActions = $scope.action ?
|
||||||
|
$scope.action.getActions(OTHERS_ACTION_CONTEXT) :
|
||||||
|
[];
|
||||||
|
|
||||||
|
// Required because Angular does not allow 'bind'
|
||||||
|
// in expressions.
|
||||||
|
$scope.actionPerformer = function (action) {
|
||||||
|
return action.perform.bind(action);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update set of actions whenever the action capability
|
// Update set of actions whenever the action capability
|
||||||
|
|||||||
@@ -67,12 +67,17 @@ define(
|
|||||||
editAction,
|
editAction,
|
||||||
editorCapability;
|
editorCapability;
|
||||||
|
|
||||||
|
function closeEditor() {
|
||||||
|
return editorCapability.finish();
|
||||||
|
}
|
||||||
|
|
||||||
function onSave() {
|
function onSave() {
|
||||||
return editorCapability.save();
|
return editorCapability.save()
|
||||||
|
.then(closeEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onCancel() {
|
function onCancel() {
|
||||||
return editorCapability.cancel();
|
return closeEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
newModel.type = this.type.getKey();
|
newModel.type = this.type.getKey();
|
||||||
@@ -85,9 +90,9 @@ define(
|
|||||||
if (editAction) {
|
if (editAction) {
|
||||||
return editAction.perform();
|
return editAction.perform();
|
||||||
} else if (editorCapability) {
|
} else if (editorCapability) {
|
||||||
//otherwise, use the save action
|
//otherwise, use the save as action
|
||||||
editorCapability.edit();
|
editorCapability.edit();
|
||||||
return newObject.getCapability("action").perform("save").then(onSave, onCancel);
|
return newObject.getCapability("action").perform("save-as").then(onSave, onCancel);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -50,17 +50,13 @@ define(
|
|||||||
this.listenHandle = undefined;
|
this.listenHandle = undefined;
|
||||||
|
|
||||||
// Mutate and persist a new version of a domain object's model.
|
// Mutate and persist a new version of a domain object's model.
|
||||||
function doPersist(model) {
|
function doMutate(model) {
|
||||||
var domainObject = self.domainObject;
|
var domainObject = self.domainObject;
|
||||||
|
|
||||||
// First, mutate; then, persist.
|
// First, mutate; then, persist.
|
||||||
return $q.when(domainObject.useCapability("mutation", function () {
|
return $q.when(domainObject.useCapability("mutation", function () {
|
||||||
return model;
|
return model;
|
||||||
})).then(function (result) {
|
}));
|
||||||
// Only persist when mutation was successful
|
|
||||||
return result &&
|
|
||||||
domainObject.getCapability("persistence").persist();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle changes to model and/or view configuration
|
// Handle changes to model and/or view configuration
|
||||||
@@ -80,14 +76,14 @@ define(
|
|||||||
].join(" "));
|
].join(" "));
|
||||||
|
|
||||||
// Update the configuration stored in the model, and persist.
|
// Update the configuration stored in the model, and persist.
|
||||||
if (domainObject && domainObject.hasCapability("persistence")) {
|
if (domainObject) {
|
||||||
// Configurations for specific views are stored by
|
// Configurations for specific views are stored by
|
||||||
// key in the "configuration" field of the model.
|
// key in the "configuration" field of the model.
|
||||||
if (self.key && configuration) {
|
if (self.key && configuration) {
|
||||||
model.configuration = model.configuration || {};
|
model.configuration = model.configuration || {};
|
||||||
model.configuration[self.key] = configuration;
|
model.configuration[self.key] = configuration;
|
||||||
}
|
}
|
||||||
doPersist(model);
|
doMutate(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
48
platform/commonUI/edit/src/services/NestedTransaction.js
Normal file
48
platform/commonUI/edit/src/services/NestedTransaction.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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(['./Transaction'], function (Transaction) {
|
||||||
|
/**
|
||||||
|
* A nested transaction is a transaction which takes place in the context
|
||||||
|
* of a larger parent transaction. It becomes part of the parent
|
||||||
|
* transaction when (and only when) committed.
|
||||||
|
* @param parent
|
||||||
|
* @constructor
|
||||||
|
* @extends {platform/commonUI/edit/services.Transaction}
|
||||||
|
* @memberof platform/commonUI/edit/services
|
||||||
|
*/
|
||||||
|
function NestedTransaction(parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
Transaction.call(this, parent.$log);
|
||||||
|
}
|
||||||
|
|
||||||
|
NestedTransaction.prototype = Object.create(Transaction.prototype);
|
||||||
|
|
||||||
|
NestedTransaction.prototype.commit = function () {
|
||||||
|
this.parent.add(
|
||||||
|
Transaction.prototype.commit.bind(this),
|
||||||
|
Transaction.prototype.cancel.bind(this)
|
||||||
|
);
|
||||||
|
return Promise.resolve(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return NestedTransaction;
|
||||||
|
});
|
||||||
96
platform/commonUI/edit/src/services/Transaction.js
Normal file
96
platform/commonUI/edit/src/services/Transaction.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 () {
|
||||||
|
/**
|
||||||
|
* A Transaction represents a set of changes that are intended to
|
||||||
|
* be kept or discarded as a unit.
|
||||||
|
* @param $log Angular's `$log` service, for logging messages
|
||||||
|
* @constructor
|
||||||
|
* @memberof platform/commonUI/edit/services
|
||||||
|
*/
|
||||||
|
function Transaction($log) {
|
||||||
|
this.$log = $log;
|
||||||
|
this.callbacks = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a change to the current transaction, as expressed by functions
|
||||||
|
* to either keep or discard the change.
|
||||||
|
* @param {Function} commit called when the transaction is committed
|
||||||
|
* @param {Function} cancel called when the transaction is cancelled
|
||||||
|
* @returns {Function) a function which may be called to remove this
|
||||||
|
* pair of callbacks from the transaction
|
||||||
|
*/
|
||||||
|
Transaction.prototype.add = function (commit, cancel) {
|
||||||
|
var callback = { commit: commit, cancel: cancel };
|
||||||
|
this.callbacks.push(callback);
|
||||||
|
return function () {
|
||||||
|
this.callbacks = this.callbacks.filter(function (c) {
|
||||||
|
return c !== callback;
|
||||||
|
});
|
||||||
|
}.bind(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of changes in the current transaction.
|
||||||
|
* @returns {number} the size of the current transaction
|
||||||
|
*/
|
||||||
|
Transaction.prototype.size = function () {
|
||||||
|
return this.callbacks.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep all changes associated with this transaction.
|
||||||
|
* @method {platform/commonUI/edit/services.Transaction#commit}
|
||||||
|
* @returns {Promise} a promise which will resolve when all callbacks
|
||||||
|
* have been handled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discard all changes associated with this transaction.
|
||||||
|
* @method {platform/commonUI/edit/services.Transaction#cancel}
|
||||||
|
* @returns {Promise} a promise which will resolve when all callbacks
|
||||||
|
* have been handled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
['commit', 'cancel'].forEach(function (method) {
|
||||||
|
Transaction.prototype[method] = function () {
|
||||||
|
var promises = [];
|
||||||
|
var callback;
|
||||||
|
|
||||||
|
while (this.callbacks.length > 0) {
|
||||||
|
callback = this.callbacks.shift();
|
||||||
|
try {
|
||||||
|
promises.push(callback[method]());
|
||||||
|
} catch (e) {
|
||||||
|
this.$log
|
||||||
|
.error("Error trying to " + method + " transaction.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return Transaction;
|
||||||
|
});
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/*global define*/
|
/*global define*/
|
||||||
define(
|
define(
|
||||||
[],
|
['./Transaction', './NestedTransaction'],
|
||||||
function () {
|
function (Transaction, NestedTransaction) {
|
||||||
/**
|
/**
|
||||||
* Implements an application-wide transaction state. Once a
|
* Implements an application-wide transaction state. Once a
|
||||||
* transaction is started, calls to
|
* transaction is started, calls to
|
||||||
@@ -34,13 +34,11 @@ 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.transaction = false;
|
this.cacheService = cacheService;
|
||||||
|
this.transactions = [];
|
||||||
this.onCommits = [];
|
|
||||||
this.onCancels = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,18 +48,18 @@ define(
|
|||||||
* #cancel} are called
|
* #cancel} are called
|
||||||
*/
|
*/
|
||||||
TransactionService.prototype.startTransaction = function () {
|
TransactionService.prototype.startTransaction = function () {
|
||||||
if (this.transaction) {
|
var transaction = this.isActive() ?
|
||||||
//Log error because this is a programming error if it occurs.
|
new NestedTransaction(this.transactions[0]) :
|
||||||
this.$log.error("Transaction already in progress");
|
new Transaction(this.$log);
|
||||||
}
|
|
||||||
this.transaction = true;
|
this.transactions.push(transaction);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {boolean} If true, indicates that a transaction is in progress
|
* @returns {boolean} If true, indicates that a transaction is in progress
|
||||||
*/
|
*/
|
||||||
TransactionService.prototype.isActive = function () {
|
TransactionService.prototype.isActive = function () {
|
||||||
return this.transaction;
|
return this.transactions.length > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,52 +70,43 @@ define(
|
|||||||
* @param onCancel A function to call on cancel
|
* @param onCancel A function to call on cancel
|
||||||
*/
|
*/
|
||||||
TransactionService.prototype.addToTransaction = function (onCommit, onCancel) {
|
TransactionService.prototype.addToTransaction = function (onCommit, onCancel) {
|
||||||
if (this.transaction) {
|
if (this.isActive()) {
|
||||||
this.onCommits.push(onCommit);
|
return this.activeTransaction().add(onCommit, onCancel);
|
||||||
if (onCancel) {
|
|
||||||
this.onCancels.push(onCancel);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
//Log error because this is a programming error if it occurs.
|
//Log error because this is a programming error if it occurs.
|
||||||
this.$log.error("No transaction in progress");
|
this.$log.error("No transaction in progress");
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return function () {
|
/**
|
||||||
this.onCommits = this.onCommits.filter(function (callback) {
|
* Get the transaction at the top of the stack.
|
||||||
return callback !== onCommit;
|
* @private
|
||||||
});
|
*/
|
||||||
this.onCancels = this.onCancels.filter(function (callback) {
|
TransactionService.prototype.activeTransaction = function () {
|
||||||
return callback !== onCancel;
|
return this.transactions[this.transactions.length - 1];
|
||||||
});
|
|
||||||
}.bind(this);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 self = this,
|
var transaction = this.transactions.pop();
|
||||||
promises = [],
|
if (!transaction) {
|
||||||
onCommit;
|
return Promise.reject();
|
||||||
|
|
||||||
while (this.onCommits.length > 0) { // ...using a while in case some onCommit adds to transaction
|
|
||||||
onCommit = this.onCommits.pop();
|
|
||||||
try { // ...also don't want to fail mid-loop...
|
|
||||||
promises.push(onCommit());
|
|
||||||
} catch (e) {
|
|
||||||
this.$log.error("Error committing transaction.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return this.$q.all(promises).then(function () {
|
if (!this.isActive()) {
|
||||||
self.transaction = false;
|
return transaction.commit()
|
||||||
|
.then(function (r) {
|
||||||
self.onCommits = [];
|
this.cacheService.flush();
|
||||||
self.onCancels = [];
|
return r;
|
||||||
});
|
}.bind(this));
|
||||||
|
}
|
||||||
|
return transaction.commit();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -129,28 +118,17 @@ define(
|
|||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
TransactionService.prototype.cancel = function () {
|
TransactionService.prototype.cancel = function () {
|
||||||
var self = this,
|
var transaction = this.transactions.pop();
|
||||||
results = [],
|
return transaction ? transaction.cancel() : Promise.reject();
|
||||||
onCancel;
|
|
||||||
|
|
||||||
while (this.onCancels.length > 0) {
|
|
||||||
onCancel = this.onCancels.pop();
|
|
||||||
try {
|
|
||||||
results.push(onCancel());
|
|
||||||
} catch (error) {
|
|
||||||
this.$log.error("Error committing transaction.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.$q.all(results).then(function () {
|
|
||||||
self.transaction = false;
|
|
||||||
|
|
||||||
self.onCommits = [];
|
|
||||||
self.onCancels = [];
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size (the number of commit/cancel callbacks) of
|
||||||
|
* the active transaction.
|
||||||
|
* @returns {number} size of the active transaction
|
||||||
|
*/
|
||||||
TransactionService.prototype.size = function () {
|
TransactionService.prototype.size = function () {
|
||||||
return this.onCommits.length;
|
return this.isActive() ? this.activeTransaction().size() : 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
return TransactionService;
|
return TransactionService;
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ define(
|
|||||||
|
|
||||||
capabilities.editor = jasmine.createSpyObj(
|
capabilities.editor = jasmine.createSpyObj(
|
||||||
"editor",
|
"editor",
|
||||||
["save", "cancel", "isEditContextRoot"]
|
["save", "finish", "isEditContextRoot"]
|
||||||
);
|
);
|
||||||
capabilities.action = jasmine.createSpyObj(
|
capabilities.action = jasmine.createSpyObj(
|
||||||
"actionCapability",
|
"actionCapability",
|
||||||
@@ -105,7 +105,7 @@ define(
|
|||||||
return !!capabilities[name];
|
return !!capabilities[name];
|
||||||
});
|
});
|
||||||
|
|
||||||
capabilities.editor.cancel.andReturn(mockPromise(true));
|
capabilities.editor.finish.andReturn(mockPromise(true));
|
||||||
|
|
||||||
action = new CancelAction(actionContext);
|
action = new CancelAction(actionContext);
|
||||||
|
|
||||||
@@ -130,8 +130,8 @@ define(
|
|||||||
capabilities.action.perform.andReturn(mockPromise(true));
|
capabilities.action.perform.andReturn(mockPromise(true));
|
||||||
action.perform();
|
action.perform();
|
||||||
|
|
||||||
// Should have called cancel
|
// Should have called finish
|
||||||
expect(capabilities.editor.cancel).toHaveBeenCalled();
|
expect(capabilities.editor.finish).toHaveBeenCalled();
|
||||||
|
|
||||||
// Definitely shouldn't call save!
|
// Definitely shouldn't call save!
|
||||||
expect(capabilities.editor.save).not.toHaveBeenCalled();
|
expect(capabilities.editor.save).not.toHaveBeenCalled();
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ define(
|
|||||||
);
|
);
|
||||||
mockEditor = jasmine.createSpyObj(
|
mockEditor = jasmine.createSpyObj(
|
||||||
"editorCapability",
|
"editorCapability",
|
||||||
["edit", "isEditContextRoot", "cancel"]
|
["edit", "isEditContextRoot"]
|
||||||
);
|
);
|
||||||
|
|
||||||
capabilities = {
|
capabilities = {
|
||||||
@@ -98,13 +98,6 @@ define(
|
|||||||
expect(EditAction.appliesTo(actionContext)).toBe(false);
|
expect(EditAction.appliesTo(actionContext)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it ("cancels editing when user navigates away", function () {
|
|
||||||
action.perform();
|
|
||||||
expect(mockNavigationService.addListener).toHaveBeenCalled();
|
|
||||||
mockNavigationService.addListener.mostRecentCall.args[0]();
|
|
||||||
expect(mockEditor.cancel).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it ("invokes the Edit capability on the object", function () {
|
it ("invokes the Edit capability on the object", function () {
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(mockDomainObject.useCapability).toHaveBeenCalledWith("editor");
|
expect(mockDomainObject.useCapability).toHaveBeenCalledWith("editor");
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ define(
|
|||||||
mockParent,
|
mockParent,
|
||||||
mockContext,
|
mockContext,
|
||||||
mockComposition,
|
mockComposition,
|
||||||
mockPersistence,
|
|
||||||
mockActionCapability,
|
mockActionCapability,
|
||||||
mockEditAction,
|
mockEditAction,
|
||||||
mockType,
|
mockType,
|
||||||
@@ -68,7 +67,6 @@ define(
|
|||||||
};
|
};
|
||||||
mockContext = jasmine.createSpyObj("context", ["getParent"]);
|
mockContext = jasmine.createSpyObj("context", ["getParent"]);
|
||||||
mockComposition = jasmine.createSpyObj("composition", ["invoke", "add"]);
|
mockComposition = jasmine.createSpyObj("composition", ["invoke", "add"]);
|
||||||
mockPersistence = jasmine.createSpyObj("persistence", ["persist"]);
|
|
||||||
mockType = jasmine.createSpyObj("type", ["hasFeature", "getKey"]);
|
mockType = jasmine.createSpyObj("type", ["hasFeature", "getKey"]);
|
||||||
mockActionCapability = jasmine.createSpyObj("actionCapability", ["getActions"]);
|
mockActionCapability = jasmine.createSpyObj("actionCapability", ["getActions"]);
|
||||||
mockEditAction = jasmine.createSpyObj("editAction", ["perform"]);
|
mockEditAction = jasmine.createSpyObj("editAction", ["perform"]);
|
||||||
@@ -84,7 +82,6 @@ define(
|
|||||||
|
|
||||||
capabilities = {
|
capabilities = {
|
||||||
composition: mockComposition,
|
composition: mockComposition,
|
||||||
persistence: mockPersistence,
|
|
||||||
action: mockActionCapability,
|
action: mockActionCapability,
|
||||||
type: mockType
|
type: mockType
|
||||||
};
|
};
|
||||||
@@ -107,11 +104,6 @@ define(
|
|||||||
.toHaveBeenCalledWith(mockDomainObject);
|
.toHaveBeenCalledWith(mockDomainObject);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("persists changes afterward", function () {
|
|
||||||
action.perform();
|
|
||||||
expect(mockPersistence.persist).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("enables edit mode for objects that have an edit action", function () {
|
it("enables edit mode for objects that have an edit action", function () {
|
||||||
mockActionCapability.getActions.andReturn([mockEditAction]);
|
mockActionCapability.getActions.andReturn([mockEditAction]);
|
||||||
action.perform();
|
action.perform();
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ define(
|
|||||||
},
|
},
|
||||||
hasFeature: jasmine.createSpy('hasFeature')
|
hasFeature: jasmine.createSpy('hasFeature')
|
||||||
},
|
},
|
||||||
persistence: jasmine.createSpyObj("persistence", ["persist"]),
|
|
||||||
mutation: jasmine.createSpy("mutation")
|
mutation: jasmine.createSpy("mutation")
|
||||||
};
|
};
|
||||||
model = {};
|
model = {};
|
||||||
@@ -78,25 +77,18 @@ define(
|
|||||||
action = new PropertiesAction(dialogService, context);
|
action = new PropertiesAction(dialogService, context);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("persists when an action is performed", function () {
|
|
||||||
action.perform();
|
|
||||||
expect(capabilities.persistence.persist)
|
|
||||||
.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not persist any changes upon cancel", function () {
|
|
||||||
input = undefined;
|
|
||||||
action.perform();
|
|
||||||
expect(capabilities.persistence.persist)
|
|
||||||
.not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("mutates an object when performed", function () {
|
it("mutates an object when performed", function () {
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(capabilities.mutation).toHaveBeenCalled();
|
expect(capabilities.mutation).toHaveBeenCalled();
|
||||||
capabilities.mutation.mostRecentCall.args[0]({});
|
capabilities.mutation.mostRecentCall.args[0]({});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not muate object upon cancel", function () {
|
||||||
|
input = undefined;
|
||||||
|
action.perform();
|
||||||
|
expect(capabilities.mutation).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it("is only applicable when a domain object is in context", function () {
|
it("is only applicable when a domain object is in context", function () {
|
||||||
expect(PropertiesAction.appliesTo(context)).toBeTruthy();
|
expect(PropertiesAction.appliesTo(context)).toBeTruthy();
|
||||||
expect(PropertiesAction.appliesTo({})).toBeFalsy();
|
expect(PropertiesAction.appliesTo({})).toBeFalsy();
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ define(
|
|||||||
mockGrandchildContext,
|
mockGrandchildContext,
|
||||||
mockRootContext,
|
mockRootContext,
|
||||||
mockMutation,
|
mockMutation,
|
||||||
mockPersistence,
|
|
||||||
mockType,
|
mockType,
|
||||||
actionContext,
|
actionContext,
|
||||||
model,
|
model,
|
||||||
@@ -53,8 +52,6 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
|
||||||
|
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
"domainObject",
|
"domainObject",
|
||||||
["getId", "getCapability"]
|
["getId", "getCapability"]
|
||||||
@@ -88,7 +85,6 @@ define(
|
|||||||
mockGrandchildContext = jasmine.createSpyObj("context", ["getParent"]);
|
mockGrandchildContext = jasmine.createSpyObj("context", ["getParent"]);
|
||||||
mockRootContext = jasmine.createSpyObj("context", ["getParent"]);
|
mockRootContext = jasmine.createSpyObj("context", ["getParent"]);
|
||||||
mockMutation = jasmine.createSpyObj("mutation", ["invoke"]);
|
mockMutation = jasmine.createSpyObj("mutation", ["invoke"]);
|
||||||
mockPersistence = jasmine.createSpyObj("persistence", ["persist"]);
|
|
||||||
mockType = jasmine.createSpyObj("type", ["hasFeature"]);
|
mockType = jasmine.createSpyObj("type", ["hasFeature"]);
|
||||||
mockNavigationService = jasmine.createSpyObj(
|
mockNavigationService = jasmine.createSpyObj(
|
||||||
"navigationService",
|
"navigationService",
|
||||||
@@ -109,7 +105,6 @@ define(
|
|||||||
|
|
||||||
capabilities = {
|
capabilities = {
|
||||||
mutation: mockMutation,
|
mutation: mockMutation,
|
||||||
persistence: mockPersistence,
|
|
||||||
type: mockType
|
type: mockType
|
||||||
};
|
};
|
||||||
model = {
|
model = {
|
||||||
@@ -118,7 +113,7 @@ define(
|
|||||||
|
|
||||||
actionContext = { domainObject: mockDomainObject };
|
actionContext = { domainObject: mockDomainObject };
|
||||||
|
|
||||||
action = new RemoveAction(mockQ, mockNavigationService, actionContext);
|
action = new RemoveAction(mockNavigationService, actionContext);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("only applies to objects with parents", function () {
|
it("only applies to objects with parents", function () {
|
||||||
@@ -154,9 +149,6 @@ define(
|
|||||||
// Should have removed "test" - that was our
|
// Should have removed "test" - that was our
|
||||||
// mock domain object's id.
|
// mock domain object's id.
|
||||||
expect(result.composition).toEqual(["a", "b"]);
|
expect(result.composition).toEqual(["a", "b"]);
|
||||||
|
|
||||||
// Finally, should have persisted
|
|
||||||
expect(mockPersistence.persist).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("removes parent of object currently navigated to", function () {
|
it("removes parent of object currently navigated to", function () {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ define(
|
|||||||
);
|
);
|
||||||
mockEditorCapability = jasmine.createSpyObj(
|
mockEditorCapability = jasmine.createSpyObj(
|
||||||
"editor",
|
"editor",
|
||||||
["save", "cancel", "isEditContextRoot"]
|
["save", "isEditContextRoot"]
|
||||||
);
|
);
|
||||||
mockActionCapability = jasmine.createSpyObj(
|
mockActionCapability = jasmine.createSpyObj(
|
||||||
"actionCapability",
|
"actionCapability",
|
||||||
@@ -105,12 +105,6 @@ define(
|
|||||||
expect(mockEditorCapability.save).toHaveBeenCalled();
|
expect(mockEditorCapability.save).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("navigates to the object after saving",
|
|
||||||
function () {
|
|
||||||
action.perform();
|
|
||||||
expect(mockActionCapability.perform).toHaveBeenCalledWith("navigate");
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("a blocking dialog", function () {
|
describe("a blocking dialog", function () {
|
||||||
var mockDialogHandle;
|
var mockDialogHandle;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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/actions/SaveAndStopEditingAction"],
|
||||||
|
function (SaveAndStopEditingAction) {
|
||||||
|
|
||||||
|
describe("The Save and Stop Editing action", function () {
|
||||||
|
|
||||||
|
// Some mocks appear unused because the
|
||||||
|
// underlying SaveAction that this action
|
||||||
|
// depends on is not mocked, so we mock some
|
||||||
|
// of SaveAction's own dependencies to make
|
||||||
|
// it run.
|
||||||
|
var mockDomainObject,
|
||||||
|
mockEditorCapability,
|
||||||
|
actionContext,
|
||||||
|
dialogService,
|
||||||
|
mockActionCapability,
|
||||||
|
capabilities = {},
|
||||||
|
action;
|
||||||
|
|
||||||
|
function mockPromise(value) {
|
||||||
|
return {
|
||||||
|
then: function (callback) {
|
||||||
|
return mockPromise(callback(value));
|
||||||
|
},
|
||||||
|
catch: function (callback) {
|
||||||
|
return mockPromise(callback(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
|
"domainObject",
|
||||||
|
[
|
||||||
|
"getCapability",
|
||||||
|
"hasCapability",
|
||||||
|
"getModel",
|
||||||
|
"getOriginalObject"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
mockEditorCapability = jasmine.createSpyObj(
|
||||||
|
"editor",
|
||||||
|
["save", "finish", "isEditContextRoot"]
|
||||||
|
);
|
||||||
|
mockActionCapability = jasmine.createSpyObj(
|
||||||
|
"actionCapability",
|
||||||
|
["perform"]
|
||||||
|
);
|
||||||
|
capabilities.editor = mockEditorCapability;
|
||||||
|
capabilities.action = mockActionCapability;
|
||||||
|
|
||||||
|
actionContext = {
|
||||||
|
domainObject: mockDomainObject
|
||||||
|
};
|
||||||
|
dialogService = jasmine.createSpyObj(
|
||||||
|
"dialogService",
|
||||||
|
["showBlockingMessage"]
|
||||||
|
);
|
||||||
|
|
||||||
|
mockDomainObject.hasCapability.andReturn(true);
|
||||||
|
mockDomainObject.getCapability.andCallFake(function (capability) {
|
||||||
|
return capabilities[capability];
|
||||||
|
});
|
||||||
|
mockDomainObject.getModel.andReturn({ persisted: 0 });
|
||||||
|
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||||
|
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||||
|
|
||||||
|
action = new SaveAndStopEditingAction(dialogService, actionContext);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("only applies to domain object with an editor capability", function () {
|
||||||
|
expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(true);
|
||||||
|
expect(mockDomainObject.hasCapability).toHaveBeenCalledWith("editor");
|
||||||
|
|
||||||
|
mockDomainObject.hasCapability.andReturn(false);
|
||||||
|
mockDomainObject.getCapability.andReturn(undefined);
|
||||||
|
expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("only applies to domain object that has already been persisted", function () {
|
||||||
|
mockDomainObject.getModel.andReturn({ persisted: undefined });
|
||||||
|
expect(SaveAndStopEditingAction.appliesTo(actionContext)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not close the editor before completing the save", function () {
|
||||||
|
mockEditorCapability.save.andReturn(new Promise(function () {
|
||||||
|
}));
|
||||||
|
action.perform();
|
||||||
|
expect(mockEditorCapability.save).toHaveBeenCalled();
|
||||||
|
expect(mockEditorCapability.finish).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("closes the editor after saving", function () {
|
||||||
|
action.perform();
|
||||||
|
expect(mockEditorCapability.save).toHaveBeenCalled();
|
||||||
|
expect(mockEditorCapability.finish).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -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 describe,it,expect,beforeEach,jasmine*/
|
/*global describe,it,expect,beforeEach,jasmine,runs,waitsFor,spyOn*/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
["../../src/actions/SaveAsAction"],
|
["../../src/actions/SaveAsAction"],
|
||||||
@@ -33,7 +33,6 @@ define(
|
|||||||
mockDialogService,
|
mockDialogService,
|
||||||
mockCopyService,
|
mockCopyService,
|
||||||
mockParent,
|
mockParent,
|
||||||
mockUrlService,
|
|
||||||
actionContext,
|
actionContext,
|
||||||
capabilities = {},
|
capabilities = {},
|
||||||
action;
|
action;
|
||||||
@@ -78,10 +77,10 @@ define(
|
|||||||
|
|
||||||
mockEditorCapability = jasmine.createSpyObj(
|
mockEditorCapability = jasmine.createSpyObj(
|
||||||
"editor",
|
"editor",
|
||||||
["save", "cancel", "isEditContextRoot"]
|
["save", "finish", "isEditContextRoot"]
|
||||||
);
|
);
|
||||||
mockEditorCapability.cancel.andReturn(mockPromise(undefined));
|
|
||||||
mockEditorCapability.save.andReturn(mockPromise(true));
|
mockEditorCapability.save.andReturn(mockPromise(true));
|
||||||
|
mockEditorCapability.finish.andReturn(mockPromise(true));
|
||||||
mockEditorCapability.isEditContextRoot.andReturn(true);
|
mockEditorCapability.isEditContextRoot.andReturn(true);
|
||||||
capabilities.editor = mockEditorCapability;
|
capabilities.editor = mockEditorCapability;
|
||||||
|
|
||||||
@@ -113,16 +112,11 @@ define(
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
mockUrlService = jasmine.createSpyObj(
|
|
||||||
"urlService",
|
|
||||||
["urlForLocation"]
|
|
||||||
);
|
|
||||||
|
|
||||||
actionContext = {
|
actionContext = {
|
||||||
domainObject: mockDomainObject
|
domainObject: mockDomainObject
|
||||||
};
|
};
|
||||||
|
|
||||||
action = new SaveAsAction(undefined, undefined, mockDialogService, undefined, mockCopyService, actionContext);
|
action = new SaveAsAction(undefined, undefined, mockDialogService, mockCopyService, actionContext);
|
||||||
|
|
||||||
spyOn(action, "getObjectService");
|
spyOn(action, "getObjectService");
|
||||||
action.getObjectService.andReturn(mockObjectService);
|
action.getObjectService.andReturn(mockObjectService);
|
||||||
@@ -156,6 +150,28 @@ define(
|
|||||||
expect(SaveAsAction.appliesTo(actionContext)).toBe(false);
|
expect(SaveAsAction.appliesTo(actionContext)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses the editor capability to save the object", function () {
|
||||||
|
mockEditorCapability.save.andReturn(new Promise(function () {}));
|
||||||
|
runs(function () {
|
||||||
|
action.perform();
|
||||||
|
});
|
||||||
|
waitsFor(function () {
|
||||||
|
return mockEditorCapability.save.calls.length > 0;
|
||||||
|
}, "perform() should call EditorCapability.save");
|
||||||
|
runs(function () {
|
||||||
|
expect(mockEditorCapability.finish).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses the editor capability to finish editing the object", function () {
|
||||||
|
runs(function () {
|
||||||
|
action.perform();
|
||||||
|
});
|
||||||
|
waitsFor(function () {
|
||||||
|
return mockEditorCapability.finish.calls.length > 0;
|
||||||
|
}, "perform() should call EditorCapability.finish");
|
||||||
|
});
|
||||||
|
|
||||||
it("returns to browse after save", function () {
|
it("returns to browse after save", function () {
|
||||||
spyOn(action, "save");
|
spyOn(action, "save");
|
||||||
action.save.andReturn(mockPromise(mockDomainObject));
|
action.save.andReturn(mockPromise(mockDomainObject));
|
||||||
|
|||||||
@@ -134,15 +134,15 @@ define(
|
|||||||
it("commits the transaction", function () {
|
it("commits the transaction", function () {
|
||||||
expect(mockTransactionService.commit).toHaveBeenCalled();
|
expect(mockTransactionService.commit).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
it("resets the edit state", function () {
|
it("begins a new transaction", function () {
|
||||||
expect(mockStatusCapability.set).toHaveBeenCalledWith('editing', false);
|
expect(mockTransactionService.startTransaction).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("cancel", function () {
|
describe("finish", function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
capability.edit();
|
capability.edit();
|
||||||
capability.cancel();
|
capability.finish();
|
||||||
});
|
});
|
||||||
it("cancels the transaction", function () {
|
it("cancels the transaction", function () {
|
||||||
expect(mockTransactionService.cancel).toHaveBeenCalled();
|
expect(mockTransactionService.cancel).toHaveBeenCalled();
|
||||||
@@ -158,7 +158,7 @@ define(
|
|||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockDomainObject.getModel.andReturn(model);
|
mockDomainObject.getModel.andReturn(model);
|
||||||
capability.edit();
|
capability.edit();
|
||||||
capability.cancel();
|
capability.finish();
|
||||||
});
|
});
|
||||||
it("returns true if the object has been modified since it" +
|
it("returns true if the object has been modified since it" +
|
||||||
" was last persisted", function () {
|
" was last persisted", function () {
|
||||||
|
|||||||
@@ -19,22 +19,51 @@
|
|||||||
* 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/controllers/EditActionController"],
|
["../../src/controllers/EditActionController"],
|
||||||
function (EditActionController) {
|
function (EditActionController) {
|
||||||
|
|
||||||
describe("The Edit Action controller", function () {
|
describe("The Edit Action controller", function () {
|
||||||
|
var mockSaveActionMetadata = {
|
||||||
|
name: "mocked-save-action",
|
||||||
|
cssclass: "mocked-save-action-css"
|
||||||
|
};
|
||||||
|
|
||||||
|
function fakeGetActions(actionContext) {
|
||||||
|
if (actionContext.category === "save") {
|
||||||
|
var mockedSaveActions = [
|
||||||
|
jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"]),
|
||||||
|
jasmine.createSpyObj("mockSaveAction", ["getMetadata", "perform"])
|
||||||
|
];
|
||||||
|
mockedSaveActions.forEach(function (action) {
|
||||||
|
action.getMetadata.andReturn(mockSaveActionMetadata);
|
||||||
|
});
|
||||||
|
return mockedSaveActions;
|
||||||
|
} else if (actionContext.category === "conclude-editing") {
|
||||||
|
return ["a", "b", "c"];
|
||||||
|
} else {
|
||||||
|
throw "EditActionController uses a context that's not covered by tests.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var mockScope,
|
var mockScope,
|
||||||
mockActions,
|
mockActions,
|
||||||
controller;
|
controller;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockActions = jasmine.createSpyObj("action", ["getActions"]);
|
mockActions = jasmine.createSpyObj("action", ["getActions"]);
|
||||||
|
mockActions.getActions.andCallFake(fakeGetActions);
|
||||||
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
|
mockScope = jasmine.createSpyObj("$scope", ["$watch"]);
|
||||||
|
mockScope.action = mockActions;
|
||||||
controller = new EditActionController(mockScope);
|
controller = new EditActionController(mockScope);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function makeControllerUpdateActions() {
|
||||||
|
mockScope.$watch.mostRecentCall.args[1]();
|
||||||
|
}
|
||||||
|
|
||||||
it("watches scope that may change applicable actions", function () {
|
it("watches scope that may change applicable actions", function () {
|
||||||
// The action capability
|
// The action capability
|
||||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
expect(mockScope.$watch).toHaveBeenCalledWith(
|
||||||
@@ -43,16 +72,34 @@ define(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("populates the scope with grouped and ungrouped actions", function () {
|
it("populates the scope with 'save' actions", function () {
|
||||||
mockScope.action = mockActions;
|
makeControllerUpdateActions();
|
||||||
|
expect(mockScope.saveActions.length).toEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
mockActions.getActions.andReturn(["a", "b", "c"]);
|
it("converts 'save' actions to their menu counterparts", function () {
|
||||||
|
makeControllerUpdateActions();
|
||||||
|
var menuOptions = mockScope.saveActionsAsMenuOptions;
|
||||||
|
|
||||||
// Call the watch
|
expect(menuOptions.length).toEqual(2);
|
||||||
mockScope.$watch.mostRecentCall.args[1]();
|
expect(menuOptions[0].key).toEqual(mockScope.saveActions[0]);
|
||||||
|
expect(menuOptions[1].key).toEqual(mockScope.saveActions[1]);
|
||||||
|
menuOptions.forEach(function (option) {
|
||||||
|
expect(option.name).toEqual(mockSaveActionMetadata.name);
|
||||||
|
expect(option.cssclass).toEqual(mockSaveActionMetadata.cssclass);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Should have grouped and ungrouped actions in scope now
|
it("uses a click handler to perform the clicked action", function () {
|
||||||
expect(mockScope.editActions.length).toEqual(3);
|
makeControllerUpdateActions();
|
||||||
|
var sampleSaveAction = mockScope.saveActions[0];
|
||||||
|
mockScope.saveActionMenuClickHandler(sampleSaveAction);
|
||||||
|
expect(sampleSaveAction.perform).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("populates the scope with other editing actions", function () {
|
||||||
|
makeControllerUpdateActions();
|
||||||
|
expect(mockScope.otherEditActions).toEqual(["a", "b", "c"]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ define(
|
|||||||
[
|
[
|
||||||
"edit",
|
"edit",
|
||||||
"save",
|
"save",
|
||||||
"cancel"
|
"finish"
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -142,6 +142,7 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("the perform function", function () {
|
describe("the perform function", function () {
|
||||||
|
var promise = jasmine.createSpyObj("promise", ["then"]);
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
capabilities.action.getActions.andReturn([mockEditAction]);
|
capabilities.action.getActions.andReturn([mockEditAction]);
|
||||||
});
|
});
|
||||||
@@ -156,19 +157,20 @@ define(
|
|||||||
expect(mockEditAction.perform).toHaveBeenCalled();
|
expect(mockEditAction.perform).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses the save action if object does not have an edit action" +
|
it("uses the save-as action if object does not have an edit action" +
|
||||||
" available", function () {
|
" available", function () {
|
||||||
capabilities.action.getActions.andReturn([]);
|
capabilities.action.getActions.andReturn([]);
|
||||||
capabilities.action.perform.andReturn(mockPromise(undefined));
|
capabilities.action.perform.andReturn(mockPromise(undefined));
|
||||||
|
capabilities.editor.save.andReturn(promise);
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(capabilities.action.perform).toHaveBeenCalledWith("save");
|
expect(capabilities.action.perform).toHaveBeenCalledWith("save-as");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("uses to editor capability", function () {
|
describe("uses to editor capability", function () {
|
||||||
var promise = jasmine.createSpyObj("promise", ["then"]);
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
capabilities.action.getActions.andReturn([]);
|
capabilities.action.getActions.andReturn([]);
|
||||||
capabilities.action.perform.andReturn(promise);
|
capabilities.action.perform.andReturn(promise);
|
||||||
|
capabilities.editor.save.andReturn(promise);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("to save the edit if user saves dialog", function () {
|
it("to save the edit if user saves dialog", function () {
|
||||||
@@ -178,10 +180,10 @@ define(
|
|||||||
expect(capabilities.editor.save).toHaveBeenCalled();
|
expect(capabilities.editor.save).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("to cancel the edit if user cancels dialog", function () {
|
it("to finish the edit if user cancels dialog", function () {
|
||||||
action.perform();
|
action.perform();
|
||||||
promise.then.mostRecentCall.args[1]();
|
promise.then.mostRecentCall.args[1]();
|
||||||
expect(capabilities.editor.cancel).toHaveBeenCalled();
|
expect(capabilities.editor.finish).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ define(
|
|||||||
mockScope,
|
mockScope,
|
||||||
testRepresentation,
|
testRepresentation,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockPersistence,
|
|
||||||
mockStatusCapability,
|
mockStatusCapability,
|
||||||
mockEditorCapability,
|
mockEditorCapability,
|
||||||
mockCapabilities,
|
mockCapabilities,
|
||||||
@@ -56,15 +55,12 @@ define(
|
|||||||
"useCapability",
|
"useCapability",
|
||||||
"hasCapability"
|
"hasCapability"
|
||||||
]);
|
]);
|
||||||
mockPersistence =
|
|
||||||
jasmine.createSpyObj("persistence", ["persist"]);
|
|
||||||
mockStatusCapability =
|
mockStatusCapability =
|
||||||
jasmine.createSpyObj("statusCapability", ["listen"]);
|
jasmine.createSpyObj("statusCapability", ["listen"]);
|
||||||
mockEditorCapability =
|
mockEditorCapability =
|
||||||
jasmine.createSpyObj("editorCapability", ["isEditContextRoot"]);
|
jasmine.createSpyObj("editorCapability", ["isEditContextRoot"]);
|
||||||
|
|
||||||
mockCapabilities = {
|
mockCapabilities = {
|
||||||
'persistence': mockPersistence,
|
|
||||||
'status': mockStatusCapability,
|
'status': mockStatusCapability,
|
||||||
'editor': mockEditorCapability
|
'editor': mockEditorCapability
|
||||||
};
|
};
|
||||||
@@ -96,7 +92,7 @@ define(
|
|||||||
expect(representer.listenHandle).toHaveBeenCalled();
|
expect(representer.listenHandle).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("mutates and persists upon observed changes", function () {
|
it("mutates upon observed changes", function () {
|
||||||
mockScope.model = { someKey: "some value" };
|
mockScope.model = { someKey: "some value" };
|
||||||
mockScope.configuration = { someConfiguration: "something" };
|
mockScope.configuration = { someConfiguration: "something" };
|
||||||
|
|
||||||
@@ -108,9 +104,6 @@ define(
|
|||||||
jasmine.any(Function)
|
jasmine.any(Function)
|
||||||
);
|
);
|
||||||
|
|
||||||
// ... and should have persisted the mutation
|
|
||||||
expect(mockPersistence.persist).toHaveBeenCalled();
|
|
||||||
|
|
||||||
// Finally, check that the provided mutation function
|
// Finally, check that the provided mutation function
|
||||||
// includes both model and configuration
|
// includes both model and configuration
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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,describe,it,expect,beforeEach,jasmine*/
|
||||||
|
|
||||||
|
define(["../../src/services/NestedTransaction"], function (NestedTransaction) {
|
||||||
|
var TRANSACTION_METHODS = ['add', 'commit', 'cancel', 'size'];
|
||||||
|
|
||||||
|
describe("A NestedTransaction", function () {
|
||||||
|
var mockTransaction,
|
||||||
|
nestedTransaction;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockTransaction =
|
||||||
|
jasmine.createSpyObj('transaction', TRANSACTION_METHODS);
|
||||||
|
nestedTransaction = new NestedTransaction(mockTransaction);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("exposes a Transaction's interface", function () {
|
||||||
|
TRANSACTION_METHODS.forEach(function (method) {
|
||||||
|
expect(nestedTransaction[method])
|
||||||
|
.toEqual(jasmine.any(Function));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when callbacks are added", function () {
|
||||||
|
var mockCommit,
|
||||||
|
mockCancel,
|
||||||
|
remove;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockCommit = jasmine.createSpy('commit');
|
||||||
|
mockCancel = jasmine.createSpy('cancel');
|
||||||
|
remove = nestedTransaction.add(mockCommit, mockCancel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not interact with its parent transaction", function () {
|
||||||
|
TRANSACTION_METHODS.forEach(function (method) {
|
||||||
|
expect(mockTransaction[method])
|
||||||
|
.not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and the transaction is committed", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
nestedTransaction.commit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds to its parent transaction", function () {
|
||||||
|
expect(mockTransaction.add).toHaveBeenCalledWith(
|
||||||
|
jasmine.any(Function),
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -57,8 +57,7 @@ define(
|
|||||||
|
|
||||||
transactionService.startTransaction();
|
transactionService.startTransaction();
|
||||||
transactionService.addToTransaction(onCommit, onCancel);
|
transactionService.addToTransaction(onCommit, onCancel);
|
||||||
expect(transactionService.onCommits.length).toBe(1);
|
expect(transactionService.size()).toBe(1);
|
||||||
expect(transactionService.onCancels.length).toBe(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("size function returns size of commit and cancel queues", function () {
|
it("size function returns size of commit and cancel queues", function () {
|
||||||
@@ -85,7 +84,7 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("commit calls all queued commit functions", function () {
|
it("commit calls all queued commit functions", function () {
|
||||||
expect(transactionService.onCommits.length).toBe(3);
|
expect(transactionService.size()).toBe(3);
|
||||||
transactionService.commit();
|
transactionService.commit();
|
||||||
onCommits.forEach(function (spy) {
|
onCommits.forEach(function (spy) {
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
@@ -95,8 +94,8 @@ define(
|
|||||||
it("commit resets active state and clears queues", function () {
|
it("commit resets active state and clears queues", function () {
|
||||||
transactionService.commit();
|
transactionService.commit();
|
||||||
expect(transactionService.isActive()).toBe(false);
|
expect(transactionService.isActive()).toBe(false);
|
||||||
expect(transactionService.onCommits.length).toBe(0);
|
expect(transactionService.size()).toBe(0);
|
||||||
expect(transactionService.onCancels.length).toBe(0);
|
expect(transactionService.size()).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@@ -116,7 +115,7 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("cancel calls all queued cancel functions", function () {
|
it("cancel calls all queued cancel functions", function () {
|
||||||
expect(transactionService.onCancels.length).toBe(3);
|
expect(transactionService.size()).toBe(3);
|
||||||
transactionService.cancel();
|
transactionService.cancel();
|
||||||
onCancels.forEach(function (spy) {
|
onCancels.forEach(function (spy) {
|
||||||
expect(spy).toHaveBeenCalled();
|
expect(spy).toHaveBeenCalled();
|
||||||
@@ -126,8 +125,7 @@ define(
|
|||||||
it("cancel resets active state and clears queues", function () {
|
it("cancel resets active state and clears queues", function () {
|
||||||
transactionService.cancel();
|
transactionService.cancel();
|
||||||
expect(transactionService.isActive()).toBe(false);
|
expect(transactionService.isActive()).toBe(false);
|
||||||
expect(transactionService.onCommits.length).toBe(0);
|
expect(transactionService.size()).toBe(0);
|
||||||
expect(transactionService.onCancels.length).toBe(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
110
platform/commonUI/edit/test/services/TransactionSpec.js
Normal file
110
platform/commonUI/edit/test/services/TransactionSpec.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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,describe,it,expect,beforeEach,jasmine*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../../src/services/Transaction"],
|
||||||
|
function (Transaction) {
|
||||||
|
|
||||||
|
describe("A Transaction", function () {
|
||||||
|
var mockLog,
|
||||||
|
transaction;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockLog = jasmine.createSpyObj(
|
||||||
|
'$log',
|
||||||
|
['warn', 'info', 'error', 'debug']
|
||||||
|
);
|
||||||
|
transaction = new Transaction(mockLog);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("initially has a size of zero", function () {
|
||||||
|
expect(transaction.size()).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when callbacks are added", function () {
|
||||||
|
var mockCommit,
|
||||||
|
mockCancel,
|
||||||
|
remove;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockCommit = jasmine.createSpy('commit');
|
||||||
|
mockCancel = jasmine.createSpy('cancel');
|
||||||
|
remove = transaction.add(mockCommit, mockCancel);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reports a new size", function () {
|
||||||
|
expect(transaction.size()).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns a function to remove those callbacks", function () {
|
||||||
|
expect(remove).toEqual(jasmine.any(Function));
|
||||||
|
remove();
|
||||||
|
expect(transaction.size()).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and the transaction is committed", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
transaction.commit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("triggers the commit callback", function () {
|
||||||
|
expect(mockCommit).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not trigger the cancel callback", function () {
|
||||||
|
expect(mockCancel).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and the transaction is cancelled", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
transaction.cancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("triggers the cancel callback", function () {
|
||||||
|
expect(mockCancel).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not trigger the commit callback", function () {
|
||||||
|
expect(mockCommit).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("and an exception is encountered during commit", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
mockCommit.andCallFake(function () {
|
||||||
|
throw new Error("test error");
|
||||||
|
});
|
||||||
|
transaction.commit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs an error", function () {
|
||||||
|
expect(mockLog.error).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
@@ -23,10 +23,12 @@
|
|||||||
define([
|
define([
|
||||||
"./src/FormatProvider",
|
"./src/FormatProvider",
|
||||||
"./src/UTCTimeFormat",
|
"./src/UTCTimeFormat",
|
||||||
|
"./src/DurationFormat",
|
||||||
'legacyRegistry'
|
'legacyRegistry'
|
||||||
], function (
|
], function (
|
||||||
FormatProvider,
|
FormatProvider,
|
||||||
UTCTimeFormat,
|
UTCTimeFormat,
|
||||||
|
DurationFormat,
|
||||||
legacyRegistry
|
legacyRegistry
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -48,6 +50,10 @@ define([
|
|||||||
{
|
{
|
||||||
"key": "utc",
|
"key": "utc",
|
||||||
"implementation": UTCTimeFormat
|
"implementation": UTCTimeFormat
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "duration",
|
||||||
|
"implementation": DurationFormat
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"constants": [
|
"constants": [
|
||||||
@@ -55,6 +61,17 @@ define([
|
|||||||
"key": "DEFAULT_TIME_FORMAT",
|
"key": "DEFAULT_TIME_FORMAT",
|
||||||
"value": "utc"
|
"value": "utc"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"licenses": [
|
||||||
|
{
|
||||||
|
"name": "d3",
|
||||||
|
"version": "3.0.0",
|
||||||
|
"description": "Incorporates modified code from d3 Time Scales",
|
||||||
|
"author": "Mike Bostock",
|
||||||
|
"copyright": "Copyright 2010-2016 Mike Bostock. " +
|
||||||
|
"All rights reserved.",
|
||||||
|
"link": "https://github.com/d3/d3/blob/master/LICENSE"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
62
platform/commonUI/formats/src/DurationFormat.js
Normal file
62
platform/commonUI/formats/src/DurationFormat.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web 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 Web 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([
|
||||||
|
'moment'
|
||||||
|
], function (
|
||||||
|
moment
|
||||||
|
) {
|
||||||
|
|
||||||
|
var DATE_FORMAT = "HH:mm:ss",
|
||||||
|
DATE_FORMATS = [
|
||||||
|
DATE_FORMAT
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatter for duration. Uses moment to produce a date from a given
|
||||||
|
* value, but output is formatted to display only time. Can be used for
|
||||||
|
* specifying a time duration. For specifying duration, it's best to
|
||||||
|
* specify a date of January 1, 1970, as the ms offset will equal the
|
||||||
|
* duration represented by the time.
|
||||||
|
*
|
||||||
|
* @implements {Format}
|
||||||
|
* @constructor
|
||||||
|
* @memberof platform/commonUI/formats
|
||||||
|
*/
|
||||||
|
function DurationFormat() {
|
||||||
|
}
|
||||||
|
|
||||||
|
DurationFormat.prototype.format = function (value) {
|
||||||
|
return moment.utc(value).format(DATE_FORMAT);
|
||||||
|
};
|
||||||
|
|
||||||
|
DurationFormat.prototype.parse = function (text) {
|
||||||
|
return moment.duration(text).asMilliseconds();
|
||||||
|
};
|
||||||
|
|
||||||
|
DurationFormat.prototype.validate = function (text) {
|
||||||
|
return moment.utc(text, DATE_FORMATS).isValid();
|
||||||
|
};
|
||||||
|
|
||||||
|
return DurationFormat;
|
||||||
|
});
|
||||||
@@ -58,6 +58,10 @@ define([
|
|||||||
* @method format
|
* @method format
|
||||||
* @memberof Format#
|
* @memberof Format#
|
||||||
* @param {number} value the numeric value to format
|
* @param {number} value the numeric value to format
|
||||||
|
* @param {number} [threshold] Optionally provides context to the
|
||||||
|
* format request, allowing for scale-appropriate formatting. This value
|
||||||
|
* should be the minimum unit to be represented by this format, in ms. For
|
||||||
|
* example, to display seconds, a threshold of 1 * 1000 should be provided.
|
||||||
* @returns {string} the text representation of the value
|
* @returns {string} the text representation of the value
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ define([
|
|||||||
"YYYY-MM-DD"
|
"YYYY-MM-DD"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Scale
|
||||||
|
* @property {number} min the minimum scale value, in ms
|
||||||
|
* @property {number} max the maximum scale value, in ms
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formatter for UTC timestamps. Interprets numeric values as
|
* Formatter for UTC timestamps. Interprets numeric values as
|
||||||
@@ -46,7 +51,64 @@ define([
|
|||||||
function UTCTimeFormat() {
|
function UTCTimeFormat() {
|
||||||
}
|
}
|
||||||
|
|
||||||
UTCTimeFormat.prototype.format = function (value) {
|
/**
|
||||||
|
* Returns an appropriate time format based on the provided value and
|
||||||
|
* the threshold required.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function getScaledFormat(d) {
|
||||||
|
var momentified = moment.utc(d);
|
||||||
|
/**
|
||||||
|
* Uses logic from d3 Time-Scales, v3 of the API. See
|
||||||
|
* https://github.com/d3/d3-3.x-api-reference/blob/master/Time-Scales.md
|
||||||
|
*
|
||||||
|
* Licensed
|
||||||
|
*/
|
||||||
|
return [
|
||||||
|
[".SSS", function (m) {
|
||||||
|
return m.milliseconds();
|
||||||
|
}],
|
||||||
|
[":ss", function (m) {
|
||||||
|
return m.seconds();
|
||||||
|
}],
|
||||||
|
["HH:mm", function (m) {
|
||||||
|
return m.minutes();
|
||||||
|
}],
|
||||||
|
["HH", function (m) {
|
||||||
|
return m.hours();
|
||||||
|
}],
|
||||||
|
["ddd DD", function (m) {
|
||||||
|
return m.days() &&
|
||||||
|
m.date() !== 1;
|
||||||
|
}],
|
||||||
|
["MMM DD", function (m) {
|
||||||
|
return m.date() !== 1;
|
||||||
|
}],
|
||||||
|
["MMMM", function (m) {
|
||||||
|
return m.month();
|
||||||
|
}],
|
||||||
|
["YYYY", function () {
|
||||||
|
return true;
|
||||||
|
}]
|
||||||
|
].filter(function (row) {
|
||||||
|
return row[1](momentified);
|
||||||
|
})[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* @param {Scale} [scale] Optionally provides context to the
|
||||||
|
* format request, allowing for scale-appropriate formatting.
|
||||||
|
* @returns {string} the formatted date
|
||||||
|
*/
|
||||||
|
UTCTimeFormat.prototype.format = function (value, scale) {
|
||||||
|
if (scale !== undefined) {
|
||||||
|
var scaledFormat = getScaledFormat(value, scale);
|
||||||
|
if (scaledFormat) {
|
||||||
|
return moment.utc(value).format(scaledFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
return moment.utc(value).format(DATE_FORMAT) + "Z";
|
return moment.utc(value).format(DATE_FORMAT) + "Z";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
62
platform/commonUI/formats/src/UTCTimeFormatSpec.js
Normal file
62
platform/commonUI/formats/src/UTCTimeFormatSpec.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web 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 Web 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([
|
||||||
|
"./UTCTimeFormat",
|
||||||
|
"moment"
|
||||||
|
], function (
|
||||||
|
UTCTimeFormat,
|
||||||
|
moment
|
||||||
|
) {
|
||||||
|
describe("The UTCTimeFormat class", function () {
|
||||||
|
var format;
|
||||||
|
var scale;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
format = new UTCTimeFormat();
|
||||||
|
scale = {min: 0, max: 0};
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Provides an appropriately scaled time format based on the input" +
|
||||||
|
" time", function () {
|
||||||
|
var TWO_HUNDRED_MS = 200;
|
||||||
|
var THREE_SECONDS = 3000;
|
||||||
|
var FIVE_MINUTES = 5 * 60 * 1000;
|
||||||
|
var ONE_HOUR_TWENTY_MINS = (1 * 60 * 60 * 1000) + (20 * 60 * 1000);
|
||||||
|
var TEN_HOURS = (10 * 60 * 60 * 1000);
|
||||||
|
|
||||||
|
var JUNE_THIRD = moment.utc("2016-06-03", "YYYY-MM-DD");
|
||||||
|
var APRIL = moment.utc("2016-04", "YYYY-MM");
|
||||||
|
var TWENTY_SIXTEEN = moment.utc("2016", "YYYY");
|
||||||
|
|
||||||
|
expect(format.format(TWO_HUNDRED_MS, scale)).toBe(".200");
|
||||||
|
expect(format.format(THREE_SECONDS, scale)).toBe(":03");
|
||||||
|
expect(format.format(FIVE_MINUTES, scale)).toBe("00:05");
|
||||||
|
expect(format.format(ONE_HOUR_TWENTY_MINS, scale)).toBe("01:20");
|
||||||
|
expect(format.format(TEN_HOURS, scale)).toBe("10");
|
||||||
|
|
||||||
|
expect(format.format(JUNE_THIRD, scale)).toBe("Fri 03");
|
||||||
|
expect(format.format(APRIL, scale)).toBe("April");
|
||||||
|
expect(format.format(TWENTY_SIXTEEN, scale)).toBe("2016");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -48,6 +48,7 @@ define([
|
|||||||
"./src/directives/MCTSplitPane",
|
"./src/directives/MCTSplitPane",
|
||||||
"./src/directives/MCTSplitter",
|
"./src/directives/MCTSplitter",
|
||||||
"./src/directives/MCTTree",
|
"./src/directives/MCTTree",
|
||||||
|
"./src/filters/ReverseFilter",
|
||||||
"text!./res/templates/bottombar.html",
|
"text!./res/templates/bottombar.html",
|
||||||
"text!./res/templates/controls/action-button.html",
|
"text!./res/templates/controls/action-button.html",
|
||||||
"text!./res/templates/controls/input-filter.html",
|
"text!./res/templates/controls/input-filter.html",
|
||||||
@@ -96,6 +97,7 @@ define([
|
|||||||
MCTSplitPane,
|
MCTSplitPane,
|
||||||
MCTSplitter,
|
MCTSplitter,
|
||||||
MCTTree,
|
MCTTree,
|
||||||
|
ReverseFilter,
|
||||||
bottombarTemplate,
|
bottombarTemplate,
|
||||||
actionButtonTemplate,
|
actionButtonTemplate,
|
||||||
inputFilterTemplate,
|
inputFilterTemplate,
|
||||||
@@ -146,7 +148,8 @@ define([
|
|||||||
"depends": [
|
"depends": [
|
||||||
"stylesheets[]",
|
"stylesheets[]",
|
||||||
"$document",
|
"$document",
|
||||||
"THEME"
|
"THEME",
|
||||||
|
"ASSETS_PATH"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -158,7 +161,7 @@ define([
|
|||||||
],
|
],
|
||||||
"filters": [
|
"filters": [
|
||||||
{
|
{
|
||||||
"implementation": "filters/ReverseFilter.js",
|
"implementation": ReverseFilter,
|
||||||
"key": "reverse"
|
"key": "reverse"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -405,6 +408,11 @@ define([
|
|||||||
"key": "THEME",
|
"key": "THEME",
|
||||||
"value": "unspecified",
|
"value": "unspecified",
|
||||||
"priority": "fallback"
|
"priority": "fallback"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ASSETS_PATH",
|
||||||
|
"value": ".",
|
||||||
|
"priority": "fallback"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"containers": [
|
"containers": [
|
||||||
|
|||||||
91
platform/commonUI/general/res/sass/_animations.scss
Normal file
91
platform/commonUI/general/res/sass/_animations.scss
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
@include keyframes(rotation) {
|
||||||
|
100% { @include transform(rotate(360deg)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@include keyframes(rotation-centered) {
|
||||||
|
0% { @include transform(translate(-50%, -50%) rotate(0deg)); }
|
||||||
|
100% { @include transform(translate(-50%, -50%) rotate(360deg)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@include keyframes(clock-hands) {
|
||||||
|
0% { @include transform(translate(-50%, -50%) rotate(0deg)); }
|
||||||
|
100% { @include transform(translate(-50%, -50%) rotate(360deg)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@include keyframes(clock-hands-sticky) {
|
||||||
|
0% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(0deg));
|
||||||
|
}
|
||||||
|
7% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(0deg));
|
||||||
|
}
|
||||||
|
8% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(30deg));
|
||||||
|
}
|
||||||
|
15% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(30deg));
|
||||||
|
}
|
||||||
|
16% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(60deg));
|
||||||
|
}
|
||||||
|
24% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(60deg));
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(90deg));
|
||||||
|
}
|
||||||
|
32% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(90deg));
|
||||||
|
}
|
||||||
|
33% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(120deg));
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(120deg));
|
||||||
|
}
|
||||||
|
41% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(150deg));
|
||||||
|
}
|
||||||
|
49% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(150deg));
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(180deg));
|
||||||
|
}
|
||||||
|
57% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(180deg));
|
||||||
|
}
|
||||||
|
58% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(210deg));
|
||||||
|
}
|
||||||
|
65% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(210deg));
|
||||||
|
}
|
||||||
|
66% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(240deg));
|
||||||
|
}
|
||||||
|
74% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(240deg));
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(270deg));
|
||||||
|
}
|
||||||
|
82% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(270deg));
|
||||||
|
}
|
||||||
|
83% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(300deg));
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(300deg));
|
||||||
|
}
|
||||||
|
91% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(330deg));
|
||||||
|
}
|
||||||
|
99% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(330deg));
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
@include transform(translate(-50%, -50%) rotate(360deg));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -108,6 +108,9 @@
|
|||||||
&.grows {
|
&.grows {
|
||||||
@include flex(1 1 auto);
|
@include flex(1 1 auto);
|
||||||
}
|
}
|
||||||
|
&.contents-align-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.flex-container {
|
.flex-container {
|
||||||
// Apply to wrapping elements, mct-includes, etc.
|
// Apply to wrapping elements, mct-includes, etc.
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ $uePaneMiniTabFontSize: 8px;
|
|||||||
$uePaneMiniTabCollapsedW: 18px;
|
$uePaneMiniTabCollapsedW: 18px;
|
||||||
$ueEditLeftPaneW: 75%;
|
$ueEditLeftPaneW: 75%;
|
||||||
$treeSearchInputBarH: 25px;
|
$treeSearchInputBarH: 25px;
|
||||||
$ueTimeControlH: (33px, 18px, 20px);
|
|
||||||
/*************** Panes */
|
/*************** Panes */
|
||||||
$ueBrowseLeftPaneTreeMinW: 150px;
|
$ueBrowseLeftPaneTreeMinW: 150px;
|
||||||
$ueBrowseLeftPaneTreeMaxW: 35%;
|
$ueBrowseLeftPaneTreeMaxW: 35%;
|
||||||
@@ -62,6 +61,9 @@ $ueDesktopMinW: 600px;
|
|||||||
$ovrTopBarH: 45px;
|
$ovrTopBarH: 45px;
|
||||||
$ovrFooterH: 24px;
|
$ovrFooterH: 24px;
|
||||||
$overlayMargin: 25px;
|
$overlayMargin: 25px;
|
||||||
|
/*************** Progress Overlay */
|
||||||
|
$durEntryMs: 50ms;
|
||||||
|
$delayEntryMs: 100ms;
|
||||||
/*************** Items */
|
/*************** Items */
|
||||||
$ueBrowseGridItemLg: 200px;
|
$ueBrowseGridItemLg: 200px;
|
||||||
$ueBrowseGridItemTopBarH: 20px;
|
$ueBrowseGridItemTopBarH: 20px;
|
||||||
@@ -109,6 +111,7 @@ $bubbleMaxW: 300px;
|
|||||||
$reqSymbolW: 15px;
|
$reqSymbolW: 15px;
|
||||||
$reqSymbolM: $interiorMargin * 2;
|
$reqSymbolM: $interiorMargin * 2;
|
||||||
$reqSymbolFontSize: 0.75em;
|
$reqSymbolFontSize: 0.75em;
|
||||||
|
$inputTextP: 3px 5px;
|
||||||
/*************** Wait Spinner Defaults */
|
/*************** Wait Spinner Defaults */
|
||||||
$waitSpinnerD: 32px;
|
$waitSpinnerD: 32px;
|
||||||
$waitSpinnerTreeD: 20px;
|
$waitSpinnerTreeD: 20px;
|
||||||
|
|||||||
@@ -4,4 +4,3 @@
|
|||||||
@include s-stale();
|
@include s-stale();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,20 +39,20 @@
|
|||||||
@include pulse($animName: pulse-subtle, $dur: 500ms, $opacity0: 0.7);
|
@include pulse($animName: pulse-subtle, $dur: 500ms, $opacity0: 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin animTo($animName, $propName, $propValStart, $propValEnd, $dur: 500ms, $delay: 0) {
|
@mixin animTo($animName, $propName, $propValStart, $propValEnd, $dur: 500ms, $delay: 0, $dir: normal, $count: 1) {
|
||||||
@include keyframes($animName) {
|
@include keyframes($animName) {
|
||||||
from { #{propName}: $propValStart; }
|
from { #{propName}: $propValStart; }
|
||||||
to { #{$propName}: $propValEnd; }
|
to { #{$propName}: $propValEnd; }
|
||||||
}
|
}
|
||||||
@include animToParams($animName, $dur: 500ms, $delay: 0)
|
@include animToParams($animName, $dur: $dur, $delay: $delay, $dir: $dir, $count: $count)
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin animToParams($animName, $dur: 500ms, $delay: 0) {
|
@mixin animToParams($animName, $dur: 500ms, $delay: 0, $dir: normal, $count: 1) {
|
||||||
@include animation-name($animName);
|
@include animation-name($animName);
|
||||||
@include animation-duration($dur);
|
@include animation-duration($dur);
|
||||||
@include animation-delay($delay);
|
@include animation-delay($delay);
|
||||||
@include animation-fill-mode(both);
|
@include animation-fill-mode(both);
|
||||||
@include animation-direction(normal);
|
@include animation-direction($dir);
|
||||||
@include animation-iteration-count(1);
|
@include animation-iteration-count($count);
|
||||||
@include animation-timing-function(ease-in-out);
|
@include animation-timing-function(ease-in-out);
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,7 @@ input, textarea {
|
|||||||
input[type="text"],
|
input[type="text"],
|
||||||
input[type="search"] {
|
input[type="search"] {
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
padding: 3px 5px;
|
padding: $inputTextP;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3 {
|
h1, h2, h3 {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
.ui-symbol {
|
.ui-symbol {
|
||||||
font-family: 'symbolsfont';
|
font-family: 'symbolsfont';
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-symbol.icon {
|
.ui-symbol.icon {
|
||||||
@@ -70,10 +71,21 @@
|
|||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
position: relative;
|
position: relative;
|
||||||
&.l-icon-link {
|
&.l-icon-link {
|
||||||
.t-item-icon-glyph {
|
&:after {
|
||||||
|
color: $colorIconLink;
|
||||||
|
content: $glyph-icon-link;
|
||||||
|
height: auto; width: auto;
|
||||||
|
position: absolute;
|
||||||
|
left: 0; top: 0; right: 0; bottom: 20%;
|
||||||
|
@include transform-origin(bottom left);
|
||||||
|
@include transform(scale(0.3));
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .t-item-icon-glyph {
|
||||||
&:after {
|
&:after {
|
||||||
color: $colorIconLink;
|
color: $colorIconLink;
|
||||||
content: $glyph-icon-link;
|
content: '\e921'; //$glyph-icon-link;
|
||||||
height: auto; width: auto;
|
height: auto; width: auto;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0; top: 0; right: 0; bottom: 20%;
|
left: 0; top: 0; right: 0; bottom: 20%;
|
||||||
@@ -81,6 +93,6 @@
|
|||||||
@include transform(scale(0.3));
|
@include transform(scale(0.3));
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
@import "effects";
|
@import "effects";
|
||||||
@import "global";
|
@import "global";
|
||||||
@import "glyphs";
|
@import "glyphs";
|
||||||
|
@import "animations";
|
||||||
@import "archetypes";
|
@import "archetypes";
|
||||||
@import "about";
|
@import "about";
|
||||||
@import "text";
|
@import "text";
|
||||||
@@ -41,7 +42,6 @@
|
|||||||
@import "controls/lists";
|
@import "controls/lists";
|
||||||
@import "controls/menus";
|
@import "controls/menus";
|
||||||
@import "controls/messages";
|
@import "controls/messages";
|
||||||
@import "controls/time-controller";
|
|
||||||
@import "mobile/controls/menus";
|
@import "mobile/controls/menus";
|
||||||
|
|
||||||
/********************************* FORMS */
|
/********************************* FORMS */
|
||||||
|
|||||||
@@ -185,21 +185,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@mixin sliderTrack($bg: $scrollbarTrackColorBg) {
|
@mixin sliderTrack($bg: $scrollbarTrackColorBg) {
|
||||||
//$b: 1px solid lighten($bg, 30%);
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@include boxIncised(0.7);
|
@include boxIncised(0.7);
|
||||||
background-color: $bg;
|
background-color: $bg;
|
||||||
//border-bottom: $b;
|
|
||||||
//border-right: $b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin controlGrippy($b, $direction: horizontal, $w: 1px, $style: dotted) {
|
@mixin controlGrippy($b, $direction: horizontal, $w: 1px, $style: dotted) {
|
||||||
//&:before {
|
|
||||||
//@include trans-prop-nice("border-color", 25ms);
|
|
||||||
content: '';
|
content: '';
|
||||||
display: block;
|
display: block;
|
||||||
//height: auto;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
@@ -274,16 +268,6 @@
|
|||||||
text-shadow: rgba(black, $sVal) 0 3px 7px;
|
text-shadow: rgba(black, $sVal) 0 3px 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@function pullForward($c, $p: 20%) {
|
|
||||||
// For dark interfaces, lighter things come forward
|
|
||||||
@return lighten($c, $p);
|
|
||||||
}
|
|
||||||
|
|
||||||
@function pushBack($c, $p: 20%) {
|
|
||||||
// For dark interfaces, darker things move back
|
|
||||||
@return darken($c, $p);
|
|
||||||
}
|
|
||||||
|
|
||||||
@function percentToDecimal($p) {
|
@function percentToDecimal($p) {
|
||||||
@return $p / 100%;
|
@return $p / 100%;
|
||||||
}
|
}
|
||||||
@@ -304,7 +288,6 @@
|
|||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: $fg;
|
color: $fg;
|
||||||
//display: inline-block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin btnBase($bg: $colorBtnBg, $bgHov: $colorBtnBgHov, $fg: $colorBtnFg, $fgHov: $colorBtnFgHov, $ic: $colorBtnIcon, $icHov: $colorBtnIconHov) {
|
@mixin btnBase($bg: $colorBtnBg, $bgHov: $colorBtnBgHov, $fg: $colorBtnFg, $fgHov: $colorBtnFgHov, $ic: $colorBtnIcon, $icHov: $colorBtnIconHov) {
|
||||||
|
|||||||
@@ -31,23 +31,10 @@
|
|||||||
.has-control-bar {
|
.has-control-bar {
|
||||||
$btnExportH: $btnFrameH;
|
$btnExportH: $btnFrameH;
|
||||||
.l-control-bar {
|
.l-control-bar {
|
||||||
@include trans-prop-nice(opacity, $dur: 50ms);
|
display: none;
|
||||||
opacity: 0;
|
|
||||||
}
|
}
|
||||||
.l-view-section {
|
.l-view-section {
|
||||||
@include trans-prop-nice(top, $dur: 150ms, $delay: 50ms);
|
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
&:hover {
|
|
||||||
.l-control-bar {
|
|
||||||
@include trans-prop-nice(opacity, 150ms, 100ms);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.l-view-section {
|
|
||||||
@include trans-prop-nice(top, $dur: 150ms);
|
|
||||||
top: $btnExportH + $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -104,9 +104,17 @@ body.desktop .mini-tab-icon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin btnSetButtonFirst() {
|
||||||
|
@include border-left-radius($controlCr);
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin btnSetButtonLast() {
|
||||||
|
@include border-right-radius($controlCr);
|
||||||
|
}
|
||||||
|
|
||||||
.l-btn-set {
|
.l-btn-set {
|
||||||
// Buttons that have a very tight conceptual grouping - no internal space between them.
|
// Buttons that have a very tight conceptual grouping - no internal space between them.
|
||||||
// Structure: .btn-set > mct-representation class=first|last > .s-button
|
|
||||||
font-size: 0; // Remove space between s-button elements due to white space in markup
|
font-size: 0; // Remove space between s-button elements due to white space in markup
|
||||||
|
|
||||||
.s-button {
|
.s-button {
|
||||||
@@ -114,20 +122,16 @@ body.desktop .mini-tab-icon {
|
|||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.first {
|
> .s-button {
|
||||||
.s-button,
|
// Styles for .s-button as immediate descendants in .l-btn-set
|
||||||
&.s-button {
|
&:first-child { @include btnSetButtonFirst(); }
|
||||||
@include border-left-radius($controlCr);
|
&:last-child { @include btnSetButtonLast(); }
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.last {
|
// Must use following due to DOM structure of action buttons,
|
||||||
.s-button,
|
// which have structure like .l-btn-set > mct-representation class=first|last > .s-button
|
||||||
&.s-button {
|
.first > .s-button { @include btnSetButtonFirst(); }
|
||||||
@include border-right-radius($controlCr);
|
.last > .s-button { @include btnSetButtonLast(); }
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.paused {
|
.paused {
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ input[type="search"] {
|
|||||||
.l-input-lg input[type="text"] { width: 100% !important; }
|
.l-input-lg input[type="text"] { width: 100% !important; }
|
||||||
.l-input-med input[type="text"] { width: 200px !important; }
|
.l-input-med input[type="text"] { width: 200px !important; }
|
||||||
.l-input-sm input[type="text"] { width: 50px !important; }
|
.l-input-sm input[type="text"] { width: 50px !important; }
|
||||||
|
.l-textarea-sm textarea { position: relative; height: 50px; }
|
||||||
.l-numeric input[type="text"] { text-align: right; }
|
.l-numeric input[type="text"] { text-align: right; }
|
||||||
|
|
||||||
.input-labeled {
|
.input-labeled {
|
||||||
@@ -295,8 +296,6 @@ input[type="search"] {
|
|||||||
.title-label {
|
.title-label {
|
||||||
color: $colorObjHdrTxt;
|
color: $colorObjHdrTxt;
|
||||||
@include ellipsize();
|
@include ellipsize();
|
||||||
@include webkitProp(flex, '0 1 auto');
|
|
||||||
padding-right: 0.35em; // For context arrow. Done with em's so pad is relative to the scale of the text.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-available-w {
|
.context-available-w {
|
||||||
@@ -307,6 +306,10 @@ input[type="search"] {
|
|||||||
font-size: 0.7em;
|
font-size: 0.7em;
|
||||||
@include flex(0 0 1);
|
@include flex(0 0 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.t-object-alert {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************** PROGRESS BAR */
|
/******************************************************** PROGRESS BAR */
|
||||||
@@ -440,6 +443,63 @@ input[type="search"] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin sliderKnob() {
|
||||||
|
$h: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: floor($h/1.75);
|
||||||
|
height: $h;
|
||||||
|
margin-top: 1 + floor($h/2) * -1;
|
||||||
|
@include btnSubtle(pullForward($colorBtnBg, 10%));
|
||||||
|
//border-radius: 50% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin sliderKnobRound() {
|
||||||
|
$h: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: $h;
|
||||||
|
height: $h;
|
||||||
|
margin-top: 1 + floor($h/2) * -1;
|
||||||
|
@include btnSubtle(pullForward($colorBtnBg, 10%));
|
||||||
|
border-radius: 50% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"] {
|
||||||
|
// HTML5 range inputs
|
||||||
|
|
||||||
|
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
|
||||||
|
background: transparent; /* Otherwise white in Chrome */
|
||||||
|
&:focus {
|
||||||
|
outline: none; /* Removes the blue border. */
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thumb
|
||||||
|
&::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
@include sliderKnobRound();
|
||||||
|
}
|
||||||
|
&::-moz-range-thumb {
|
||||||
|
border: none;
|
||||||
|
@include sliderKnobRound();
|
||||||
|
}
|
||||||
|
&::-ms-thumb {
|
||||||
|
border: none;
|
||||||
|
@include sliderKnobRound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track
|
||||||
|
&::-webkit-slider-runnable-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 3px;
|
||||||
|
@include sliderTrack();
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-moz-range-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 3px;
|
||||||
|
@include sliderTrack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************** DATETIME PICKER */
|
/******************************************************** DATETIME PICKER */
|
||||||
.l-datetime-picker {
|
.l-datetime-picker {
|
||||||
$r1H: 15px;
|
$r1H: 15px;
|
||||||
|
|||||||
@@ -178,7 +178,7 @@
|
|||||||
}
|
}
|
||||||
.pane {
|
.pane {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
&.left {
|
&.menu-items {
|
||||||
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
border-right: 1px solid pullForward($colorMenuBg, 10%);
|
||||||
left: 0;
|
left: 0;
|
||||||
padding-right: $interiorMargin;
|
padding-right: $interiorMargin;
|
||||||
@@ -194,38 +194,53 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.right {
|
&.menu-item-description {
|
||||||
left: auto;
|
left: auto;
|
||||||
right: 0;
|
right: 0;
|
||||||
padding: $interiorMargin * 5;
|
padding: $interiorMargin * 5;
|
||||||
width: $prw;
|
width: $prw;
|
||||||
|
.desc-area {
|
||||||
|
&.icon {
|
||||||
|
color: $colorCreateMenuLgIcon;
|
||||||
|
font-size: 8em;
|
||||||
|
margin-bottom: $interiorMargin * 3;
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
&.title {
|
||||||
|
color: $colorCreateMenuText;
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin-bottom: $interiorMargin * 2;
|
||||||
|
}
|
||||||
|
&.description {
|
||||||
|
color: pushBack($colorCreateMenuText, 20%);
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.menu-item-description {
|
|
||||||
.desc-area {
|
&.mini {
|
||||||
&.icon {
|
width: 400px;
|
||||||
$h: 150px;
|
height: 300px;
|
||||||
color: $colorCreateMenuLgIcon;
|
.pane {
|
||||||
position: relative;
|
&.menu-items {
|
||||||
font-size: 8em;
|
font-size: 0.8em;
|
||||||
left: 0;
|
}
|
||||||
height: $h;
|
&.menu-item-description {
|
||||||
line-height: $h;
|
padding: $interiorMargin * 3;
|
||||||
margin-bottom: $interiorMargin * 5;
|
.desc-area {
|
||||||
text-align: center;
|
&.icon {
|
||||||
}
|
font-size: 4em;
|
||||||
&.title {
|
}
|
||||||
color: $colorCreateMenuText;
|
&.title {
|
||||||
font-size: 1.2em;
|
font-size: 1em;
|
||||||
margin-bottom: 0.5em;
|
}
|
||||||
}
|
}
|
||||||
&.description {
|
}
|
||||||
color: $colorCreateMenuText;
|
}
|
||||||
font-size: 0.8em;
|
}
|
||||||
line-height: 1.5em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.context-menu {
|
.context-menu {
|
||||||
font-size: 0.80rem;
|
font-size: 0.80rem;
|
||||||
@@ -258,7 +273,12 @@
|
|||||||
|
|
||||||
.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menus-up .menu {
|
||||||
|
bottom: $btnStdH; top: auto;
|
||||||
|
}
|
||||||
|
|||||||
@@ -345,3 +345,29 @@ body.desktop .t-message-single {
|
|||||||
body.desktop .t-message-list {
|
body.desktop .t-message-list {
|
||||||
.message-contents .l-message { margin-right: $interiorMarginLg; }
|
.message-contents .l-message { margin-right: $interiorMarginLg; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alert elements in views
|
||||||
|
.s-unsynced {
|
||||||
|
$c: $colorPausedBg;
|
||||||
|
border: 1px solid $c;
|
||||||
|
@include animTo($animName: pulsePaused, $propName: border-color, $propValStart: rgba($c, 0.8), $propValEnd: rgba($c, 0.5), $dur: $animPausedPulseDur, $dir: alternate, $count: infinite);
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-status-timeconductor-unsynced {
|
||||||
|
// Plot areas
|
||||||
|
.gl-plot .gl-plot-display-area {
|
||||||
|
@extend .s-unsynced;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object headers
|
||||||
|
.object-header {
|
||||||
|
.t-object-alert {
|
||||||
|
display: inline;
|
||||||
|
&.t-alert-unsynced {
|
||||||
|
@extend .icon-alert-triangle;
|
||||||
|
color: $colorPausedBg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,266 +0,0 @@
|
|||||||
@mixin toiLineHovEffects() {
|
|
||||||
&:before,
|
|
||||||
&:after {
|
|
||||||
background-color: $timeControllerToiLineColorHov;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.l-time-controller {
|
|
||||||
$minW: 500px;
|
|
||||||
$knobHOffset: 0px;
|
|
||||||
$knobM: ($sliderKnobW + $knobHOffset) * -1;
|
|
||||||
$rangeValPad: $interiorMargin;
|
|
||||||
$rangeValOffset: $sliderKnobW + $interiorMargin;
|
|
||||||
$timeRangeSliderLROffset: 150px + ($sliderKnobW * 2);
|
|
||||||
$r1H: nth($ueTimeControlH,1); // Not currently used
|
|
||||||
$r2H: nth($ueTimeControlH,2);
|
|
||||||
$r3H: nth($ueTimeControlH,3);
|
|
||||||
|
|
||||||
min-width: $minW;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
|
|
||||||
.l-time-range-inputs-holder,
|
|
||||||
.l-time-range-slider-holder,
|
|
||||||
.l-time-range-ticks-holder
|
|
||||||
{
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: relative;
|
|
||||||
&:not(:first-child) {
|
|
||||||
margin-top: $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.l-time-range-slider,
|
|
||||||
.l-time-range-ticks {
|
|
||||||
@include absPosDefault(0, visible);
|
|
||||||
left: $timeRangeSliderLROffset; right: $timeRangeSliderLROffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.l-time-range-inputs-holder {
|
|
||||||
border-top: 1px solid $colorInteriorBorder;
|
|
||||||
padding-top: $interiorMargin;
|
|
||||||
&.l-flex-row,
|
|
||||||
.l-flex-row {
|
|
||||||
@include align-items(center);
|
|
||||||
.flex-elem {
|
|
||||||
height: auto;
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.type-icon {
|
|
||||||
font-size: 120%;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
.l-time-range-input-w,
|
|
||||||
.l-time-range-inputs-elem {
|
|
||||||
margin-right: $interiorMargin;
|
|
||||||
.lbl {
|
|
||||||
color: $colorPlotLabelFg;
|
|
||||||
}
|
|
||||||
.ui-symbol.icon {
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.l-time-range-input-w {
|
|
||||||
// Wraps a datetime text input field
|
|
||||||
position: relative;
|
|
||||||
input[type="text"] {
|
|
||||||
width: 200px;
|
|
||||||
&.picker-icon {
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.icon-calendar {
|
|
||||||
position: absolute;
|
|
||||||
right: 5px;
|
|
||||||
top: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.l-time-range-slider-holder {
|
|
||||||
height: $r2H;
|
|
||||||
.range-holder {
|
|
||||||
box-shadow: none;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
.range {
|
|
||||||
.toi-line {
|
|
||||||
$myC: $timeControllerToiLineColor;
|
|
||||||
$myW: 8px;
|
|
||||||
@include transform(translateX(50%));
|
|
||||||
position: absolute;
|
|
||||||
top: 0; right: 0; bottom: 0px; left: auto;
|
|
||||||
width: $myW;
|
|
||||||
height: auto;
|
|
||||||
z-index: 2;
|
|
||||||
&:before {
|
|
||||||
// Vert line
|
|
||||||
background-color: $myC;
|
|
||||||
position: absolute;
|
|
||||||
content: "";
|
|
||||||
top: 0; right: auto; bottom: -10px; left: floor($myW/2) - 1;
|
|
||||||
width: 1px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:hover .toi-line {
|
|
||||||
@include toiLineHovEffects;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:not(:active) {
|
|
||||||
.knob,
|
|
||||||
.range {
|
|
||||||
@include transition-property(left, right);
|
|
||||||
@include transition-duration(500ms);
|
|
||||||
@include transition-timing-function(ease-in-out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.l-time-range-ticks-holder {
|
|
||||||
height: $r3H;
|
|
||||||
.l-time-range-ticks {
|
|
||||||
border-top: 1px solid $colorTick;
|
|
||||||
.tick {
|
|
||||||
background-color: $colorTick;
|
|
||||||
border:none;
|
|
||||||
height: 5px;
|
|
||||||
width: 1px;
|
|
||||||
margin-left: -1px;
|
|
||||||
position: absolute;
|
|
||||||
&:first-child {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
.l-time-range-tick-label {
|
|
||||||
@include webkitProp(transform, translateX(-50%));
|
|
||||||
color: $colorPlotLabelFg;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
position: absolute;
|
|
||||||
top: 5px;
|
|
||||||
white-space: nowrap;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.knob {
|
|
||||||
z-index: 2;
|
|
||||||
&:before {
|
|
||||||
$mTB: 2px;
|
|
||||||
$grippyW: 3px;
|
|
||||||
$mLR: ($sliderKnobW - $grippyW)/2;
|
|
||||||
@include bgStripes($c: pullForward($sliderColorKnob, 20%), $a: 1, $bgsize: 4px, $angle: 0deg);
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
top: $mTB; right: $mLR; bottom: $mTB; left: $mLR;
|
|
||||||
}
|
|
||||||
.range-value {
|
|
||||||
@include trans-prop-nice-fade(.25s);
|
|
||||||
font-size: 0.7rem;
|
|
||||||
position: absolute;
|
|
||||||
height: $r2H;
|
|
||||||
line-height: $r2H;
|
|
||||||
white-space: nowrap;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
.range-value {
|
|
||||||
color: $sliderColorKnobHov;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.knob-l {
|
|
||||||
margin-left: $knobM;
|
|
||||||
.range-value {
|
|
||||||
text-align: right;
|
|
||||||
right: $rangeValOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.knob-r {
|
|
||||||
margin-right: $knobM;
|
|
||||||
.range-value {
|
|
||||||
left: $rangeValOffset;
|
|
||||||
}
|
|
||||||
&:hover + .range-holder .range .toi-line {
|
|
||||||
@include toiLineHovEffects;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.l-time-domain-selector {
|
|
||||||
position: absolute;
|
|
||||||
right: 0px;
|
|
||||||
top: $interiorMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.s-time-range-val {
|
|
||||||
border-radius: $controlCr;
|
|
||||||
background-color: $colorInputBg;
|
|
||||||
padding: 1px 1px 0 $interiorMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************** MOBILE */
|
|
||||||
|
|
||||||
@include phoneandtablet {
|
|
||||||
.l-time-controller {
|
|
||||||
min-width: 0;
|
|
||||||
.l-time-range-slider-holder,
|
|
||||||
.l-time-range-ticks-holder {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include phone {
|
|
||||||
.l-time-controller {
|
|
||||||
.l-time-range-inputs-holder {
|
|
||||||
&.l-flex-row,
|
|
||||||
.l-flex-row {
|
|
||||||
@include align-items(flex-start);
|
|
||||||
}
|
|
||||||
.l-time-range-inputs-elem {
|
|
||||||
&.type-icon {
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.t-inputs-w {
|
|
||||||
@include flex-direction(column);
|
|
||||||
.l-time-range-input-w:not(:first-child) {
|
|
||||||
&:not(:first-child) {
|
|
||||||
margin-top: $interiorMargin;
|
|
||||||
}
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
.l-time-range-inputs-elem {
|
|
||||||
&.lbl { display: none; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include phonePortrait {
|
|
||||||
.l-time-controller {
|
|
||||||
.l-time-range-inputs-holder {
|
|
||||||
.t-inputs-w {
|
|
||||||
@include flex(1 1 auto);
|
|
||||||
padding-top: 25px; // Make room for the ever lovin' Time Domain Selector
|
|
||||||
.flex-elem {
|
|
||||||
@include flex(1 1 auto);
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
input[type="text"] {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.l-time-domain-selector {
|
|
||||||
right: auto;
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
.s-image-main {
|
.s-image-main {
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
&.paused {
|
&.paused {
|
||||||
border-color: $colorPausedBg;
|
@extend .s-unsynced;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,15 +19,6 @@
|
|||||||
* 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.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
@include keyframes(rotation) {
|
|
||||||
100% { @include transform(rotate(360deg)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@include keyframes(rotation-centered) {
|
|
||||||
0% { @include transform(translate(-50%, -50%) rotate(0deg)); }
|
|
||||||
100% { @include transform(translate(-50%, -50%) rotate(360deg)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin spinner($b: 5px, $c: $colorKey) {
|
@mixin spinner($b: 5px, $c: $colorKey) {
|
||||||
@include transform-origin(center);
|
@include transform-origin(center);
|
||||||
@include animation-name(rotation-centered);
|
@include animation-name(rotation-centered);
|
||||||
|
|||||||
@@ -19,8 +19,17 @@
|
|||||||
* 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.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
font-size: 90%;
|
font-size: 90%;
|
||||||
|
&.delayEntry100ms {
|
||||||
|
@include keyframes(fadeInFromNone) {
|
||||||
|
0% { display: none; opacity: 0; }
|
||||||
|
100% { display: block; opacity: 1; }
|
||||||
|
}
|
||||||
|
@include animation-delay($delayEntryMs);
|
||||||
|
@include animation(fadeInFromNone $durEntryMs ease-in);
|
||||||
|
}
|
||||||
.blocker {
|
.blocker {
|
||||||
background: $colorOvrBlocker;
|
background: $colorOvrBlocker;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
line-height: $ueTopBarH;
|
line-height: $ueTopBarH;
|
||||||
}
|
}
|
||||||
|
|
||||||
.primary-pane {
|
.t-object.primary-pane {
|
||||||
// Need to lift up this pane to ensure that 'collapsed' panes don't block user interactions
|
// Need to lift up this pane to ensure that 'collapsed' panes don't block user interactions
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
}
|
}
|
||||||
@@ -212,6 +212,8 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
|
|||||||
.holder-object {
|
.holder-object {
|
||||||
top: $bodyMargin;
|
top: $bodyMargin;
|
||||||
bottom: $interiorMargin;
|
bottom: $interiorMargin;
|
||||||
|
// Clip element that have min-widths
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.holder-inspector {
|
.holder-inspector {
|
||||||
top: $bodyMargin;
|
top: $bodyMargin;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
<input type="text"
|
<input type="text"
|
||||||
ng-model="textValue"
|
ng-model="textValue"
|
||||||
ng-blur="restoreTextValue(); ngBlur()"
|
ng-blur="restoreTextValue(); ngBlur()"
|
||||||
|
ng-mouseup="ngMouseup()"
|
||||||
|
ng-disabled="ngDisabled"
|
||||||
ng-class="{
|
ng-class="{
|
||||||
error: textInvalid ||
|
error: textInvalid ||
|
||||||
(structure.validate &&
|
(structure.validate &&
|
||||||
|
|||||||
@@ -37,8 +37,10 @@ define(
|
|||||||
* @param {object[]} stylesheets stylesheet extension definitions
|
* @param {object[]} stylesheets stylesheet extension definitions
|
||||||
* @param $document Angular's jqLite-wrapped document element
|
* @param $document Angular's jqLite-wrapped document element
|
||||||
* @param {string} activeTheme the theme in use
|
* @param {string} activeTheme the theme in use
|
||||||
|
* @param {string} [assetPath] the directory relative to which
|
||||||
|
* stylesheets will be found
|
||||||
*/
|
*/
|
||||||
function StyleSheetLoader(stylesheets, $document, activeTheme) {
|
function StyleSheetLoader(stylesheets, $document, activeTheme, assetPath) {
|
||||||
var head = $document.find('head'),
|
var head = $document.find('head'),
|
||||||
document = $document[0];
|
document = $document[0];
|
||||||
|
|
||||||
@@ -47,6 +49,7 @@ define(
|
|||||||
// Create a link element, and construct full path
|
// Create a link element, and construct full path
|
||||||
var link = document.createElement('link'),
|
var link = document.createElement('link'),
|
||||||
path = [
|
path = [
|
||||||
|
assetPath,
|
||||||
stylesheet.bundle.path,
|
stylesheet.bundle.path,
|
||||||
stylesheet.bundle.resources,
|
stylesheet.bundle.resources,
|
||||||
stylesheet.stylesheetUrl
|
stylesheet.stylesheetUrl
|
||||||
@@ -68,6 +71,8 @@ define(
|
|||||||
stylesheet.theme === activeTheme;
|
stylesheet.theme === activeTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assetPath = assetPath || ".";
|
||||||
|
|
||||||
// Add all stylesheets from extensions
|
// Add all stylesheets from extensions
|
||||||
stylesheets.filter(matchesTheme).forEach(addStyleSheet);
|
stylesheets.filter(matchesTheme).forEach(addStyleSheet);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ define(
|
|||||||
|
|
||||||
it("adjusts link locations", function () {
|
it("adjusts link locations", function () {
|
||||||
expect(mockElement.setAttribute)
|
expect(mockElement.setAttribute)
|
||||||
.toHaveBeenCalledWith('href', "a/b/c/d.css");
|
.toHaveBeenCalledWith('href', "./a/b/c/d.css");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("for themed stylesheets", function () {
|
describe("for themed stylesheets", function () {
|
||||||
@@ -95,12 +95,13 @@ define(
|
|||||||
|
|
||||||
it("includes matching themes", function () {
|
it("includes matching themes", function () {
|
||||||
expect(mockElement.setAttribute)
|
expect(mockElement.setAttribute)
|
||||||
.toHaveBeenCalledWith('href', "a/b/c/themed.css");
|
.toHaveBeenCalledWith('href', "./a/b/c/themed.css");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("excludes mismatching themes", function () {
|
it("excludes mismatching themes", function () {
|
||||||
expect(mockElement.setAttribute)
|
expect(mockElement.setAttribute)
|
||||||
.not.toHaveBeenCalledWith('href', "a/b/c/bad-theme.css");
|
.not
|
||||||
|
.toHaveBeenCalledWith('href', "./a/b/c/bad-theme.css");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ define(
|
|||||||
* @property {boolean} [unknownProgress] a boolean indicating that the
|
* @property {boolean} [unknownProgress] a boolean indicating that the
|
||||||
* progress of the underlying task is unknown. This will result in a
|
* progress of the underlying task is unknown. This will result in a
|
||||||
* visually distinct progress bar.
|
* visually distinct progress bar.
|
||||||
* @property {boolean | number} [autoDismiss] If truthy, dialog will
|
* @property {boolean} [autoDismiss] If truthy, dialog will
|
||||||
* be automatically minimized or dismissed (depending on severity).
|
* be automatically minimized or dismissed (depending on severity).
|
||||||
* Additionally, if the provided value is a number, it will be used
|
* Additionally, if the provided value is a number, it will be used
|
||||||
* as the delay period before being dismissed.
|
* as the delay period before being dismissed.
|
||||||
@@ -109,18 +109,18 @@ define(
|
|||||||
* @memberof platform/commonUI/notification
|
* @memberof platform/commonUI/notification
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param $timeout the Angular $timeout service
|
* @param $timeout the Angular $timeout service
|
||||||
* @param DEFAULT_AUTO_DISMISS The period of time that an
|
* @param defaultAutoDismissTimeout The period of time that an
|
||||||
* auto-dismissed message will be displayed for.
|
* auto-dismissed message will be displayed for.
|
||||||
* @param MINIMIZE_TIMEOUT When notifications are minimized, a brief
|
* @param minimizeAnimationTimeout When notifications are minimized, a brief
|
||||||
* animation is shown. This animation requires some time to execute,
|
* animation is shown. This animation requires some time to execute,
|
||||||
* so a timeout is required before the notification is hidden
|
* so a timeout is required before the notification is hidden
|
||||||
*/
|
*/
|
||||||
function NotificationService($timeout, topic, DEFAULT_AUTO_DISMISS, MINIMIZE_TIMEOUT) {
|
function NotificationService($timeout, topic, defaultAutoDismissTimeout, minimizeAnimationTimeout) {
|
||||||
this.notifications = [];
|
this.notifications = [];
|
||||||
this.$timeout = $timeout;
|
this.$timeout = $timeout;
|
||||||
this.highest = { severity: "info" };
|
this.highest = { severity: "info" };
|
||||||
this.DEFAULT_AUTO_DISMISS = DEFAULT_AUTO_DISMISS;
|
this.AUTO_DISMISS_TIMEOUT = defaultAutoDismissTimeout;
|
||||||
this.MINIMIZE_TIMEOUT = MINIMIZE_TIMEOUT;
|
this.MINIMIZE_ANIMATION_TIMEOUT = minimizeAnimationTimeout;
|
||||||
this.topic = topic;
|
this.topic = topic;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -162,7 +162,7 @@ define(
|
|||||||
// in order to allow the minimize animation to run through.
|
// in order to allow the minimize animation to run through.
|
||||||
service.$timeout(function () {
|
service.$timeout(function () {
|
||||||
service.setActiveNotification(service.selectNextNotification());
|
service.setActiveNotification(service.selectNextNotification());
|
||||||
}, service.MINIMIZE_TIMEOUT);
|
}, service.MINIMIZE_ANIMATION_TIMEOUT);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -208,11 +208,16 @@ define(
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
NotificationService.prototype.dismissOrMinimize = function (notification) {
|
NotificationService.prototype.dismissOrMinimize = function (notification) {
|
||||||
|
var model = notification.model;
|
||||||
//For now minimize everything, and have discussion around which
|
if (model.severity === "info") {
|
||||||
//kind of messages should or should not be in the minimized
|
if (model.autoDismiss === false) {
|
||||||
//notifications list
|
notification.minimize();
|
||||||
notification.minimize();
|
} else {
|
||||||
|
notification.dismiss();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
notification.minimize();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -226,7 +231,9 @@ define(
|
|||||||
/**
|
/**
|
||||||
* A convenience method for info notifications. Notifications
|
* A convenience method for info notifications. Notifications
|
||||||
* created via this method will be auto-dismissed after a default
|
* created via this method will be auto-dismissed after a default
|
||||||
* wait period
|
* wait period unless explicitly forbidden by the caller through
|
||||||
|
* the {autoDismiss} property on the {NotificationModel}, in which
|
||||||
|
* case the notification will be minimized after the wait.
|
||||||
* @param {NotificationModel | string} message either a string for
|
* @param {NotificationModel | string} message either a string for
|
||||||
* the title of the notification message, or a {@link NotificationModel}
|
* the title of the notification message, or a {@link NotificationModel}
|
||||||
* defining the options notification to display
|
* defining the options notification to display
|
||||||
@@ -235,7 +242,6 @@ define(
|
|||||||
*/
|
*/
|
||||||
NotificationService.prototype.info = function (message) {
|
NotificationService.prototype.info = function (message) {
|
||||||
var notificationModel = typeof message === "string" ? {title: message} : message;
|
var notificationModel = typeof message === "string" ? {title: message} : message;
|
||||||
notificationModel.autoDismiss = notificationModel.autoDismiss || true;
|
|
||||||
notificationModel.severity = "info";
|
notificationModel.severity = "info";
|
||||||
return this.notify(notificationModel);
|
return this.notify(notificationModel);
|
||||||
};
|
};
|
||||||
@@ -306,28 +312,29 @@ define(
|
|||||||
activeNotification = self.active.notification,
|
activeNotification = self.active.notification,
|
||||||
topic = this.topic();
|
topic = this.topic();
|
||||||
|
|
||||||
|
notificationModel.severity = notificationModel.severity || "info";
|
||||||
|
|
||||||
notification = {
|
notification = {
|
||||||
model: notificationModel,
|
model: notificationModel,
|
||||||
|
|
||||||
minimize: function () {
|
minimize: function () {
|
||||||
self.minimize(self, notification);
|
self.minimize(self, notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
dismiss: function () {
|
dismiss: function () {
|
||||||
self.dismiss(self, notification);
|
self.dismiss(self, notification);
|
||||||
topic.notify();
|
topic.notify();
|
||||||
},
|
},
|
||||||
|
|
||||||
dismissOrMinimize: function () {
|
dismissOrMinimize: function () {
|
||||||
self.dismissOrMinimize(notification);
|
self.dismissOrMinimize(notification);
|
||||||
},
|
},
|
||||||
|
|
||||||
onDismiss: function (callback) {
|
onDismiss: function (callback) {
|
||||||
topic.listen(callback);
|
topic.listen(callback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
notificationModel.severity = notificationModel.severity || "info";
|
|
||||||
if (notificationModel.autoDismiss === true) {
|
|
||||||
notificationModel.autoDismiss = this.DEFAULT_AUTO_DISMISS;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Notifications support a 'dismissable' attribute. This is a
|
//Notifications support a 'dismissable' attribute. This is a
|
||||||
// convenience to support adding a 'dismiss' option to the
|
// convenience to support adding a 'dismiss' option to the
|
||||||
// notification for the common case of dismissing a
|
// notification for the common case of dismissing a
|
||||||
@@ -366,38 +373,39 @@ define(
|
|||||||
*/
|
*/
|
||||||
this.active.timeout = this.$timeout(function () {
|
this.active.timeout = this.$timeout(function () {
|
||||||
activeNotification.dismissOrMinimize();
|
activeNotification.dismissOrMinimize();
|
||||||
}, this.DEFAULT_AUTO_DISMISS);
|
}, this.AUTO_DISMISS_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return notification;
|
return notification;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used internally by the NotificationService
|
* Used internally by the NotificationService
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
NotificationService.prototype.setActiveNotification =
|
NotificationService.prototype.setActiveNotification = function (notification) {
|
||||||
function (notification) {
|
var shouldAutoDismiss;
|
||||||
var timeout;
|
this.active.notification = notification;
|
||||||
|
|
||||||
this.active.notification = notification;
|
if (!notification) {
|
||||||
/*
|
delete this.active.timeout;
|
||||||
If autoDismiss has been specified, OR there are other
|
return;
|
||||||
notifications queued for display, setup a timeout to
|
}
|
||||||
dismiss the dialog.
|
|
||||||
*/
|
|
||||||
if (notification && (notification.model.autoDismiss ||
|
|
||||||
this.selectNextNotification())) {
|
|
||||||
|
|
||||||
timeout = notification.model.autoDismiss || this.DEFAULT_AUTO_DISMISS;
|
if (notification.model.severity === "info") {
|
||||||
this.active.timeout = this.$timeout(function () {
|
shouldAutoDismiss = true;
|
||||||
notification.dismissOrMinimize();
|
} else {
|
||||||
}, timeout);
|
shouldAutoDismiss = notification.model.autoDismiss;
|
||||||
} else {
|
}
|
||||||
delete this.active.timeout;
|
|
||||||
}
|
if (shouldAutoDismiss || this.selectNextNotification()) {
|
||||||
};
|
this.active.timeout = this.$timeout(function () {
|
||||||
|
notification.dismissOrMinimize();
|
||||||
|
}, this.AUTO_DISMISS_TIMEOUT);
|
||||||
|
} else {
|
||||||
|
delete this.active.timeout;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used internally by the NotificationService
|
* Used internally by the NotificationService
|
||||||
|
|||||||
@@ -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/NotificationService'],
|
['../src/NotificationService'],
|
||||||
@@ -29,11 +30,16 @@ define(
|
|||||||
mockTimeout,
|
mockTimeout,
|
||||||
mockAutoDismiss,
|
mockAutoDismiss,
|
||||||
mockMinimizeTimeout,
|
mockMinimizeTimeout,
|
||||||
successModel,
|
|
||||||
mockTopicFunction,
|
mockTopicFunction,
|
||||||
mockTopicObject,
|
mockTopicObject,
|
||||||
|
infoModel,
|
||||||
|
alertModel,
|
||||||
errorModel;
|
errorModel;
|
||||||
|
|
||||||
|
function elapseTimeout() {
|
||||||
|
mockTimeout.mostRecentCall.args[0]();
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockTimeout = jasmine.createSpy("$timeout");
|
mockTimeout = jasmine.createSpy("$timeout");
|
||||||
mockTopicFunction = jasmine.createSpy("topic");
|
mockTopicFunction = jasmine.createSpy("topic");
|
||||||
@@ -41,153 +47,189 @@ define(
|
|||||||
mockTopicFunction.andReturn(mockTopicObject);
|
mockTopicFunction.andReturn(mockTopicObject);
|
||||||
|
|
||||||
mockAutoDismiss = mockMinimizeTimeout = 1000;
|
mockAutoDismiss = mockMinimizeTimeout = 1000;
|
||||||
notificationService = new NotificationService(
|
notificationService = new NotificationService(mockTimeout, mockTopicFunction, mockAutoDismiss, mockMinimizeTimeout);
|
||||||
mockTimeout, mockTopicFunction, mockAutoDismiss, mockMinimizeTimeout);
|
|
||||||
successModel = {
|
infoModel = {
|
||||||
title: "Mock Success Notification",
|
title: "Mock Info Notification",
|
||||||
severity: "info"
|
severity: "info"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
alertModel = {
|
||||||
|
title: "Mock Alert Notification",
|
||||||
|
severity: "alert"
|
||||||
|
};
|
||||||
|
|
||||||
errorModel = {
|
errorModel = {
|
||||||
title: "Mock Error Notification",
|
title: "Mock Error Notification",
|
||||||
severity: "error"
|
severity: "error"
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
it("gets a new success notification, making" +
|
|
||||||
" the notification active", function () {
|
|
||||||
var activeNotification;
|
|
||||||
notificationService.notify(successModel);
|
|
||||||
activeNotification = notificationService.getActiveNotification();
|
|
||||||
expect(activeNotification.model).toBe(successModel);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("notifies listeners on dismissal of notification", function () {
|
it("notifies listeners on dismissal of notification", function () {
|
||||||
var notification,
|
var dismissListener = jasmine.createSpy("ondismiss");
|
||||||
dismissListener = jasmine.createSpy("ondismiss");
|
var notification = notificationService.notify(infoModel);
|
||||||
notification = notificationService.notify(successModel);
|
|
||||||
notification.onDismiss(dismissListener);
|
notification.onDismiss(dismissListener);
|
||||||
expect(mockTopicObject.listen).toHaveBeenCalled();
|
expect(mockTopicObject.listen).toHaveBeenCalled();
|
||||||
notification.dismiss();
|
notification.dismiss();
|
||||||
expect(mockTopicObject.notify).toHaveBeenCalled();
|
expect(mockTopicObject.notify).toHaveBeenCalled();
|
||||||
mockTopicObject.listen.mostRecentCall.args[0]();
|
mockTopicObject.listen.mostRecentCall.args[0]();
|
||||||
expect(dismissListener).toHaveBeenCalled();
|
expect(dismissListener).toHaveBeenCalled();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows specification of an info notification given just a" +
|
it("dismisses a notification when the notification's dismiss method is used", function () {
|
||||||
" title, making the notification active", function () {
|
var notification = notificationService.info(infoModel);
|
||||||
var activeNotification,
|
|
||||||
notificationTitle = "Test info notification";
|
|
||||||
notificationService.info(notificationTitle);
|
|
||||||
activeNotification = notificationService.getActiveNotification();
|
|
||||||
expect(activeNotification.model.title).toBe(notificationTitle);
|
|
||||||
expect(activeNotification.model.severity).toBe("info");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("gets a new success notification with" +
|
|
||||||
" numerical auto-dismiss specified. ", function () {
|
|
||||||
var activeNotification;
|
|
||||||
successModel.autoDismiss = 1000;
|
|
||||||
notificationService.notify(successModel);
|
|
||||||
activeNotification = notificationService.getActiveNotification();
|
|
||||||
expect(activeNotification.model).toBe(successModel);
|
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
|
||||||
expect(mockTimeout.calls.length).toBe(2);
|
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
|
||||||
activeNotification = notificationService.getActiveNotification();
|
|
||||||
expect(activeNotification).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("gets a new notification with" +
|
|
||||||
" boolean auto-dismiss specified. ", function () {
|
|
||||||
var activeNotification;
|
|
||||||
successModel.autoDismiss = true;
|
|
||||||
notificationService.notify(successModel);
|
|
||||||
activeNotification = notificationService.getActiveNotification();
|
|
||||||
expect(activeNotification.model).toBe(successModel);
|
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
|
||||||
expect(mockTimeout.calls.length).toBe(2);
|
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
|
||||||
activeNotification = notificationService.getActiveNotification();
|
|
||||||
expect(activeNotification).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows minimization of notifications", function () {
|
|
||||||
var notification,
|
|
||||||
activeNotification;
|
|
||||||
|
|
||||||
successModel.autoDismiss = false;
|
|
||||||
notification = notificationService.notify(successModel);
|
|
||||||
|
|
||||||
activeNotification = notificationService.getActiveNotification();
|
|
||||||
expect(activeNotification.model).toBe(successModel);
|
|
||||||
notification.minimize();
|
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
|
||||||
activeNotification = notificationService.getActiveNotification();
|
|
||||||
expect(activeNotification).toBeUndefined();
|
|
||||||
expect(notificationService.notifications.length).toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows dismissal of notifications", function () {
|
|
||||||
var notification,
|
|
||||||
activeNotification;
|
|
||||||
|
|
||||||
successModel.autoDismiss = false;
|
|
||||||
notification = notificationService.notify(successModel);
|
|
||||||
|
|
||||||
activeNotification = notificationService.getActiveNotification();
|
|
||||||
expect(activeNotification.model).toBe(successModel);
|
|
||||||
notification.dismiss();
|
notification.dismiss();
|
||||||
activeNotification = notificationService.getActiveNotification();
|
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||||
expect(activeNotification).toBeUndefined();
|
expect(notificationService.notifications.length).toEqual(0);
|
||||||
expect(notificationService.notifications.length).toBe(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe(" gets called with multiple notifications", function () {
|
it("minimizes a notification when the notification's minimize method is used", function () {
|
||||||
it("auto-dismisses the previously active notification, making" +
|
var notification = notificationService.info(infoModel);
|
||||||
" the new notification active", function () {
|
notification.minimize();
|
||||||
|
elapseTimeout(); // needed for the minimize animation timeout
|
||||||
|
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||||
|
expect(notificationService.notifications.length).toEqual(1);
|
||||||
|
expect(notificationService.notifications[0]).toEqual(notification);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when receiving info notifications", function () {
|
||||||
|
it("minimizes info notifications if the caller disables auto-dismiss", function () {
|
||||||
|
infoModel.autoDismiss = false;
|
||||||
|
var notification = notificationService.info(infoModel);
|
||||||
|
elapseTimeout();
|
||||||
|
// 2nd elapse for the minimize animation timeout
|
||||||
|
elapseTimeout();
|
||||||
|
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||||
|
expect(notificationService.notifications.length).toEqual(1);
|
||||||
|
expect(notificationService.notifications[0]).toEqual(notification);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("dismisses info notifications if the caller ignores auto-dismiss", function () {
|
||||||
|
notificationService.info(infoModel);
|
||||||
|
elapseTimeout();
|
||||||
|
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||||
|
expect(notificationService.notifications.length).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("dismisses info notifications if the caller requests auto-dismiss", function () {
|
||||||
|
infoModel.autoDismiss = true;
|
||||||
|
notificationService.info(infoModel);
|
||||||
|
elapseTimeout();
|
||||||
|
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||||
|
expect(notificationService.notifications.length).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when receiving alert notifications", function () {
|
||||||
|
it("minimizes alert notifications if the caller enables auto-dismiss", function () {
|
||||||
|
alertModel.autoDismiss = true;
|
||||||
|
var notification = notificationService.alert(alertModel);
|
||||||
|
elapseTimeout();
|
||||||
|
elapseTimeout();
|
||||||
|
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||||
|
expect(notificationService.notifications.length).toEqual(1);
|
||||||
|
expect(notificationService.notifications[0]).toEqual(notification);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps alert notifications active if the caller disables auto-dismiss", function () {
|
||||||
|
mockTimeout.andCallFake(function (callback, time) {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
alertModel.autoDismiss = false;
|
||||||
|
var notification = notificationService.alert(alertModel);
|
||||||
|
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||||
|
expect(notificationService.notifications.length).toEqual(1);
|
||||||
|
expect(notificationService.notifications[0]).toEqual(notification);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps alert notifications active if the caller ignores auto-dismiss", function () {
|
||||||
|
mockTimeout.andCallFake(function (callback, time) {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
var notification = notificationService.alert(alertModel);
|
||||||
|
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||||
|
expect(notificationService.notifications.length).toEqual(1);
|
||||||
|
expect(notificationService.notifications[0]).toEqual(notification);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when receiving error notifications", function () {
|
||||||
|
it("minimizes error notifications if the caller enables auto-dismiss", function () {
|
||||||
|
errorModel.autoDismiss = true;
|
||||||
|
var notification = notificationService.error(errorModel);
|
||||||
|
elapseTimeout();
|
||||||
|
elapseTimeout();
|
||||||
|
expect(notificationService.getActiveNotification()).toBeUndefined();
|
||||||
|
expect(notificationService.notifications.length).toEqual(1);
|
||||||
|
expect(notificationService.notifications[0]).toEqual(notification);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps error notifications active if the caller disables auto-dismiss", function () {
|
||||||
|
mockTimeout.andCallFake(function (callback, time) {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
errorModel.autoDismiss = false;
|
||||||
|
var notification = notificationService.error(errorModel);
|
||||||
|
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||||
|
expect(notificationService.notifications.length).toEqual(1);
|
||||||
|
expect(notificationService.notifications[0]).toEqual(notification);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps error notifications active if the caller ignores auto-dismiss", function () {
|
||||||
|
mockTimeout.andCallFake(function (callback, time) {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
var notification = notificationService.error(errorModel);
|
||||||
|
expect(notificationService.getActiveNotification()).toEqual(notification);
|
||||||
|
expect(notificationService.notifications.length).toEqual(1);
|
||||||
|
expect(notificationService.notifications[0]).toEqual(notification);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when called with multiple notifications", function () {
|
||||||
|
it("auto-dismisses the previously active notification, making the new notification active", function () {
|
||||||
var activeNotification;
|
var activeNotification;
|
||||||
|
infoModel.autoDismiss = false;
|
||||||
//First pre-load with a info message
|
//First pre-load with a info message
|
||||||
notificationService.notify(successModel);
|
notificationService.notify(infoModel);
|
||||||
activeNotification =
|
activeNotification = notificationService.getActiveNotification();
|
||||||
notificationService.getActiveNotification();
|
|
||||||
//Initially expect the active notification to be info
|
//Initially expect the active notification to be info
|
||||||
expect(activeNotification.model).toBe(successModel);
|
expect(activeNotification.model).toBe(infoModel);
|
||||||
//Then notify of an error
|
//Then notify of an error
|
||||||
notificationService.notify(errorModel);
|
notificationService.notify(errorModel);
|
||||||
//But it should be auto-dismissed and replaced with the
|
//But it should be auto-dismissed and replaced with the
|
||||||
// error notification
|
// error notification
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
elapseTimeout();
|
||||||
//Two timeouts, one is to force minimization after
|
//Two timeouts, one is to force minimization after
|
||||||
// displaying the message for a minimum period, the
|
// displaying the message for a minimum period, the
|
||||||
// second is to allow minimization animation to take place.
|
// second is to allow minimization animation to take place.
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
elapseTimeout();
|
||||||
activeNotification = notificationService.getActiveNotification();
|
activeNotification = notificationService.getActiveNotification();
|
||||||
expect(activeNotification.model).toBe(errorModel);
|
expect(activeNotification.model).toBe(errorModel);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("auto-minimizes an active error notification", function () {
|
it("auto-minimizes an active error notification", function () {
|
||||||
var activeNotification;
|
var activeNotification;
|
||||||
//First pre-load with an error message
|
//First pre-load with an error message
|
||||||
notificationService.notify(errorModel);
|
notificationService.notify(errorModel);
|
||||||
//Then notify of info
|
//Then notify of info
|
||||||
notificationService.notify(successModel);
|
notificationService.notify(infoModel);
|
||||||
expect(notificationService.notifications.length).toEqual(2);
|
expect(notificationService.notifications.length).toEqual(2);
|
||||||
//Mock the auto-minimize
|
//Mock the auto-minimize
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
elapseTimeout();
|
||||||
//Two timeouts, one is to force minimization after
|
//Two timeouts, one is to force minimization after
|
||||||
// displaying the message for a minimum period, the
|
// displaying the message for a minimum period, the
|
||||||
// second is to allow minimization animation to take place.
|
// second is to allow minimization animation to take place.
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
elapseTimeout();
|
||||||
//Previous error message should be minimized, not
|
//Previous error message should be minimized, not
|
||||||
// dismissed
|
// dismissed
|
||||||
expect(notificationService.notifications.length).toEqual(2);
|
expect(notificationService.notifications.length).toEqual(2);
|
||||||
activeNotification =
|
activeNotification = notificationService.getActiveNotification();
|
||||||
notificationService.getActiveNotification();
|
expect(activeNotification.model).toBe(infoModel);
|
||||||
expect(activeNotification.model).toBe(successModel);
|
|
||||||
expect(errorModel.minimized).toEqual(true);
|
expect(errorModel.minimized).toEqual(true);
|
||||||
});
|
});
|
||||||
it("auto-minimizes errors when a number of them arrive in" +
|
|
||||||
" short succession ", function () {
|
it("auto-minimizes errors when a number of them arrive in short succession", function () {
|
||||||
var activeNotification,
|
var activeNotification,
|
||||||
error2 = {
|
error2 = {
|
||||||
title: "Second Mock Error Notification",
|
title: "Second Mock Error Notification",
|
||||||
@@ -205,30 +247,27 @@ define(
|
|||||||
notificationService.notify(error3);
|
notificationService.notify(error3);
|
||||||
expect(notificationService.notifications.length).toEqual(3);
|
expect(notificationService.notifications.length).toEqual(3);
|
||||||
//Mock the auto-minimize
|
//Mock the auto-minimize
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
elapseTimeout();
|
||||||
//Two timeouts, one is to force minimization after
|
//Two timeouts, one is to force minimization after
|
||||||
// displaying the message for a minimum period, the
|
// displaying the message for a minimum period, the
|
||||||
// second is to allow minimization animation to take place.
|
// second is to allow minimization animation to take place.
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
elapseTimeout();
|
||||||
//Previous error message should be minimized, not
|
//Previous error message should be minimized, not
|
||||||
// dismissed
|
// dismissed
|
||||||
expect(notificationService.notifications.length).toEqual(3);
|
expect(notificationService.notifications.length).toEqual(3);
|
||||||
activeNotification =
|
activeNotification = notificationService.getActiveNotification();
|
||||||
notificationService.getActiveNotification();
|
|
||||||
expect(activeNotification.model).toBe(error2);
|
expect(activeNotification.model).toBe(error2);
|
||||||
expect(errorModel.minimized).toEqual(true);
|
expect(errorModel.minimized).toEqual(true);
|
||||||
|
|
||||||
//Mock the second auto-minimize
|
//Mock the second auto-minimize
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
elapseTimeout();
|
||||||
//Two timeouts, one is to force minimization after
|
//Two timeouts, one is to force minimization after
|
||||||
// displaying the message for a minimum period, the
|
// displaying the message for a minimum period, the
|
||||||
// second is to allow minimization animation to take place.
|
// second is to allow minimization animation to take place.
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
elapseTimeout();
|
||||||
activeNotification =
|
activeNotification = notificationService.getActiveNotification();
|
||||||
notificationService.getActiveNotification();
|
|
||||||
expect(activeNotification.model).toBe(error3);
|
expect(activeNotification.model).toBe(error3);
|
||||||
expect(error2.minimized).toEqual(true);
|
expect(error2.minimized).toEqual(true);
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ $colorProgressBarAmt: $colorKey;
|
|||||||
$progressBarHOverlay: 15px;
|
$progressBarHOverlay: 15px;
|
||||||
$progressBarStripeW: 20px;
|
$progressBarStripeW: 20px;
|
||||||
$shdwStatusIc: rgba(black, 0.4) 0 1px 2px;
|
$shdwStatusIc: rgba(black, 0.4) 0 1px 2px;
|
||||||
|
$animPausedPulseDur: 500ms;
|
||||||
|
|
||||||
// Selects
|
// Selects
|
||||||
$colorSelectBg: $colorBtnBg;
|
$colorSelectBg: $colorBtnBg;
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ $colorProgressBarAmt: #0a0;
|
|||||||
$progressBarHOverlay: 15px;
|
$progressBarHOverlay: 15px;
|
||||||
$progressBarStripeW: 20px;
|
$progressBarStripeW: 20px;
|
||||||
$shdwStatusIc: rgba(white, 0.8) 0 0px 5px;
|
$shdwStatusIc: rgba(white, 0.8) 0 0px 5px;
|
||||||
|
$animPausedPulseDur: 1s;
|
||||||
|
|
||||||
// Selects
|
// Selects
|
||||||
$colorSelectBg: $colorBtnBg;
|
$colorSelectBg: $colorBtnBg;
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ define([
|
|||||||
"./src/capabilities/MutationCapability",
|
"./src/capabilities/MutationCapability",
|
||||||
"./src/capabilities/DelegationCapability",
|
"./src/capabilities/DelegationCapability",
|
||||||
"./src/capabilities/InstantiationCapability",
|
"./src/capabilities/InstantiationCapability",
|
||||||
|
"./src/runs/TransactingMutationListener",
|
||||||
"./src/services/Now",
|
"./src/services/Now",
|
||||||
"./src/services/Throttle",
|
"./src/services/Throttle",
|
||||||
"./src/services/Topic",
|
"./src/services/Topic",
|
||||||
@@ -78,6 +79,7 @@ define([
|
|||||||
MutationCapability,
|
MutationCapability,
|
||||||
DelegationCapability,
|
DelegationCapability,
|
||||||
InstantiationCapability,
|
InstantiationCapability,
|
||||||
|
TransactingMutationListener,
|
||||||
Now,
|
Now,
|
||||||
Throttle,
|
Throttle,
|
||||||
Topic,
|
Topic,
|
||||||
@@ -252,6 +254,14 @@ define([
|
|||||||
"pattern": "\\S+",
|
"pattern": "\\S+",
|
||||||
"required": true,
|
"required": true,
|
||||||
"cssclass": "l-input-lg"
|
"cssclass": "l-input-lg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Notes",
|
||||||
|
"key": "notes",
|
||||||
|
"property": "notes",
|
||||||
|
"control": "textarea",
|
||||||
|
"required": false,
|
||||||
|
"cssclass": "l-textarea-sm"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -399,14 +409,10 @@ define([
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"roots": [
|
"runs": [
|
||||||
{
|
{
|
||||||
"id": "mine",
|
"implementation": TransactingMutationListener,
|
||||||
"model": {
|
"depends": ["topic", "transactionService", "cacheService"]
|
||||||
"name": "My Items",
|
|
||||||
"type": "folder",
|
|
||||||
"composition": []
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"constants": [
|
"constants": [
|
||||||
|
|||||||
@@ -49,8 +49,7 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a domain object to the composition of the field.
|
* Add a domain object to the composition of this domain object.
|
||||||
* This mutates but does not persist the modified object.
|
|
||||||
*
|
*
|
||||||
* If no index is given, this is added to the end of the composition.
|
* If no index is given, this is added to the end of the composition.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -113,11 +113,16 @@ define(
|
|||||||
domainObject = this.domainObject,
|
domainObject = this.domainObject,
|
||||||
model = domainObject.getModel(),
|
model = domainObject.getModel(),
|
||||||
modified = model.modified,
|
modified = model.modified,
|
||||||
|
persisted = model.persisted,
|
||||||
persistenceService = this.persistenceService,
|
persistenceService = this.persistenceService,
|
||||||
persistenceFn = model.persisted !== undefined ?
|
persistenceFn = persisted !== undefined ?
|
||||||
this.persistenceService.updateObject :
|
this.persistenceService.updateObject :
|
||||||
this.persistenceService.createObject;
|
this.persistenceService.createObject;
|
||||||
|
|
||||||
|
if (persisted !== undefined && persisted === modified) {
|
||||||
|
return this.$q.when(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Update persistence timestamp...
|
// Update persistence timestamp...
|
||||||
domainObject.useCapability("mutation", function (m) {
|
domainObject.useCapability("mutation", function (m) {
|
||||||
m.persisted = modified;
|
m.persisted = modified;
|
||||||
@@ -178,6 +183,15 @@ define(
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this domain object has been persisted at some
|
||||||
|
* point.
|
||||||
|
* @returns {boolean} true if the object has been persisted
|
||||||
|
*/
|
||||||
|
PersistenceCapability.prototype.persisted = function () {
|
||||||
|
return this.domainObject.getModel().persisted !== undefined;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the key for this domain object in the given space.
|
* Get the key for this domain object in the given space.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -38,75 +38,25 @@ define(
|
|||||||
this.modelService = modelService;
|
this.modelService = modelService;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast-resolving promise
|
|
||||||
function fastPromise(value) {
|
|
||||||
return (value || {}).then ? value : {
|
|
||||||
then: function (callback) {
|
|
||||||
return fastPromise(callback(value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
CachingModelDecorator.prototype.getModels = function (ids) {
|
CachingModelDecorator.prototype.getModels = function (ids) {
|
||||||
var cacheService = this.cacheService,
|
var loadFromCache = ids.filter(function cached(id) {
|
||||||
neededIds = ids.filter(function notCached(id) {
|
return this.cacheService.has(id);
|
||||||
return !cacheService.has(id);
|
}, this),
|
||||||
});
|
loadFromService = ids.filter(function notCached(id) {
|
||||||
|
return !this.cacheService.has(id);
|
||||||
|
}, this);
|
||||||
|
|
||||||
// Update the cached instance of a model to a new value.
|
if (!loadFromCache.length) {
|
||||||
// We update in-place to ensure there is only ever one instance
|
return this.modelService.getModels(loadFromService);
|
||||||
// of any given model exposed by the modelService as a whole.
|
|
||||||
function updateModel(id, model) {
|
|
||||||
var oldModel = cacheService.get(id);
|
|
||||||
|
|
||||||
// Same object instance is a possibility, so don't copy
|
|
||||||
if (oldModel === model) {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we'd previously cached an undefined value, or are now
|
|
||||||
// seeing undefined, replace the item in the cache entirely.
|
|
||||||
if (oldModel === undefined || model === undefined) {
|
|
||||||
cacheService.put(id, model);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, empty out the old model...
|
|
||||||
Object.keys(oldModel).forEach(function (k) {
|
|
||||||
delete oldModel[k];
|
|
||||||
});
|
|
||||||
|
|
||||||
// ...and replace it with the contents of the new model.
|
|
||||||
Object.keys(model).forEach(function (k) {
|
|
||||||
oldModel[k] = model[k];
|
|
||||||
});
|
|
||||||
|
|
||||||
return oldModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the provided models in our cache
|
return this.modelService.getModels(loadFromService)
|
||||||
function cacheAll(models) {
|
.then(function (modelResults) {
|
||||||
Object.keys(models).forEach(function (id) {
|
loadFromCache.forEach(function (id) {
|
||||||
var model = cacheService.has(id) ?
|
modelResults[id] = this.cacheService.get(id);
|
||||||
updateModel(id, models[id]) : models[id];
|
}, this);
|
||||||
cacheService.put(id, model);
|
return modelResults;
|
||||||
});
|
}.bind(this));
|
||||||
}
|
|
||||||
|
|
||||||
// Expose the cache (for promise chaining)
|
|
||||||
function giveCache() {
|
|
||||||
return cacheService.all();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look up if we have unknown IDs
|
|
||||||
if (neededIds.length > 0) {
|
|
||||||
return this.modelService.getModels(neededIds)
|
|
||||||
.then(cacheAll)
|
|
||||||
.then(giveCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, just expose the cache directly
|
|
||||||
return fastPromise(cacheService.all());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return CachingModelDecorator;
|
return CachingModelDecorator;
|
||||||
|
|||||||
@@ -77,5 +77,9 @@ define([], function () {
|
|||||||
return this.cache;
|
return this.cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ModelCacheService.prototype.flush = function () {
|
||||||
|
this.cache = {};
|
||||||
|
};
|
||||||
|
|
||||||
return ModelCacheService;
|
return ModelCacheService;
|
||||||
});
|
});
|
||||||
|
|||||||
61
platform/core/src/runs/TransactingMutationListener.js
Normal file
61
platform/core/src/runs/TransactingMutationListener.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT Web 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 Web 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 () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for mutation on domain objects and triggers persistence when
|
||||||
|
* it occurs.
|
||||||
|
* @param {Topic} topic the `topic` service; used to listen for mutation
|
||||||
|
* @memberof platform/core
|
||||||
|
*/
|
||||||
|
function TransactingMutationListener(
|
||||||
|
topic,
|
||||||
|
transactionService,
|
||||||
|
cacheService
|
||||||
|
) {
|
||||||
|
var mutationTopic = topic('mutation');
|
||||||
|
mutationTopic.listen(function (domainObject) {
|
||||||
|
var persistence = domainObject.getCapability('persistence');
|
||||||
|
var wasActive = transactionService.isActive();
|
||||||
|
cacheService.put(domainObject.getId(), domainObject.getModel());
|
||||||
|
|
||||||
|
if (persistence.persisted()) {
|
||||||
|
if (!wasActive) {
|
||||||
|
transactionService.startTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionService.addToTransaction(
|
||||||
|
persistence.persist.bind(persistence),
|
||||||
|
persistence.refresh.bind(persistence)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!wasActive) {
|
||||||
|
transactionService.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return TransactingMutationListener;
|
||||||
|
});
|
||||||
@@ -67,6 +67,7 @@ define(
|
|||||||
listener(message);
|
listener(message);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$log.error(ERROR_PREFIX + e.message);
|
$log.error(ERROR_PREFIX + e.message);
|
||||||
|
$log.error(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user