mirror of
https://github.com/nasa/openmct.git
synced 2025-06-29 12:13:03 +00:00
Compare commits
79 Commits
ch-plot-st
...
number-inp
Author | SHA1 | Date | |
---|---|---|---|
9ca8975baf | |||
e7ba13f844 | |||
a3520ba51e | |||
8e0b7fce7f | |||
52f5bea215 | |||
26b4e5498f | |||
ce733628b2 | |||
73e452edc0 | |||
bbeb97e93c | |||
e6d65f3549 | |||
1ab4cf68d7 | |||
7fcaf6510e | |||
0713941812 | |||
1d7d56d5e7 | |||
ba9941891d | |||
d3b313d8aa | |||
e2f0f61862 | |||
ecdcebce0c | |||
5fbf71264e | |||
8519e7660f | |||
e75d1f62ec | |||
1c9a9baf77 | |||
44246e6973 | |||
61f59a94e4 | |||
a8a689f69a | |||
c7787aa1f0 | |||
c1128c3156 | |||
485944a782 | |||
beb24adf7a | |||
6de5f73d78 | |||
35b51d151d | |||
b2333d83d2 | |||
7513f24ff3 | |||
cb9231f453 | |||
1c9230029d | |||
e300b49c95 | |||
f6cd35a631 | |||
53cecb8909 | |||
95188f6ce6 | |||
8c7e8dab8e | |||
305d2f60d0 | |||
b60eb6d6ae | |||
26a7fee869 | |||
1bdc0497c7 | |||
04c2eac9ef | |||
5daaae36bc | |||
3dea30b1e1 | |||
9f8578d79e | |||
0fa5609396 | |||
9956ce31e5 | |||
25ff430368 | |||
cc9a2cbf4f | |||
cd8c0fa72f | |||
fff75d111e | |||
f4df84bfa1 | |||
0dc65f5dfb | |||
6e4abcfed8 | |||
e2f9a0c9cd | |||
749a2ba088 | |||
b70501a7ed | |||
b8ae741969 | |||
9f32b4b9cd | |||
fbf736aaf7 | |||
344a325cb5 | |||
f8a04d0fc2 | |||
6e1a43130d | |||
906646704e | |||
8fb9306272 | |||
5aa93ba50c | |||
26db493b0d | |||
6459f410e7 | |||
4072b91808 | |||
8750bdd778 | |||
60a8ee657a | |||
ecf1bac5c7 | |||
d04bdd2685 | |||
165bd4f638 | |||
9df59522d9 | |||
7fc66e2de8 |
107
API.md
107
API.md
@ -372,6 +372,7 @@ Known hints:
|
|||||||
|
|
||||||
* `domain`: Indicates that the value represents the "input" of a datum. Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
|
* `domain`: Indicates that the value represents the "input" of a datum. Values with a `domain` hint will be used for the x-axis of a plot, and tables will render columns for these values first.
|
||||||
* `range`: Indicates that the value is the "output" of a datum. Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
|
* `range`: Indicates that the value is the "output" of a datum. Values with a `range` hint will be used as the y-axis on a plot, and tables will render columns for these values after the `domain` values.
|
||||||
|
* `image`: Indicates that the value may be interpreted as the URL to an image file, in which case appropriate views will be made available.
|
||||||
|
|
||||||
##### The Time Conductor and Telemetry
|
##### The Time Conductor and Telemetry
|
||||||
|
|
||||||
@ -547,29 +548,24 @@ numbers in UTC terrestrial time.
|
|||||||
|
|
||||||
#### Getting and Setting the Active Time System
|
#### Getting and Setting the Active Time System
|
||||||
|
|
||||||
Once registered, a time system can be activated using a key, or an instance of
|
Once registered, a time system can be activated by calling `timeSystem` with
|
||||||
the time system itself.
|
the timeSystem `key` or an instance of the time system. If you are not using a
|
||||||
|
[clock](#clocks), you must also specify valid [bounds](#time-bounds) for the
|
||||||
|
timeSystem.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
openmct.time.timeSystem('utc');
|
openmct.time.timeSystem('utc', bounds);
|
||||||
```
|
```
|
||||||
|
|
||||||
A time system can be immediately activated upon registration:
|
A time system can be immediately activated after registration:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var utcTimeSystem = {
|
|
||||||
key: 'utc',
|
|
||||||
name: 'UTC Time',
|
|
||||||
cssClass = 'icon-clock',
|
|
||||||
timeFormat = 'utc',
|
|
||||||
durationFormat = 'duration',
|
|
||||||
isUTCBased = true
|
|
||||||
};
|
|
||||||
openmct.time.addTimeSystem(utcTimeSystem);
|
openmct.time.addTimeSystem(utcTimeSystem);
|
||||||
openmct.time.timeSystem(utcTimeSystem);
|
openmct.time.timeSystem(utcTimeSystem, bounds);
|
||||||
```
|
```
|
||||||
|
|
||||||
Setting the active time system will trigger a [time system event](#time-events).
|
Setting the active time system will trigger a [`'timeSystem'`](#time-events)
|
||||||
|
event. If you supplied bounds, a [`'bounds'`](#time-events) event will be triggered afterwards with your newly supplied bounds.
|
||||||
|
|
||||||
### Time Bounds
|
### Time Bounds
|
||||||
|
|
||||||
@ -592,8 +588,8 @@ let now = Date.now();
|
|||||||
openmct.time.bounds({start: now - ONE_HOUR, now);
|
openmct.time.bounds({start: now - ONE_HOUR, now);
|
||||||
```
|
```
|
||||||
|
|
||||||
To respond to bounds change events, simply register a callback against the `bounds`
|
To respond to bounds change events, listen for the [`'bounds'`](#time-events)
|
||||||
event. For more information on the bounds event, please see the section on [Time Events](#time-events).
|
event.
|
||||||
|
|
||||||
## Clocks
|
## Clocks
|
||||||
|
|
||||||
@ -673,14 +669,16 @@ An example clock implementation is provided in the form of the [LocalClock](http
|
|||||||
Once registered a clock can be activated by calling the `clock` function on the
|
Once registered a clock can be activated by calling the `clock` function on the
|
||||||
Time API passing in the key or instance of a registered clock. Only one clock
|
Time API passing in the key or instance of a registered clock. Only one clock
|
||||||
may be active at once, so activating a clock will deactivate any currently
|
may be active at once, so activating a clock will deactivate any currently
|
||||||
active clock. Setting the clock will also trigger a ['clock' event](#time-events).
|
active clock. [`clockOffsets`](#clock-offsets) must be specified when changing a clock.
|
||||||
|
|
||||||
|
Setting the clock triggers a [`'clock'`](#time-events) event, followed by a [`'clockOffsets'`](#time-events) event, and then a [`'bounds'`](#time-events) event as the offsets are applied to the clock's currentValue().
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
openmct.time.clock(someClock);
|
openmct.time.clock(someClock, clockOffsets);
|
||||||
```
|
```
|
||||||
|
|
||||||
Upon being activated, a clock's `on` function will be immediately called to subscribe
|
Upon being activated, the time API will listen for tick events on the clock by calling `clock.on`.
|
||||||
to `tick` events.
|
|
||||||
|
|
||||||
The currently active clock (if any) can be retrieved by calling the same
|
The currently active clock (if any) can be retrieved by calling the same
|
||||||
function without any arguments.
|
function without any arguments.
|
||||||
@ -707,7 +705,7 @@ relative time spans. Offsets are defined as an object with two properties:
|
|||||||
* `start`: A `number` that must be < 0 and which is used to calculate the start
|
* `start`: A `number` that must be < 0 and which is used to calculate the start
|
||||||
bounds on each clock tick. The start offset will be calculated relative to the
|
bounds on each clock tick. The start offset will be calculated relative to the
|
||||||
value provided by a clock's tick callback, or its `currentValue()` function.
|
value provided by a clock's tick callback, or its `currentValue()` function.
|
||||||
* `end`: A `number` that must be >=0 and which is used to calculate the end
|
* `end`: A `number` that must be >= 0 and which is used to calculate the end
|
||||||
bounds on each clock tick.
|
bounds on each clock tick.
|
||||||
|
|
||||||
The `clockOffsets` function can be used to get or set clock offsets. For example,
|
The `clockOffsets` function can be used to get or set clock offsets. For example,
|
||||||
@ -728,16 +726,9 @@ Clock offsets are only relevant when a clock source is active.
|
|||||||
|
|
||||||
## Time Events
|
## Time Events
|
||||||
|
|
||||||
The time API supports the registration of listeners that will be invoked when the
|
The Time API is a standard event emitter; you can register callbacks for events using the `on` method and remove callbacks for events with the `off` method.
|
||||||
application's temporal state changes. Events listeners can be registered using
|
|
||||||
the `on` function. They can be deregistered using the `off` function. The arguments
|
|
||||||
accepted by the `on` and `off` functions are:
|
|
||||||
|
|
||||||
* `event`: A `string` naming the event to subscribe to. Event names correspond to
|
For example:
|
||||||
the property of the Time API you're interested in. A [full list of time events](#list-of-time-events)
|
|
||||||
is provided later.
|
|
||||||
|
|
||||||
As an example, the code to listen to bounds change events looks like:
|
|
||||||
|
|
||||||
``` javascript
|
``` javascript
|
||||||
openmct.time.on('bounds', function callback (newBounds, tick) {
|
openmct.time.on('bounds', function callback (newBounds, tick) {
|
||||||
@ -747,40 +738,38 @@ openmct.time.on('bounds', function callback (newBounds, tick) {
|
|||||||
|
|
||||||
#### List of Time Events
|
#### List of Time Events
|
||||||
|
|
||||||
The events supported by the Time API are:
|
The events emitted by the Time API are:
|
||||||
|
|
||||||
* `bounds`: Listen for changes to current bounds. The callback will be invoked
|
* `bounds`: emitted whenever the bounds change. The callback will be invoked
|
||||||
with two arguments:
|
with two arguments:
|
||||||
* `bounds`: A [bounds](#getting-and-setting-bounds) bounds object representing
|
* `bounds`: A [bounds](#getting-and-setting-bounds) bounds object
|
||||||
a new time period bound by the specified start and send times.
|
representing a new time period bound by the specified start and send times.
|
||||||
* `tick`: A `boolean` indicating whether or not this bounds change is due to a
|
* `tick`: A `boolean` indicating whether or not this bounds change is due to
|
||||||
"tick" from a [clock source](#clocks). This information can be useful when
|
a "tick" from a [clock source](#clocks). This information can be useful
|
||||||
determining a strategy for fetching telemetry data in response to a bounds
|
when determining a strategy for fetching telemetry data in response to a
|
||||||
change event. For example, if the bounds change was automatic, and is due
|
bounds change event. For example, if the bounds change was automatic, and
|
||||||
to a tick then it's unlikely that you would need to perform a historical
|
is due to a tick then it's unlikely that you would need to perform a
|
||||||
data query. It should be sufficient to just show any new telemetry received
|
historical data query. It should be sufficient to just show any new
|
||||||
via subscription since the last tick, and optionally to discard any older
|
telemetry received via subscription since the last tick, and optionally to
|
||||||
data that now falls outside of the currently set bounds. If `tick` is false,
|
discard any older data that now falls outside of the currently set bounds.
|
||||||
then the bounds change was not due to an automatic tick, and a query for
|
If `tick` is false,then the bounds change was not due to an automatic tick,
|
||||||
historical data may be necessary, depending on your data caching strategy,
|
and a query for historical data may be necessary, depending on your data
|
||||||
and how significantly the start bound has changed.
|
caching strategy, and how significantly the start bound has changed.
|
||||||
* `timeSystem`: Listen for changes to the active [time system](#defining-and-registering-time-systems).
|
* `timeSystem`: emitted whenever the active time system changes. The callback will be invoked with a single argument:
|
||||||
The callback will be invoked with a single argument - the newly active time system.
|
* `timeSystem`: The newly active [time system](#defining-and-registering-time-systems).
|
||||||
* `timeSystem`: The newly active [time system](#defining-and-registering-time-systems) object.
|
* `clock`: emitted whenever the clock changes. The callback will be invoked
|
||||||
* `clock`: Listen for changes to the active clock. When invoked, the callback
|
with a single argument:
|
||||||
will be provided with the new clock.
|
* `clock`: The newly active [clock](#clocks), or `undefined` if an active
|
||||||
* `clock`: The newly active [clock](#clocks), or `undefined` if an active clock
|
clock has been deactivated.
|
||||||
has been deactivated.
|
* `clockOffsets`: emitted whenever the active clock offsets change. The
|
||||||
* `clockOffsets`: Listen for changes to active clock offsets. When invoked the
|
callback will be invoked with a single argument:
|
||||||
callback will be provided with the new clock offsets.
|
* `clockOffsets`: The new [clock offsets](#clock-offsets).
|
||||||
* `clockOffsets`: A [clock offsets](#clock-offsets) object.
|
|
||||||
|
|
||||||
|
|
||||||
## The Time Conductor
|
## The Time Conductor
|
||||||
|
|
||||||
The Time Conductor provides a user interface for managing time bounds in Open MCT.
|
The Time Conductor provides a user interface for managing time bounds in Open
|
||||||
It allows a user to select from configured time systems and clocks, and to set bounds
|
MCT. It allows a user to select from configured time systems and clocks, and to set bounds and clock offsets.
|
||||||
and clock offsets.
|
|
||||||
|
|
||||||
If activated, the time conductor must be provided with configuration options,
|
If activated, the time conductor must be provided with configuration options,
|
||||||
detailed below.
|
detailed below.
|
||||||
|
14
README.md
14
README.md
@ -137,20 +137,6 @@ naming convention is otherwise the same.)
|
|||||||
When `npm test` is run, test results will be written as HTML to
|
When `npm test` is run, test results will be written as HTML to
|
||||||
`target/tests`. Code coverage information is written to `target/coverage`.
|
`target/tests`. Code coverage information is written to `target/coverage`.
|
||||||
|
|
||||||
|
|
||||||
### Functional Testing
|
|
||||||
|
|
||||||
The tests described above are all at the unit-level; an additional
|
|
||||||
test suite using [Protractor](https://angular.github.io/protractor/)
|
|
||||||
is under development, in the `protractor` folder.
|
|
||||||
|
|
||||||
To run:
|
|
||||||
|
|
||||||
* Install protractor following the instructions above.
|
|
||||||
* `cd protractor`
|
|
||||||
* `npm install`
|
|
||||||
* `npm run all`
|
|
||||||
|
|
||||||
# Glossary
|
# Glossary
|
||||||
|
|
||||||
Certain terms are used throughout Open MCT with consistent meanings
|
Certain terms are used throughout Open MCT with consistent meanings
|
||||||
|
@ -933,9 +933,10 @@ Note that `templateUrl` is not supported for `containers`.
|
|||||||
|
|
||||||
Controls provide options for the `mct-control` directive.
|
Controls provide options for the `mct-control` directive.
|
||||||
|
|
||||||
Ten standard control types are included in the forms bundle:
|
These standard control types are included in the forms bundle:
|
||||||
|
|
||||||
* `textfield`: An area to enter plain text.
|
* `textfield`: A text input to enter plain text.
|
||||||
|
* `numberfield`: A text input to enter numbers.
|
||||||
* `select`: A drop-down list of options.
|
* `select`: A drop-down list of options.
|
||||||
* `checkbox`: A box which may be checked/unchecked.
|
* `checkbox`: A box which may be checked/unchecked.
|
||||||
* `color`: A color picker.
|
* `color`: A color picker.
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
/*global define*/
|
|
||||||
|
|
||||||
define([
|
|
||||||
"./src/ImageTelemetryProvider",
|
|
||||||
'legacyRegistry'
|
|
||||||
], function (
|
|
||||||
ImageTelemetryProvider,
|
|
||||||
legacyRegistry
|
|
||||||
) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
legacyRegistry.register("example/imagery", {
|
|
||||||
"name": "Imagery",
|
|
||||||
"description": "Example of a component that produces image telemetry.",
|
|
||||||
"extensions": {
|
|
||||||
"components": [
|
|
||||||
{
|
|
||||||
"implementation": ImageTelemetryProvider,
|
|
||||||
"type": "provider",
|
|
||||||
"provides": "telemetryService",
|
|
||||||
"depends": [
|
|
||||||
"$q",
|
|
||||||
"$timeout"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"types": [
|
|
||||||
{
|
|
||||||
"key": "imagery",
|
|
||||||
"name": "Example Imagery",
|
|
||||||
"cssClass": "icon-image",
|
|
||||||
"features": "creation",
|
|
||||||
"description": "For development use. Creates example imagery data that mimics a live imagery stream.",
|
|
||||||
"priority": 10,
|
|
||||||
"model": {
|
|
||||||
"telemetry": {}
|
|
||||||
},
|
|
||||||
"telemetry": {
|
|
||||||
"source": "imagery",
|
|
||||||
"domains": [
|
|
||||||
{
|
|
||||||
"name": "Time",
|
|
||||||
"key": "time",
|
|
||||||
"format": "utc"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ranges": [
|
|
||||||
{
|
|
||||||
"name": "Image",
|
|
||||||
"key": "url",
|
|
||||||
"format": "imageUrl"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
140
example/imagery/plugin.js
Normal file
140
example/imagery/plugin.js
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define([
|
||||||
|
|
||||||
|
], function(
|
||||||
|
|
||||||
|
) {
|
||||||
|
function ImageryPlugin() {
|
||||||
|
|
||||||
|
var IMAGE_SAMPLES = [
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18731.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18732.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18733.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18734.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18735.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18736.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18737.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18738.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18739.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18740.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18741.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18742.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18743.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18744.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18745.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18746.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18747.jpg",
|
||||||
|
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg"
|
||||||
|
];
|
||||||
|
|
||||||
|
function pointForTimestamp(timestamp) {
|
||||||
|
return {
|
||||||
|
utc: Math.floor(timestamp / 5000) * 5000,
|
||||||
|
url: IMAGE_SAMPLES[Math.floor(timestamp / 5000) % IMAGE_SAMPLES.length]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var realtimeProvider = {
|
||||||
|
supportsSubscribe: function (domainObject) {
|
||||||
|
return domainObject.type === 'example.imagery';
|
||||||
|
},
|
||||||
|
subscribe: function (domainObject, callback) {
|
||||||
|
var interval = setInterval(function () {
|
||||||
|
callback(pointForTimestamp(Date.now()));
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
return function (interval) {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var historicalProvider = {
|
||||||
|
supportsRequest: function (domainObject, options) {
|
||||||
|
return domainObject.type === 'example.imagery'
|
||||||
|
&& options.strategy !== 'latest';
|
||||||
|
},
|
||||||
|
request: function (domainObject, options) {
|
||||||
|
var start = options.start;
|
||||||
|
var end = options.end;
|
||||||
|
var data = [];
|
||||||
|
while (start < end) {
|
||||||
|
data.push(pointForTimestamp(start));
|
||||||
|
start += 5000;
|
||||||
|
}
|
||||||
|
return Promise.resolve(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var ladProvider = {
|
||||||
|
supportsRequest: function (domainObject, options) {
|
||||||
|
return domainObject.type === 'example.imagery' &&
|
||||||
|
options.strategy === 'latest';
|
||||||
|
},
|
||||||
|
request: function (domainObject, options) {
|
||||||
|
return Promise.resolve([pointForTimestamp(Date.now())]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return function install(openmct) {
|
||||||
|
openmct.types.addType('example.imagery', {
|
||||||
|
key: 'example.imagery',
|
||||||
|
name: 'Example Imagery',
|
||||||
|
cssClass: 'icon-image',
|
||||||
|
description: 'For development use. Creates example imagery ' +
|
||||||
|
'data that mimics a live imagery stream.',
|
||||||
|
creatable: true,
|
||||||
|
initialize: function (object) {
|
||||||
|
object.telemetry = {
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
name: 'Time',
|
||||||
|
key: 'utc',
|
||||||
|
format: 'utc',
|
||||||
|
hints: {
|
||||||
|
domain: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Image',
|
||||||
|
key: 'url',
|
||||||
|
format: 'image',
|
||||||
|
hints: {
|
||||||
|
image: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
openmct.telemetry.addProvider(realtimeProvider);
|
||||||
|
openmct.telemetry.addProvider(historicalProvider);
|
||||||
|
openmct.telemetry.addProvider(ladProvider);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ImageryPlugin;
|
||||||
|
});
|
@ -1,81 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
/*global define,Promise*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module defining ImageTelemetry. Created by vwoeltje on 06/22/15.
|
|
||||||
*/
|
|
||||||
define(
|
|
||||||
[],
|
|
||||||
function () {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var firstObservedTime = Date.now(),
|
|
||||||
images = [
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18731.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18732.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18733.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18734.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18735.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18736.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18737.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18738.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18739.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18740.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18741.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18742.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18743.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18744.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18745.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18746.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18747.jpg",
|
|
||||||
"https://www.hq.nasa.gov/alsj/a16/AS16-117-18748.jpg"
|
|
||||||
|
|
||||||
].map(function (url, index) {
|
|
||||||
return {
|
|
||||||
timestamp: firstObservedTime + 1000 * index,
|
|
||||||
url: url
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
function ImageTelemetry() {
|
|
||||||
return {
|
|
||||||
getPointCount: function () {
|
|
||||||
return Math.floor((Date.now() - firstObservedTime) / 1000);
|
|
||||||
},
|
|
||||||
getDomainValue: function (i, domain) {
|
|
||||||
return images[i % images.length].timestamp;
|
|
||||||
},
|
|
||||||
getRangeValue: function (i, range) {
|
|
||||||
return images[i % images.length].url;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return ImageTelemetry;
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,115 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT, Copyright (c) 2014-2017, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
/*global define,Promise*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module defining ImageTelemetryProvider. Created by vwoeltje on 06/22/15.
|
|
||||||
*/
|
|
||||||
define(
|
|
||||||
["./ImageTelemetry"],
|
|
||||||
function (ImageTelemetry) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
function ImageTelemetryProvider($q, $timeout) {
|
|
||||||
var subscriptions = [];
|
|
||||||
|
|
||||||
//
|
|
||||||
function matchesSource(request) {
|
|
||||||
return request.source === "imagery";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used internally; this will be repacked by doPackage
|
|
||||||
function generateData(request) {
|
|
||||||
return {
|
|
||||||
key: request.key,
|
|
||||||
telemetry: new ImageTelemetry()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
function doPackage(results) {
|
|
||||||
var packaged = {};
|
|
||||||
results.forEach(function (result) {
|
|
||||||
packaged[result.key] = result.telemetry;
|
|
||||||
});
|
|
||||||
// Format as expected (sources -> keys -> telemetry)
|
|
||||||
return { imagery: packaged };
|
|
||||||
}
|
|
||||||
|
|
||||||
function requestTelemetry(requests) {
|
|
||||||
return $timeout(function () {
|
|
||||||
return doPackage(requests.filter(matchesSource).map(generateData));
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSubscriptions() {
|
|
||||||
subscriptions.forEach(function (subscription) {
|
|
||||||
var requests = subscription.requests;
|
|
||||||
subscription.callback(doPackage(
|
|
||||||
requests.filter(matchesSource).map(generateData)
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function startGenerating() {
|
|
||||||
$timeout(function () {
|
|
||||||
handleSubscriptions();
|
|
||||||
if (subscriptions.length > 0) {
|
|
||||||
startGenerating();
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function subscribe(callback, requests) {
|
|
||||||
var subscription = {
|
|
||||||
callback: callback,
|
|
||||||
requests: requests
|
|
||||||
};
|
|
||||||
|
|
||||||
function unsubscribe() {
|
|
||||||
subscriptions = subscriptions.filter(function (s) {
|
|
||||||
return s !== subscription;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
subscriptions.push(subscription);
|
|
||||||
|
|
||||||
if (subscriptions.length === 1) {
|
|
||||||
startGenerating();
|
|
||||||
}
|
|
||||||
|
|
||||||
return unsubscribe;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
requestTelemetry: requestTelemetry,
|
|
||||||
subscribe: subscribe
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return ImageTelemetryProvider;
|
|
||||||
}
|
|
||||||
);
|
|
@ -77,11 +77,15 @@ if (process.env.NODE_ENV === 'development') {
|
|||||||
gulp.task('scripts', function () {
|
gulp.task('scripts', function () {
|
||||||
var requirejsOptimize = require('gulp-requirejs-optimize');
|
var requirejsOptimize = require('gulp-requirejs-optimize');
|
||||||
var replace = require('gulp-replace-task');
|
var replace = require('gulp-replace-task');
|
||||||
|
var header = require('gulp-header');
|
||||||
|
var comment = fs.readFileSync('src/about.frag');
|
||||||
|
|
||||||
return gulp.src(paths.main)
|
return gulp.src(paths.main)
|
||||||
.pipe(sourcemaps.init())
|
.pipe(sourcemaps.init())
|
||||||
.pipe(requirejsOptimize(options.requirejsOptimize))
|
.pipe(requirejsOptimize(options.requirejsOptimize))
|
||||||
.pipe(sourcemaps.write('.'))
|
.pipe(sourcemaps.write('.'))
|
||||||
.pipe(replace(options.replace))
|
.pipe(replace(options.replace))
|
||||||
|
.pipe(header(comment, options.replace.variables))
|
||||||
.pipe(gulp.dest(paths.dist));
|
.pipe(gulp.dest(paths.dist));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
26
index.html
26
index.html
@ -32,7 +32,6 @@
|
|||||||
|
|
||||||
require(['openmct'], function (openmct) {
|
require(['openmct'], function (openmct) {
|
||||||
[
|
[
|
||||||
'example/imagery',
|
|
||||||
'example/eventGenerator',
|
'example/eventGenerator',
|
||||||
'example/styleguide'
|
'example/styleguide'
|
||||||
].forEach(
|
].forEach(
|
||||||
@ -42,8 +41,31 @@
|
|||||||
openmct.install(openmct.plugins.LocalStorage());
|
openmct.install(openmct.plugins.LocalStorage());
|
||||||
openmct.install(openmct.plugins.Espresso());
|
openmct.install(openmct.plugins.Espresso());
|
||||||
openmct.install(openmct.plugins.Generator());
|
openmct.install(openmct.plugins.Generator());
|
||||||
|
openmct.install(openmct.plugins.ExampleImagery());
|
||||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||||
openmct.time.timeSystem("utc", {start: Date.now() - THIRTY_MINUTES, end: Date.now()});
|
openmct.install(openmct.plugins.Conductor({
|
||||||
|
menuOptions: [
|
||||||
|
{
|
||||||
|
name: "Fixed",
|
||||||
|
timeSystem: 'utc',
|
||||||
|
bounds: {
|
||||||
|
start: Date.now() - 30 * 60 * 1000,
|
||||||
|
end: Date.now()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Realtime",
|
||||||
|
timeSystem: 'utc',
|
||||||
|
clock: 'local',
|
||||||
|
clockOffsets: {
|
||||||
|
start: -25 * 60 * 1000,
|
||||||
|
end: 5 * 60 * 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
|
||||||
|
openmct.time.timeSystem('utc');
|
||||||
openmct.start();
|
openmct.start();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
"git-rev-sync": "^1.4.0",
|
"git-rev-sync": "^1.4.0",
|
||||||
"glob": ">= 3.0.0",
|
"glob": ">= 3.0.0",
|
||||||
"gulp": "^3.9.0",
|
"gulp": "^3.9.0",
|
||||||
|
"gulp-header": "^1.8.8",
|
||||||
"gulp-jscs": "^3.0.2",
|
"gulp-jscs": "^3.0.2",
|
||||||
"gulp-jshint": "^2.0.0",
|
"gulp-jshint": "^2.0.0",
|
||||||
"gulp-jshint-html-reporter": "^0.1.3",
|
"gulp-jshint-html-reporter": "^0.1.3",
|
||||||
|
@ -240,7 +240,7 @@ define([
|
|||||||
"views": [
|
"views": [
|
||||||
{
|
{
|
||||||
"key": "items",
|
"key": "items",
|
||||||
"name": "Items",
|
"name": "Grid",
|
||||||
"cssClass": "icon-thumbs-strip",
|
"cssClass": "icon-thumbs-strip",
|
||||||
"description": "Grid of available items",
|
"description": "Grid of available items",
|
||||||
"template": itemsTemplate,
|
"template": itemsTemplate,
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div ng-controller="BrowseObjectController" class="abs l-flex-col">
|
<div ng-controller="BrowseObjectController" class="abs l-flex-col">
|
||||||
<div class="holder flex-elem l-flex-row object-browse-bar ">
|
<div class="holder flex-elem l-flex-row object-browse-bar t-primary">
|
||||||
<div class="items-select left flex-elem l-flex-row grows">
|
<div class="items-select left flex-elem l-flex-row grows">
|
||||||
<mct-representation key="'back-arrow'"
|
<mct-representation key="'back-arrow'"
|
||||||
mct-object="domainObject"
|
mct-object="domainObject"
|
||||||
@ -31,16 +31,18 @@
|
|||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
|
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
|
||||||
<mct-representation key="'switcher'"
|
<span class="l-object-action-buttons">
|
||||||
mct-object="domainObject"
|
<mct-representation key="'switcher'"
|
||||||
ng-model="representation">
|
mct-object="domainObject"
|
||||||
</mct-representation>
|
ng-model="representation">
|
||||||
<!-- Temporarily, on mobile, the action buttons are hidden-->
|
</mct-representation>
|
||||||
<mct-representation key="'action-group'"
|
<!-- Temporarily, on mobile, the action buttons are hidden-->
|
||||||
mct-object="domainObject"
|
<mct-representation key="'action-group'"
|
||||||
parameters="{ category: 'view-control' }"
|
mct-object="domainObject"
|
||||||
class="mobile-hide">
|
parameters="{ category: 'view-control' }"
|
||||||
</mct-representation>
|
class="mobile-hide l-object-action-buttons">
|
||||||
|
</mct-representation>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<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 l-controls-visible l-time-controller-visible">
|
||||||
|
@ -29,3 +29,7 @@
|
|||||||
mct-object='domainObject'
|
mct-object='domainObject'
|
||||||
class="flex-elem context-available-w"></mct-representation>
|
class="flex-elem context-available-w"></mct-representation>
|
||||||
</span>
|
</span>
|
||||||
|
<a class="s-button icon-expand t-btn-view-large"
|
||||||
|
title="View large"
|
||||||
|
mct-trigger-modal>
|
||||||
|
</a>
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="bottom-bar">
|
<div class="bottom-bar">
|
||||||
<a ng-repeat="dialogOption in ngModel.options"
|
<a ng-repeat="dialogOption in ngModel.options"
|
||||||
class="s-button major"
|
class="s-button"
|
||||||
ng-click="dialogOption.callback()">
|
ng-click="dialogOption.callback()">
|
||||||
{{dialogOption.label}}
|
{{dialogOption.label}}
|
||||||
</a>
|
</a>
|
||||||
|
@ -19,12 +19,12 @@
|
|||||||
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" ng-class="{'delayEntry100ms' : ngModel.delay}">
|
<div class="abs overlay l-dialog" ng-class="{'delayEntry100ms' : ngModel.delay}">
|
||||||
<div class="abs blocker"></div>
|
<div class="abs blocker"></div>
|
||||||
<div class="abs holder">
|
<div class="abs outer-holder">
|
||||||
<a ng-click="ngModel.cancel()"
|
<a ng-click="ngModel.cancel()"
|
||||||
ng-if="ngModel.cancel"
|
ng-if="ngModel.cancel"
|
||||||
class="close icon-x"></a>
|
class="close icon-x-in-circle"></a>
|
||||||
<div class="abs contents" ng-transclude></div>
|
<div class="abs inner-holder contents" ng-transclude></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "openmct-symbols-16px",
|
"name": "openmct-symbols-16px",
|
||||||
"lastOpened": 1487197651675,
|
"lastOpened": 0,
|
||||||
"created": 1487194069058
|
"created": 1497475810461
|
||||||
},
|
},
|
||||||
"iconSets": [
|
"iconSets": [
|
||||||
{
|
{
|
||||||
@ -573,20 +573,28 @@
|
|||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 122,
|
"order": 124,
|
||||||
"id": 106,
|
"id": 106,
|
||||||
"name": "icon-expand",
|
"name": "icon-expand",
|
||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"code": 921665,
|
"code": 921665,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"order": 123,
|
||||||
|
"id": 107,
|
||||||
|
"name": "icon-list-view",
|
||||||
|
"prevSize": 24,
|
||||||
|
"code": 921666,
|
||||||
|
"tempChar": ""
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"order": 37,
|
"order": 37,
|
||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"name": "icon-activity",
|
"name": "icon-activity",
|
||||||
"id": 32,
|
"id": 32,
|
||||||
"code": 921856,
|
"code": 921856,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 36,
|
"order": 36,
|
||||||
@ -594,7 +602,7 @@
|
|||||||
"name": "icon-activity-mode",
|
"name": "icon-activity-mode",
|
||||||
"id": 31,
|
"id": 31,
|
||||||
"code": 921857,
|
"code": 921857,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 52,
|
"order": 52,
|
||||||
@ -602,7 +610,7 @@
|
|||||||
"name": "icon-autoflow-tabular",
|
"name": "icon-autoflow-tabular",
|
||||||
"id": 47,
|
"id": 47,
|
||||||
"code": 921858,
|
"code": 921858,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 55,
|
"order": 55,
|
||||||
@ -610,7 +618,7 @@
|
|||||||
"name": "icon-clock",
|
"name": "icon-clock",
|
||||||
"id": 50,
|
"id": 50,
|
||||||
"code": 921859,
|
"code": 921859,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 58,
|
"order": 58,
|
||||||
@ -618,7 +626,7 @@
|
|||||||
"name": "icon-database",
|
"name": "icon-database",
|
||||||
"id": 53,
|
"id": 53,
|
||||||
"code": 921860,
|
"code": 921860,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 57,
|
"order": 57,
|
||||||
@ -626,7 +634,7 @@
|
|||||||
"name": "icon-database-query",
|
"name": "icon-database-query",
|
||||||
"id": 52,
|
"id": 52,
|
||||||
"code": 921861,
|
"code": 921861,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 17,
|
"order": 17,
|
||||||
@ -634,7 +642,7 @@
|
|||||||
"name": "icon-dataset",
|
"name": "icon-dataset",
|
||||||
"id": 12,
|
"id": 12,
|
||||||
"code": 921862,
|
"code": 921862,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 22,
|
"order": 22,
|
||||||
@ -642,7 +650,7 @@
|
|||||||
"name": "icon-datatable",
|
"name": "icon-datatable",
|
||||||
"id": 17,
|
"id": 17,
|
||||||
"code": 921863,
|
"code": 921863,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 59,
|
"order": 59,
|
||||||
@ -650,7 +658,7 @@
|
|||||||
"name": "icon-dictionary",
|
"name": "icon-dictionary",
|
||||||
"id": 54,
|
"id": 54,
|
||||||
"code": 921864,
|
"code": 921864,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 62,
|
"order": 62,
|
||||||
@ -658,7 +666,7 @@
|
|||||||
"name": "icon-folder",
|
"name": "icon-folder",
|
||||||
"id": 57,
|
"id": 57,
|
||||||
"code": 921865,
|
"code": 921865,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 66,
|
"order": 66,
|
||||||
@ -666,7 +674,7 @@
|
|||||||
"name": "icon-image",
|
"name": "icon-image",
|
||||||
"id": 61,
|
"id": 61,
|
||||||
"code": 921872,
|
"code": 921872,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 68,
|
"order": 68,
|
||||||
@ -674,7 +682,7 @@
|
|||||||
"name": "icon-layout",
|
"name": "icon-layout",
|
||||||
"id": 63,
|
"id": 63,
|
||||||
"code": 921873,
|
"code": 921873,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 77,
|
"order": 77,
|
||||||
@ -682,7 +690,7 @@
|
|||||||
"name": "icon-object",
|
"name": "icon-object",
|
||||||
"id": 72,
|
"id": 72,
|
||||||
"code": 921874,
|
"code": 921874,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 78,
|
"order": 78,
|
||||||
@ -690,7 +698,7 @@
|
|||||||
"name": "icon-object-unknown",
|
"name": "icon-object-unknown",
|
||||||
"id": 73,
|
"id": 73,
|
||||||
"code": 921875,
|
"code": 921875,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 79,
|
"order": 79,
|
||||||
@ -698,7 +706,7 @@
|
|||||||
"name": "icon-packet",
|
"name": "icon-packet",
|
||||||
"id": 74,
|
"id": 74,
|
||||||
"code": 921876,
|
"code": 921876,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 80,
|
"order": 80,
|
||||||
@ -706,7 +714,7 @@
|
|||||||
"name": "icon-page",
|
"name": "icon-page",
|
||||||
"id": 75,
|
"id": 75,
|
||||||
"code": 921877,
|
"code": 921877,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 114,
|
"order": 114,
|
||||||
@ -714,7 +722,7 @@
|
|||||||
"name": "icon-plot-overlay",
|
"name": "icon-plot-overlay",
|
||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"code": 921878,
|
"code": 921878,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 113,
|
"order": 113,
|
||||||
@ -722,7 +730,7 @@
|
|||||||
"name": "icon-plot-stacked",
|
"name": "icon-plot-stacked",
|
||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"code": 921879,
|
"code": 921879,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 10,
|
"order": 10,
|
||||||
@ -730,7 +738,7 @@
|
|||||||
"name": "icon-session",
|
"name": "icon-session",
|
||||||
"id": 5,
|
"id": 5,
|
||||||
"code": 921880,
|
"code": 921880,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 24,
|
"order": 24,
|
||||||
@ -738,7 +746,7 @@
|
|||||||
"name": "icon-tabular",
|
"name": "icon-tabular",
|
||||||
"id": 19,
|
"id": 19,
|
||||||
"code": 921881,
|
"code": 921881,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 7,
|
"order": 7,
|
||||||
@ -746,7 +754,7 @@
|
|||||||
"name": "icon-tabular-lad",
|
"name": "icon-tabular-lad",
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"code": 921888,
|
"code": 921888,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 6,
|
"order": 6,
|
||||||
@ -754,7 +762,7 @@
|
|||||||
"name": "icon-tabular-lad-set",
|
"name": "icon-tabular-lad-set",
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"code": 921889,
|
"code": 921889,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 8,
|
"order": 8,
|
||||||
@ -762,7 +770,7 @@
|
|||||||
"name": "icon-tabular-realtime",
|
"name": "icon-tabular-realtime",
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"code": 921890,
|
"code": 921890,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 23,
|
"order": 23,
|
||||||
@ -770,7 +778,7 @@
|
|||||||
"name": "icon-tabular-scrolling",
|
"name": "icon-tabular-scrolling",
|
||||||
"id": 18,
|
"id": 18,
|
||||||
"code": 921891,
|
"code": 921891,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 112,
|
"order": 112,
|
||||||
@ -778,7 +786,7 @@
|
|||||||
"name": "icon-telemetry",
|
"name": "icon-telemetry",
|
||||||
"id": 86,
|
"id": 86,
|
||||||
"code": 921892,
|
"code": 921892,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 90,
|
"order": 90,
|
||||||
@ -786,7 +794,7 @@
|
|||||||
"name": "icon-telemetry-panel",
|
"name": "icon-telemetry-panel",
|
||||||
"id": 85,
|
"id": 85,
|
||||||
"code": 921893,
|
"code": 921893,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 93,
|
"order": 93,
|
||||||
@ -794,7 +802,7 @@
|
|||||||
"name": "icon-timeline",
|
"name": "icon-timeline",
|
||||||
"id": 88,
|
"id": 88,
|
||||||
"code": 921894,
|
"code": 921894,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 116,
|
"order": 116,
|
||||||
@ -802,7 +810,7 @@
|
|||||||
"name": "icon-timer-v1.5",
|
"name": "icon-timer-v1.5",
|
||||||
"prevSize": 24,
|
"prevSize": 24,
|
||||||
"code": 921895,
|
"code": 921895,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 11,
|
"order": 11,
|
||||||
@ -810,7 +818,7 @@
|
|||||||
"name": "icon-topic",
|
"name": "icon-topic",
|
||||||
"id": 6,
|
"id": 6,
|
||||||
"code": 921896,
|
"code": 921896,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"order": 115,
|
"order": 115,
|
||||||
@ -818,7 +826,7 @@
|
|||||||
"name": "icon-box-with-dashed-lines",
|
"name": "icon-box-with-dashed-lines",
|
||||||
"id": 29,
|
"id": 29,
|
||||||
"code": 921897,
|
"code": 921897,
|
||||||
"tempChar": ""
|
"tempChar": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
@ -2363,6 +2371,51 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": 107,
|
||||||
|
"paths": [
|
||||||
|
"M0 64h1024v128h-1024v-128z",
|
||||||
|
"M0 320h1024v128h-1024v-128z",
|
||||||
|
"M0 576h1024v128h-1024v-128z",
|
||||||
|
"M0 832h1024v128h-1024v-128z"
|
||||||
|
],
|
||||||
|
"attrs": [
|
||||||
|
{
|
||||||
|
"fill": "rgb(0, 161, 75)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fill": "rgb(0, 161, 75)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fill": "rgb(0, 161, 75)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fill": "rgb(0, 161, 75)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"isMulticolor": false,
|
||||||
|
"isMulticolor2": false,
|
||||||
|
"grid": 16,
|
||||||
|
"tags": [
|
||||||
|
"icon-list-view"
|
||||||
|
],
|
||||||
|
"colorPermutations": {
|
||||||
|
"1161751207457516161751": [
|
||||||
|
{
|
||||||
|
"f": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"f": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"f": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"f": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"paths": [
|
"paths": [
|
||||||
"M576 64h-256l320 320h-290.256c-44.264-76.516-126.99-128-221.744-128h-128v512h128c94.754 0 177.48-51.484 221.744-128h290.256l-320 320h256l448-448-448-448z"
|
"M576 64h-256l320 320h-290.256c-44.264-76.516-126.99-128-221.744-128h-128v512h128c94.754 0 177.48-51.484 221.744-128h290.256l-320 320h256l448-448-448-448z"
|
||||||
|
Binary file not shown.
@ -78,6 +78,7 @@
|
|||||||
<glyph unicode="󡀹" glyph-name="icon-brightness" d="M253.414 641.939l-155.172 116.384c-50.233-66.209-85.127-146.713-97.91-234.39l191.586-30.216c8.145 56.552 29.998 106.879 62.068 149.006zM191.98 402.283l-191.919-27.434c13.115-90.459 48.009-170.963 99.174-238.453l154.18 117.665c-31.476 41.347-53.309 91.675-61.231 146.504zM466.283 768.020l-27.434 191.919c-90.459-13.115-170.963-48.009-238.453-99.174l117.665-154.18c41.347 31.476 91.675 53.309 146.504 61.231zM822.323 861.758c-66.209 50.233-146.713 85.127-234.39 97.91l-30.216-191.586c56.552-8.145 106.879-29.998 149.006-62.068zM832.020 493.717l191.919 27.434c-13.115 90.459-48.009 170.963-99.174 238.453l-154.18-117.665c31.476-41.347 53.309-91.675 61.231-146.504zM201.677 34.242c66.209-50.233 146.713-85.127 234.39-97.91l30.216 191.586c-56.552 8.145-106.879 29.998-149.006 62.068zM770.586 254.061l155.131-116.343c50.233 66.209 85.127 146.713 97.91 234.39l-191.586 30.216c-8.125-56.564-29.966-106.906-62.028-149.049zM557.717 127.98l27.434-191.919c90.459 13.115 170.963 48.009 238.453 99.174l-117.665 154.18c-41.347-31.476-91.675-53.309-146.504-61.231zM770.586 448c0-142.813-115.773-258.586-258.586-258.586s-258.586 115.773-258.586 258.586c0 142.813 115.773 258.586 258.586 258.586s258.586-115.773 258.586-258.586z" />
|
<glyph unicode="󡀹" glyph-name="icon-brightness" d="M253.414 641.939l-155.172 116.384c-50.233-66.209-85.127-146.713-97.91-234.39l191.586-30.216c8.145 56.552 29.998 106.879 62.068 149.006zM191.98 402.283l-191.919-27.434c13.115-90.459 48.009-170.963 99.174-238.453l154.18 117.665c-31.476 41.347-53.309 91.675-61.231 146.504zM466.283 768.020l-27.434 191.919c-90.459-13.115-170.963-48.009-238.453-99.174l117.665-154.18c41.347 31.476 91.675 53.309 146.504 61.231zM822.323 861.758c-66.209 50.233-146.713 85.127-234.39 97.91l-30.216-191.586c56.552-8.145 106.879-29.998 149.006-62.068zM832.020 493.717l191.919 27.434c-13.115 90.459-48.009 170.963-99.174 238.453l-154.18-117.665c31.476-41.347 53.309-91.675 61.231-146.504zM201.677 34.242c66.209-50.233 146.713-85.127 234.39-97.91l30.216 191.586c-56.552 8.145-106.879 29.998-149.006 62.068zM770.586 254.061l155.131-116.343c50.233 66.209 85.127 146.713 97.91 234.39l-191.586 30.216c-8.125-56.564-29.966-106.906-62.028-149.049zM557.717 127.98l27.434-191.919c90.459 13.115 170.963 48.009 238.453 99.174l-117.665 154.18c-41.347-31.476-91.675-53.309-146.504-61.231zM770.586 448c0-142.813-115.773-258.586-258.586-258.586s-258.586 115.773-258.586 258.586c0 142.813 115.773 258.586 258.586 258.586s258.586-115.773 258.586-258.586z" />
|
||||||
<glyph unicode="󡁀" glyph-name="icon-contrast" d="M512 960c-282.78 0-512-229.24-512-512s229.22-512 512-512 512 229.24 512 512-229.22 512-512 512zM783.52 176.48c-69.111-69.481-164.785-112.481-270.502-112.481-0.358 0-0.716 0-1.074 0.001l0.055 768c212.070-0.010 383.982-171.929 383.982-384 0-106.034-42.977-202.031-112.462-271.52z" />
|
<glyph unicode="󡁀" glyph-name="icon-contrast" d="M512 960c-282.78 0-512-229.24-512-512s229.22-512 512-512 512 229.24 512 512-229.22 512-512 512zM783.52 176.48c-69.111-69.481-164.785-112.481-270.502-112.481-0.358 0-0.716 0-1.074 0.001l0.055 768c212.070-0.010 383.982-171.929 383.982-384 0-106.034-42.977-202.031-112.462-271.52z" />
|
||||||
<glyph unicode="󡁁" glyph-name="icon-expand" d="M960 960c0 0 0 0 0 0h-320v-128h165.4l-210.6-210.8c-25-25-25-65.6 0-90.6 12.4-12.4 28.8-18.8 45.2-18.8s32.8 6.2 45.2 18.8l210.8 210.8v-165.4h128v384h-64zM896 154.6l-210.8 210.6c-25 25-65.6 25-90.6 0s-25-65.6 0-90.6l210.8-210.6h-165.4v-128h384v384h-128v-165.4zM218.6 832h165.4v128h-320c0 0 0 0 0 0h-64v-384h128v165.4l210.8-210.8c12.4-12.4 28.8-18.8 45.2-18.8s32.8 6.2 45.2 18.8c25 25 25 65.6 0 90.6l-210.6 210.8zM338.8 365.2l-210.8-210.6v165.4h-128v-384h384v128h-165.4l210.8 210.8c25 25 25 65.6 0 90.6-25.2 24.8-65.6 24.8-90.6-0.2z" />
|
<glyph unicode="󡁁" glyph-name="icon-expand" d="M960 960c0 0 0 0 0 0h-320v-128h165.4l-210.6-210.8c-25-25-25-65.6 0-90.6 12.4-12.4 28.8-18.8 45.2-18.8s32.8 6.2 45.2 18.8l210.8 210.8v-165.4h128v384h-64zM896 154.6l-210.8 210.6c-25 25-65.6 25-90.6 0s-25-65.6 0-90.6l210.8-210.6h-165.4v-128h384v384h-128v-165.4zM218.6 832h165.4v128h-320c0 0 0 0 0 0h-64v-384h128v165.4l210.8-210.8c12.4-12.4 28.8-18.8 45.2-18.8s32.8 6.2 45.2 18.8c25 25 25 65.6 0 90.6l-210.6 210.8zM338.8 365.2l-210.8-210.6v165.4h-128v-384h384v128h-165.4l210.8 210.8c25 25 25 65.6 0 90.6-25.2 24.8-65.6 24.8-90.6-0.2z" />
|
||||||
|
<glyph unicode="󡁂" glyph-name="icon-list-view" d="M0 896h1024v-128h-1024v128zM0 640h1024v-128h-1024v128zM0 384h1024v-128h-1024v128zM0 128h1024v-128h-1024v128z" />
|
||||||
<glyph unicode="󡄀" glyph-name="icon-activity" d="M576 896h-256l320-320h-290.256c-44.264 76.516-126.99 128-221.744 128h-128v-512h128c94.754 0 177.48 51.484 221.744 128h290.256l-320-320h256l448 448-448 448z" />
|
<glyph unicode="󡄀" glyph-name="icon-activity" d="M576 896h-256l320-320h-290.256c-44.264 76.516-126.99 128-221.744 128h-128v-512h128c94.754 0 177.48 51.484 221.744 128h290.256l-320-320h256l448 448-448 448z" />
|
||||||
<glyph unicode="󡄁" glyph-name="icon-activity-mode" d="M512 960c-214.866 0-398.786-132.372-474.744-320h90.744c56.86 0 107.938-24.724 143.094-64h240.906l-192 192h256l320-320-320-320h-256l192 192h-240.906c-35.156-39.276-86.234-64-143.094-64h-90.744c75.958-187.628 259.878-320 474.744-320 282.77 0 512 229.23 512 512s-229.23 512-512 512z" />
|
<glyph unicode="󡄁" glyph-name="icon-activity-mode" d="M512 960c-214.866 0-398.786-132.372-474.744-320h90.744c56.86 0 107.938-24.724 143.094-64h240.906l-192 192h256l320-320-320-320h-256l192 192h-240.906c-35.156-39.276-86.234-64-143.094-64h-90.744c75.958-187.628 259.878-320 474.744-320 282.77 0 512 229.23 512 512s-229.23 512-512 512z" />
|
||||||
<glyph unicode="󡄂" glyph-name="icon-autoflow-tabular" d="M192 960c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h64v1024h-64zM384 960h256v-1024h-256v1024zM832 960h-64v-704h256v512c0 105.6-86.4 192-192 192z" />
|
<glyph unicode="󡄂" glyph-name="icon-autoflow-tabular" d="M192 960c-105.6 0-192-86.4-192-192v-640c0-105.6 86.4-192 192-192h64v1024h-64zM384 960h256v-1024h-256v1024zM832 960h-64v-704h256v512c0 105.6-86.4 192-192 192z" />
|
||||||
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Binary file not shown.
Binary file not shown.
@ -81,12 +81,6 @@ input, textarea {
|
|||||||
letter-spacing: inherit;
|
letter-spacing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"],
|
|
||||||
input[type="search"] {
|
|
||||||
vertical-align: baseline;
|
|
||||||
padding: $inputTextP;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3 {
|
h1, h2, h3 {
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
@mixin glyph($unicode, $family: 'symbolsfont') {
|
@mixin glyphBefore($unicode, $family: 'symbolsfont') {
|
||||||
&:before {
|
&:before {
|
||||||
content: $unicode;
|
content: $unicode;
|
||||||
font-family: $family;
|
font-family: $family;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin glyphAfter($unicode, $family: 'symbolsfont') {
|
||||||
|
&:after {
|
||||||
|
content: $unicode;
|
||||||
|
font-family: $family;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/************************** CHAR UNICODES */
|
/************************** CHAR UNICODES */
|
||||||
|
|
||||||
$glyph-icon-alert-rect: '\e900';
|
$glyph-icon-alert-rect: '\e900';
|
||||||
@ -78,6 +85,7 @@ $glyph-icon-x-in-circle: '\e1038';
|
|||||||
$glyph-icon-brightness: '\e1039';
|
$glyph-icon-brightness: '\e1039';
|
||||||
$glyph-icon-contrast: '\e1040';
|
$glyph-icon-contrast: '\e1040';
|
||||||
$glyph-icon-expand: '\e1041';
|
$glyph-icon-expand: '\e1041';
|
||||||
|
$glyph-icon-list-view: '\e1042';
|
||||||
$glyph-icon-activity: '\e1100';
|
$glyph-icon-activity: '\e1100';
|
||||||
$glyph-icon-activity-mode: '\e1101';
|
$glyph-icon-activity-mode: '\e1101';
|
||||||
$glyph-icon-autoflow-tabular: '\e1102';
|
$glyph-icon-autoflow-tabular: '\e1102';
|
||||||
@ -111,111 +119,112 @@ $glyph-icon-box-with-dashed-lines: '\e1129';
|
|||||||
|
|
||||||
/************************** 16 PX CLASSES */
|
/************************** 16 PX CLASSES */
|
||||||
|
|
||||||
.icon-alert-rect { @include glyph($glyph-icon-alert-rect); }
|
.icon-alert-rect { @include glyphBefore($glyph-icon-alert-rect); }
|
||||||
.icon-alert-triangle { @include glyph($glyph-icon-alert-triangle); }
|
.icon-alert-triangle { @include glyphBefore($glyph-icon-alert-triangle); }
|
||||||
.icon-arrow-down { @include glyph($glyph-icon-arrow-down); }
|
.icon-arrow-down { @include glyphBefore($glyph-icon-arrow-down); }
|
||||||
.icon-arrow-left { @include glyph($glyph-icon-arrow-left); }
|
.icon-arrow-left { @include glyphBefore($glyph-icon-arrow-left); }
|
||||||
.icon-arrow-right { @include glyph($glyph-icon-arrow-right); }
|
.icon-arrow-right { @include glyphBefore($glyph-icon-arrow-right); }
|
||||||
.icon-arrow-double-up { @include glyph($glyph-icon-arrow-double-up); }
|
.icon-arrow-double-up { @include glyphBefore($glyph-icon-arrow-double-up); }
|
||||||
.icon-arrow-tall-up { @include glyph($glyph-icon-arrow-tall-up); }
|
.icon-arrow-tall-up { @include glyphBefore($glyph-icon-arrow-tall-up); }
|
||||||
.icon-arrow-tall-down { @include glyph($glyph-icon-arrow-tall-down); }
|
.icon-arrow-tall-down { @include glyphBefore($glyph-icon-arrow-tall-down); }
|
||||||
.icon-arrow-double-down { @include glyph($glyph-icon-arrow-double-down); }
|
.icon-arrow-double-down { @include glyphBefore($glyph-icon-arrow-double-down); }
|
||||||
.icon-arrow-up { @include glyph($glyph-icon-arrow-up); }
|
.icon-arrow-up { @include glyphBefore($glyph-icon-arrow-up); }
|
||||||
.icon-asterisk { @include glyph($glyph-icon-asterisk); }
|
.icon-asterisk { @include glyphBefore($glyph-icon-asterisk); }
|
||||||
.icon-bell { @include glyph($glyph-icon-bell); }
|
.icon-bell { @include glyphBefore($glyph-icon-bell); }
|
||||||
.icon-box { @include glyph($glyph-icon-box); }
|
.icon-box { @include glyphBefore($glyph-icon-box); }
|
||||||
.icon-box-with-arrow { @include glyph($glyph-icon-box-with-arrow); }
|
.icon-box-with-arrow { @include glyphBefore($glyph-icon-box-with-arrow); }
|
||||||
.icon-check { @include glyph($glyph-icon-check); }
|
.icon-check { @include glyphBefore($glyph-icon-check); }
|
||||||
.icon-connectivity { @include glyph($glyph-icon-connectivity); }
|
.icon-connectivity { @include glyphBefore($glyph-icon-connectivity); }
|
||||||
.icon-database-in-brackets { @include glyph($glyph-icon-database-in-brackets); }
|
.icon-database-in-brackets { @include glyphBefore($glyph-icon-database-in-brackets); }
|
||||||
.icon-eye-open { @include glyph($glyph-icon-eye-open); }
|
.icon-eye-open { @include glyphBefore($glyph-icon-eye-open); }
|
||||||
.icon-gear { @include glyph($glyph-icon-gear); }
|
.icon-gear { @include glyphBefore($glyph-icon-gear); }
|
||||||
.icon-hourglass { @include glyph($glyph-icon-hourglass); }
|
.icon-hourglass { @include glyphBefore($glyph-icon-hourglass); }
|
||||||
.icon-info { @include glyph($glyph-icon-info); }
|
.icon-info { @include glyphBefore($glyph-icon-info); }
|
||||||
.icon-link { @include glyph($glyph-icon-link); }
|
.icon-link { @include glyphBefore($glyph-icon-link); }
|
||||||
.icon-lock { @include glyph($glyph-icon-lock); }
|
.icon-lock { @include glyphBefore($glyph-icon-lock); }
|
||||||
.icon-minus { @include glyph($glyph-icon-minus); }
|
.icon-minus { @include glyphBefore($glyph-icon-minus); }
|
||||||
.icon-people { @include glyph($glyph-icon-people); }
|
.icon-people { @include glyphBefore($glyph-icon-people); }
|
||||||
.icon-person { @include glyph($glyph-icon-person); }
|
.icon-person { @include glyphBefore($glyph-icon-person); }
|
||||||
.icon-plus { @include glyph($glyph-icon-plus); }
|
.icon-plus { @include glyphBefore($glyph-icon-plus); }
|
||||||
.icon-trash { @include glyph($glyph-icon-trash); }
|
.icon-trash { @include glyphBefore($glyph-icon-trash); }
|
||||||
.icon-x { @include glyph($glyph-icon-x); }
|
.icon-x { @include glyphBefore($glyph-icon-x); }
|
||||||
.icon-brackets { @include glyph($glyph-icon-brackets); }
|
.icon-brackets { @include glyphBefore($glyph-icon-brackets); }
|
||||||
.icon-arrows-out { @include glyph($glyph-icon-arrows-out); }
|
.icon-arrows-out { @include glyphBefore($glyph-icon-arrows-out); }
|
||||||
.icon-arrows-right-left { @include glyph($glyph-icon-arrows-right-left); }
|
.icon-arrows-right-left { @include glyphBefore($glyph-icon-arrows-right-left); }
|
||||||
.icon-arrows-up-down { @include glyph($glyph-icon-arrows-up-down); }
|
.icon-arrows-up-down { @include glyphBefore($glyph-icon-arrows-up-down); }
|
||||||
.icon-bullet { @include glyph($glyph-icon-bullet); }
|
.icon-bullet { @include glyphBefore($glyph-icon-bullet); }
|
||||||
.icon-calendar { @include glyph($glyph-icon-calendar); }
|
.icon-calendar { @include glyphBefore($glyph-icon-calendar); }
|
||||||
.icon-chain-links { @include glyph($glyph-icon-chain-links); }
|
.icon-chain-links { @include glyphBefore($glyph-icon-chain-links); }
|
||||||
.icon-collapse-pane-left { @include glyph($glyph-icon-collapse-pane-left); }
|
.icon-collapse-pane-left { @include glyphBefore($glyph-icon-collapse-pane-left); }
|
||||||
.icon-collapse-pane-right { @include glyph($glyph-icon-collapse-pane-right); }
|
.icon-collapse-pane-right { @include glyphBefore($glyph-icon-collapse-pane-right); }
|
||||||
.icon-download { @include glyph($glyph-icon-download); }
|
.icon-download { @include glyphBefore($glyph-icon-download); }
|
||||||
.icon-duplicate { @include glyph($glyph-icon-duplicate); }
|
.icon-duplicate { @include glyphBefore($glyph-icon-duplicate); }
|
||||||
.icon-folder-new { @include glyph($glyph-icon-folder-new); }
|
.icon-folder-new { @include glyphBefore($glyph-icon-folder-new); }
|
||||||
.icon-fullscreen-collapse { @include glyph($glyph-icon-fullscreen-collapse); }
|
.icon-fullscreen-collapse { @include glyphBefore($glyph-icon-fullscreen-collapse); }
|
||||||
.icon-fullscreen-expand { @include glyph($glyph-icon-fullscreen-expand); }
|
.icon-fullscreen-expand { @include glyphBefore($glyph-icon-fullscreen-expand); }
|
||||||
.icon-layers { @include glyph($glyph-icon-layers); }
|
.icon-layers { @include glyphBefore($glyph-icon-layers); }
|
||||||
.icon-line-horz { @include glyph($glyph-icon-line-horz); }
|
.icon-line-horz { @include glyphBefore($glyph-icon-line-horz); }
|
||||||
.icon-magnify { @include glyph($glyph-icon-magnify); }
|
.icon-magnify { @include glyphBefore($glyph-icon-magnify); }
|
||||||
.icon-magnify-in { @include glyph($glyph-icon-magnify-in); }
|
.icon-magnify-in { @include glyphBefore($glyph-icon-magnify-in); }
|
||||||
.icon-magnify-out { @include glyph($glyph-icon-magnify-out); }
|
.icon-magnify-out { @include glyphBefore($glyph-icon-magnify-out); }
|
||||||
.icon-menu-hamburger { @include glyph($glyph-icon-menu-hamburger); }
|
.icon-menu-hamburger { @include glyphBefore($glyph-icon-menu-hamburger); }
|
||||||
.icon-move { @include glyph($glyph-icon-move); }
|
.icon-move { @include glyphBefore($glyph-icon-move); }
|
||||||
.icon-new-window { @include glyph($glyph-icon-new-window); }
|
.icon-new-window { @include glyphBefore($glyph-icon-new-window); }
|
||||||
.icon-paint-bucket { @include glyph($glyph-icon-paint-bucket); }
|
.icon-paint-bucket { @include glyphBefore($glyph-icon-paint-bucket); }
|
||||||
.icon-pause { @include glyph($glyph-icon-pause); }
|
.icon-pause { @include glyphBefore($glyph-icon-pause); }
|
||||||
.icon-pencil { @include glyph($glyph-icon-pencil); }
|
.icon-pencil { @include glyphBefore($glyph-icon-pencil); }
|
||||||
.icon-play { @include glyph($glyph-icon-play); }
|
.icon-play { @include glyphBefore($glyph-icon-play); }
|
||||||
.icon-plot-resource { @include glyph($glyph-icon-plot-resource); }
|
.icon-plot-resource { @include glyphBefore($glyph-icon-plot-resource); }
|
||||||
.icon-pointer-left { @include glyph($glyph-icon-pointer-left); }
|
.icon-pointer-left { @include glyphBefore($glyph-icon-pointer-left); }
|
||||||
.icon-pointer-right { @include glyph($glyph-icon-pointer-right); }
|
.icon-pointer-right { @include glyphBefore($glyph-icon-pointer-right); }
|
||||||
.icon-refresh { @include glyph($glyph-icon-refresh); }
|
.icon-refresh { @include glyphBefore($glyph-icon-refresh); }
|
||||||
.icon-save { @include glyph($glyph-icon-save); }
|
.icon-save { @include glyphBefore($glyph-icon-save); }
|
||||||
.icon-sine { @include glyph($glyph-icon-sine); }
|
.icon-sine { @include glyphBefore($glyph-icon-sine); }
|
||||||
.icon-T { @include glyph($glyph-icon-T); }
|
.icon-T { @include glyphBefore($glyph-icon-T); }
|
||||||
.icon-thumbs-strip { @include glyph($glyph-icon-thumbs-strip); }
|
.icon-thumbs-strip { @include glyphBefore($glyph-icon-thumbs-strip); }
|
||||||
.icon-two-parts-both { @include glyph($glyph-icon-two-parts-both); }
|
.icon-two-parts-both { @include glyphBefore($glyph-icon-two-parts-both); }
|
||||||
.icon-two-parts-one-only { @include glyph($glyph-icon-two-parts-one-only); }
|
.icon-two-parts-one-only { @include glyphBefore($glyph-icon-two-parts-one-only); }
|
||||||
.icon-resync { @include glyph($glyph-icon-resync); }
|
.icon-resync { @include glyphBefore($glyph-icon-resync); }
|
||||||
.icon-reset { @include glyph($glyph-icon-reset); }
|
.icon-reset { @include glyphBefore($glyph-icon-reset); }
|
||||||
.icon-x-in-circle { @include glyph($glyph-icon-x-in-circle); }
|
.icon-x-in-circle { @include glyphBefore($glyph-icon-x-in-circle); }
|
||||||
.icon-brightness { @include glyph($glyph-icon-brightness); }
|
.icon-brightness { @include glyphBefore($glyph-icon-brightness); }
|
||||||
.icon-contrast { @include glyph($glyph-icon-contrast); }
|
.icon-contrast { @include glyphBefore($glyph-icon-contrast); }
|
||||||
.icon-expand { @include glyph($glyph-icon-expand); }
|
.icon-expand { @include glyphBefore($glyph-icon-expand); }
|
||||||
.icon-activity { @include glyph($glyph-icon-activity); }
|
.icon-list-view { @include glyphBefore($glyph-icon-list-view); }
|
||||||
.icon-activity-mode { @include glyph($glyph-icon-activity-mode); }
|
.icon-activity { @include glyphBefore($glyph-icon-activity); }
|
||||||
.icon-autoflow-tabular { @include glyph($glyph-icon-autoflow-tabular); }
|
.icon-activity-mode { @include glyphBefore($glyph-icon-activity-mode); }
|
||||||
.icon-clock { @include glyph($glyph-icon-clock); }
|
.icon-autoflow-tabular { @include glyphBefore($glyph-icon-autoflow-tabular); }
|
||||||
.icon-database { @include glyph($glyph-icon-database); }
|
.icon-clock { @include glyphBefore($glyph-icon-clock); }
|
||||||
.icon-database-query { @include glyph($glyph-icon-database-query); }
|
.icon-database { @include glyphBefore($glyph-icon-database); }
|
||||||
.icon-dataset { @include glyph($glyph-icon-dataset); }
|
.icon-database-query { @include glyphBefore($glyph-icon-database-query); }
|
||||||
.icon-datatable { @include glyph($glyph-icon-datatable); }
|
.icon-dataset { @include glyphBefore($glyph-icon-dataset); }
|
||||||
.icon-dictionary { @include glyph($glyph-icon-dictionary); }
|
.icon-datatable { @include glyphBefore($glyph-icon-datatable); }
|
||||||
.icon-folder { @include glyph($glyph-icon-folder); }
|
.icon-dictionary { @include glyphBefore($glyph-icon-dictionary); }
|
||||||
.icon-image { @include glyph($glyph-icon-image); }
|
.icon-folder { @include glyphBefore($glyph-icon-folder); }
|
||||||
.icon-layout { @include glyph($glyph-icon-layout); }
|
.icon-image { @include glyphBefore($glyph-icon-image); }
|
||||||
.icon-object { @include glyph($glyph-icon-object); }
|
.icon-layout { @include glyphBefore($glyph-icon-layout); }
|
||||||
.icon-object-unknown { @include glyph($glyph-icon-object-unknown); }
|
.icon-object { @include glyphBefore($glyph-icon-object); }
|
||||||
.icon-packet { @include glyph($glyph-icon-packet); }
|
.icon-object-unknown { @include glyphBefore($glyph-icon-object-unknown); }
|
||||||
.icon-page { @include glyph($glyph-icon-page); }
|
.icon-packet { @include glyphBefore($glyph-icon-packet); }
|
||||||
.icon-plot-overlay { @include glyph($glyph-icon-plot-overlay); }
|
.icon-page { @include glyphBefore($glyph-icon-page); }
|
||||||
.icon-plot-stacked { @include glyph($glyph-icon-plot-stacked); }
|
.icon-plot-overlay { @include glyphBefore($glyph-icon-plot-overlay); }
|
||||||
.icon-session { @include glyph($glyph-icon-session); }
|
.icon-plot-stacked { @include glyphBefore($glyph-icon-plot-stacked); }
|
||||||
.icon-tabular { @include glyph($glyph-icon-tabular); }
|
.icon-session { @include glyphBefore($glyph-icon-session); }
|
||||||
.icon-tabular-lad { @include glyph($glyph-icon-tabular-lad); }
|
.icon-tabular { @include glyphBefore($glyph-icon-tabular); }
|
||||||
.icon-tabular-lad-set { @include glyph($glyph-icon-tabular-lad-set); }
|
.icon-tabular-lad { @include glyphBefore($glyph-icon-tabular-lad); }
|
||||||
.icon-tabular-realtime { @include glyph($glyph-icon-tabular-realtime); }
|
.icon-tabular-lad-set { @include glyphBefore($glyph-icon-tabular-lad-set); }
|
||||||
.icon-tabular-scrolling { @include glyph($glyph-icon-tabular-scrolling); }
|
.icon-tabular-realtime { @include glyphBefore($glyph-icon-tabular-realtime); }
|
||||||
.icon-telemetry { @include glyph($glyph-icon-telemetry); }
|
.icon-tabular-scrolling { @include glyphBefore($glyph-icon-tabular-scrolling); }
|
||||||
.icon-telemetry-panel { @include glyph($glyph-icon-telemetry-panel); }
|
.icon-telemetry { @include glyphBefore($glyph-icon-telemetry); }
|
||||||
.icon-timeline { @include glyph($glyph-icon-timeline); }
|
.icon-telemetry-panel { @include glyphBefore($glyph-icon-telemetry-panel); }
|
||||||
.icon-timer { @include glyph($glyph-icon-timer); }
|
.icon-timeline { @include glyphBefore($glyph-icon-timeline); }
|
||||||
.icon-topic { @include glyph($glyph-icon-topic); }
|
.icon-timer { @include glyphBefore($glyph-icon-timer); }
|
||||||
.icon-box-with-dashed-lines { @include glyph($glyph-icon-box-with-dashed-lines); }
|
.icon-topic { @include glyphBefore($glyph-icon-topic); }
|
||||||
|
.icon-box-with-dashed-lines { @include glyphBefore($glyph-icon-box-with-dashed-lines); }
|
||||||
|
|
||||||
/************************** 12 PX CLASSES */
|
/************************** 12 PX CLASSES */
|
||||||
|
|
||||||
.icon-eye-open-12px { @include glyph($glyph-icon-eye-open,'symbolsfont-12px'); }
|
.icon-eye-open-12px { @include glyphBefore($glyph-icon-eye-open,'symbolsfont-12px'); }
|
||||||
.icon-collapse-pane-left-12px { @include glyph($glyph-icon-collapse-pane-left,'symbolsfont-12px'); }
|
.icon-collapse-pane-left-12px { @include glyphBefore($glyph-icon-collapse-pane-left,'symbolsfont-12px'); }
|
||||||
.icon-collapse-pane-right-12px { @include glyph($glyph-icon-collapse-pane-right,'symbolsfont-12px'); }
|
.icon-collapse-pane-right-12px { @include glyphBefore($glyph-icon-collapse-pane-right,'symbolsfont-12px'); }
|
||||||
.icon-folder-12px { @include glyph($glyph-icon-folder,'symbolsfont-12px'); }
|
.icon-folder-12px { @include glyphBefore($glyph-icon-folder,'symbolsfont-12px'); }
|
||||||
|
@ -58,7 +58,6 @@
|
|||||||
@import "search/search";
|
@import "search/search";
|
||||||
@import "mobile/search/search";
|
@import "mobile/search/search";
|
||||||
@import "overlay/overlay";
|
@import "overlay/overlay";
|
||||||
@import "mobile/overlay/overlay";
|
|
||||||
@import "tree/tree";
|
@import "tree/tree";
|
||||||
@import "object-label";
|
@import "object-label";
|
||||||
@import "mobile/tree";
|
@import "mobile/tree";
|
||||||
|
@ -285,13 +285,14 @@
|
|||||||
|
|
||||||
@mixin containerBase($bg: $colorBodyBg, $fg: $colorBodyFg) {
|
@mixin containerBase($bg: $colorBodyBg, $fg: $colorBodyFg) {
|
||||||
background-color: $bg;
|
background-color: $bg;
|
||||||
border-radius: $controlCr;
|
//border-radius: $controlCr;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: $fg;
|
color: $fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
@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) {
|
||||||
@include user-select(none);
|
@include user-select(none);
|
||||||
|
border-radius: $controlCr;
|
||||||
color: $fg;
|
color: $fg;
|
||||||
.icon,
|
.icon,
|
||||||
&:before {
|
&:before {
|
||||||
|
@ -224,18 +224,28 @@ textarea {
|
|||||||
|
|
||||||
/******************************************************** INPUTS */
|
/******************************************************** INPUTS */
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
input[type="search"] {
|
input[type="search"],
|
||||||
|
input[type="number"] {
|
||||||
@include nice-input();
|
@include nice-input();
|
||||||
|
vertical-align: baseline;
|
||||||
|
padding: $inputTextP;
|
||||||
&.numeric {
|
&.numeric {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.l-input-sm {
|
||||||
|
input[type="text"],
|
||||||
|
input[type="search"],
|
||||||
|
input[type="number"] {
|
||||||
|
width: 50px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.l-input-lg input[type="text"],
|
.l-input-lg input[type="text"],
|
||||||
input[type="text"].lg { width: 100% !important; }
|
input[type="text"].lg { width: 100% !important; }
|
||||||
.l-input-med input[type="text"],
|
.l-input-med input[type="text"],
|
||||||
input[type="text"].med { width: 200px !important; }
|
input[type="text"].med { width: 200px !important; }
|
||||||
.l-input-sm input[type="text"],
|
|
||||||
input[type="text"].sm { width: 50px !important; }
|
input[type="text"].sm { width: 50px !important; }
|
||||||
.l-numeric input[type="text"],
|
.l-numeric input[type="text"],
|
||||||
input[type="text"].numeric { text-align: right; }
|
input[type="text"].numeric { text-align: right; }
|
||||||
@ -651,7 +661,8 @@ textarea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-switcher {
|
.view-switcher,
|
||||||
|
.t-btn-view-large {
|
||||||
@include trans-prop-nice-fade($controlFadeMs);
|
@include trans-prop-nice-fade($controlFadeMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +249,7 @@
|
|||||||
.context-menu-holder,
|
.context-menu-holder,
|
||||||
.menu-holder {
|
.menu-holder {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 70;
|
z-index: 120;
|
||||||
.context-menu-wrapper {
|
.context-menu-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -273,7 +273,7 @@
|
|||||||
|
|
||||||
.btn-bar.right .menu,
|
.btn-bar.right .menu,
|
||||||
.menus-to-left .menu {
|
.menus-to-left .menu {
|
||||||
z-index: 79;
|
z-index: 79;
|
||||||
left: auto;
|
left: auto;
|
||||||
right: 0;
|
right: 0;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
@ -311,6 +311,24 @@ body.desktop .t-message-single {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include phonePortrait {
|
||||||
|
.t-message-single {
|
||||||
|
.l-message {
|
||||||
|
@include flex-direction(column);
|
||||||
|
.message-contents { margin-left: 0; }
|
||||||
|
}
|
||||||
|
.type-icon.message-type {
|
||||||
|
margin-bottom: $interiorMarginLg;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-bar {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Messages in list
|
// Messages in list
|
||||||
.t-message-list {
|
.t-message-list {
|
||||||
@include messageBlock(32px);
|
@include messageBlock(32px);
|
||||||
@ -350,7 +368,6 @@ body.desktop .t-message-list {
|
|||||||
.s-unsynced {
|
.s-unsynced {
|
||||||
$c: $colorPausedBg;
|
$c: $colorPausedBg;
|
||||||
border: 1px solid $c;
|
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 {
|
.s-status-timeconductor-unsynced {
|
||||||
|
@ -1,33 +1,36 @@
|
|||||||
.l-time-display {
|
.l-time-display {
|
||||||
$transTime: 200ms;
|
$transTime: 200ms;
|
||||||
|
$controlSize: 14px;
|
||||||
|
$control1ControlW: $controlSize + $interiorMargin;
|
||||||
|
$control2ControlW: $control1ControlW * 2;
|
||||||
line-height: 140%;
|
line-height: 140%;
|
||||||
&:hover {
|
&:hover {
|
||||||
.l-btn.control {
|
.l-btn.controls {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.l-timer {
|
&.l-timer {
|
||||||
.l-value:before,
|
|
||||||
.control {
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.l-value:before {
|
.l-value:before {
|
||||||
// Direction +/- element
|
// Direction +/- element
|
||||||
|
font-size: $controlSize;
|
||||||
margin-right: $interiorMarginSm;
|
margin-right: $interiorMarginSm;
|
||||||
|
|
||||||
}
|
}
|
||||||
.control {
|
.controls {
|
||||||
@include trans-prop-nice((width, opacity), $transTime);
|
@include trans-prop-nice((width, opacity), $transTime);
|
||||||
|
font-size: $controlSize;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
|
.flex-elem {
|
||||||
|
margin-right: $interiorMargin;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&:hover .control {
|
&:hover .controls {
|
||||||
margin-right: $interiorMargin;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
width: 1em;
|
width: $control2ControlW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,4 +38,34 @@
|
|||||||
color: pullForward($colorBodyFg, 50%);
|
color: pullForward($colorBodyFg, 50%);
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// States
|
||||||
|
&.s-state-stopped,
|
||||||
|
&.s-state-paused {
|
||||||
|
.l-value {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.s-state-started {
|
||||||
|
.l-value {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.s-state-stopped {
|
||||||
|
// Hide Stop button, 1controlW
|
||||||
|
.t-btn-stop {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&:hover .controls { width: $control1ControlW; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&.s-state-paused {
|
||||||
|
// Paused, do something visual
|
||||||
|
.l-value {
|
||||||
|
&:before { @extend .pulse; }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,44 +19,40 @@
|
|||||||
* 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.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
@mixin labelValidate($sym, $c) {
|
||||||
|
> .label {
|
||||||
|
@include glyphAfter($sym);
|
||||||
|
&:after {
|
||||||
|
color: $c;
|
||||||
|
margin-left: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
mct-form.validates {
|
mct-form.validates {
|
||||||
.form-row.validates {
|
.form-row.validates {
|
||||||
> .label {
|
> .label {
|
||||||
padding-right: $reqSymbolM; // Keep room for validation element
|
padding-right: $reqSymbolM; // Keep room for validation element
|
||||||
&:before {
|
&:after {
|
||||||
position: absolute;
|
|
||||||
right: $interiorMargin;
|
|
||||||
font-size: $reqSymbolFontSize;
|
font-size: $reqSymbolFontSize;
|
||||||
height: 100%;
|
|
||||||
line-height: 200%;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.invalid,
|
&.invalid,
|
||||||
&.invalid.req {
|
&.invalid.req { @include labelValidate($glyph-icon-x, $colorFormInvalid); }
|
||||||
> .label {
|
|
||||||
@extend .icon-x;
|
|
||||||
&:before {
|
|
||||||
color: $colorFormInvalid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.valid,
|
&.valid,
|
||||||
&.valid.req {
|
&.valid.req { @include labelValidate($glyph-icon-check, $colorFormValid); }
|
||||||
> .label {
|
|
||||||
@extend .icon-check;
|
&.req { @include labelValidate($glyph-icon-asterisk, $colorFormRequired); }
|
||||||
&:before {
|
}
|
||||||
color: $colorFormValid;
|
}
|
||||||
}
|
|
||||||
}
|
body.desktop .form-row.validates > .label {
|
||||||
}
|
&:after {
|
||||||
&.req {
|
position: absolute;
|
||||||
> .label {
|
right: $interiorMargin;
|
||||||
@extend .icon-asterisk;
|
height: 100%;
|
||||||
&:before {
|
line-height: 200%;
|
||||||
color: $colorFormRequired;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,5 +132,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.list-view {
|
||||||
|
$s: 1.2em;
|
||||||
|
width: 100%;
|
||||||
|
th, td {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
tr:hover td {
|
||||||
|
background-color: $colorItemBg;
|
||||||
|
color: $colorItemFg;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
$p: $interiorMargin;
|
||||||
|
@include ellipsize;
|
||||||
|
color: $colorItemFg;
|
||||||
|
font-size: 1em;
|
||||||
|
line-height: $s;
|
||||||
|
max-width: 0;
|
||||||
|
padding-top: $p;
|
||||||
|
padding-bottom: $p;
|
||||||
|
}
|
||||||
|
.t-item-icon {
|
||||||
|
font-size: $s;
|
||||||
|
margin-right: $interiorMargin;
|
||||||
|
}
|
||||||
|
.t-title-label {
|
||||||
|
@include ellipsize; // Yep, need it here too as well as the <td>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ body.mobile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.object-browse-bar {
|
.object-browse-bar {
|
||||||
margin-left: 45px;
|
&.t-primary { margin-left: 45px; }
|
||||||
.context-available {
|
.context-available {
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
@include phoneandtablet {
|
|
||||||
.overlay {
|
|
||||||
.clk-icon.close {
|
|
||||||
top: $mobileOverlayMargin; right: $mobileOverlayMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .holder {
|
|
||||||
height: 90%; width: 90%;
|
|
||||||
|
|
||||||
> .contents {
|
|
||||||
top: $mobileOverlayMargin;
|
|
||||||
right: $mobileOverlayMargin;
|
|
||||||
bottom: $mobileOverlayMargin;
|
|
||||||
left: $mobileOverlayMargin;
|
|
||||||
|
|
||||||
.top-bar {
|
|
||||||
> .title {
|
|
||||||
margin-right: 1.2em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include phone {
|
|
||||||
.overlay > .holder {
|
|
||||||
$m: 0;
|
|
||||||
border-radius: $m;
|
|
||||||
top: $m;
|
|
||||||
right: $m;
|
|
||||||
bottom: $m;
|
|
||||||
left: $m;
|
|
||||||
height: auto; width: auto;
|
|
||||||
min-width: 200px; min-height: 200px;
|
|
||||||
max-height: 100%; max-width: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
@include transform(none);
|
|
||||||
|
|
||||||
.editor .form .form-row.l-flex-row {
|
|
||||||
// Display elements in a columnar view
|
|
||||||
@include flex-direction(column);
|
|
||||||
> .flex-elem {
|
|
||||||
&:not(:first-child) {
|
|
||||||
margin-top: $interiorMargin;
|
|
||||||
}
|
|
||||||
&.label {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
&.controls {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.validates > .label:before {
|
|
||||||
position: relative;
|
|
||||||
right: auto;
|
|
||||||
line-height: inherit;
|
|
||||||
margin-right: $interiorMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.t-dialog-sm .overlay > .holder {
|
|
||||||
height: auto; max-height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include phonePortrait {
|
|
||||||
.overlay > .holder {
|
|
||||||
.contents .bottom-bar {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,11 +21,16 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
font-size: 90%;
|
|
||||||
&.delayEntry100ms {
|
&.delayEntry100ms {
|
||||||
@include keyframes(fadeInFromNone) {
|
@include keyframes(fadeInFromNone) {
|
||||||
0% { display: none; opacity: 0; }
|
0% {
|
||||||
100% { display: block; opacity: 1; }
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
display: block;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@include animation-delay($delayEntryMs);
|
@include animation-delay($delayEntryMs);
|
||||||
@include animation(fadeInFromNone $durEntryMs ease-in);
|
@include animation(fadeInFromNone $durEntryMs ease-in);
|
||||||
@ -35,29 +40,21 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
.close {
|
.close {
|
||||||
font-size: 0.8rem;
|
$d: $interiorMargin;
|
||||||
|
opacity: 0.3;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: $interiorMarginLg;
|
top: $d;
|
||||||
right: $interiorMarginLg;
|
right: $d;
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
left: auto;
|
left: auto;
|
||||||
z-index: 100;
|
&:hover {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
> .holder {
|
|
||||||
@include containerSubtle($colorOvrBg, $colorOvrFg);
|
> .abs.outer-holder {
|
||||||
border-radius: $basicCr * 3;
|
z-index: 102;
|
||||||
color: $colorOvrFg;
|
> .abs.inner-holder {
|
||||||
top: 50%;
|
|
||||||
right: auto;
|
|
||||||
bottom: auto;
|
|
||||||
left: 50%;
|
|
||||||
@include transform(translateX(-50%) translateY(-50%));
|
|
||||||
height: 70%;
|
|
||||||
width: 50%;
|
|
||||||
min-height: 300px;
|
|
||||||
min-width: 600px;
|
|
||||||
z-index: 101;
|
|
||||||
> .contents {
|
|
||||||
$m: $overlayMargin;
|
$m: $overlayMargin;
|
||||||
top: $m;
|
top: $m;
|
||||||
right: $m;
|
right: $m;
|
||||||
@ -65,78 +62,176 @@
|
|||||||
left: $m;
|
left: $m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.title {
|
|
||||||
@include ellipsize();
|
|
||||||
font-size: 1.2em;
|
|
||||||
line-height: 120%;
|
|
||||||
margin-bottom: $interiorMargin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hint, .field-hints { color: $colorFieldHintOverlay !important; }
|
|
||||||
|
|
||||||
.abs.top-bar {
|
|
||||||
height: $ovrTopBarH;
|
|
||||||
}
|
|
||||||
|
|
||||||
.abs.editor,
|
|
||||||
.abs.message-body {
|
|
||||||
top: $ovrTopBarH + $interiorMarginLg;
|
|
||||||
bottom: $ovrFooterH + $interiorMarginLg;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
overflow: auto;
|
|
||||||
.field.l-input-med {
|
|
||||||
input[type='text'] {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-bar {
|
.bottom-bar {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
.s-button {
|
.s-button {
|
||||||
$bg: $colorOvrBtnBg;
|
|
||||||
&:not(.major) {
|
|
||||||
@include btnSubtle($bg, pullForward($bg, 10%), $colorOvrBtnFg, $colorOvrBtnFg);
|
|
||||||
}
|
|
||||||
font-size: 95%;
|
font-size: 95%;
|
||||||
height: $ovrFooterH;
|
height: $ovrFooterH;
|
||||||
line-height: $ovrFooterH;
|
line-height: $ovrFooterH;
|
||||||
margin-left: $interiorMargin;
|
margin-bottom: $interiorMarginSm;
|
||||||
padding: 0 $interiorMargin * 3;
|
padding: 0 $interiorMargin * 3;
|
||||||
&:first-child {
|
&:not(:last-child) {
|
||||||
margin-left: 0;
|
margin-right: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialog boxes, size constrained and centered in desktop/tablet
|
||||||
|
&.l-dialog {
|
||||||
|
.s-button {
|
||||||
|
&:not(.major) {
|
||||||
|
@include btnSubtle($bg: $colorOvrBtnBg, $bgHov: pullForward($colorOvrBtnBg, 10%), $fg: $colorOvrBtnFg, $fgHov: $colorOvrBtnFg, $ic: $colorOvrBtnFg, $icHov: $colorOvrBtnFg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .abs.outer-holder {
|
||||||
|
@include desktopandtablet {
|
||||||
|
$max: 1280px;
|
||||||
|
@include transform(translateX(-50%) translateY(-50%));
|
||||||
|
border-radius: $overlayCr;
|
||||||
|
top: 50%; right: auto; bottom: auto; left: 50%;
|
||||||
|
width: 70%; height: 70%;
|
||||||
|
min-width: 520px;
|
||||||
|
max-width: $max; max-height: $max;
|
||||||
|
}
|
||||||
|
@include phone {
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
.editor .form .form-row.l-flex-row {
|
||||||
|
// Display elements in a columnar view
|
||||||
|
@include flex-direction(column);
|
||||||
|
> .flex-elem {
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-top: $interiorMargin;
|
||||||
|
}
|
||||||
|
&.label {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
&.controls {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.validates > .label:before {
|
||||||
|
position: relative;
|
||||||
|
right: auto;
|
||||||
|
line-height: inherit;
|
||||||
|
margin-right: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include containerSubtle($colorOvrBg, $colorOvrFg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include ellipsize();
|
||||||
|
font-size: 1.2em;
|
||||||
|
line-height: 120%;
|
||||||
|
margin-bottom: $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint, .field-hints {
|
||||||
|
color: $colorFieldHintOverlay !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.abs.top-bar {
|
||||||
|
height: $ovrTopBarH;
|
||||||
|
}
|
||||||
|
|
||||||
|
.abs.bottom-bar {
|
||||||
|
top: auto;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
overflow: visible;
|
||||||
|
height: $ovrFooterH;
|
||||||
|
}
|
||||||
|
|
||||||
|
.abs.editor,
|
||||||
|
.abs.message-body {
|
||||||
|
top: $ovrTopBarH + $interiorMarginLg;
|
||||||
|
bottom: $ovrFooterH + $interiorMarginLg;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
overflow: auto;
|
||||||
|
.field.l-input-med {
|
||||||
|
input[type='text'] {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-progress-bar {
|
||||||
|
$h: $progressBarHOverlay;
|
||||||
|
display: block;
|
||||||
|
height: $h;
|
||||||
|
line-height: $h;
|
||||||
|
margin: .5em 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select {
|
||||||
|
box-shadow: $shdwBtnsOverlay;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.abs.bottom-bar {
|
// Large view overlays for mobile and desktop
|
||||||
top: auto;
|
&.l-large-view {
|
||||||
right: 0;
|
> .abs.outer-holder {
|
||||||
bottom: 0;
|
@include keyframes(overlayIn) {
|
||||||
left: 0;
|
from {
|
||||||
overflow: visible;
|
opacity: 0;
|
||||||
height: $ovrFooterH;
|
transform: scale(0, 0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.0, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@include animToParams(overlayIn, $dur: $durLargeViewExpand, $delay: 0);
|
||||||
|
background: $colorBodyBg;
|
||||||
|
|
||||||
|
.abs.inner-holder {
|
||||||
|
opacity: 0;
|
||||||
|
@include keyframes(contentsIn) {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
@include animToParams(contentsIn, $dur: 50ms, $delay: $durLargeViewExpand * 1.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-btn-view-large {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
z-index: 101;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.desktop {
|
||||||
|
.overlay {
|
||||||
|
> .abs.outer-holder {
|
||||||
|
border-radius: $overlayCr;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.l-large-view {
|
||||||
|
> .abs.outer-holder {
|
||||||
|
width: 90%;
|
||||||
|
height: 90%;
|
||||||
|
top: 5%;
|
||||||
|
left: 5%;
|
||||||
|
@include boxShdwLarge();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-progress-bar {
|
.t-dialog-sm .overlay > .outer-holder {
|
||||||
$h: $progressBarHOverlay;
|
// Used for blocker and in-progress dialogs, modal alerts, etc.
|
||||||
display: block;
|
$h: 225px;
|
||||||
|
max-height: $h;
|
||||||
height: $h;
|
height: $h;
|
||||||
line-height: $h;
|
|
||||||
margin: .5em 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select {
|
|
||||||
box-shadow: $shdwBtnsOverlay;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.t-dialog-sm .overlay > .holder {
|
|
||||||
// Used for blocker and in-progress dialogs, modal alerts, etc.
|
|
||||||
$h: 225px;
|
|
||||||
min-height: $h;
|
|
||||||
height: $h;
|
|
||||||
}
|
|
||||||
|
@ -20,69 +20,77 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
.frame {
|
.frame {
|
||||||
$ohH: $btnFrameH;
|
$ohH: $btnFrameH;
|
||||||
$bc: $colorInteriorBorder;
|
$bc: $colorInteriorBorder;
|
||||||
&.child-frame.panel {
|
&.child-frame.panel {
|
||||||
background: $colorBodyBg;
|
background: $colorBodyBg;
|
||||||
border: 1px solid $bc;
|
border: 1px solid $bc;
|
||||||
z-index: 0; // Needed to prevent child-frame controls from showing through when another child-frame is above
|
z-index: 0; // Needed to prevent child-frame controls from showing through when another child-frame is above
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: lighten($bc, 10%);
|
border-color: lighten($bc, 10%);
|
||||||
}
|
|
||||||
}
|
|
||||||
.object-top-bar {
|
|
||||||
font-size: 0.75em;
|
|
||||||
height: $ohH;
|
|
||||||
line-height: $ohH;
|
|
||||||
.left {
|
|
||||||
padding-right: $interiorMarginLg;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>.object-holder.abs {
|
.object-browse-bar {
|
||||||
top: $ohH + $interiorMargin;
|
font-size: 0.75em;
|
||||||
}
|
height: $ohH;
|
||||||
.contents {
|
line-height: $ohH;
|
||||||
$myM: $interiorMargin;
|
}
|
||||||
top: $myM;
|
|
||||||
right: $myM;
|
> .object-holder.abs {
|
||||||
bottom: $myM;
|
top: $ohH + $interiorMargin;
|
||||||
left: $myM;
|
}
|
||||||
}
|
.contents {
|
||||||
&.frame-template {
|
$myM: $interiorMargin;
|
||||||
.s-button,
|
top: $myM;
|
||||||
.s-menu-button {
|
right: $myM;
|
||||||
height: $ohH;
|
bottom: $myM;
|
||||||
line-height: $ohH;
|
left: $myM;
|
||||||
padding: 0 $interiorMargin;
|
}
|
||||||
> span,
|
&.frame-template {
|
||||||
|
.s-button,
|
||||||
|
.s-menu-button {
|
||||||
|
height: $ohH;
|
||||||
|
line-height: $ohH;
|
||||||
|
padding: 0 $interiorMargin;
|
||||||
|
> span,
|
||||||
&:before {
|
&:before {
|
||||||
font-size: 0.65rem;
|
font-size: 0.65rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-menu-button:after {
|
.s-menu-button:after {
|
||||||
font-size: 8px;
|
font-size: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-switcher {
|
.view-switcher {
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.view-switcher {
|
.view-switcher {
|
||||||
// Hide the name when the view switcher is in a frame context
|
margin-left: $interiorMargin; // Kick other top bar elements away when I'm present.
|
||||||
.title-label {
|
// Hide the name when the view switcher is in a frame context
|
||||||
display: none;
|
.title-label {
|
||||||
}
|
display: none;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body.desktop .frame {
|
||||||
|
// Hide local controls initially and show it them on hover when they're in an element that's in a frame context
|
||||||
|
// Frame template is used because we need to target the lowest nested frame
|
||||||
|
.view-switcher,
|
||||||
|
.t-btn-view-large {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target the first descendant so that we only show the elements in the outermost container.
|
||||||
|
// Handles the case where we have layouts in layouts.
|
||||||
|
&:hover > .object-browse-bar {
|
||||||
|
.view-switcher,
|
||||||
|
.t-btn-view-large {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
body.desktop .frame.frame-template {
|
|
||||||
// Hide the view switcher by default when it's in an element that's in a frame context
|
|
||||||
// Frame template is used because we need to target the lowest nested frame
|
|
||||||
.view-switcher {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
&:hover .view-switcher {
|
|
||||||
// Show the view switcher on frame hover
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -136,14 +136,6 @@
|
|||||||
.mini-tab-icon.toggle-pane {
|
.mini-tab-icon.toggle-pane {
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
&.items {
|
|
||||||
.object-browse-bar {
|
|
||||||
.left.abs,
|
|
||||||
.right.abs {
|
|
||||||
top: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body.desktop .pane .mini-tab-icon.toggle-pane {
|
body.desktop .pane .mini-tab-icon.toggle-pane {
|
||||||
@ -235,8 +227,13 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
|
|||||||
top: $ueTopBarH + $interiorMarginLg;
|
top: $ueTopBarH + $interiorMarginLg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-object-wrapper-inner {
|
.l-object-wrapper {
|
||||||
@include trans-prop-nice-resize(0.25s);
|
padding: 0;
|
||||||
|
@include trans-prop-nice((padding), 0.25s);
|
||||||
|
.l-edit-controls {
|
||||||
|
@include trans-prop-nice((height), 0.5s);
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.object-browse-bar .s-button,
|
.object-browse-bar .s-button,
|
||||||
@ -250,10 +247,9 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
|
|||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.object-browse-bar,
|
.object-browse-bar {
|
||||||
.top-bar {
|
.l-object-action-buttons {
|
||||||
.view-switcher {
|
margin-left: $interiorMarginLg; // Kick the view switcher and other elements away
|
||||||
margin-right: $interiorMarginLg * 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +261,6 @@ body.desktop .pane .mini-tab-icon.toggle-pane {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
padding-right: $interiorMarginLg;
|
|
||||||
.l-back {
|
.l-back {
|
||||||
margin-right: $interiorMarginLg;
|
margin-right: $interiorMarginLg;
|
||||||
&.s-status-editing { display: none; }
|
&.s-status-editing { display: none; }
|
||||||
@ -348,53 +343,21 @@ body.desktop {
|
|||||||
.pane:not(.resizing) {
|
.pane:not(.resizing) {
|
||||||
@include trans-prop-nice-resize-w(250ms);
|
@include trans-prop-nice-resize-w(250ms);
|
||||||
}
|
}
|
||||||
.pane.primary-pane .object-browse-bar {
|
.pane.primary-pane > .object-browse-bar {
|
||||||
min-width: 200px; // Needed for nice display when primary pane is constrained severely via splitters
|
min-width: 200px; // Needed for nice display when primary pane is constrained severely via splitters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-status-editing {
|
.s-status-editing {
|
||||||
.l-object-wrapper {
|
.l-object-wrapper {
|
||||||
$t2Dur: $browseToEditAnimMs;
|
|
||||||
$t1Dur: $t2Dur / 2;
|
|
||||||
$pulseDur: $editBorderPulseMs;
|
|
||||||
$bC0: rgba($colorEditAreaFg, 0.5);
|
|
||||||
$bC100: rgba($colorEditAreaFg, 1);
|
|
||||||
|
|
||||||
background-color: $colorEditAreaBg;
|
background-color: $colorEditAreaBg;
|
||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
border: 1px dotted $bC0;
|
border: 1px dotted $colorEditAreaFg;
|
||||||
|
padding: $interiorMargin;
|
||||||
// Transition 1
|
|
||||||
@include keyframes(wrapperIn) {
|
|
||||||
from { border: 0px dotted transparent; padding: 0; }
|
|
||||||
to { border: 1px dotted $bC0; padding: 5px; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do last
|
|
||||||
@include keyframes(pulseNew) {
|
|
||||||
from { border-color: $bC0; }
|
|
||||||
to { border-color: $bC100; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@include animation-name(wrapperIn, pulseNew);
|
|
||||||
@include animation-duration($t1Dur, $pulseDur);
|
|
||||||
@include animation-delay(0s, $t1Dur + $t2Dur);
|
|
||||||
@include animation-direction(normal, alternate);
|
|
||||||
@include animation-fill-mode(both, none);
|
|
||||||
@include animation-iteration-count(1, infinite);
|
|
||||||
@include animation-timing-function(ease-in-out, linear);
|
|
||||||
|
|
||||||
|
|
||||||
.l-edit-controls {
|
.l-edit-controls {
|
||||||
height: 0;
|
height: $ueEditToolBarH + $interiorMargin; margin-bottom: $interiorMargin;
|
||||||
border-bottom: 1px solid $colorInteriorBorder;
|
border-bottom: 1px solid $colorInteriorBorder;
|
||||||
// Transition 2: reveal edit controls
|
|
||||||
@include keyframes(editIn) {
|
|
||||||
from { border-bottom: 0px solid transparent; height: 0; margin-bottom: 0; }
|
|
||||||
to { border-bottom: 1px solid $colorInteriorBorder; height: $ueEditToolBarH + $interiorMargin; margin-bottom: $interiorMargin; }
|
|
||||||
}
|
|
||||||
@include animToParams(editIn, $dur: $t2Dur, $delay: $t1Dur);
|
|
||||||
.tool-bar {
|
.tool-bar {
|
||||||
right: $interiorMargin;
|
right: $interiorMargin;
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,11 @@
|
|||||||
.l-control-group {
|
.l-control-group {
|
||||||
height: $btnToolbarH;
|
height: $btnToolbarH;
|
||||||
}
|
}
|
||||||
input[type="text"] {
|
input[type="text"],
|
||||||
|
input[type="search"],
|
||||||
|
input[type="number"] {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-size: .9em;
|
font-size: .8em;
|
||||||
height: $btnToolbarH;
|
height: $btnToolbarH;
|
||||||
margin-bottom: 1px;
|
margin-bottom: 1px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -17,6 +17,7 @@ $hoverRatioPercent: 10%;
|
|||||||
$basicCr: 3px;
|
$basicCr: 3px;
|
||||||
$controlCr: 2px;
|
$controlCr: 2px;
|
||||||
$smallCr: 2px;
|
$smallCr: 2px;
|
||||||
|
$overlayCr: 11px;
|
||||||
|
|
||||||
// Buttons and Controls
|
// Buttons and Controls
|
||||||
$colorBtnBg: pullForward($colorBodyBg, $contrastRatioPercent);
|
$colorBtnBg: pullForward($colorBodyBg, $contrastRatioPercent);
|
||||||
@ -146,6 +147,7 @@ $colorOvrFg: pullForward($colorBodyFg, 30%);
|
|||||||
$colorOvrBtnBg: pullForward($colorOvrBg, 20%);
|
$colorOvrBtnBg: pullForward($colorOvrBg, 20%);
|
||||||
$colorOvrBtnFg: #fff;
|
$colorOvrBtnFg: #fff;
|
||||||
$colorFieldHintOverlay: pullForward($colorOvrBg, 30%);
|
$colorFieldHintOverlay: pullForward($colorOvrBg, 30%);
|
||||||
|
$durLargeViewExpand: 250ms;
|
||||||
|
|
||||||
// Items
|
// Items
|
||||||
$colorItemBg: lighten($colorBodyBg, 5%);
|
$colorItemBg: lighten($colorBodyBg, 5%);
|
||||||
|
@ -17,6 +17,7 @@ $hoverRatioPercent: 10%;
|
|||||||
$basicCr: 4px;
|
$basicCr: 4px;
|
||||||
$controlCr: $basicCr;
|
$controlCr: $basicCr;
|
||||||
$smallCr: 3px;
|
$smallCr: 3px;
|
||||||
|
$overlayCr: 11px;
|
||||||
|
|
||||||
// Buttons and Controls
|
// Buttons and Controls
|
||||||
$colorBtnBg: pullForward($colorBodyBg, $contrastRatioPercent);
|
$colorBtnBg: pullForward($colorBodyBg, $contrastRatioPercent);
|
||||||
@ -146,6 +147,7 @@ $colorOvrFg: $colorBodyFg;
|
|||||||
$colorOvrBtnBg: pullForward($colorOvrBg, 40%);
|
$colorOvrBtnBg: pullForward($colorOvrBg, 40%);
|
||||||
$colorOvrBtnFg: #fff;
|
$colorOvrBtnFg: #fff;
|
||||||
$colorFieldHintOverlay: pullForward($colorOvrBg, 40%);
|
$colorFieldHintOverlay: pullForward($colorOvrBg, 40%);
|
||||||
|
$durLargeViewExpand: 250ms;
|
||||||
|
|
||||||
// Items
|
// Items
|
||||||
$colorItemBg: #ddd;
|
$colorItemBg: #ddd;
|
||||||
|
@ -28,6 +28,8 @@ define([
|
|||||||
"./src/controllers/RefreshingController",
|
"./src/controllers/RefreshingController",
|
||||||
"./src/actions/StartTimerAction",
|
"./src/actions/StartTimerAction",
|
||||||
"./src/actions/RestartTimerAction",
|
"./src/actions/RestartTimerAction",
|
||||||
|
"./src/actions/StopTimerAction",
|
||||||
|
"./src/actions/PauseTimerAction",
|
||||||
"text!./res/templates/clock.html",
|
"text!./res/templates/clock.html",
|
||||||
"text!./res/templates/timer.html",
|
"text!./res/templates/timer.html",
|
||||||
'legacyRegistry'
|
'legacyRegistry'
|
||||||
@ -39,6 +41,8 @@ define([
|
|||||||
RefreshingController,
|
RefreshingController,
|
||||||
StartTimerAction,
|
StartTimerAction,
|
||||||
RestartTimerAction,
|
RestartTimerAction,
|
||||||
|
StopTimerAction,
|
||||||
|
PauseTimerAction,
|
||||||
clockTemplate,
|
clockTemplate,
|
||||||
timerTemplate,
|
timerTemplate,
|
||||||
legacyRegistry
|
legacyRegistry
|
||||||
@ -139,6 +143,17 @@ define([
|
|||||||
"cssClass": "icon-play",
|
"cssClass": "icon-play",
|
||||||
"priority": "preferred"
|
"priority": "preferred"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "timer.pause",
|
||||||
|
"implementation": PauseTimerAction,
|
||||||
|
"depends": [
|
||||||
|
"now"
|
||||||
|
],
|
||||||
|
"category": "contextual",
|
||||||
|
"name": "Pause",
|
||||||
|
"cssClass": "icon-pause",
|
||||||
|
"priority": "preferred"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "timer.restart",
|
"key": "timer.restart",
|
||||||
"implementation": RestartTimerAction,
|
"implementation": RestartTimerAction,
|
||||||
@ -149,6 +164,17 @@ define([
|
|||||||
"name": "Restart at 0",
|
"name": "Restart at 0",
|
||||||
"cssClass": "icon-refresh",
|
"cssClass": "icon-refresh",
|
||||||
"priority": "preferred"
|
"priority": "preferred"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "timer.stop",
|
||||||
|
"implementation": StopTimerAction,
|
||||||
|
"depends": [
|
||||||
|
"now"
|
||||||
|
],
|
||||||
|
"category": "contextual",
|
||||||
|
"name": "Stop",
|
||||||
|
"cssClass": "icon-box",
|
||||||
|
"priority": "preferred"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"types": [
|
"types": [
|
||||||
|
@ -19,11 +19,16 @@
|
|||||||
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="l-time-display l-digital l-timer s-timer" ng-controller="TimerController as timer">
|
<div class="l-time-display l-digital l-timer s-timer s-state-{{timer.timerState}}" ng-controller="TimerController as timer">
|
||||||
<div class="l-elem-wrapper l-flex-row">
|
<div class="l-elem-wrapper l-flex-row">
|
||||||
<a ng-click="timer.clickButton()"
|
<div class="l-elem-wrapper l-flex-row controls">
|
||||||
title="{{timer.buttonText()}}"
|
<a ng-click="timer.clickStopButton()"
|
||||||
class="flex-elem control s-icon-button {{timer.buttonCssClass()}}"></a>
|
title="Stop"
|
||||||
|
class="flex-elem s-icon-button t-btn-stop icon-box"></a>
|
||||||
|
<a ng-click="timer.clickButton()"
|
||||||
|
title="{{timer.buttonText()}}"
|
||||||
|
class="flex-elem s-icon-button t-btn-pauseplay {{timer.buttonCssClass()}}"></a>
|
||||||
|
</div>
|
||||||
<span class="flex-elem l-value {{timer.signClass()}}">
|
<span class="flex-elem l-value {{timer.signClass()}}">
|
||||||
<span class="value"
|
<span class="value"
|
||||||
ng-class="{ active:timer.text() }">{{timer.text() || "--:--:--"}}
|
ng-class="{ active:timer.text() }">{{timer.text() || "--:--:--"}}
|
||||||
|
@ -25,13 +25,10 @@ define(
|
|||||||
function () {
|
function () {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the "Start" and "Restart" action for timers.
|
* Implements the "Pause" action for timers.
|
||||||
*
|
*
|
||||||
* Sets the reference timestamp in a timer to the current
|
* Sets the reference pausedTime in a timer to the current
|
||||||
* time, such that it begins counting up.
|
* time, such that it stops counting up.
|
||||||
*
|
|
||||||
* Both "Start" and "Restart" share this implementation, but
|
|
||||||
* control their visibility with different `appliesTo` behavior.
|
|
||||||
*
|
*
|
||||||
* @implements {Action}
|
* @implements {Action}
|
||||||
* @memberof platform/features/clock
|
* @memberof platform/features/clock
|
||||||
@ -40,22 +37,35 @@ define(
|
|||||||
* time (typically wrapping `Date.now`)
|
* time (typically wrapping `Date.now`)
|
||||||
* @param {ActionContext} context the context for this action
|
* @param {ActionContext} context the context for this action
|
||||||
*/
|
*/
|
||||||
function AbstractStartTimerAction(now, context) {
|
function PauseTimerAction(now, context) {
|
||||||
this.domainObject = context.domainObject;
|
this.domainObject = context.domainObject;
|
||||||
this.now = now;
|
this.now = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractStartTimerAction.prototype.perform = function () {
|
PauseTimerAction.appliesTo = function (context) {
|
||||||
|
var model =
|
||||||
|
(context.domainObject && context.domainObject.getModel()) ||
|
||||||
|
{};
|
||||||
|
|
||||||
|
|
||||||
|
// We show this variant for timers which have
|
||||||
|
// a target time, or is in a playing state.
|
||||||
|
return model.type === 'timer' &&
|
||||||
|
model.timerState === 'started';
|
||||||
|
};
|
||||||
|
|
||||||
|
PauseTimerAction.prototype.perform = function () {
|
||||||
var domainObject = this.domainObject,
|
var domainObject = this.domainObject,
|
||||||
now = this.now;
|
now = this.now;
|
||||||
|
|
||||||
function setTimestamp(model) {
|
function updateModel(model) {
|
||||||
model.timestamp = now();
|
model.timerState = 'paused';
|
||||||
|
model.pausedTime = now();
|
||||||
}
|
}
|
||||||
|
|
||||||
return domainObject.useCapability('mutation', setTimestamp);
|
return domainObject.useCapability('mutation', updateModel);
|
||||||
};
|
};
|
||||||
|
|
||||||
return AbstractStartTimerAction;
|
return PauseTimerAction;
|
||||||
}
|
}
|
||||||
);
|
);
|
@ -21,8 +21,8 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
['./AbstractStartTimerAction'],
|
[],
|
||||||
function (AbstractStartTimerAction) {
|
function () {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the "Restart at 0" action.
|
* Implements the "Restart at 0" action.
|
||||||
@ -30,7 +30,6 @@ define(
|
|||||||
* Behaves the same as (and delegates functionality to)
|
* Behaves the same as (and delegates functionality to)
|
||||||
* the "Start" action.
|
* the "Start" action.
|
||||||
*
|
*
|
||||||
* @extends {platform/features/clock.AbstractTimerAction}
|
|
||||||
* @implements {Action}
|
* @implements {Action}
|
||||||
* @memberof platform/features/clock
|
* @memberof platform/features/clock
|
||||||
* @constructor
|
* @constructor
|
||||||
@ -39,24 +38,33 @@ define(
|
|||||||
* @param {ActionContext} context the context for this action
|
* @param {ActionContext} context the context for this action
|
||||||
*/
|
*/
|
||||||
function RestartTimerAction(now, context) {
|
function RestartTimerAction(now, context) {
|
||||||
AbstractStartTimerAction.apply(this, [now, context]);
|
this.domainObject = context.domainObject;
|
||||||
|
this.now = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
RestartTimerAction.prototype =
|
|
||||||
Object.create(AbstractStartTimerAction.prototype);
|
|
||||||
|
|
||||||
RestartTimerAction.appliesTo = function (context) {
|
RestartTimerAction.appliesTo = function (context) {
|
||||||
var model =
|
var model =
|
||||||
(context.domainObject && context.domainObject.getModel()) ||
|
(context.domainObject && context.domainObject.getModel()) ||
|
||||||
{};
|
{};
|
||||||
|
|
||||||
// We show this variant for timers which already have
|
// We show this variant for timers which already have a target time.
|
||||||
// a target time.
|
|
||||||
return model.type === 'timer' &&
|
return model.type === 'timer' &&
|
||||||
model.timestamp !== undefined;
|
model.timerState !== 'stopped';
|
||||||
|
};
|
||||||
|
|
||||||
|
RestartTimerAction.prototype.perform = function () {
|
||||||
|
var domainObject = this.domainObject,
|
||||||
|
now = this.now;
|
||||||
|
|
||||||
|
function updateModel(model) {
|
||||||
|
model.timestamp = now();
|
||||||
|
model.timerState = 'started';
|
||||||
|
model.pausedTime = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return domainObject.useCapability('mutation', updateModel);
|
||||||
};
|
};
|
||||||
|
|
||||||
return RestartTimerAction;
|
return RestartTimerAction;
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -21,8 +21,8 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
['./AbstractStartTimerAction'],
|
[],
|
||||||
function (AbstractStartTimerAction) {
|
function () {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the "Start" action for timers.
|
* Implements the "Start" action for timers.
|
||||||
@ -30,7 +30,6 @@ define(
|
|||||||
* Sets the reference timestamp in a timer to the current
|
* Sets the reference timestamp in a timer to the current
|
||||||
* time, such that it begins counting up.
|
* time, such that it begins counting up.
|
||||||
*
|
*
|
||||||
* @extends {platform/features/clock.AbstractTimerAction}
|
|
||||||
* @implements {Action}
|
* @implements {Action}
|
||||||
* @memberof platform/features/clock
|
* @memberof platform/features/clock
|
||||||
* @constructor
|
* @constructor
|
||||||
@ -39,12 +38,10 @@ define(
|
|||||||
* @param {ActionContext} context the context for this action
|
* @param {ActionContext} context the context for this action
|
||||||
*/
|
*/
|
||||||
function StartTimerAction(now, context) {
|
function StartTimerAction(now, context) {
|
||||||
AbstractStartTimerAction.apply(this, [now, context]);
|
this.domainObject = context.domainObject;
|
||||||
|
this.now = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
StartTimerAction.prototype =
|
|
||||||
Object.create(AbstractStartTimerAction.prototype);
|
|
||||||
|
|
||||||
StartTimerAction.appliesTo = function (context) {
|
StartTimerAction.appliesTo = function (context) {
|
||||||
var model =
|
var model =
|
||||||
(context.domainObject && context.domainObject.getModel()) ||
|
(context.domainObject && context.domainObject.getModel()) ||
|
||||||
@ -53,10 +50,28 @@ define(
|
|||||||
// We show this variant for timers which do not yet have
|
// We show this variant for timers which do not yet have
|
||||||
// a target time.
|
// a target time.
|
||||||
return model.type === 'timer' &&
|
return model.type === 'timer' &&
|
||||||
model.timestamp === undefined;
|
model.timerState !== 'started';
|
||||||
|
};
|
||||||
|
|
||||||
|
StartTimerAction.prototype.perform = function () {
|
||||||
|
var domainObject = this.domainObject,
|
||||||
|
now = this.now;
|
||||||
|
|
||||||
|
function updateModel(model) {
|
||||||
|
//if we are resuming
|
||||||
|
if (model.pausedTime) {
|
||||||
|
var timeShift = now() - model.pausedTime;
|
||||||
|
model.timestamp = model.timestamp + timeShift;
|
||||||
|
} else {
|
||||||
|
model.timestamp = now();
|
||||||
|
}
|
||||||
|
model.timerState = 'started';
|
||||||
|
model.pausedTime = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return domainObject.useCapability('mutation', updateModel);
|
||||||
};
|
};
|
||||||
|
|
||||||
return StartTimerAction;
|
return StartTimerAction;
|
||||||
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
71
platform/features/clock/src/actions/StopTimerAction.js
Normal file
71
platform/features/clock/src/actions/StopTimerAction.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the "Stop" action for timers.
|
||||||
|
*
|
||||||
|
* Sets the reference timestamp in a timer undefined,
|
||||||
|
* such that it is reset and makes no movements.
|
||||||
|
*
|
||||||
|
* @implements {Action}
|
||||||
|
* @memberof platform/features/clock
|
||||||
|
* @constructor
|
||||||
|
* @param {Function} now a function which returns the current
|
||||||
|
* time (typically wrapping `Date.now`)
|
||||||
|
* @param {ActionContext} context the context for this action
|
||||||
|
*/
|
||||||
|
function StopTimerAction(now, context) {
|
||||||
|
this.domainObject = context.domainObject;
|
||||||
|
this.now = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
StopTimerAction.appliesTo = function (context) {
|
||||||
|
var model =
|
||||||
|
(context.domainObject && context.domainObject.getModel()) ||
|
||||||
|
{};
|
||||||
|
|
||||||
|
|
||||||
|
// We show this variant for timers which do not yet have
|
||||||
|
// a target time.
|
||||||
|
return model.type === 'timer' &&
|
||||||
|
model.timerState !== 'stopped';
|
||||||
|
};
|
||||||
|
|
||||||
|
StopTimerAction.prototype.perform = function () {
|
||||||
|
var domainObject = this.domainObject;
|
||||||
|
|
||||||
|
function updateModel(model) {
|
||||||
|
model.timestamp = undefined;
|
||||||
|
model.timerState = 'stopped';
|
||||||
|
model.pausedTime = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return domainObject.useCapability('mutation', updateModel);
|
||||||
|
};
|
||||||
|
|
||||||
|
return StopTimerAction;
|
||||||
|
}
|
||||||
|
);
|
@ -42,6 +42,7 @@ define(
|
|||||||
active = true,
|
active = true,
|
||||||
relativeTimestamp,
|
relativeTimestamp,
|
||||||
lastTimestamp,
|
lastTimestamp,
|
||||||
|
relativeTimerState,
|
||||||
self = this;
|
self = this;
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
@ -51,12 +52,9 @@ define(
|
|||||||
self.textValue = formatter(timeDelta);
|
self.textValue = formatter(timeDelta);
|
||||||
self.signValue = timeDelta < 0 ? "-" :
|
self.signValue = timeDelta < 0 ? "-" :
|
||||||
timeDelta >= 1000 ? "+" : "";
|
timeDelta >= 1000 ? "+" : "";
|
||||||
self.signCssClass = timeDelta < 0 ? "icon-minus" :
|
|
||||||
timeDelta >= 1000 ? "icon-plus" : "";
|
|
||||||
} else {
|
} else {
|
||||||
self.textValue = "";
|
self.textValue = "";
|
||||||
self.signValue = "";
|
self.signValue = "";
|
||||||
self.signCssClass = "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,19 +66,50 @@ define(
|
|||||||
relativeTimestamp = timestamp;
|
relativeTimestamp = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateTimerState(timerState) {
|
||||||
|
self.timerState = relativeTimerState = timerState;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateActions(actionCapability, actionKey) {
|
||||||
|
self.relevantAction = actionCapability &&
|
||||||
|
actionCapability.getActions(actionKey)[0];
|
||||||
|
|
||||||
|
self.stopAction = relativeTimerState !== 'stopped' ?
|
||||||
|
actionCapability && actionCapability.getActions('timer.stop')[0] : undefined;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPaused() {
|
||||||
|
return relativeTimerState === 'paused';
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLegacyTimer(model) {
|
||||||
|
if (model.timerState === undefined) {
|
||||||
|
model.timerState = model.timestamp === undefined ?
|
||||||
|
'stopped' : 'started';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateObject(domainObject) {
|
function updateObject(domainObject) {
|
||||||
var model = domainObject.getModel(),
|
var model = domainObject.getModel();
|
||||||
timestamp = model.timestamp,
|
handleLegacyTimer(model);
|
||||||
|
|
||||||
|
var timestamp = model.timestamp,
|
||||||
formatKey = model.timerFormat,
|
formatKey = model.timerFormat,
|
||||||
|
timerState = model.timerState,
|
||||||
actionCapability = domainObject.getCapability('action'),
|
actionCapability = domainObject.getCapability('action'),
|
||||||
actionKey = (timestamp === undefined) ?
|
actionKey = (timerState !== 'started') ?
|
||||||
'timer.start' : 'timer.restart';
|
'timer.start' : 'timer.pause';
|
||||||
|
|
||||||
updateFormat(formatKey);
|
updateFormat(formatKey);
|
||||||
updateTimestamp(timestamp);
|
updateTimestamp(timestamp);
|
||||||
|
updateTimerState(timerState);
|
||||||
|
updateActions(actionCapability, actionKey);
|
||||||
|
|
||||||
self.relevantAction = actionCapability &&
|
//if paused on startup show last known position
|
||||||
actionCapability.getActions(actionKey)[0];
|
if (isPaused() && !lastTimestamp) {
|
||||||
|
lastTimestamp = model.pausedTime;
|
||||||
|
}
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
@ -98,8 +127,16 @@ define(
|
|||||||
function tick() {
|
function tick() {
|
||||||
var lastSign = self.signValue,
|
var lastSign = self.signValue,
|
||||||
lastText = self.textValue;
|
lastText = self.textValue;
|
||||||
lastTimestamp = now();
|
|
||||||
update();
|
if (!isPaused()) {
|
||||||
|
lastTimestamp = now();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relativeTimerState === undefined) {
|
||||||
|
handleModification();
|
||||||
|
}
|
||||||
|
|
||||||
// We're running in an animation frame, not in a digest cycle.
|
// We're running in an animation frame, not in a digest cycle.
|
||||||
// We need to trigger a digest cycle if our displayable data
|
// We need to trigger a digest cycle if our displayable data
|
||||||
// changes.
|
// changes.
|
||||||
@ -130,27 +167,27 @@ define(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the CSS class to display the right icon
|
* Get the CSS class to display the right icon
|
||||||
* for the start/restart button.
|
* for the start/pause button.
|
||||||
* @returns {string} cssClass to display
|
* @returns {string} cssclass to display
|
||||||
*/
|
*/
|
||||||
TimerController.prototype.buttonCssClass = function () {
|
TimerController.prototype.buttonCssClass = function () {
|
||||||
return this.relevantAction ?
|
return this.relevantAction ?
|
||||||
this.relevantAction.getMetadata().cssClass : "";
|
this.relevantAction.getMetadata().cssClass : "";
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the text to show for the start/restart button
|
* Get the text to show for the start/pause button
|
||||||
* (e.g. in a tooltip)
|
* (e.g. in a tooltip)
|
||||||
* @returns {string} name of the action
|
* @returns {string} name of the action
|
||||||
*/
|
*/
|
||||||
TimerController.prototype.buttonText = function () {
|
TimerController.prototype.buttonText = function () {
|
||||||
return this.relevantAction ?
|
return this.relevantAction ?
|
||||||
this.relevantAction.getMetadata().name : "";
|
this.relevantAction.getMetadata().name : "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform the action associated with the start/restart button.
|
* Perform the action associated with the start/pause button.
|
||||||
*/
|
*/
|
||||||
TimerController.prototype.clickButton = function () {
|
TimerController.prototype.clickButton = function () {
|
||||||
if (this.relevantAction) {
|
if (this.relevantAction) {
|
||||||
@ -159,6 +196,16 @@ define(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the action associated with the stop button.
|
||||||
|
*/
|
||||||
|
TimerController.prototype.clickStopButton = function () {
|
||||||
|
if (this.stopAction) {
|
||||||
|
this.stopAction.perform();
|
||||||
|
this.updateObject(this.$scope.domainObject);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the sign (+ or -) of the current timer value, as
|
* Get the sign (+ or -) of the current timer value, as
|
||||||
* displayable text.
|
* displayable text.
|
||||||
@ -168,15 +215,6 @@ define(
|
|||||||
return this.signValue;
|
return this.signValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the sign (+ or -) of the current timer value, as
|
|
||||||
* a CSS class.
|
|
||||||
* @returns {string} sign of the current timer value
|
|
||||||
*/
|
|
||||||
TimerController.prototype.signClass = function () {
|
|
||||||
return this.signCssClass;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the text to display for the current timer value.
|
* Get the text to display for the current timer value.
|
||||||
* @returns {string} current timer value
|
* @returns {string} current timer value
|
||||||
|
@ -21,28 +21,41 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
["../../src/actions/AbstractStartTimerAction"],
|
["../../src/actions/PauseTimerAction"],
|
||||||
function (AbstractStartTimerAction) {
|
function (PauseTimerAction) {
|
||||||
|
|
||||||
describe("A timer's start/restart action", function () {
|
describe("A timer's Pause action", function () {
|
||||||
var mockNow,
|
var mockNow,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
testModel,
|
testModel,
|
||||||
|
testContext,
|
||||||
action;
|
action;
|
||||||
|
|
||||||
function asPromise(value) {
|
function asPromise(value) {
|
||||||
return (value || {}).then ? value : {
|
return (value || {}).then ? value : {
|
||||||
then: function (callback) {
|
then: function (callback) {
|
||||||
return asPromise(callback(value));
|
return asPromise(callback(value));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function testState(type, timerState, timestamp, expected) {
|
||||||
|
testModel.type = type;
|
||||||
|
testModel.timerState = timerState;
|
||||||
|
testModel.timestamp = timestamp;
|
||||||
|
|
||||||
|
if (expected) {
|
||||||
|
expect(PauseTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||||
|
} else {
|
||||||
|
expect(PauseTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockNow = jasmine.createSpy('now');
|
mockNow = jasmine.createSpy('now');
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
'domainObject',
|
'domainObject',
|
||||||
['getCapability', 'useCapability']
|
['getCapability', 'useCapability', 'getModel']
|
||||||
);
|
);
|
||||||
|
|
||||||
mockDomainObject.useCapability.andCallFake(function (c, v) {
|
mockDomainObject.useCapability.andCallFake(function (c, v) {
|
||||||
@ -51,24 +64,41 @@ define(
|
|||||||
return asPromise(true);
|
return asPromise(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
mockDomainObject.getModel.andCallFake(function () {
|
||||||
|
return testModel;
|
||||||
|
});
|
||||||
|
|
||||||
testModel = {};
|
testModel = {};
|
||||||
|
testContext = {domainObject: mockDomainObject};
|
||||||
|
|
||||||
action = new AbstractStartTimerAction(mockNow, {
|
action = new PauseTimerAction(mockNow, testContext);
|
||||||
domainObject: mockDomainObject
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates the model with a timestamp", function () {
|
it("updates the model with a timerState", function () {
|
||||||
|
testModel.timerState = 'started';
|
||||||
|
action.perform();
|
||||||
|
expect(testModel.timerState).toEqual('paused');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the model with a pausedTime", function () {
|
||||||
|
testModel.pausedTime = undefined;
|
||||||
mockNow.andReturn(12000);
|
mockNow.andReturn(12000);
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(testModel.timestamp).toEqual(12000);
|
expect(testModel.pausedTime).toEqual(12000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not truncate milliseconds", function () {
|
it("applies only to timers in a playing state", function () {
|
||||||
mockNow.andReturn(42321);
|
//in a stopped state
|
||||||
action.perform();
|
testState('timer', 'stopped', undefined, false);
|
||||||
expect(testModel.timestamp).toEqual(42321);
|
|
||||||
|
//in a paused state
|
||||||
|
testState('timer', 'paused', 12000, false);
|
||||||
|
|
||||||
|
//in a playing state
|
||||||
|
testState('timer', 'started', 12000, true);
|
||||||
|
|
||||||
|
//not a timer
|
||||||
|
testState('clock', 'started', 12000, false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
@ -39,6 +39,18 @@ define(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testState(type, timerState, timestamp, expected) {
|
||||||
|
testModel.type = type;
|
||||||
|
testModel.timerState = timerState;
|
||||||
|
testModel.timestamp = timestamp;
|
||||||
|
|
||||||
|
if (expected) {
|
||||||
|
expect(RestartTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||||
|
} else {
|
||||||
|
expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockNow = jasmine.createSpy('now');
|
mockNow = jasmine.createSpy('now');
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
@ -63,23 +75,36 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("updates the model with a timestamp", function () {
|
it("updates the model with a timestamp", function () {
|
||||||
|
testModel.pausedTime = 12000;
|
||||||
mockNow.andReturn(12000);
|
mockNow.andReturn(12000);
|
||||||
action.perform();
|
action.perform();
|
||||||
expect(testModel.timestamp).toEqual(12000);
|
expect(testModel.timestamp).toEqual(12000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("applies only to timers with a target time", function () {
|
it("updates the model with a pausedTime", function () {
|
||||||
testModel.type = 'timer';
|
testModel.pausedTime = 12000;
|
||||||
testModel.timestamp = 12000;
|
action.perform();
|
||||||
expect(RestartTimerAction.appliesTo(testContext)).toBeTruthy();
|
expect(testModel.pausedTime).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
testModel.type = 'timer';
|
it("updates the model with a timerState", function () {
|
||||||
testModel.timestamp = undefined;
|
testModel.timerState = 'stopped';
|
||||||
expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy();
|
action.perform();
|
||||||
|
expect(testModel.timerState).toEqual('started');
|
||||||
|
});
|
||||||
|
|
||||||
testModel.type = 'clock';
|
it("applies only to timers in a non-stopped state", function () {
|
||||||
testModel.timestamp = 12000;
|
//in a stopped state
|
||||||
expect(RestartTimerAction.appliesTo(testContext)).toBeFalsy();
|
testState('timer', 'stopped', undefined, false);
|
||||||
|
|
||||||
|
//in a paused state
|
||||||
|
testState('timer', 'paused', 12000, true);
|
||||||
|
|
||||||
|
//in a playing state
|
||||||
|
testState('timer', 'started', 12000, true);
|
||||||
|
|
||||||
|
//not a timer
|
||||||
|
testState('clock', 'paused', 12000, false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,22 @@ define(
|
|||||||
|
|
||||||
function asPromise(value) {
|
function asPromise(value) {
|
||||||
return (value || {}).then ? value : {
|
return (value || {}).then ? value : {
|
||||||
then: function (callback) {
|
then: function (callback) {
|
||||||
return asPromise(callback(value));
|
return asPromise(callback(value));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function testState(type, timerState, timestamp, expected) {
|
||||||
|
testModel.type = type;
|
||||||
|
testModel.timerState = timerState;
|
||||||
|
testModel.timestamp = timestamp;
|
||||||
|
|
||||||
|
if (expected) {
|
||||||
|
expect(StartTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||||
|
} else {
|
||||||
|
expect(StartTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
@ -57,7 +69,7 @@ define(
|
|||||||
});
|
});
|
||||||
|
|
||||||
testModel = {};
|
testModel = {};
|
||||||
testContext = { domainObject: mockDomainObject };
|
testContext = {domainObject: mockDomainObject};
|
||||||
|
|
||||||
action = new StartTimerAction(mockNow, testContext);
|
action = new StartTimerAction(mockNow, testContext);
|
||||||
});
|
});
|
||||||
@ -68,18 +80,30 @@ define(
|
|||||||
expect(testModel.timestamp).toEqual(12000);
|
expect(testModel.timestamp).toEqual(12000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("applies only to timers without a target time", function () {
|
it("updates the model with a pausedTime", function () {
|
||||||
testModel.type = 'timer';
|
testModel.pausedTime = 12000;
|
||||||
testModel.timestamp = 12000;
|
action.perform();
|
||||||
expect(StartTimerAction.appliesTo(testContext)).toBeFalsy();
|
expect(testModel.pausedTime).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
testModel.type = 'timer';
|
it("updates the model with a timerState", function () {
|
||||||
testModel.timestamp = undefined;
|
testModel.timerState = undefined;
|
||||||
expect(StartTimerAction.appliesTo(testContext)).toBeTruthy();
|
action.perform();
|
||||||
|
expect(testModel.timerState).toEqual('started');
|
||||||
|
});
|
||||||
|
|
||||||
testModel.type = 'clock';
|
it("applies only to timers not in a playing state", function () {
|
||||||
testModel.timestamp = 12000;
|
//in a stopped state
|
||||||
expect(StartTimerAction.appliesTo(testContext)).toBeFalsy();
|
testState('timer', 'stopped', undefined, true);
|
||||||
|
|
||||||
|
//in a paused state
|
||||||
|
testState('timer', 'paused', 12000, true);
|
||||||
|
|
||||||
|
//in a playing state
|
||||||
|
testState('timer', 'started', 12000, false);
|
||||||
|
|
||||||
|
//not a timer
|
||||||
|
testState('clock', 'paused', 12000, false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
110
platform/features/clock/test/actions/StopTimerActionSpec.js
Normal file
110
platform/features/clock/test/actions/StopTimerActionSpec.js
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../../src/actions/StopTimerAction"],
|
||||||
|
function (StopTimerAction) {
|
||||||
|
|
||||||
|
describe("A timer's stop action", function () {
|
||||||
|
var mockNow,
|
||||||
|
mockDomainObject,
|
||||||
|
testModel,
|
||||||
|
testContext,
|
||||||
|
action;
|
||||||
|
|
||||||
|
function asPromise(value) {
|
||||||
|
return (value || {}).then ? value : {
|
||||||
|
then: function (callback) {
|
||||||
|
return asPromise(callback(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function testState(type, timerState, timestamp, expected) {
|
||||||
|
testModel.type = type;
|
||||||
|
testModel.timerState = timerState;
|
||||||
|
testModel.timestamp = timestamp;
|
||||||
|
|
||||||
|
if (expected) {
|
||||||
|
expect(StopTimerAction.appliesTo(testContext)).toBeTruthy();
|
||||||
|
} else {
|
||||||
|
expect(StopTimerAction.appliesTo(testContext)).toBeFalsy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockNow = jasmine.createSpy('now');
|
||||||
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
|
'domainObject',
|
||||||
|
['getCapability', 'useCapability', 'getModel']
|
||||||
|
);
|
||||||
|
|
||||||
|
mockDomainObject.useCapability.andCallFake(function (c, v) {
|
||||||
|
if (c === 'mutation') {
|
||||||
|
testModel = v(testModel) || testModel;
|
||||||
|
return asPromise(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mockDomainObject.getModel.andCallFake(function () {
|
||||||
|
return testModel;
|
||||||
|
});
|
||||||
|
|
||||||
|
testModel = {};
|
||||||
|
testContext = {domainObject: mockDomainObject};
|
||||||
|
|
||||||
|
action = new StopTimerAction(mockNow, testContext);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the model with a timestamp", function () {
|
||||||
|
mockNow.andReturn(12000);
|
||||||
|
action.perform();
|
||||||
|
expect(testModel.timestamp).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the model with a pausedTime", function () {
|
||||||
|
testModel.pausedTime = 12000;
|
||||||
|
action.perform();
|
||||||
|
expect(testModel.pausedTime).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates the model with a timerState", function () {
|
||||||
|
testModel.timerState = 'started';
|
||||||
|
action.perform();
|
||||||
|
expect(testModel.timerState).toEqual('stopped');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("applies only to timers in a non-stopped state", function () {
|
||||||
|
//in a stopped state
|
||||||
|
testState('timer', 'stopped', undefined, false);
|
||||||
|
|
||||||
|
//in a paused state
|
||||||
|
testState('timer', 'paused', 12000, true);
|
||||||
|
|
||||||
|
//in a playing state
|
||||||
|
testState('timer', 'started', 12000, true);
|
||||||
|
|
||||||
|
//not a timer
|
||||||
|
testState('clock', 'paused', 12000, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -34,13 +34,14 @@ define(
|
|||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockActionCapability,
|
mockActionCapability,
|
||||||
mockStart,
|
mockStart,
|
||||||
mockRestart,
|
mockPause,
|
||||||
|
mockStop,
|
||||||
testModel,
|
testModel,
|
||||||
controller;
|
controller;
|
||||||
|
|
||||||
function invokeWatch(expr, value) {
|
function invokeWatch(expr, value) {
|
||||||
mockScope.$watch.calls.forEach(function (call) {
|
mockScope.$watch.calls.forEach(function (call) {
|
||||||
if (call.args[0] === expr) {
|
if (call.args[0] === expr) {
|
||||||
call.args[1](value);
|
call.args[1](value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -67,8 +68,12 @@ define(
|
|||||||
'start',
|
'start',
|
||||||
['getMetadata', 'perform']
|
['getMetadata', 'perform']
|
||||||
);
|
);
|
||||||
mockRestart = jasmine.createSpyObj(
|
mockPause = jasmine.createSpyObj(
|
||||||
'restart',
|
'paused',
|
||||||
|
['getMetadata', 'perform']
|
||||||
|
);
|
||||||
|
mockStop = jasmine.createSpyObj(
|
||||||
|
'stopped',
|
||||||
['getMetadata', 'perform']
|
['getMetadata', 'perform']
|
||||||
);
|
);
|
||||||
mockNow = jasmine.createSpy('now');
|
mockNow = jasmine.createSpy('now');
|
||||||
@ -82,11 +87,14 @@ define(
|
|||||||
mockActionCapability.getActions.andCallFake(function (k) {
|
mockActionCapability.getActions.andCallFake(function (k) {
|
||||||
return [{
|
return [{
|
||||||
'timer.start': mockStart,
|
'timer.start': mockStart,
|
||||||
'timer.restart': mockRestart
|
'timer.pause': mockPause,
|
||||||
|
'timer.stop': mockStop
|
||||||
}[k]];
|
}[k]];
|
||||||
});
|
});
|
||||||
mockStart.getMetadata.andReturn({ cssClass: "icon-play", name: "Start" });
|
|
||||||
mockRestart.getMetadata.andReturn({ cssClass: "icon-refresh", name: "Restart" });
|
mockStart.getMetadata.andReturn({cssClass: "icon-play", name: "Start"});
|
||||||
|
mockPause.getMetadata.andReturn({cssClass: "icon-pause", name: "Pause"});
|
||||||
|
mockStop.getMetadata.andReturn({cssClass: "icon-box", name: "Stop"});
|
||||||
mockScope.domainObject = mockDomainObject;
|
mockScope.domainObject = mockDomainObject;
|
||||||
|
|
||||||
testModel = {};
|
testModel = {};
|
||||||
@ -144,28 +152,37 @@ define(
|
|||||||
expect(controller.text()).toEqual("0D 00:00:00");
|
expect(controller.text()).toEqual("0D 00:00:00");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows cssClass & name for the applicable start/restart action", function () {
|
it("shows cssClass & name for the applicable start/pause action", function () {
|
||||||
invokeWatch('domainObject', mockDomainObject);
|
invokeWatch('domainObject', mockDomainObject);
|
||||||
expect(controller.buttonCssClass()).toEqual("icon-play");
|
expect(controller.buttonCssClass()).toEqual("icon-play");
|
||||||
expect(controller.buttonText()).toEqual("Start");
|
expect(controller.buttonText()).toEqual("Start");
|
||||||
|
|
||||||
testModel.timestamp = 12321;
|
testModel.timestamp = 12321;
|
||||||
|
testModel.timerState = 'started';
|
||||||
invokeWatch('model.modified', 1);
|
invokeWatch('model.modified', 1);
|
||||||
expect(controller.buttonCssClass()).toEqual("icon-refresh");
|
expect(controller.buttonCssClass()).toEqual("icon-pause");
|
||||||
expect(controller.buttonText()).toEqual("Restart");
|
expect(controller.buttonText()).toEqual("Pause");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("performs correct start/restart action on click", function () {
|
it("performs correct start/pause/stop action on click", function () {
|
||||||
|
//test start
|
||||||
invokeWatch('domainObject', mockDomainObject);
|
invokeWatch('domainObject', mockDomainObject);
|
||||||
expect(mockStart.perform).not.toHaveBeenCalled();
|
expect(mockStart.perform).not.toHaveBeenCalled();
|
||||||
controller.clickButton();
|
controller.clickButton();
|
||||||
expect(mockStart.perform).toHaveBeenCalled();
|
expect(mockStart.perform).toHaveBeenCalled();
|
||||||
|
|
||||||
|
//test pause
|
||||||
testModel.timestamp = 12321;
|
testModel.timestamp = 12321;
|
||||||
|
testModel.timerState = 'started';
|
||||||
invokeWatch('model.modified', 1);
|
invokeWatch('model.modified', 1);
|
||||||
expect(mockRestart.perform).not.toHaveBeenCalled();
|
expect(mockPause.perform).not.toHaveBeenCalled();
|
||||||
controller.clickButton();
|
controller.clickButton();
|
||||||
expect(mockRestart.perform).toHaveBeenCalled();
|
expect(mockPause.perform).toHaveBeenCalled();
|
||||||
|
|
||||||
|
//test stop
|
||||||
|
expect(mockStop.perform).not.toHaveBeenCalled();
|
||||||
|
controller.clickStopButton();
|
||||||
|
expect(mockStop.perform).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("stops requesting animation frames when destroyed", function () {
|
it("stops requesting animation frames when destroyed", function () {
|
||||||
|
@ -19,7 +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.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
/* global console*/
|
|
||||||
|
|
||||||
define(
|
define(
|
||||||
[
|
[
|
||||||
@ -98,7 +97,6 @@ define(
|
|||||||
this.validation = new TimeConductorValidation(this.timeAPI);
|
this.validation = new TimeConductorValidation(this.timeAPI);
|
||||||
this.formatService = formatService;
|
this.formatService = formatService;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.clocksForTimeSystem = {};
|
|
||||||
this.timeSystemsForClocks = {};
|
this.timeSystemsForClocks = {};
|
||||||
this.$scope.timeSystemModel = {};
|
this.$scope.timeSystemModel = {};
|
||||||
this.$scope.boundsModel = {};
|
this.$scope.boundsModel = {};
|
||||||
@ -171,23 +169,20 @@ define(
|
|||||||
TimeConductorController.prototype.selectMenuOption = function (newOption, oldOption) {
|
TimeConductorController.prototype.selectMenuOption = function (newOption, oldOption) {
|
||||||
if (newOption !== oldOption) {
|
if (newOption !== oldOption) {
|
||||||
var config = this.getConfig(this.timeAPI.timeSystem(), newOption.clock);
|
var config = this.getConfig(this.timeAPI.timeSystem(), newOption.clock);
|
||||||
|
if (!config) {
|
||||||
/*
|
// Clock does not support this timeSystem, fallback to first
|
||||||
* If there is no configuration defined for the selected clock
|
// option provided for clock.
|
||||||
* and time system default to the first time system that
|
config = this.config.menuOptions.filter(function (menuOption) {
|
||||||
* configuration is available for.
|
return menuOption.clock === (newOption.clock && newOption.clock.key);
|
||||||
*/
|
})[0];
|
||||||
if (config === undefined) {
|
|
||||||
var timeSystem = this.timeSystemsForClocks[newOption.key][0];
|
|
||||||
this.$scope.timeSystemModel.selected = timeSystem;
|
|
||||||
this.setTimeSystemFromView(timeSystem.key);
|
|
||||||
config = this.getConfig(timeSystem, newOption.clock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newOption.key === 'fixed') {
|
if (config.clock) {
|
||||||
this.timeAPI.stopClock();
|
this.timeAPI.clock(config.clock, config.clockOffsets);
|
||||||
|
this.timeAPI.timeSystem(config.timeSystem);
|
||||||
} else {
|
} else {
|
||||||
this.timeAPI.clock(newOption.key, config.clockOffsets);
|
this.timeAPI.stopClock();
|
||||||
|
this.timeAPI.timeSystem(config.timeSystem, config.bounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -209,28 +204,22 @@ define(
|
|||||||
cssClass: 'icon-calendar'
|
cssClass: 'icon-calendar'
|
||||||
}];
|
}];
|
||||||
var clocks = {};
|
var clocks = {};
|
||||||
var clocksForTimeSystem = this.clocksForTimeSystem;
|
|
||||||
var timeSystemsForClocks = this.timeSystemsForClocks;
|
var timeSystemsForClocks = this.timeSystemsForClocks;
|
||||||
|
|
||||||
(config.menuOptions || []).forEach(function (menuOption) {
|
(config.menuOptions || []).forEach(function (menuOption) {
|
||||||
var clock = this.getClock(menuOption.clock);
|
|
||||||
var clockKey = menuOption.clock || 'fixed';
|
var clockKey = menuOption.clock || 'fixed';
|
||||||
|
var clock = this.getClock(clockKey);
|
||||||
|
|
||||||
|
if (clock !== undefined) {
|
||||||
|
clocks[clock.key] = clock;
|
||||||
|
}
|
||||||
|
|
||||||
var timeSystem = this.timeSystems[menuOption.timeSystem];
|
var timeSystem = this.timeSystems[menuOption.timeSystem];
|
||||||
if (timeSystem !== undefined) {
|
if (timeSystem !== undefined) {
|
||||||
if (clock !== undefined) {
|
|
||||||
// Use an associative array to built a set of unique
|
|
||||||
// clocks
|
|
||||||
clocks[clock.key] = clock;
|
|
||||||
clocksForTimeSystem[timeSystem.key] = clocksForTimeSystem[timeSystem.key] || [];
|
|
||||||
clocksForTimeSystem[timeSystem.key].push(clock);
|
|
||||||
}
|
|
||||||
timeSystemsForClocks[clockKey] = timeSystemsForClocks[clockKey] || [];
|
timeSystemsForClocks[clockKey] = timeSystemsForClocks[clockKey] || [];
|
||||||
timeSystemsForClocks[clockKey].push(timeSystem);
|
timeSystemsForClocks[clockKey].push(timeSystem);
|
||||||
} else if (menuOption.clock !== undefined) {
|
|
||||||
console.error('Unknown clock "' + clockKey + '", has it been registered?');
|
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}, this);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Populate the clocks menu with metadata from the available clocks
|
* Populate the clocks menu with metadata from the available clocks
|
||||||
@ -362,10 +351,10 @@ define(
|
|||||||
* @param {Clock} clock
|
* @param {Clock} clock
|
||||||
*/
|
*/
|
||||||
TimeConductorController.prototype.setViewFromClock = function (clock) {
|
TimeConductorController.prototype.setViewFromClock = function (clock) {
|
||||||
var newClockKey = clock && clock.key;
|
var newClockKey = clock ? clock.key : 'fixed';
|
||||||
var timeSystems = this.timeSystemsForClocks[newClockKey || 'fixed'];
|
var timeSystems = this.timeSystemsForClocks[newClockKey];
|
||||||
var menuOption = this.menu.options.filter(function (option) {
|
var menuOption = this.menu.options.filter(function (option) {
|
||||||
return option.key === (newClockKey || 'fixed');
|
return option.key === (newClockKey);
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
this.menu.selected = menuOption;
|
this.menu.selected = menuOption;
|
||||||
@ -394,9 +383,7 @@ define(
|
|||||||
|
|
||||||
this.isFixed = clock === undefined;
|
this.isFixed = clock === undefined;
|
||||||
|
|
||||||
if (clock !== undefined) {
|
if (clock === undefined) {
|
||||||
this.setViewFromOffsets(this.timeAPI.clockOffsets());
|
|
||||||
} else {
|
|
||||||
this.setViewFromBounds(this.timeAPI.bounds());
|
this.setViewFromBounds(this.timeAPI.bounds());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,34 +404,15 @@ define(
|
|||||||
var clock = this.menu.selected.clock;
|
var clock = this.menu.selected.clock;
|
||||||
var timeSystem = this.timeSystems[key];
|
var timeSystem = this.timeSystems[key];
|
||||||
var config = this.getConfig(timeSystem, clock);
|
var config = this.getConfig(timeSystem, clock);
|
||||||
var bounds;
|
|
||||||
|
|
||||||
this.$scope.timeSystemModel.selected = timeSystem;
|
this.$scope.timeSystemModel.selected = timeSystem;
|
||||||
|
this.$scope.timeSystemModel.format = timeSystem.timeFormat;
|
||||||
|
|
||||||
/**
|
|
||||||
* Time systems require default bounds to be specified when they
|
|
||||||
* are set
|
|
||||||
*/
|
|
||||||
if (clock === undefined) {
|
if (clock === undefined) {
|
||||||
bounds = config.bounds;
|
this.timeAPI.timeSystem(timeSystem, config.bounds);
|
||||||
this.timeAPI.timeSystem(timeSystem, bounds);
|
|
||||||
} else {
|
} else {
|
||||||
bounds = {
|
this.timeAPI.clock(clock, config.clockOffsets);
|
||||||
start: clock.currentValue() + config.clockOffsets.start,
|
this.timeAPI.timeSystem(timeSystem);
|
||||||
end: clock.currentValue() + config.clockOffsets.end
|
|
||||||
};
|
|
||||||
//Has time system change resulted in offsets change (based on config)?
|
|
||||||
this.timeAPI.timeSystem(timeSystem, bounds);
|
|
||||||
var configOffsets = config.clockOffsets;
|
|
||||||
var apiOffsets = this.timeAPI.clockOffsets();
|
|
||||||
|
|
||||||
//Checking if a clock is actually set on the Time API before
|
|
||||||
// trying to set offsets.
|
|
||||||
if (this.timeAPI.clock() !== undefined &&
|
|
||||||
(configOffsets.start !== apiOffsets.start ||
|
|
||||||
configOffsets.end !== apiOffsets.end)) {
|
|
||||||
this.timeAPI.clockOffsets(configOffsets);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,7 +53,10 @@ define([
|
|||||||
"policies": [
|
"policies": [
|
||||||
{
|
{
|
||||||
"category": "view",
|
"category": "view",
|
||||||
"implementation": ImageryViewPolicy
|
"implementation": ImageryViewPolicy,
|
||||||
|
"depends": [
|
||||||
|
"openmct"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"controllers": [
|
"controllers": [
|
||||||
@ -62,7 +65,7 @@ define([
|
|||||||
"implementation": ImageryController,
|
"implementation": ImageryController,
|
||||||
"depends": [
|
"depends": [
|
||||||
"$scope",
|
"$scope",
|
||||||
"telemetryHandler"
|
"openmct"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -34,10 +34,8 @@
|
|||||||
<div class="l-image-main-controlbar flex-elem l-flex-row">
|
<div class="l-image-main-controlbar flex-elem l-flex-row">
|
||||||
<div class="left flex-elem grows">
|
<div class="left flex-elem grows">
|
||||||
<a class="s-button show-thumbs sm hidden icon-thumbs-strip"
|
<a class="s-button show-thumbs sm hidden icon-thumbs-strip"
|
||||||
ng-click="showThumbsBubble = (showThumbsBubble)? false:true"></a>
|
ng-click="showThumbsBubble = (showThumbsBubble) ? false:true"></a>
|
||||||
<span class="l-timezone">{{imagery.getZone()}}</span>
|
|
||||||
<span class="l-time">{{imagery.getTime()}}</span>
|
<span class="l-time">{{imagery.getTime()}}</span>
|
||||||
<span class="l-date">{{imagery.getDate()}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="right flex-elem">
|
<div class="right flex-elem">
|
||||||
<a class="s-button pause-play"
|
<a class="s-button pause-play"
|
||||||
|
@ -28,74 +28,82 @@ define(
|
|||||||
['moment'],
|
['moment'],
|
||||||
function (moment) {
|
function (moment) {
|
||||||
|
|
||||||
var DATE_FORMAT = "YYYY-MM-DD",
|
|
||||||
TIME_FORMAT = "HH:mm:ss.SSS";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for the "Imagery" view of a domain object which
|
* Controller for the "Imagery" view of a domain object which
|
||||||
* provides image telemetry.
|
* provides image telemetry.
|
||||||
* @constructor
|
* @constructor
|
||||||
* @memberof platform/features/imagery
|
* @memberof platform/features/imagery
|
||||||
*/
|
*/
|
||||||
function ImageryController($scope, telemetryHandler) {
|
function ImageryController($scope, openmct) {
|
||||||
var self = this;
|
this.$scope = $scope;
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.date = "";
|
||||||
|
this.time = "";
|
||||||
|
this.zone = "";
|
||||||
|
this.imageUrl = "";
|
||||||
|
|
||||||
function releaseSubscription() {
|
this.$scope.filters = {
|
||||||
if (self.handle) {
|
|
||||||
self.handle.unsubscribe();
|
|
||||||
self.handle = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateValuesCallback() {
|
|
||||||
return self.updateValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new subscription; telemetrySubscriber gets
|
|
||||||
// to do the meaningful work here.
|
|
||||||
function subscribe(domainObject) {
|
|
||||||
releaseSubscription();
|
|
||||||
self.date = "";
|
|
||||||
self.time = "";
|
|
||||||
self.zone = "";
|
|
||||||
self.imageUrl = "";
|
|
||||||
self.handle = domainObject && telemetryHandler.handle(
|
|
||||||
domainObject,
|
|
||||||
updateValuesCallback,
|
|
||||||
true // Lossless
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.filters = {
|
|
||||||
brightness: 100,
|
brightness: 100,
|
||||||
contrast: 100
|
contrast: 100
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.subscribe = this.subscribe.bind(this);
|
||||||
|
this.stopListening = this.stopListening.bind(this);
|
||||||
|
this.updateValues = this.updateValues.bind(this);
|
||||||
|
|
||||||
// Subscribe to telemetry when a domain object becomes available
|
// Subscribe to telemetry when a domain object becomes available
|
||||||
$scope.$watch('domainObject', subscribe);
|
this.subscribe(this.$scope.domainObject);
|
||||||
|
|
||||||
// Unsubscribe when the plot is destroyed
|
// Unsubscribe when the plot is destroyed
|
||||||
$scope.$on("$destroy", releaseSubscription);
|
this.$scope.$on("$destroy", this.stopListening);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update displayable values to reflect latest image telemetry
|
ImageryController.prototype.subscribe = function (domainObject) {
|
||||||
ImageryController.prototype.updateValues = function () {
|
this.date = "";
|
||||||
var imageObject =
|
this.imageUrl = "";
|
||||||
this.handle && this.handle.getTelemetryObjects()[0],
|
this.openmct.objects.get(domainObject.getId())
|
||||||
timestamp,
|
.then(function (object) {
|
||||||
m;
|
this.domainObject = object;
|
||||||
if (imageObject && !this.isPaused) {
|
var metadata = this.openmct
|
||||||
timestamp = this.handle.getDomainValue(imageObject);
|
.telemetry
|
||||||
m = timestamp !== undefined ?
|
.getMetadata(this.domainObject);
|
||||||
moment.utc(timestamp) :
|
var timeKey = this.openmct.time.timeSystem().key;
|
||||||
undefined;
|
this.timeFormat = this.openmct
|
||||||
this.date = m ? m.format(DATE_FORMAT) : "";
|
.telemetry
|
||||||
this.time = m ? m.format(TIME_FORMAT) : "";
|
.getValueFormatter(metadata.value(timeKey));
|
||||||
this.zone = m ? "UTC" : "";
|
this.imageFormat = this.openmct
|
||||||
this.imageUrl = this.handle.getRangeValue(imageObject);
|
.telemetry
|
||||||
|
.getValueFormatter(metadata.valuesForHints(['image'])[0]);
|
||||||
|
this.unsubscribe = this.openmct.telemetry
|
||||||
|
.subscribe(this.domainObject, this.updateValues);
|
||||||
|
this.openmct.telemetry
|
||||||
|
.request(this.domainObject, {
|
||||||
|
strategy: 'latest',
|
||||||
|
size: 1
|
||||||
|
})
|
||||||
|
.then(function (values) {
|
||||||
|
this.updateValues(values[0]);
|
||||||
|
}.bind(this));
|
||||||
|
}.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
ImageryController.prototype.stopListening = function () {
|
||||||
|
if (this.unsubscribe) {
|
||||||
|
this.unsubscribe();
|
||||||
|
delete this.unsubscribe;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Update displayable values to reflect latest image telemetry
|
||||||
|
ImageryController.prototype.updateValues = function (datum) {
|
||||||
|
if (this.isPaused) {
|
||||||
|
this.nextDatum = datum;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.time = this.timeFormat.format(datum);
|
||||||
|
this.imageUrl = this.imageFormat.format(datum);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the time portion (hours, minutes, seconds) of the
|
* Get the time portion (hours, minutes, seconds) of the
|
||||||
* timestamp associated with the incoming image telemetry.
|
* timestamp associated with the incoming image telemetry.
|
||||||
@ -105,25 +113,6 @@ define(
|
|||||||
return this.time;
|
return this.time;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the date portion (month, year) of the
|
|
||||||
* timestamp associated with the incoming image telemetry.
|
|
||||||
* @returns {string} the date
|
|
||||||
*/
|
|
||||||
ImageryController.prototype.getDate = function () {
|
|
||||||
return this.date;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the time zone for the displayed time/date corresponding
|
|
||||||
* to the timestamp associated with the incoming image
|
|
||||||
* telemetry.
|
|
||||||
* @returns {string} the time
|
|
||||||
*/
|
|
||||||
ImageryController.prototype.getZone = function () {
|
|
||||||
return this.zone;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the URL of the image telemetry to display.
|
* Get the URL of the image telemetry to display.
|
||||||
* @returns {string} URL for telemetry image
|
* @returns {string} URL for telemetry image
|
||||||
@ -141,8 +130,10 @@ define(
|
|||||||
ImageryController.prototype.paused = function (state) {
|
ImageryController.prototype.paused = function (state) {
|
||||||
if (arguments.length > 0 && state !== this.isPaused) {
|
if (arguments.length > 0 && state !== this.isPaused) {
|
||||||
this.isPaused = state;
|
this.isPaused = state;
|
||||||
// Switch to latest image
|
if (this.nextDatum) {
|
||||||
this.updateValues();
|
this.updateValues(this.nextDatum);
|
||||||
|
delete this.nextDatum;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this.isPaused;
|
return this.isPaused;
|
||||||
};
|
};
|
||||||
@ -150,4 +141,3 @@ define(
|
|||||||
return ImageryController;
|
return ImageryController;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -20,39 +20,40 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define([
|
||||||
function () {
|
'../../../../../src/api/objects/object-utils'
|
||||||
|
], function (
|
||||||
/**
|
objectUtils
|
||||||
* Policy preventing the Imagery view from being made available for
|
) {
|
||||||
* domain objects which do not have associated image telemetry.
|
/**
|
||||||
* @implements {Policy.<View, DomainObject>}
|
* Policy preventing the Imagery view from being made available for
|
||||||
* @constructor
|
* domain objects which do not have associated image telemetry.
|
||||||
*/
|
* @implements {Policy.<View, DomainObject>}
|
||||||
function ImageryViewPolicy() {
|
* @constructor
|
||||||
}
|
*/
|
||||||
|
function ImageryViewPolicy(openmct) {
|
||||||
function hasImageTelemetry(domainObject) {
|
this.openmct = openmct;
|
||||||
var telemetry = domainObject &&
|
|
||||||
domainObject.getCapability('telemetry'),
|
|
||||||
metadata = telemetry ? telemetry.getMetadata() : {},
|
|
||||||
ranges = metadata.ranges || [];
|
|
||||||
|
|
||||||
return ranges.some(function (range) {
|
|
||||||
return range.format === 'imageUrl' ||
|
|
||||||
range.format === 'image';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageryViewPolicy.prototype.allow = function (view, domainObject) {
|
|
||||||
if (view.key === 'imagery') {
|
|
||||||
return hasImageTelemetry(domainObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
return ImageryViewPolicy;
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
ImageryViewPolicy.prototype.hasImageTelemetry = function (domainObject) {
|
||||||
|
var newDO = objectUtils.toNewFormat(
|
||||||
|
domainObject.getModel(),
|
||||||
|
domainObject.getId()
|
||||||
|
);
|
||||||
|
|
||||||
|
var metadata = this.openmct.telemetry.getMetadata(newDO);
|
||||||
|
var values = metadata.valuesForHints(['image']);
|
||||||
|
return values.length >= 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
ImageryViewPolicy.prototype.allow = function (view, domainObject) {
|
||||||
|
if (view.key === 'imagery') {
|
||||||
|
return this.hasImageTelemetry(domainObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return ImageryViewPolicy;
|
||||||
|
});
|
||||||
|
|
||||||
|
@ -25,137 +25,161 @@ define(
|
|||||||
function (ImageryController) {
|
function (ImageryController) {
|
||||||
|
|
||||||
describe("The Imagery controller", function () {
|
describe("The Imagery controller", function () {
|
||||||
var mockScope,
|
var $scope,
|
||||||
mockTelemetryHandler,
|
openmct,
|
||||||
mockHandle,
|
oldDomainObject,
|
||||||
mockDomainObject,
|
newDomainObject,
|
||||||
controller;
|
unsubscribe,
|
||||||
|
metadata,
|
||||||
function invokeWatch(expr, value) {
|
prefix,
|
||||||
mockScope.$watch.calls.forEach(function (call) {
|
controller,
|
||||||
if (call.args[0] === expr) {
|
hasLoaded;
|
||||||
call.args[1](value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockScope = jasmine.createSpyObj('$scope', ['$on', '$watch']);
|
$scope = jasmine.createSpyObj('$scope', ['$on', '$watch']);
|
||||||
mockTelemetryHandler = jasmine.createSpyObj(
|
oldDomainObject = jasmine.createSpyObj(
|
||||||
'telemetryHandler',
|
|
||||||
['handle']
|
|
||||||
);
|
|
||||||
mockHandle = jasmine.createSpyObj(
|
|
||||||
'handle',
|
|
||||||
[
|
|
||||||
'getDomainValue',
|
|
||||||
'getRangeValue',
|
|
||||||
'getTelemetryObjects',
|
|
||||||
'unsubscribe'
|
|
||||||
]
|
|
||||||
);
|
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
|
||||||
'domainObject',
|
'domainObject',
|
||||||
['getId', 'getModel', 'getCapability']
|
['getId']
|
||||||
);
|
);
|
||||||
|
newDomainObject = { name: 'foo' };
|
||||||
|
|
||||||
mockTelemetryHandler.handle.andReturn(mockHandle);
|
oldDomainObject.getId.andReturn('testID');
|
||||||
mockHandle.getTelemetryObjects.andReturn([mockDomainObject]);
|
openmct = {
|
||||||
|
objects: jasmine.createSpyObj('objectAPI', [
|
||||||
controller = new ImageryController(
|
'get'
|
||||||
mockScope,
|
]),
|
||||||
mockTelemetryHandler
|
time: jasmine.createSpyObj('timeAPI', [
|
||||||
);
|
'timeSystem'
|
||||||
invokeWatch('domainObject', mockDomainObject);
|
]),
|
||||||
});
|
telemetry: jasmine.createSpyObj('telemetryAPI', [
|
||||||
|
'subscribe',
|
||||||
it("unsubscribes when scope is destroyed", function () {
|
'request',
|
||||||
expect(mockHandle.unsubscribe).not.toHaveBeenCalled();
|
'getValueFormatter',
|
||||||
|
'getMetadata'
|
||||||
// Find the $destroy listener and call it
|
])
|
||||||
mockScope.$on.calls.forEach(function (call) {
|
};
|
||||||
if (call.args[0] === '$destroy') {
|
metadata = jasmine.createSpyObj('metadata', [
|
||||||
call.args[1]();
|
'value',
|
||||||
}
|
'valuesForHints'
|
||||||
|
]);
|
||||||
|
prefix = "formatted ";
|
||||||
|
unsubscribe = jasmine.createSpy('unsubscribe');
|
||||||
|
openmct.telemetry.subscribe.andReturn(unsubscribe);
|
||||||
|
openmct.time.timeSystem.andReturn({
|
||||||
|
key: 'testKey'
|
||||||
});
|
});
|
||||||
expect(mockHandle.unsubscribe).toHaveBeenCalled();
|
$scope.domainObject = oldDomainObject;
|
||||||
|
openmct.objects.get.andReturn(Promise.resolve(newDomainObject));
|
||||||
|
openmct.telemetry.getMetadata.andReturn(metadata);
|
||||||
|
openmct.telemetry.getValueFormatter.andCallFake(function (property) {
|
||||||
|
var formatter =
|
||||||
|
jasmine.createSpyObj("formatter-" + property, ['format']);
|
||||||
|
var isTime = (property === "timestamp");
|
||||||
|
formatter.format.andCallFake(function (datum) {
|
||||||
|
return (isTime ? prefix : "") + datum[property];
|
||||||
|
});
|
||||||
|
return formatter;
|
||||||
|
});
|
||||||
|
hasLoaded = false;
|
||||||
|
openmct.telemetry.request.andCallFake(function () {
|
||||||
|
setTimeout(function () {
|
||||||
|
hasLoaded = true;
|
||||||
|
}, 10);
|
||||||
|
return Promise.resolve([{
|
||||||
|
timestamp: 1434600258123,
|
||||||
|
value: 'some/url'
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
metadata.value.andReturn("timestamp");
|
||||||
|
metadata.valuesForHints.andReturn(["value"]);
|
||||||
|
|
||||||
|
controller = new ImageryController($scope, openmct);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("exposes the latest telemetry values", function () {
|
describe("when loaded", function () {
|
||||||
// 06/18/2015 4:04am UTC
|
var callback;
|
||||||
var testTimestamp = 1434600258123,
|
beforeEach(function () {
|
||||||
testUrl = "some/url",
|
waitsFor(function () {
|
||||||
nextTimestamp = 1434600259456, // 4:05.456
|
return hasLoaded;
|
||||||
nextUrl = "some/other/url";
|
}, 500);
|
||||||
|
|
||||||
mockHandle.getDomainValue.andReturn(testTimestamp);
|
|
||||||
mockHandle.getRangeValue.andReturn(testUrl);
|
|
||||||
|
|
||||||
// Call the subscription listener
|
runs(function () {
|
||||||
mockTelemetryHandler.handle.mostRecentCall.args[1]();
|
callback =
|
||||||
|
openmct.telemetry.subscribe.mostRecentCall.args[1];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
expect(controller.getTime()).toEqual("04:04:18.123");
|
|
||||||
expect(controller.getDate()).toEqual("2015-06-18");
|
|
||||||
expect(controller.getZone()).toEqual("UTC");
|
|
||||||
expect(controller.getImageUrl()).toEqual(testUrl);
|
|
||||||
|
|
||||||
mockHandle.getDomainValue.andReturn(nextTimestamp);
|
it("uses LAD telemetry", function () {
|
||||||
mockHandle.getRangeValue.andReturn(nextUrl);
|
expect(openmct.telemetry.request).toHaveBeenCalledWith(
|
||||||
mockTelemetryHandler.handle.mostRecentCall.args[1]();
|
newDomainObject,
|
||||||
|
{
|
||||||
|
strategy: 'latest',
|
||||||
|
size: 1
|
||||||
|
}
|
||||||
|
);
|
||||||
|
expect(controller.getTime()).toEqual(prefix + 1434600258123);
|
||||||
|
expect(controller.getImageUrl()).toEqual('some/url');
|
||||||
|
});
|
||||||
|
|
||||||
expect(controller.getTime()).toEqual("04:04:19.456");
|
|
||||||
expect(controller.getDate()).toEqual("2015-06-18");
|
|
||||||
expect(controller.getZone()).toEqual("UTC");
|
|
||||||
expect(controller.getImageUrl()).toEqual(nextUrl);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows updates to be paused", function () {
|
it("exposes the latest telemetry values", function () {
|
||||||
// 06/18/2015 4:04am UTC
|
callback({
|
||||||
var testTimestamp = 1434600258123,
|
timestamp: 1434600259456,
|
||||||
testUrl = "some/url",
|
value: "some/other/url"
|
||||||
nextTimestamp = 1434600259456, // 4:05.456
|
});
|
||||||
nextUrl = "some/other/url";
|
|
||||||
|
|
||||||
// As above, but pause in between. Expect details
|
expect(controller.getTime()).toEqual(prefix + 1434600259456);
|
||||||
// not to change this time
|
expect(controller.getImageUrl()).toEqual("some/other/url");
|
||||||
|
});
|
||||||
|
|
||||||
mockHandle.getDomainValue.andReturn(testTimestamp);
|
it("allows updates to be paused and unpaused", function () {
|
||||||
mockHandle.getRangeValue.andReturn(testUrl);
|
var newTimestamp = 1434600259456,
|
||||||
|
newUrl = "some/other/url",
|
||||||
|
initialTimestamp = controller.getTime(),
|
||||||
|
initialUrl = controller.getImageUrl();
|
||||||
|
|
||||||
// Call the subscription listener
|
expect(initialTimestamp).not.toBe(prefix + newTimestamp);
|
||||||
mockTelemetryHandler.handle.mostRecentCall.args[1]();
|
expect(initialUrl).not.toBe(newUrl);
|
||||||
|
expect(controller.paused()).toBeFalsy();
|
||||||
|
|
||||||
expect(controller.getTime()).toEqual("04:04:18.123");
|
controller.paused(true);
|
||||||
expect(controller.getDate()).toEqual("2015-06-18");
|
expect(controller.paused()).toBeTruthy();
|
||||||
expect(controller.getZone()).toEqual("UTC");
|
callback({ timestamp: newTimestamp, value: newUrl });
|
||||||
expect(controller.getImageUrl()).toEqual(testUrl);
|
|
||||||
|
|
||||||
expect(controller.paused()).toBeFalsy();
|
expect(controller.getTime()).toEqual(initialTimestamp);
|
||||||
controller.paused(true); // Pause!
|
expect(controller.getImageUrl()).toEqual(initialUrl);
|
||||||
expect(controller.paused()).toBeTruthy();
|
|
||||||
|
|
||||||
mockHandle.getDomainValue.andReturn(nextTimestamp);
|
controller.paused(false);
|
||||||
mockHandle.getRangeValue.andReturn(nextUrl);
|
expect(controller.paused()).toBeFalsy();
|
||||||
mockTelemetryHandler.handle.mostRecentCall.args[1]();
|
expect(controller.getTime()).toEqual(prefix + newTimestamp);
|
||||||
|
expect(controller.getImageUrl()).toEqual(newUrl);
|
||||||
|
});
|
||||||
|
|
||||||
expect(controller.getTime()).toEqual("04:04:18.123");
|
it("subscribes to telemetry", function () {
|
||||||
expect(controller.getDate()).toEqual("2015-06-18");
|
expect(openmct.telemetry.subscribe).toHaveBeenCalledWith(
|
||||||
expect(controller.getZone()).toEqual("UTC");
|
newDomainObject,
|
||||||
expect(controller.getImageUrl()).toEqual(testUrl);
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("unsubscribes when scope is destroyed", function () {
|
||||||
|
expect(unsubscribe).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
$scope.$on.calls.forEach(function (call) {
|
||||||
|
if (call.args[0] === '$destroy') {
|
||||||
|
call.args[1]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(unsubscribe).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("initially shows an empty string for date/time", function () {
|
it("initially shows an empty string for date/time", function () {
|
||||||
// Call the subscription listener while domain/range
|
|
||||||
// values are still undefined
|
|
||||||
mockHandle.getDomainValue.andReturn(undefined);
|
|
||||||
mockHandle.getRangeValue.andReturn(undefined);
|
|
||||||
mockTelemetryHandler.handle.mostRecentCall.args[1]();
|
|
||||||
|
|
||||||
// Should have empty strings for date/time/zone
|
|
||||||
expect(controller.getTime()).toEqual("");
|
expect(controller.getTime()).toEqual("");
|
||||||
expect(controller.getDate()).toEqual("");
|
expect(controller.getImageUrl()).toEqual("");
|
||||||
expect(controller.getZone()).toEqual("");
|
|
||||||
expect(controller.getImageUrl()).toBeUndefined();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -26,14 +26,17 @@ define(
|
|||||||
|
|
||||||
describe("Imagery view policy", function () {
|
describe("Imagery view policy", function () {
|
||||||
var testView,
|
var testView,
|
||||||
|
openmct,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockTelemetry,
|
mockTelemetry,
|
||||||
testMetadata,
|
mockMetadata,
|
||||||
policy;
|
policy;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
testView = { key: "imagery" };
|
testView = { key: "imagery" };
|
||||||
testMetadata = {};
|
mockMetadata = jasmine.createSpyObj('metadata', [
|
||||||
|
"valuesForHints"
|
||||||
|
]);
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
mockDomainObject = jasmine.createSpyObj(
|
||||||
'domainObject',
|
'domainObject',
|
||||||
['getId', 'getModel', 'getCapability']
|
['getId', 'getModel', 'getCapability']
|
||||||
@ -45,30 +48,33 @@ define(
|
|||||||
mockDomainObject.getCapability.andCallFake(function (c) {
|
mockDomainObject.getCapability.andCallFake(function (c) {
|
||||||
return c === 'telemetry' ? mockTelemetry : undefined;
|
return c === 'telemetry' ? mockTelemetry : undefined;
|
||||||
});
|
});
|
||||||
mockTelemetry.getMetadata.andReturn(testMetadata);
|
mockDomainObject.getId.andReturn("some-id");
|
||||||
|
mockDomainObject.getModel.andReturn({ name: "foo" });
|
||||||
|
mockTelemetry.getMetadata.andReturn(mockMetadata);
|
||||||
|
mockMetadata.valuesForHints.andReturn(["bar"]);
|
||||||
|
|
||||||
policy = new ImageryViewPolicy();
|
openmct = { telemetry: mockTelemetry };
|
||||||
|
|
||||||
|
policy = new ImageryViewPolicy(openmct);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("checks for hints indicating image telemetry", function () {
|
||||||
|
policy.allow(testView, mockDomainObject);
|
||||||
|
expect(mockMetadata.valuesForHints)
|
||||||
|
.toHaveBeenCalledWith(["image"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows the imagery view for domain objects with image telemetry", function () {
|
it("allows the imagery view for domain objects with image telemetry", function () {
|
||||||
testMetadata.ranges = [{ key: "foo", format: "imageUrl" }];
|
|
||||||
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
|
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("disallows the imagery view for domain objects without image telemetry", function () {
|
it("disallows the imagery view for domain objects without image telemetry", function () {
|
||||||
testMetadata.ranges = [{ key: "foo", format: "somethingElse" }];
|
mockMetadata.valuesForHints.andReturn([]);
|
||||||
expect(policy.allow(testView, mockDomainObject)).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("disallows the imagery view for domain objects without telemetry", function () {
|
|
||||||
testMetadata.ranges = [{ key: "foo", format: "imageUrl" }];
|
|
||||||
mockDomainObject.getCapability.andReturn(undefined);
|
|
||||||
expect(policy.allow(testView, mockDomainObject)).toBeFalsy();
|
expect(policy.allow(testView, mockDomainObject)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows other views", function () {
|
it("allows other views", function () {
|
||||||
testView.key = "somethingElse";
|
testView.key = "somethingElse";
|
||||||
testMetadata.ranges = [{ key: "foo", format: "somethingElse" }];
|
|
||||||
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
|
expect(policy.allow(testView, mockDomainObject)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ define([
|
|||||||
"./src/LayoutController",
|
"./src/LayoutController",
|
||||||
"./src/FixedController",
|
"./src/FixedController",
|
||||||
"./src/LayoutCompositionPolicy",
|
"./src/LayoutCompositionPolicy",
|
||||||
|
'./src/MCTTriggerModal',
|
||||||
"text!./res/templates/layout.html",
|
"text!./res/templates/layout.html",
|
||||||
"text!./res/templates/fixed.html",
|
"text!./res/templates/fixed.html",
|
||||||
"text!./res/templates/frame.html",
|
"text!./res/templates/frame.html",
|
||||||
@ -37,6 +38,7 @@ define([
|
|||||||
LayoutController,
|
LayoutController,
|
||||||
FixedController,
|
FixedController,
|
||||||
LayoutCompositionPolicy,
|
LayoutCompositionPolicy,
|
||||||
|
MCTTriggerModal,
|
||||||
layoutTemplate,
|
layoutTemplate,
|
||||||
fixedTemplate,
|
fixedTemplate,
|
||||||
frameTemplate,
|
frameTemplate,
|
||||||
@ -222,6 +224,15 @@ define([
|
|||||||
"template": frameTemplate
|
"template": frameTemplate
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"directives": [
|
||||||
|
{
|
||||||
|
"key": "mctTriggerModal",
|
||||||
|
"implementation": MCTTriggerModal,
|
||||||
|
"depends": [
|
||||||
|
"$document"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"controllers": [
|
"controllers": [
|
||||||
{
|
{
|
||||||
"key": "LayoutController",
|
"key": "LayoutController",
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="frame frame-template abs">
|
<div class="frame frame-template abs">
|
||||||
<div class="abs object-top-bar l-flex-row">
|
<div class="abs object-browse-bar l-flex-row">
|
||||||
<div class="left flex-elem l-flex-row grows">
|
<div class="left flex-elem l-flex-row grows">
|
||||||
<mct-representation
|
<mct-representation
|
||||||
key="'object-header'"
|
key="'object-header'"
|
||||||
|
137
platform/features/layout/src/MCTTriggerModal.js
Normal file
137
platform/features/layout/src/MCTTriggerModal.js
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2016, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define([
|
||||||
|
'zepto'
|
||||||
|
], function (
|
||||||
|
$
|
||||||
|
) {
|
||||||
|
|
||||||
|
var OVERLAY_TEMPLATE = '' +
|
||||||
|
'<div class="abs overlay l-large-view">' +
|
||||||
|
' <div class="abs blocker"></div>' +
|
||||||
|
' <div class="abs outer-holder">' +
|
||||||
|
' <a class="close icon-x-in-circle"></a>' +
|
||||||
|
' <div class="abs inner-holder l-flex-col">' +
|
||||||
|
' <div class="t-contents flex-elem holder grows"></div>' +
|
||||||
|
' <div class="bottom-bar flex-elem holder">' +
|
||||||
|
' <a class="t-done s-button major">Done</a>' +
|
||||||
|
' </div>' +
|
||||||
|
' </div>' +
|
||||||
|
' </div>' +
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCT Trigger Modal is intended for use in only one location: inside the
|
||||||
|
* object-header to allow views in a layout to be popped out in a modal.
|
||||||
|
* Users can close the modal and go back to normal, and everything generally
|
||||||
|
* just works fine.
|
||||||
|
*
|
||||||
|
* This code is sensitive to how our html is constructed-- particularly with
|
||||||
|
* how it locates the the container of an element in a layout. However, it
|
||||||
|
* should be able to handle slight relocations so long as it is always a
|
||||||
|
* descendent of a `.frame` element.
|
||||||
|
*/
|
||||||
|
function MCTTriggerModal($document) {
|
||||||
|
var document = $document[0];
|
||||||
|
|
||||||
|
function link($scope, $element) {
|
||||||
|
var frame = $element.parent();
|
||||||
|
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
if (frame.hasClass('frame')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
frame = frame.parent();
|
||||||
|
}
|
||||||
|
if (!frame.hasClass('frame')) {
|
||||||
|
$element.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame = frame[0];
|
||||||
|
var layoutContainer = frame.parentElement,
|
||||||
|
isOpen = false,
|
||||||
|
toggleOverlay,
|
||||||
|
overlay,
|
||||||
|
closeButton,
|
||||||
|
doneButton,
|
||||||
|
blocker,
|
||||||
|
overlayContainer;
|
||||||
|
|
||||||
|
function openOverlay() {
|
||||||
|
// Remove frame classes from being applied in a non-frame context
|
||||||
|
$(frame).removeClass('frame frame-template');
|
||||||
|
overlay = document.createElement('span');
|
||||||
|
overlay.innerHTML = OVERLAY_TEMPLATE;
|
||||||
|
overlayContainer = overlay.querySelector('.t-contents');
|
||||||
|
closeButton = overlay.querySelector('a.close');
|
||||||
|
closeButton.addEventListener('click', toggleOverlay);
|
||||||
|
doneButton = overlay.querySelector('a.t-done');
|
||||||
|
doneButton.addEventListener('click', toggleOverlay);
|
||||||
|
blocker = overlay.querySelector('.abs.blocker');
|
||||||
|
blocker.addEventListener('click', toggleOverlay);
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
layoutContainer.removeChild(frame);
|
||||||
|
overlayContainer.appendChild(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeOverlay() {
|
||||||
|
$(frame).addClass('frame frame-template');
|
||||||
|
overlayContainer.removeChild(frame);
|
||||||
|
layoutContainer.appendChild(frame);
|
||||||
|
document.body.removeChild(overlay);
|
||||||
|
closeButton.removeEventListener('click', toggleOverlay);
|
||||||
|
closeButton = undefined;
|
||||||
|
doneButton.removeEventListener('click', toggleOverlay);
|
||||||
|
doneButton = undefined;
|
||||||
|
blocker.removeEventListener('click', toggleOverlay);
|
||||||
|
blocker = undefined;
|
||||||
|
overlayContainer = undefined;
|
||||||
|
overlay = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleOverlay = function () {
|
||||||
|
if (!isOpen) {
|
||||||
|
openOverlay();
|
||||||
|
isOpen = true;
|
||||||
|
} else {
|
||||||
|
closeOverlay();
|
||||||
|
isOpen = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$element.on('click', toggleOverlay);
|
||||||
|
$scope.$on('$destroy', function () {
|
||||||
|
$element.off('click', toggleOverlay);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
link: link
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return MCTTriggerModal;
|
||||||
|
|
||||||
|
});
|
122
platform/features/layout/test/MCTTriggerModalSpec.js
Normal file
122
platform/features/layout/test/MCTTriggerModalSpec.js
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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/MCTTriggerModal'
|
||||||
|
], function (
|
||||||
|
MCTTriggerModal
|
||||||
|
) {
|
||||||
|
describe('MCTTriggerModal', function () {
|
||||||
|
var $scope,
|
||||||
|
$element,
|
||||||
|
frame,
|
||||||
|
layoutContainer,
|
||||||
|
$document,
|
||||||
|
mctTriggerModal;
|
||||||
|
|
||||||
|
function makeElement(classes, parentEl) {
|
||||||
|
var elem = jasmine.createSpyObj('element.' + classes.join('.'), [
|
||||||
|
'hasClass',
|
||||||
|
'parent'
|
||||||
|
]);
|
||||||
|
elem.hasClass.andCallFake(function (className) {
|
||||||
|
return classes.indexOf(className) !== -1;
|
||||||
|
});
|
||||||
|
elem.parent.andReturn(parentEl);
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.className = classes.join(' ');
|
||||||
|
parentEl[0].appendChild(div);
|
||||||
|
elem[0] = div;
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
$scope = jasmine.createSpyObj('$scope', ['$on']);
|
||||||
|
$element = jasmine.createSpyObj('$element', [
|
||||||
|
'parent',
|
||||||
|
'remove',
|
||||||
|
'on',
|
||||||
|
'off'
|
||||||
|
]);
|
||||||
|
layoutContainer = document.createElement('div');
|
||||||
|
frame = makeElement(['frame'], [layoutContainer]);
|
||||||
|
var child = makeElement([], frame);
|
||||||
|
for (var i = 0; i < 5; i++) {
|
||||||
|
child = makeElement([], child);
|
||||||
|
}
|
||||||
|
$element.parent.andReturn(child);
|
||||||
|
$document = [jasmine.createSpyObj('document', ['createElement'])];
|
||||||
|
$document[0].body = document.createElement('div');
|
||||||
|
$document[0].createElement.andCallFake(function (tag) {
|
||||||
|
return document.createElement(tag);
|
||||||
|
});
|
||||||
|
|
||||||
|
mctTriggerModal = new MCTTriggerModal($document);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is a directive definition', function () {
|
||||||
|
expect(mctTriggerModal.restrict).toBe('A');
|
||||||
|
expect(mctTriggerModal.link).toEqual(jasmine.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('link', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
mctTriggerModal.link($scope, $element);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('attaches handlers to $element', function () {
|
||||||
|
expect($element.on).toHaveBeenCalledWith(
|
||||||
|
'click',
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cleans up on $scope $destroy', function () {
|
||||||
|
expect($scope.$on).toHaveBeenCalledWith(
|
||||||
|
'$destroy',
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
$scope.$on.mostRecentCall.args[1]();
|
||||||
|
expect($element.off).toHaveBeenCalledWith(
|
||||||
|
'click',
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('opens and closes overlays', function () {
|
||||||
|
[
|
||||||
|
'a.close', 'a.t-done', '.abs.blocker'
|
||||||
|
].forEach(function (selector) {
|
||||||
|
$element.on.mostRecentCall.args[1]();
|
||||||
|
var container = $document[0].body.querySelector('.t-contents');
|
||||||
|
expect(container.children[0]).toBe(frame[0]);
|
||||||
|
expect(layoutContainer.children[0]).not.toBe(frame[0]);
|
||||||
|
$document[0].body.querySelector(selector)
|
||||||
|
.dispatchEvent(new Event('click'));
|
||||||
|
expect(container.children[0]).not.toBe(frame[0]);
|
||||||
|
expect(layoutContainer.children[0]).toBe(frame[0]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
64
platform/features/listview/bundle.js
Normal file
64
platform/features/listview/bundle.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define([
|
||||||
|
'./src/controllers/ListViewController',
|
||||||
|
'./src/directives/MCTGesture',
|
||||||
|
'text!./res/templates/listview.html',
|
||||||
|
'legacyRegistry'
|
||||||
|
], function (
|
||||||
|
ListViewController,
|
||||||
|
MCTGesture,
|
||||||
|
listViewTemplate,
|
||||||
|
legacyRegistry
|
||||||
|
) {
|
||||||
|
legacyRegistry.register("platform/features/listview", {
|
||||||
|
"name": "List View Plugin",
|
||||||
|
"description": "Allows folder contents to be shown in list format",
|
||||||
|
"extensions":
|
||||||
|
{
|
||||||
|
"views": [
|
||||||
|
{
|
||||||
|
"key": "list",
|
||||||
|
"type": "folder",
|
||||||
|
"name": "List",
|
||||||
|
"cssClass": "icon-list-view",
|
||||||
|
"template": listViewTemplate
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"controllers": [
|
||||||
|
{
|
||||||
|
"key": "ListViewController",
|
||||||
|
"implementation": ListViewController,
|
||||||
|
"depends": ["$scope"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"directives": [
|
||||||
|
{
|
||||||
|
"key": "mctGesture",
|
||||||
|
"implementation" : MCTGesture,
|
||||||
|
"depends": ["gestureService"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
88
platform/features/listview/res/templates/listview.html
Normal file
88
platform/features/listview/res/templates/listview.html
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<!--
|
||||||
|
Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
Administration. All rights reserved.
|
||||||
|
|
||||||
|
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
License for the specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
|
||||||
|
Open MCT includes source code licensed under additional open source
|
||||||
|
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
this source code distribution or the Licensing information page available
|
||||||
|
at runtime from the About dialog for additional information.
|
||||||
|
-->
|
||||||
|
<div ng-controller="ListViewController">
|
||||||
|
<table class="list-view">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="sortable"
|
||||||
|
ng-click="reverseSort = (orderByField === 'title')? !reverseSort:false; orderByField='title';"
|
||||||
|
ng-class="{
|
||||||
|
sort: orderByField == 'title',
|
||||||
|
asc: !reverseSort,
|
||||||
|
desc: reverseSort
|
||||||
|
}">
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
|
||||||
|
|
||||||
|
<th class="sortable"
|
||||||
|
ng-click="reverseSort = (orderByField === 'type')? !reverseSort:false; orderByField='type';"
|
||||||
|
ng-class="{
|
||||||
|
sort: orderByField == 'type',
|
||||||
|
asc: !reverseSort,
|
||||||
|
desc: reverseSort
|
||||||
|
}">
|
||||||
|
Type
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th class="sortable"
|
||||||
|
ng-click="reverseSort = (orderByField === 'persisted')? !reverseSort:true; orderByField='persisted';"
|
||||||
|
ng-class="{
|
||||||
|
sort: orderByField == 'persisted',
|
||||||
|
asc: !reverseSort,
|
||||||
|
desc: reverseSort
|
||||||
|
}">
|
||||||
|
Created Date
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th class="sortable"
|
||||||
|
ng-click="reverseSort = (orderByField === 'modified')? !reverseSort:true; orderByField='modified';"
|
||||||
|
ng-class="{
|
||||||
|
sort: orderByField == 'modified',
|
||||||
|
asc: !reverseSort,
|
||||||
|
desc: reverseSort
|
||||||
|
}">
|
||||||
|
Update Date
|
||||||
|
</th>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr ng-repeat="child in children|orderBy:orderByField:reverseSort"
|
||||||
|
mct-gesture="['menu','info']"
|
||||||
|
mct-object="child.asDomainObject"
|
||||||
|
ng-click="child.action.perform('navigate')">
|
||||||
|
<td>
|
||||||
|
<div class="l-flex-row">
|
||||||
|
<span class="flex-elem t-item-icon" ng-class="{ 'l-icon-link': child.location.isLink()}">
|
||||||
|
<span class="t-item-icon-glyph {{child.icon}}"></span>
|
||||||
|
</span>
|
||||||
|
<span class="t-title-label flex-elem grows">{{child.title}}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>{{child.type}}</td>
|
||||||
|
<td>{{child.persisted}}</td>
|
||||||
|
<td>{{child.modified}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
@ -0,0 +1,67 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(function () {
|
||||||
|
function ListViewController($scope) {
|
||||||
|
this.$scope = $scope;
|
||||||
|
$scope.orderByField = 'title';
|
||||||
|
$scope.reverseSort = false;
|
||||||
|
|
||||||
|
this.updateView();
|
||||||
|
var unlisten = $scope.domainObject.getCapability('mutation')
|
||||||
|
.listen(this.updateView.bind(this));
|
||||||
|
|
||||||
|
$scope.$on('$destroy', function () {
|
||||||
|
unlisten();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
ListViewController.prototype.updateView = function () {
|
||||||
|
this.$scope.domainObject.useCapability('composition')
|
||||||
|
.then(function (children) {
|
||||||
|
var formattedChildren = this.formatChildren(children);
|
||||||
|
this.$scope.children = formattedChildren;
|
||||||
|
this.$scope.data = {children: formattedChildren};
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
ListViewController.prototype.formatChildren = function (children) {
|
||||||
|
return children.map(function (child) {
|
||||||
|
return {
|
||||||
|
icon: child.getCapability('type').getCssClass(),
|
||||||
|
title: child.getModel().name,
|
||||||
|
type: child.getCapability('type').getName(),
|
||||||
|
persisted: new Date(
|
||||||
|
child.getModel().persisted
|
||||||
|
).toUTCString(),
|
||||||
|
modified: new Date(
|
||||||
|
child.getModel().modified
|
||||||
|
).toUTCString(),
|
||||||
|
asDomainObject: child,
|
||||||
|
location: child.getCapability('location'),
|
||||||
|
action: child.getCapability('action')
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return ListViewController;
|
||||||
|
});
|
44
platform/features/listview/src/directives/MCTGesture.js
Normal file
44
platform/features/listview/src/directives/MCTGesture.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(function () {
|
||||||
|
function MCTGesture(gestureService) {
|
||||||
|
return {
|
||||||
|
restrict : 'A',
|
||||||
|
scope: {
|
||||||
|
domainObject: '=mctObject'
|
||||||
|
},
|
||||||
|
link : function ($scope, $element, attrs) {
|
||||||
|
var activeGestures = gestureService.attachGestures(
|
||||||
|
$element,
|
||||||
|
$scope.domainObject,
|
||||||
|
$scope.$eval(attrs.mctGesture)
|
||||||
|
);
|
||||||
|
$scope.$on('$destroy', function () {
|
||||||
|
activeGestures.destroy();
|
||||||
|
delete this.activeGestures;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return MCTGesture;
|
||||||
|
});
|
@ -0,0 +1,138 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../../src/controllers/ListViewController"],
|
||||||
|
function (ListViewController) {
|
||||||
|
describe("The Controller for the ListView", function () {
|
||||||
|
var scope,
|
||||||
|
unlistenFunc,
|
||||||
|
domainObject,
|
||||||
|
childObject,
|
||||||
|
controller,
|
||||||
|
childModel,
|
||||||
|
typeCapability,
|
||||||
|
mutationCapability;
|
||||||
|
beforeEach(function () {
|
||||||
|
unlistenFunc = jasmine.createSpy("unlisten");
|
||||||
|
|
||||||
|
mutationCapability = jasmine.createSpyObj(
|
||||||
|
"mutationCapability",
|
||||||
|
["listen"]
|
||||||
|
);
|
||||||
|
mutationCapability.listen.andReturn(unlistenFunc);
|
||||||
|
|
||||||
|
typeCapability = jasmine.createSpyObj(
|
||||||
|
"typeCapability",
|
||||||
|
["getCssClass", "getName"]
|
||||||
|
);
|
||||||
|
typeCapability.getCssClass.andReturn("icon-folder");
|
||||||
|
typeCapability.getName.andReturn("Folder");
|
||||||
|
|
||||||
|
|
||||||
|
childModel = jasmine.createSpyObj(
|
||||||
|
"childModel",
|
||||||
|
["persisted", "modified", "name"]
|
||||||
|
);
|
||||||
|
childModel.persisted = 1496867697303;
|
||||||
|
childModel.modified = 1496867697303;
|
||||||
|
childModel.name = "Battery Charge Status";
|
||||||
|
|
||||||
|
childObject = jasmine.createSpyObj(
|
||||||
|
"childObject",
|
||||||
|
["getModel", "getCapability"]
|
||||||
|
);
|
||||||
|
childObject.getModel.andReturn(
|
||||||
|
childModel
|
||||||
|
);
|
||||||
|
// childObject.getCapability.andReturn(
|
||||||
|
// typeCapability
|
||||||
|
// );
|
||||||
|
childObject.getCapability.andCallFake(function (arg) {
|
||||||
|
if (arg === 'location') {
|
||||||
|
return '';
|
||||||
|
} else if (arg === 'type') {
|
||||||
|
return typeCapability;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
childObject.location = '';
|
||||||
|
|
||||||
|
domainObject = jasmine.createSpyObj(
|
||||||
|
"domainObject",
|
||||||
|
["getCapability", "useCapability"]
|
||||||
|
);
|
||||||
|
domainObject.useCapability.andReturn(
|
||||||
|
Promise.resolve([childObject])
|
||||||
|
);
|
||||||
|
domainObject.getCapability.andReturn(
|
||||||
|
mutationCapability
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
scope = jasmine.createSpyObj(
|
||||||
|
"$scope",
|
||||||
|
["$on"]
|
||||||
|
);
|
||||||
|
scope.domainObject = domainObject;
|
||||||
|
|
||||||
|
controller = new ListViewController(scope);
|
||||||
|
|
||||||
|
waitsFor(function () {
|
||||||
|
return scope.children;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("updates the view", function () {
|
||||||
|
expect(scope.children[0]).toEqual(
|
||||||
|
{
|
||||||
|
icon: "icon-folder",
|
||||||
|
title: "Battery Charge Status",
|
||||||
|
type: "Folder",
|
||||||
|
persisted: "Wed, 07 Jun 2017 20:34:57 GMT",
|
||||||
|
modified: "Wed, 07 Jun 2017 20:34:57 GMT",
|
||||||
|
asDomainObject: childObject,
|
||||||
|
location: ''
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("updates the scope when mutation occurs", function () {
|
||||||
|
domainObject.useCapability.andReturn(
|
||||||
|
Promise.resolve([])
|
||||||
|
);
|
||||||
|
expect(mutationCapability.listen).toHaveBeenCalledWith(jasmine.any(Function));
|
||||||
|
mutationCapability.listen.mostRecentCall.args[0]();
|
||||||
|
waitsFor(function () {
|
||||||
|
return scope.children.length !== 1;
|
||||||
|
});
|
||||||
|
runs(function () {
|
||||||
|
expect(scope.children.length).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("releases listeners on $destroy", function () {
|
||||||
|
expect(scope.$on).toHaveBeenCalledWith('$destroy', jasmine.any(Function));
|
||||||
|
scope.$on.mostRecentCall.args[1]();
|
||||||
|
expect(unlistenFunc).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
86
platform/features/listview/test/directives/MCTGestureSpec.js
Normal file
86
platform/features/listview/test/directives/MCTGestureSpec.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
["../../src/directives/MCTGesture"],
|
||||||
|
function (MCTGesture) {
|
||||||
|
describe("The Gesture Listener for the ListView items", function () {
|
||||||
|
var mctGesture,
|
||||||
|
gestureService,
|
||||||
|
scope,
|
||||||
|
element,
|
||||||
|
attrs,
|
||||||
|
attachedGesture;
|
||||||
|
beforeEach(function () {
|
||||||
|
attachedGesture = jasmine.createSpyObj(
|
||||||
|
"attachedGesture",
|
||||||
|
['destroy']
|
||||||
|
);
|
||||||
|
gestureService = jasmine.createSpyObj(
|
||||||
|
"gestureService",
|
||||||
|
["attachGestures"]
|
||||||
|
);
|
||||||
|
gestureService.attachGestures.andReturn(
|
||||||
|
attachedGesture
|
||||||
|
);
|
||||||
|
mctGesture = MCTGesture(gestureService);
|
||||||
|
});
|
||||||
|
it("creates a directive Object", function () {
|
||||||
|
expect(mctGesture).toBeDefined();
|
||||||
|
});
|
||||||
|
it("has link function that attaches gesture to gestureService",
|
||||||
|
function () {
|
||||||
|
attrs = {
|
||||||
|
mctGesture: "menu,info"
|
||||||
|
};
|
||||||
|
element = jasmine.createSpy("element");
|
||||||
|
scope = jasmine.createSpyObj(
|
||||||
|
"$scope",
|
||||||
|
["$on", "$eval"]
|
||||||
|
);
|
||||||
|
scope.domainObject = "fake domainObject";
|
||||||
|
mctGesture.link(scope, element, attrs);
|
||||||
|
expect(gestureService.attachGestures).toHaveBeenCalled();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
it("release gesture service on $destroy", function () {
|
||||||
|
attrs = {
|
||||||
|
mctGesture: "menu,info"
|
||||||
|
};
|
||||||
|
element = jasmine.createSpy("element");
|
||||||
|
scope = jasmine.createSpyObj(
|
||||||
|
"$scope",
|
||||||
|
["$on", "$eval"]
|
||||||
|
);
|
||||||
|
scope.domainObject = "fake domainObject";
|
||||||
|
mctGesture.link(scope, element, attrs);
|
||||||
|
expect(scope.$on).toHaveBeenCalledWith(
|
||||||
|
'$destroy',
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
scope.$on.mostRecentCall.args[1]();
|
||||||
|
expect(attachedGesture.destroy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -100,9 +100,10 @@ a form appropriate to pass to a `RegExp` constructor.
|
|||||||
|
|
||||||
## Adding controls
|
## Adding controls
|
||||||
|
|
||||||
Four standard control types are included in the forms bundle:
|
These control types are included in the forms bundle:
|
||||||
|
|
||||||
* `textfield`: An area to enter plain text.
|
* `textfield`: A text input to enter plain text.
|
||||||
|
* `numberfield`: A text input to enter numbers.
|
||||||
* `select`: A drop-down list of options.
|
* `select`: A drop-down list of options.
|
||||||
* `checkbox`: A box which may be checked/unchecked.
|
* `checkbox`: A box which may be checked/unchecked.
|
||||||
* `color`: A color picker.
|
* `color`: A color picker.
|
||||||
|
@ -32,6 +32,7 @@ define([
|
|||||||
"text!./res/templates/controls/datetime.html",
|
"text!./res/templates/controls/datetime.html",
|
||||||
"text!./res/templates/controls/select.html",
|
"text!./res/templates/controls/select.html",
|
||||||
"text!./res/templates/controls/textfield.html",
|
"text!./res/templates/controls/textfield.html",
|
||||||
|
"text!./res/templates/controls/numberfield.html",
|
||||||
"text!./res/templates/controls/textarea.html",
|
"text!./res/templates/controls/textarea.html",
|
||||||
"text!./res/templates/controls/button.html",
|
"text!./res/templates/controls/button.html",
|
||||||
"text!./res/templates/controls/color.html",
|
"text!./res/templates/controls/color.html",
|
||||||
@ -52,6 +53,7 @@ define([
|
|||||||
datetimeTemplate,
|
datetimeTemplate,
|
||||||
selectTemplate,
|
selectTemplate,
|
||||||
textfieldTemplate,
|
textfieldTemplate,
|
||||||
|
numberfieldTemplate,
|
||||||
textareaTemplate,
|
textareaTemplate,
|
||||||
buttonTemplate,
|
buttonTemplate,
|
||||||
colorTemplate,
|
colorTemplate,
|
||||||
@ -105,6 +107,10 @@ define([
|
|||||||
"key": "textfield",
|
"key": "textfield",
|
||||||
"template": textfieldTemplate
|
"template": textfieldTemplate
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "numberfield",
|
||||||
|
"template": numberfieldTemplate
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "textarea",
|
"key": "textarea",
|
||||||
"template": textareaTemplate
|
"template": textareaTemplate
|
||||||
|
34
platform/forms/res/templates/controls/numberfield.html
Normal file
34
platform/forms/res/templates/controls/numberfield.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<!--
|
||||||
|
Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
Administration. All rights reserved.
|
||||||
|
|
||||||
|
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
License for the specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
|
||||||
|
Open MCT includes source code licensed under additional open source
|
||||||
|
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
this source code distribution or the Licensing information page available
|
||||||
|
at runtime from the About dialog for additional information.
|
||||||
|
-->
|
||||||
|
<span class='form-control shell'>
|
||||||
|
<span class='field control {{structure.cssClass}}'>
|
||||||
|
<input type="number"
|
||||||
|
ng-required="ngRequired"
|
||||||
|
ng-model="ngModel[field]"
|
||||||
|
ng-blur="ngBlur()"
|
||||||
|
ng-pattern="ngPattern"
|
||||||
|
min="{{structure.min}}"
|
||||||
|
max="{{structure.max}}"
|
||||||
|
step="{{structure.step}}"
|
||||||
|
name="mctControl">
|
||||||
|
</span>
|
||||||
|
</span>
|
@ -100,7 +100,8 @@ define([
|
|||||||
"modelService",
|
"modelService",
|
||||||
"workerService",
|
"workerService",
|
||||||
"topic",
|
"topic",
|
||||||
"GENERIC_SEARCH_ROOTS"
|
"GENERIC_SEARCH_ROOTS",
|
||||||
|
"openmct"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -19,14 +19,14 @@
|
|||||||
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="angular-w" ng-controller="SearchController as controller">
|
<div class="angular-w l-flex-col flex-elem grows holder" ng-controller="SearchController as controller">
|
||||||
<div class="l-flex-col flex-elem grows holder holder-search" ng-controller="SearchMenuController as menuController">
|
<div class="l-flex-col flex-elem grows holder holder-search" ng-controller="SearchMenuController as menuController">
|
||||||
<div class="search-bar flex-elem l-flex-row"
|
<div class="search-bar flex-elem l-flex-row"
|
||||||
ng-controller="ToggleController as toggle"
|
ng-controller="ToggleController as toggle"
|
||||||
ng-class="{ holder: !(ngModel.input === '' || ngModel.input === undefined) }">
|
ng-class="{ holder: !(ngModel.input === '' || ngModel.input === undefined) }">
|
||||||
<div class="holder flex-elem grows">
|
<div class="holder flex-elem grows">
|
||||||
<input class="search-input"
|
<input class="search-input"
|
||||||
type="text"
|
type="text" tabindex="10000"
|
||||||
ng-model="ngModel.input"
|
ng-model="ngModel.input"
|
||||||
ng-keyup="controller.search()"/>
|
ng-keyup="controller.search()"/>
|
||||||
<a class="clear-icon clear-input icon-x-in-circle"
|
<a class="clear-icon clear-input icon-x-in-circle"
|
||||||
|
@ -25,9 +25,11 @@
|
|||||||
* Module defining GenericSearchProvider. Created by shale on 07/16/2015.
|
* Module defining GenericSearchProvider. Created by shale on 07/16/2015.
|
||||||
*/
|
*/
|
||||||
define([
|
define([
|
||||||
|
'../../../../src/api/objects/object-utils',
|
||||||
|
'lodash'
|
||||||
], function (
|
], function (
|
||||||
|
objectUtils,
|
||||||
|
_
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,11 +44,12 @@ define([
|
|||||||
* @param {TopicService} topic the topic service.
|
* @param {TopicService} topic the topic service.
|
||||||
* @param {Array} ROOTS An array of object Ids to begin indexing.
|
* @param {Array} ROOTS An array of object Ids to begin indexing.
|
||||||
*/
|
*/
|
||||||
function GenericSearchProvider($q, $log, modelService, workerService, topic, ROOTS) {
|
function GenericSearchProvider($q, $log, modelService, workerService, topic, ROOTS, openmct) {
|
||||||
var provider = this;
|
var provider = this;
|
||||||
this.$q = $q;
|
this.$q = $q;
|
||||||
this.$log = $log;
|
this.$log = $log;
|
||||||
this.modelService = modelService;
|
this.modelService = modelService;
|
||||||
|
this.openmct = openmct;
|
||||||
|
|
||||||
this.indexedIds = {};
|
this.indexedIds = {};
|
||||||
this.idsToIndex = [];
|
this.idsToIndex = [];
|
||||||
@ -171,17 +174,29 @@ define([
|
|||||||
GenericSearchProvider.prototype.index = function (id, model) {
|
GenericSearchProvider.prototype.index = function (id, model) {
|
||||||
var provider = this;
|
var provider = this;
|
||||||
|
|
||||||
this.worker.postMessage({
|
if (id !== 'ROOT') {
|
||||||
request: 'index',
|
this.worker.postMessage({
|
||||||
model: model,
|
request: 'index',
|
||||||
id: id
|
model: model,
|
||||||
});
|
id: id
|
||||||
|
|
||||||
if (Array.isArray(model.composition)) {
|
|
||||||
model.composition.forEach(function (idToIndex) {
|
|
||||||
provider.scheduleForIndexing(idToIndex);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var domainObject = objectUtils.toNewFormat(model, id);
|
||||||
|
var composition = _.find(this.openmct.composition.registry, function (p) {
|
||||||
|
return p.appliesTo(domainObject);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!composition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
composition.load(domainObject)
|
||||||
|
.then(function (children) {
|
||||||
|
children.forEach(function (child) {
|
||||||
|
provider.scheduleForIndexing(objectUtils.makeKeyString(child));
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,6 +39,8 @@ define([
|
|||||||
topic,
|
topic,
|
||||||
mutationTopic,
|
mutationTopic,
|
||||||
ROOTS,
|
ROOTS,
|
||||||
|
compositionProvider,
|
||||||
|
openmct,
|
||||||
provider;
|
provider;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
@ -77,6 +79,21 @@ define([
|
|||||||
ROOTS = [
|
ROOTS = [
|
||||||
'mine'
|
'mine'
|
||||||
];
|
];
|
||||||
|
compositionProvider = jasmine.createSpyObj(
|
||||||
|
'compositionProvider',
|
||||||
|
['load', 'appliesTo']
|
||||||
|
);
|
||||||
|
compositionProvider.load.andCallFake(function (domainObject) {
|
||||||
|
return Promise.resolve(domainObject.composition);
|
||||||
|
});
|
||||||
|
compositionProvider.appliesTo.andCallFake(function (domainObject) {
|
||||||
|
return !!domainObject.composition;
|
||||||
|
});
|
||||||
|
openmct = {
|
||||||
|
composition: {
|
||||||
|
registry: [compositionProvider]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
spyOn(GenericSearchProvider.prototype, 'scheduleForIndexing');
|
spyOn(GenericSearchProvider.prototype, 'scheduleForIndexing');
|
||||||
|
|
||||||
@ -86,7 +103,8 @@ define([
|
|||||||
modelService,
|
modelService,
|
||||||
workerService,
|
workerService,
|
||||||
topic,
|
topic,
|
||||||
ROOTS
|
ROOTS,
|
||||||
|
openmct
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -208,13 +226,42 @@ define([
|
|||||||
|
|
||||||
it('schedules composed ids for indexing', function () {
|
it('schedules composed ids for indexing', function () {
|
||||||
var id = 'anId',
|
var id = 'anId',
|
||||||
model = {composition: ['abc', 'def']};
|
model = {composition: ['abc', 'def']},
|
||||||
|
calls = provider.scheduleForIndexing.calls.length;
|
||||||
|
|
||||||
provider.index(id, model);
|
provider.index(id, model);
|
||||||
expect(provider.scheduleForIndexing)
|
|
||||||
.toHaveBeenCalledWith('abc');
|
expect(compositionProvider.appliesTo).toHaveBeenCalledWith({
|
||||||
expect(provider.scheduleForIndexing)
|
identifier: {key: 'anId', namespace: ''},
|
||||||
.toHaveBeenCalledWith('def');
|
composition: [jasmine.any(Object), jasmine.any(Object)]
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(compositionProvider.load).toHaveBeenCalledWith({
|
||||||
|
identifier: {key: 'anId', namespace: ''},
|
||||||
|
composition: [jasmine.any(Object), jasmine.any(Object)]
|
||||||
|
});
|
||||||
|
|
||||||
|
waitsFor(function () {
|
||||||
|
return provider.scheduleForIndexing.calls.length > calls;
|
||||||
|
});
|
||||||
|
|
||||||
|
runs(function () {
|
||||||
|
expect(provider.scheduleForIndexing)
|
||||||
|
.toHaveBeenCalledWith('abc');
|
||||||
|
expect(provider.scheduleForIndexing)
|
||||||
|
.toHaveBeenCalledWith('def');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not index ROOT, but checks composition', function () {
|
||||||
|
var id = 'ROOT',
|
||||||
|
model = {};
|
||||||
|
|
||||||
|
provider.index(id, model);
|
||||||
|
expect(worker.postMessage).not.toHaveBeenCalled();
|
||||||
|
expect(compositionProvider.appliesTo).toHaveBeenCalledWith({
|
||||||
|
identifier: {key: 'ROOT', namespace: ''}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
7
src/about.frag
Normal file
7
src/about.frag
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Open MCT https://nasa.github.io/openmct/
|
||||||
|
* Version: ${version}
|
||||||
|
* Built: ${timestamp}
|
||||||
|
* Revision: ${revision}
|
||||||
|
* Branch: ${branch}
|
||||||
|
*/
|
@ -32,6 +32,7 @@ define([
|
|||||||
'./policies/AdapterCompositionPolicy',
|
'./policies/AdapterCompositionPolicy',
|
||||||
'./policies/AdaptedViewPolicy',
|
'./policies/AdaptedViewPolicy',
|
||||||
'./runs/AlternateCompositionInitializer',
|
'./runs/AlternateCompositionInitializer',
|
||||||
|
'./runs/TimeSettingsURLHandler',
|
||||||
'text!./templates/adapted-view-template.html'
|
'text!./templates/adapted-view-template.html'
|
||||||
], function (
|
], function (
|
||||||
legacyRegistry,
|
legacyRegistry,
|
||||||
@ -45,6 +46,7 @@ define([
|
|||||||
AdapterCompositionPolicy,
|
AdapterCompositionPolicy,
|
||||||
AdaptedViewPolicy,
|
AdaptedViewPolicy,
|
||||||
AlternateCompositionInitializer,
|
AlternateCompositionInitializer,
|
||||||
|
TimeSettingsURLHandler,
|
||||||
adaptedViewTemplate
|
adaptedViewTemplate
|
||||||
) {
|
) {
|
||||||
legacyRegistry.register('src/adapter', {
|
legacyRegistry.register('src/adapter', {
|
||||||
@ -121,6 +123,16 @@ define([
|
|||||||
{
|
{
|
||||||
implementation: AlternateCompositionInitializer,
|
implementation: AlternateCompositionInitializer,
|
||||||
depends: ["openmct"]
|
depends: ["openmct"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
implementation: function (openmct, $location, $rootScope) {
|
||||||
|
return new TimeSettingsURLHandler(
|
||||||
|
openmct.time,
|
||||||
|
$location,
|
||||||
|
$rootScope
|
||||||
|
);
|
||||||
|
},
|
||||||
|
depends: ["openmct", "$location", "$rootScope"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
views: [
|
views: [
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
define([
|
define([
|
||||||
'angular',
|
'angular',
|
||||||
'./Region',
|
'./Region'
|
||||||
], function (
|
], function (
|
||||||
angular,
|
angular,
|
||||||
Region
|
Region
|
||||||
|
117
src/adapter/runs/TimeSettingsURLHandler.js
Normal file
117
src/adapter/runs/TimeSettingsURLHandler.js
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define([], function () {
|
||||||
|
// Parameter names in query string
|
||||||
|
var SEARCH = {
|
||||||
|
MODE: 'tc.mode',
|
||||||
|
TIME_SYSTEM: 'tc.timeSystem',
|
||||||
|
START_BOUND: 'tc.startBound',
|
||||||
|
END_BOUND: 'tc.endBound',
|
||||||
|
START_DELTA: 'tc.startDelta',
|
||||||
|
END_DELTA: 'tc.endDelta'
|
||||||
|
};
|
||||||
|
var TIME_EVENTS = ['bounds', 'timeSystem', 'clock', 'clockOffsets'];
|
||||||
|
// Used to shorthand calls to $location, which clears null parameters
|
||||||
|
var NULL_PARAMETERS = { key: null, start: null, end: null };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Communicates settings from the URL to the time API,
|
||||||
|
* and vice versa.
|
||||||
|
*/
|
||||||
|
function TimeSettingsURLHandler(time, $location, $rootScope) {
|
||||||
|
this.time = time;
|
||||||
|
this.$location = $location;
|
||||||
|
|
||||||
|
$rootScope.$on('$locationChangeSuccess', this.updateTime.bind(this));
|
||||||
|
|
||||||
|
TIME_EVENTS.forEach(function (event) {
|
||||||
|
this.time.on(event, this.updateQueryParams.bind(this));
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
this.updateTime(); // Initialize
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeSettingsURLHandler.prototype.updateQueryParams = function () {
|
||||||
|
var clock = this.time.clock();
|
||||||
|
var fixed = !clock;
|
||||||
|
var mode = fixed ? 'fixed' : clock.key;
|
||||||
|
var timeSystem = this.time.timeSystem() || NULL_PARAMETERS;
|
||||||
|
var bounds = fixed ? this.time.bounds() : NULL_PARAMETERS;
|
||||||
|
var deltas = fixed ? NULL_PARAMETERS : this.time.clockOffsets();
|
||||||
|
|
||||||
|
bounds = bounds || NULL_PARAMETERS;
|
||||||
|
deltas = deltas || NULL_PARAMETERS;
|
||||||
|
if (deltas.start) {
|
||||||
|
deltas = { start: -deltas.start, end: deltas.end };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$location.search(SEARCH.MODE, mode);
|
||||||
|
this.$location.search(SEARCH.TIME_SYSTEM, timeSystem.key);
|
||||||
|
this.$location.search(SEARCH.START_BOUND, bounds.start);
|
||||||
|
this.$location.search(SEARCH.END_BOUND, bounds.end);
|
||||||
|
this.$location.search(SEARCH.START_DELTA, deltas.start);
|
||||||
|
this.$location.search(SEARCH.END_DELTA, deltas.end);
|
||||||
|
};
|
||||||
|
|
||||||
|
TimeSettingsURLHandler.prototype.updateTime = function () {
|
||||||
|
var searchParams = this.$location.search();
|
||||||
|
var mode = searchParams[SEARCH.MODE];
|
||||||
|
var timeSystem = searchParams[SEARCH.TIME_SYSTEM];
|
||||||
|
var clockOffsets = {
|
||||||
|
start: -searchParams[SEARCH.START_DELTA],
|
||||||
|
end: +searchParams[SEARCH.END_DELTA]
|
||||||
|
};
|
||||||
|
var bounds = {
|
||||||
|
start: +searchParams[SEARCH.START_BOUND],
|
||||||
|
end: +searchParams[SEARCH.END_BOUND]
|
||||||
|
};
|
||||||
|
var fixed = (mode === 'fixed');
|
||||||
|
var clock = fixed ? undefined : mode;
|
||||||
|
var hasDeltas =
|
||||||
|
!isNaN(parseInt(searchParams[SEARCH.START_DELTA], 0xA)) &&
|
||||||
|
!isNaN(parseInt(searchParams[SEARCH.END_DELTA], 0xA));
|
||||||
|
var hasBounds =
|
||||||
|
!isNaN(parseInt(searchParams[SEARCH.START_BOUND], 0xA)) &&
|
||||||
|
!isNaN(parseInt(searchParams[SEARCH.END_BOUND], 0xA));
|
||||||
|
|
||||||
|
if (fixed && timeSystem && hasBounds) {
|
||||||
|
this.time.timeSystem(timeSystem, bounds);
|
||||||
|
this.time.stopClock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fixed && clock && hasDeltas) {
|
||||||
|
this.time.clock(clock, clockOffsets);
|
||||||
|
this.time.timeSystem(timeSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDeltas && !fixed) {
|
||||||
|
this.time.clockOffsets(clockOffsets);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasBounds && fixed) {
|
||||||
|
this.time.bounds(bounds);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return TimeSettingsURLHandler;
|
||||||
|
});
|
200
src/adapter/runs/TimeSettingsURLHandlerSpec.js
Normal file
200
src/adapter/runs/TimeSettingsURLHandlerSpec.js
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(['./TimeSettingsURLHandler'], function (TimeSettingsURLHandler) {
|
||||||
|
describe("TimeSettingsURLHandler", function () {
|
||||||
|
var time;
|
||||||
|
var $location;
|
||||||
|
var $rootScope;
|
||||||
|
var search;
|
||||||
|
var handler;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
time = jasmine.createSpyObj('time', [
|
||||||
|
'on',
|
||||||
|
'bounds',
|
||||||
|
'clockOffsets',
|
||||||
|
'timeSystem',
|
||||||
|
'clock',
|
||||||
|
'stopClock'
|
||||||
|
]);
|
||||||
|
$location = jasmine.createSpyObj('$location', [
|
||||||
|
'search'
|
||||||
|
]);
|
||||||
|
$rootScope = jasmine.createSpyObj('$rootScope', [
|
||||||
|
'$on'
|
||||||
|
]);
|
||||||
|
|
||||||
|
time.timeSystem.andReturn({ key: 'test-time-system' });
|
||||||
|
|
||||||
|
search = {};
|
||||||
|
$location.search.andCallFake(function (key, value) {
|
||||||
|
if (arguments.length === 0) {
|
||||||
|
return search;
|
||||||
|
}
|
||||||
|
if (value === null) {
|
||||||
|
delete search[key];
|
||||||
|
} else {
|
||||||
|
search[key] = String(value);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
|
||||||
|
handler = new TimeSettingsURLHandler(
|
||||||
|
time,
|
||||||
|
$location,
|
||||||
|
$rootScope
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
['bounds', 'timeSystem', 'clock', 'clockOffsets'].forEach(function (event) {
|
||||||
|
it("listens for " + event + " time events", function () {
|
||||||
|
expect(time.on)
|
||||||
|
.toHaveBeenCalledWith(event, jasmine.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when " + event + " time event occurs with no clock", function () {
|
||||||
|
var expected;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
expected = {
|
||||||
|
'tc.mode': 'fixed',
|
||||||
|
'tc.timeSystem': 'test-time-system',
|
||||||
|
'tc.startBound': '123',
|
||||||
|
'tc.endBound': '456'
|
||||||
|
};
|
||||||
|
time.clock.andReturn(undefined);
|
||||||
|
time.bounds.andReturn({ start: 123, end: 456 });
|
||||||
|
|
||||||
|
time.on.calls.forEach(function (call) {
|
||||||
|
if (call.args[0] === event) {
|
||||||
|
call.args[1]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates query parameters for fixed mode", function () {
|
||||||
|
expect(search).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when " + event + " time event occurs with no time system", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
time.timeSystem.andReturn(undefined);
|
||||||
|
time.on.calls.forEach(function (call) {
|
||||||
|
if (call.args[0] === event) {
|
||||||
|
call.args[1]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clears the time system from the URL", function () {
|
||||||
|
expect(search['tc.timeSystem']).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when " + event + " time event occurs with a clock", function () {
|
||||||
|
var expected;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
expected = {
|
||||||
|
'tc.mode': 'clocky',
|
||||||
|
'tc.timeSystem': 'test-time-system',
|
||||||
|
'tc.startDelta': '123',
|
||||||
|
'tc.endDelta': '456'
|
||||||
|
};
|
||||||
|
time.clock.andReturn({ key: 'clocky' });
|
||||||
|
time.clockOffsets.andReturn({ start: -123, end: 456 });
|
||||||
|
|
||||||
|
time.on.calls.forEach(function (call) {
|
||||||
|
if (call.args[0] === event) {
|
||||||
|
call.args[1]();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updates query parameters for realtime mode", function () {
|
||||||
|
expect(search).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("listens for location changes", function () {
|
||||||
|
expect($rootScope.$on)
|
||||||
|
.toHaveBeenCalledWith('$locationChangeSuccess', jasmine.any(Function));
|
||||||
|
});
|
||||||
|
|
||||||
|
[false, true].forEach(function (fixed) {
|
||||||
|
var name = fixed ? "fixed-time" : "real-time";
|
||||||
|
var suffix = fixed ? 'Bound' : 'Delta';
|
||||||
|
describe("when " + name + " location changes occur", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
search['tc.mode'] = fixed ? 'fixed' : 'clocky';
|
||||||
|
search['tc.timeSystem'] = 'some-time-system';
|
||||||
|
search['tc.start' + suffix] = '12321';
|
||||||
|
search['tc.end' + suffix] = '32123';
|
||||||
|
$rootScope.$on.mostRecentCall.args[1]();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fixed) {
|
||||||
|
var bounds = { start: 12321, end: 32123 };
|
||||||
|
it("stops the clock", function () {
|
||||||
|
expect(time.stopClock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the bounds", function () {
|
||||||
|
expect(time.bounds).toHaveBeenCalledWith(bounds);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the time system with bounds", function () {
|
||||||
|
expect(time.timeSystem).toHaveBeenCalledWith(
|
||||||
|
search['tc.timeSystem'],
|
||||||
|
bounds
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var clockOffsets = { start: -12321, end: 32123 };
|
||||||
|
|
||||||
|
it("sets the clock", function () {
|
||||||
|
expect(time.stopClock).not.toHaveBeenCalled();
|
||||||
|
expect(time.clock).toHaveBeenCalledWith(
|
||||||
|
search['tc.mode'],
|
||||||
|
clockOffsets
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets clock offsets", function () {
|
||||||
|
expect(time.clockOffsets)
|
||||||
|
.toHaveBeenCalledWith(clockOffsets);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sets the time system without bounds", function () {
|
||||||
|
expect(time.timeSystem).toHaveBeenCalledWith(
|
||||||
|
search['tc.timeSystem']
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
@ -21,7 +21,7 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define([
|
define([
|
||||||
'lodash',
|
'lodash'
|
||||||
], function (
|
], function (
|
||||||
_
|
_
|
||||||
) {
|
) {
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
define([
|
define([
|
||||||
'./TelemetryMetadataManager',
|
'./TelemetryMetadataManager',
|
||||||
'./TelemetryValueFormatter',
|
'./TelemetryValueFormatter',
|
||||||
'lodash',
|
'lodash'
|
||||||
], function (
|
], function (
|
||||||
TelemetryMetadataManager,
|
TelemetryMetadataManager,
|
||||||
TelemetryValueFormatter,
|
TelemetryValueFormatter,
|
||||||
|
@ -239,7 +239,13 @@ define(['EventEmitter'], function (EventEmitter) {
|
|||||||
* @method timeSystem
|
* @method timeSystem
|
||||||
*/
|
*/
|
||||||
TimeAPI.prototype.timeSystem = function (timeSystemOrKey, bounds) {
|
TimeAPI.prototype.timeSystem = function (timeSystemOrKey, bounds) {
|
||||||
if (arguments.length >= 2) {
|
if (arguments.length >= 1) {
|
||||||
|
if (arguments.length === 1 && !this.activeClock) {
|
||||||
|
throw new Error(
|
||||||
|
"Must specify bounds when changing time system without " +
|
||||||
|
"an active clock."
|
||||||
|
);
|
||||||
|
}
|
||||||
var timeSystem;
|
var timeSystem;
|
||||||
|
|
||||||
if (timeSystemOrKey === undefined) {
|
if (timeSystemOrKey === undefined) {
|
||||||
@ -274,10 +280,10 @@ define(['EventEmitter'], function (EventEmitter) {
|
|||||||
* Time System
|
* Time System
|
||||||
* */
|
* */
|
||||||
this.emit('timeSystem', this.system);
|
this.emit('timeSystem', this.system);
|
||||||
this.bounds(bounds);
|
if (bounds) {
|
||||||
|
this.bounds(bounds);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (arguments.length === 1) {
|
|
||||||
throw new Error('Must set bounds when changing time system');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.system;
|
return this.system;
|
||||||
@ -366,11 +372,6 @@ define(['EventEmitter'], function (EventEmitter) {
|
|||||||
|
|
||||||
this.activeClock = clock;
|
this.activeClock = clock;
|
||||||
|
|
||||||
if (this.activeClock !== undefined) {
|
|
||||||
this.offsets = offsets;
|
|
||||||
this.activeClock.on("tick", this.tick);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The active clock has changed. Clock can be unset by calling {@link stopClock}
|
* The active clock has changed. Clock can be unset by calling {@link stopClock}
|
||||||
* @event clock
|
* @event clock
|
||||||
@ -380,6 +381,11 @@ define(['EventEmitter'], function (EventEmitter) {
|
|||||||
*/
|
*/
|
||||||
this.emit("clock", this.activeClock);
|
this.emit("clock", this.activeClock);
|
||||||
|
|
||||||
|
if (this.activeClock !== undefined) {
|
||||||
|
this.clockOffsets(offsets);
|
||||||
|
this.activeClock.on("tick", this.tick);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (arguments.length === 1) {
|
} else if (arguments.length === 1) {
|
||||||
throw "When setting the clock, clock offsets must also be provided";
|
throw "When setting the clock, clock offsets must also be provided";
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ define(['./TimeAPI'], function (TimeAPI) {
|
|||||||
var api,
|
var api,
|
||||||
timeSystemKey,
|
timeSystemKey,
|
||||||
timeSystem,
|
timeSystem,
|
||||||
|
clockKey,
|
||||||
|
clock,
|
||||||
bounds,
|
bounds,
|
||||||
eventListener,
|
eventListener,
|
||||||
toi;
|
toi;
|
||||||
@ -33,7 +35,15 @@ define(['./TimeAPI'], function (TimeAPI) {
|
|||||||
api = new TimeAPI();
|
api = new TimeAPI();
|
||||||
timeSystemKey = "timeSystemKey";
|
timeSystemKey = "timeSystemKey";
|
||||||
timeSystem = {key: timeSystemKey};
|
timeSystem = {key: timeSystemKey};
|
||||||
bounds = {start: 0, end: 0};
|
clockKey = "someClockKey";
|
||||||
|
clock = jasmine.createSpyObj("clock", [
|
||||||
|
"on",
|
||||||
|
"off",
|
||||||
|
"currentValue"
|
||||||
|
]);
|
||||||
|
clock.currentValue.andReturn(100);
|
||||||
|
clock.key = clockKey;
|
||||||
|
bounds = {start: 0, end: 1};
|
||||||
eventListener = jasmine.createSpy("eventListener");
|
eventListener = jasmine.createSpy("eventListener");
|
||||||
toi = 111;
|
toi = 111;
|
||||||
});
|
});
|
||||||
@ -74,11 +84,23 @@ define(['./TimeAPI'], function (TimeAPI) {
|
|||||||
|
|
||||||
it("Disallows setting of time system without bounds", function () {
|
it("Disallows setting of time system without bounds", function () {
|
||||||
api.addTimeSystem(timeSystem);
|
api.addTimeSystem(timeSystem);
|
||||||
expect(api.timeSystem()).not.toBe(timeSystemKey);
|
expect(api.timeSystem()).not.toBe(timeSystem);
|
||||||
expect(function () {
|
expect(function () {
|
||||||
api.timeSystem(timeSystemKey);
|
api.timeSystem(timeSystemKey);
|
||||||
}).toThrow();
|
}).toThrow();
|
||||||
expect(api.timeSystem()).not.toBe(timeSystemKey);
|
expect(api.timeSystem()).not.toBe(timeSystem);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows setting of timesystem without bounds with clock", function () {
|
||||||
|
api.addTimeSystem(timeSystem);
|
||||||
|
api.addClock(clock);
|
||||||
|
api.clock(clockKey, {start: 0, end: 1});
|
||||||
|
expect(api.timeSystem()).not.toBe(timeSystem);
|
||||||
|
expect(function () {
|
||||||
|
api.timeSystem(timeSystemKey);
|
||||||
|
}).not.toThrow();
|
||||||
|
expect(api.timeSystem()).toBe(timeSystem);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Emits an event when time system changes", function () {
|
it("Emits an event when time system changes", function () {
|
||||||
@ -136,26 +158,35 @@ define(['./TimeAPI'], function (TimeAPI) {
|
|||||||
var anotherMockTickSource;
|
var anotherMockTickSource;
|
||||||
var mockOffsets = {
|
var mockOffsets = {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0
|
end: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockTickSource = jasmine.createSpyObj("clock", [
|
mockTickSource = jasmine.createSpyObj("clock", [
|
||||||
"on",
|
"on",
|
||||||
"off"
|
"off",
|
||||||
|
"currentValue"
|
||||||
]);
|
]);
|
||||||
|
mockTickSource.currentValue.andReturn(10);
|
||||||
mockTickSource.key = "mts";
|
mockTickSource.key = "mts";
|
||||||
|
|
||||||
anotherMockTickSource = jasmine.createSpyObj("clock", [
|
anotherMockTickSource = jasmine.createSpyObj("clock", [
|
||||||
"on",
|
"on",
|
||||||
"off"
|
"off",
|
||||||
|
"currentValue"
|
||||||
]);
|
]);
|
||||||
anotherMockTickSource.key = "amts";
|
anotherMockTickSource.key = "amts";
|
||||||
|
anotherMockTickSource.currentValue.andReturn(10);
|
||||||
|
|
||||||
api.addClock(mockTickSource);
|
api.addClock(mockTickSource);
|
||||||
api.addClock(anotherMockTickSource);
|
api.addClock(anotherMockTickSource);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("sets bounds based on current value", function () {
|
||||||
|
api.clock("mts", mockOffsets);
|
||||||
|
expect(api.bounds()).toEqual({start: 10, end: 11});
|
||||||
|
});
|
||||||
|
|
||||||
it("a new tick listener is registered", function () {
|
it("a new tick listener is registered", function () {
|
||||||
api.clock("mts", mockOffsets);
|
api.clock("mts", mockOffsets);
|
||||||
expect(mockTickSource.on).toHaveBeenCalledWith("tick", jasmine.any(Function));
|
expect(mockTickSource.on).toHaveBeenCalledWith("tick", jasmine.any(Function));
|
||||||
@ -180,8 +211,10 @@ define(['./TimeAPI'], function (TimeAPI) {
|
|||||||
it("on tick, observes offsets, and indicates tick in bounds callback", function () {
|
it("on tick, observes offsets, and indicates tick in bounds callback", function () {
|
||||||
var mockTickSource = jasmine.createSpyObj("clock", [
|
var mockTickSource = jasmine.createSpyObj("clock", [
|
||||||
"on",
|
"on",
|
||||||
"off"
|
"off",
|
||||||
|
"currentValue"
|
||||||
]);
|
]);
|
||||||
|
mockTickSource.currentValue.andReturn(100);
|
||||||
var tickCallback;
|
var tickCallback;
|
||||||
var boundsCallback = jasmine.createSpy("boundsCallback");
|
var boundsCallback = jasmine.createSpy("boundsCallback");
|
||||||
var clockOffsets = {
|
var clockOffsets = {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<div class="abs overlay">
|
<div class="abs overlay l-dialog">
|
||||||
<div class="abs blocker"></div>
|
<div class="abs blocker"></div>
|
||||||
<div class="abs holder">
|
<div class="abs outer-holder">
|
||||||
<a class="clk-icon icon ui-symbol close">x</a>
|
<a class="close icon-x-in-circle"></a>
|
||||||
<div class="abs contents">
|
<div class="abs inner-holder contents">
|
||||||
<div class="abs top-bar">
|
<div class="abs top-bar">
|
||||||
<div class="title"></div>
|
<div class="title"></div>
|
||||||
<div class="hint"></div>
|
<div class="hint"></div>
|
||||||
@ -10,12 +10,9 @@
|
|||||||
<div class='abs editor'>
|
<div class='abs editor'>
|
||||||
</div>
|
</div>
|
||||||
<div class="abs bottom-bar">
|
<div class="abs bottom-bar">
|
||||||
<a class='s-btn major ok'>OK</a>
|
<a class='s-button major'>OK</a>
|
||||||
<a class='s-btn cancel'>Cancel</a>
|
<a class='s-button'>Cancel</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ define([
|
|||||||
'../example/extensions/bundle',
|
'../example/extensions/bundle',
|
||||||
'../example/forms/bundle',
|
'../example/forms/bundle',
|
||||||
'../example/identity/bundle',
|
'../example/identity/bundle',
|
||||||
'../example/imagery/bundle',
|
|
||||||
'../example/mobile/bundle',
|
'../example/mobile/bundle',
|
||||||
'../example/msl/bundle',
|
'../example/msl/bundle',
|
||||||
'../example/notifications/bundle',
|
'../example/notifications/bundle',
|
||||||
@ -70,6 +69,7 @@ define([
|
|||||||
'../platform/features/conductor/compatibility/bundle',
|
'../platform/features/conductor/compatibility/bundle',
|
||||||
'../platform/features/imagery/bundle',
|
'../platform/features/imagery/bundle',
|
||||||
'../platform/features/layout/bundle',
|
'../platform/features/layout/bundle',
|
||||||
|
'../platform/features/listview/bundle',
|
||||||
'../platform/features/my-items/bundle',
|
'../platform/features/my-items/bundle',
|
||||||
'../platform/features/pages/bundle',
|
'../platform/features/pages/bundle',
|
||||||
'../platform/features/plot/bundle',
|
'../platform/features/plot/bundle',
|
||||||
@ -116,6 +116,7 @@ define([
|
|||||||
'platform/features/fixed',
|
'platform/features/fixed',
|
||||||
'platform/features/imagery',
|
'platform/features/imagery',
|
||||||
'platform/features/layout',
|
'platform/features/layout',
|
||||||
|
'platform/features/listview',
|
||||||
'platform/features/pages',
|
'platform/features/pages',
|
||||||
'platform/features/plot',
|
'platform/features/plot',
|
||||||
'platform/features/timeline',
|
'platform/features/timeline',
|
||||||
|
@ -25,13 +25,15 @@ define([
|
|||||||
'./utcTimeSystem/plugin',
|
'./utcTimeSystem/plugin',
|
||||||
'../../example/generator/plugin',
|
'../../example/generator/plugin',
|
||||||
'../../platform/features/autoflow/plugin',
|
'../../platform/features/autoflow/plugin',
|
||||||
'./timeConductor/plugin'
|
'./timeConductor/plugin',
|
||||||
|
'../../example/imagery/plugin'
|
||||||
], function (
|
], function (
|
||||||
_,
|
_,
|
||||||
UTCTimeSystem,
|
UTCTimeSystem,
|
||||||
GeneratorPlugin,
|
GeneratorPlugin,
|
||||||
AutoflowPlugin,
|
AutoflowPlugin,
|
||||||
TimeConductorPlugin
|
TimeConductorPlugin,
|
||||||
|
ExampleImagery
|
||||||
) {
|
) {
|
||||||
var bundleMap = {
|
var bundleMap = {
|
||||||
CouchDB: 'platform/persistence/couch',
|
CouchDB: 'platform/persistence/couch',
|
||||||
@ -113,5 +115,7 @@ define([
|
|||||||
return GeneratorPlugin;
|
return GeneratorPlugin;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
plugins.ExampleImagery = ExampleImagery;
|
||||||
|
|
||||||
return plugins;
|
return plugins;
|
||||||
});
|
});
|
||||||
|
@ -22,28 +22,79 @@
|
|||||||
|
|
||||||
define([], function () {
|
define([], function () {
|
||||||
|
|
||||||
|
function isTruthy(a) {
|
||||||
|
return !!a;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateMenuOption(menuOption, index) {
|
||||||
|
if (menuOption.clock && !menuOption.clockOffsets) {
|
||||||
|
return "clock-based menuOption at index " + index + " is " +
|
||||||
|
"missing required property 'clockOffsets'.";
|
||||||
|
}
|
||||||
|
if (!menuOption.timeSystem) {
|
||||||
|
return "menuOption at index " + index + " is missing " +
|
||||||
|
"required property 'timeSystem'.";
|
||||||
|
}
|
||||||
|
if (!menuOption.bounds && !menuOption.clock) {
|
||||||
|
return "fixed-bounds menuOption at index " + index + " is " +
|
||||||
|
"missing required property 'bounds'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateConfiguration(config) {
|
||||||
|
if (config === undefined ||
|
||||||
|
config.menuOptions === undefined ||
|
||||||
|
config.menuOptions.length === 0) {
|
||||||
|
return "You must specify one or more 'menuOptions'.";
|
||||||
|
}
|
||||||
|
if (config.menuOptions.some(validateMenuOption)) {
|
||||||
|
return config.menuOptions.map(validateMenuOption)
|
||||||
|
.filter(isTruthy)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateRuntimeConfiguration(config, openmct) {
|
||||||
|
var systems = openmct.time.getAllTimeSystems()
|
||||||
|
.reduce(function (m, ts) {
|
||||||
|
m[ts.key] = ts;
|
||||||
|
return m;
|
||||||
|
}, {});
|
||||||
|
var clocks = openmct.time.getAllClocks()
|
||||||
|
.reduce(function (m, c) {
|
||||||
|
m[c.key] = c;
|
||||||
|
return m;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return config.menuOptions.map(function (menuOption, index) {
|
||||||
|
if (menuOption.timeSystem && !systems[menuOption.timeSystem]) {
|
||||||
|
return "menuOption at index " + index + " specifies a " +
|
||||||
|
"timeSystem that does not exist: " + menuOption.timeSystem;
|
||||||
|
}
|
||||||
|
if (menuOption.clock && !clocks[menuOption.clock]) {
|
||||||
|
return "menuOption at index " + index + " specifies a " +
|
||||||
|
"clock that does not exist: " + menuOption.clock;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(isTruthy)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function throwConfigErrorIfExists(error) {
|
||||||
|
if (error) {
|
||||||
|
throw new Error("Invalid Time Conductor Configuration: \n" +
|
||||||
|
error + '\n' +
|
||||||
|
"https://github.com/nasa/openmct/blob/master/API.md#the-time-conductor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return function (config) {
|
return function (config) {
|
||||||
|
|
||||||
function validateConfiguration() {
|
throwConfigErrorIfExists(validateConfiguration(config));
|
||||||
if (config === undefined || config.menuOptions === undefined || config.menuOptions.length === 0) {
|
|
||||||
return "Please provide some configuration for the time conductor. https://github.com/nasa/openmct/blob/master/API.md#the-time-conductor";
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return function (openmct) {
|
return function (openmct) {
|
||||||
|
|
||||||
function getTimeSystem(key) {
|
|
||||||
return openmct.time.getAllTimeSystems().filter(function (timeSystem) {
|
|
||||||
return timeSystem.key === key;
|
|
||||||
})[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
var validationError = validateConfiguration();
|
|
||||||
if (validationError) {
|
|
||||||
throw validationError;
|
|
||||||
}
|
|
||||||
|
|
||||||
openmct.legacyExtension('constants', {
|
openmct.legacyExtension('constants', {
|
||||||
key: 'CONDUCTOR_CONFIG',
|
key: 'CONDUCTOR_CONFIG',
|
||||||
value: config,
|
value: config,
|
||||||
@ -54,39 +105,24 @@ define([], function () {
|
|||||||
openmct.legacyRegistry.enable('platform/features/conductor/compatibility');
|
openmct.legacyRegistry.enable('platform/features/conductor/compatibility');
|
||||||
|
|
||||||
openmct.on('start', function () {
|
openmct.on('start', function () {
|
||||||
|
|
||||||
|
throwConfigErrorIfExists(validateRuntimeConfiguration(config, openmct));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
On app startup, default the conductor
|
On app startup, default the conductor if not already set.
|
||||||
*/
|
*/
|
||||||
var timeSystem = openmct.time.timeSystem();
|
if (openmct.time.timeSystem() !== undefined) {
|
||||||
var clock = openmct.time.clock();
|
return;
|
||||||
|
|
||||||
if (timeSystem === undefined) {
|
|
||||||
timeSystem = getTimeSystem(config.menuOptions[0].timeSystem);
|
|
||||||
if (timeSystem === undefined) {
|
|
||||||
throw 'Please install and configure at least one time system';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var configForTimeSystem = config.menuOptions.filter(function (menuOption) {
|
var defaults = config.menuOptions[0];
|
||||||
return menuOption.timeSystem === timeSystem.key && menuOption.clock === (clock && clock.key);
|
if (defaults.clock) {
|
||||||
})[0];
|
openmct.time.clock(defaults.clock, defaults.clockOffsets);
|
||||||
|
openmct.time.timeSystem(defaults.timeSystem, openmct.time.bounds());
|
||||||
if (configForTimeSystem !== undefined) {
|
|
||||||
var bounds;
|
|
||||||
if (clock === undefined) {
|
|
||||||
bounds = configForTimeSystem.bounds;
|
|
||||||
} else {
|
|
||||||
var clockOffsets = configForTimeSystem.clockOffsets;
|
|
||||||
|
|
||||||
bounds = {
|
|
||||||
start: clock.currentValue() + clockOffsets.start,
|
|
||||||
end: clock.currentValue() + clockOffsets.end
|
|
||||||
};
|
|
||||||
}
|
|
||||||
openmct.time.timeSystem(timeSystem, bounds);
|
|
||||||
} else {
|
} else {
|
||||||
throw 'Invalid time conductor configuration. Please define defaults for time system "' + timeSystem.key + '"';
|
openmct.time.timeSystem(defaults.timeSystem, defaults.bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -20,14 +20,6 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/**
|
|
||||||
* Open MCT https://nasa.github.io/openmct/
|
|
||||||
* Version @@version
|
|
||||||
* Built @@timestamp
|
|
||||||
* Revision @@revision
|
|
||||||
* Branch @@branch
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function (root, factory) {
|
(function (root, factory) {
|
||||||
if (typeof define === 'function' && define.amd) {
|
if (typeof define === 'function' && define.amd) {
|
||||||
define([], factory);
|
define([], factory);
|
||||||
|
Reference in New Issue
Block a user