Compare commits

..

57 Commits

Author SHA1 Message Date
9553e0c64f Tutorial packages 2017-02-27 19:28:21 -08:00
da40f4c96e [Plot] Add current conductor bounds to telemetry requests via the old API. Replaces telemetry decorator. Fixes #1458 2017-02-27 19:26:51 -08:00
6fa5a31217 Merge pull request #1433 from nasa/open1432
[Build] Installed sound suppression system for Bourbon deprecation messages.
2017-02-27 11:12:27 -08:00
5bc7a701dc Merge pull request #1455 from nasa/api-conductor-config
[Conductor] Allow configuration of conductor defaults
2017-02-24 14:58:50 -08:00
5cd0516048 Enable TC by default and hide
Set up TC so that it is always enabled and defaults to realtime mode.

This allows us to have warp-like functionality without showing
the TC interface, and simplifies a lot of tutorials.  We can still
reconfigure the TC by re-installing the plugin with different settings
2017-02-24 14:56:18 -08:00
48a603ece8 Merge branch 'api-legacy-telemetry-provider-v2' 2017-02-24 14:47:10 -08:00
abfa56464a Merge branch 'plugins-functions' 2017-02-24 14:34:54 -08:00
c1d6e21c3c Added tests. Fixes #1364 2017-02-23 13:50:14 -08:00
3bd556a406 Fixed failing tests 2017-02-23 10:21:33 -08:00
347fb6d882 Fixed style errors 2017-02-23 10:19:10 -08:00
b3cf7a5d93 Added support for new style telemetry providers from old screens. Converted SWG to new style data adapter 2017-02-23 09:58:46 -08:00
c7cffdeb3b Merge pull request #1453 from nasa/restore-search-navigation
[Search] Allow navigation from results
2017-02-22 13:21:03 -08:00
d45ae7908d [Search] Allow navigation from results
Fix search item so that navigation occurs after clicking on
a search result, while still properly preventing navigation
when required.

Related to #1366
2017-02-22 13:08:54 -08:00
b10fb4533e All builtin plugins now standardized as functions 2017-02-22 12:51:49 -08:00
c74fdb816f Merge pull request #1450 from nasa/fix-table-tests
Add hasCapability to mock
2017-02-21 18:14:18 -08:00
40985a56c8 Add hasCapability to mock 2017-02-21 18:01:43 -08:00
5c01f0be24 Merge branch 'open1077' into 1435-integration 2017-02-21 17:22:36 -08:00
f7ff5af60b Merge pull request #1448 from nasa/update-composition-policy
Update composition policy
2017-02-21 17:18:59 -08:00
2088fc52f3 Merge pull request #1446 from nasa/fix-orphan-navigation
Orphan check uses capability not model
2017-02-21 17:08:40 -08:00
db33ab143e Merge pull request #1445 from nasa/api-updates
Api updates
2017-02-21 17:04:45 -08:00
8c77d4006a Pass options to support checks 2017-02-21 17:03:16 -08:00
2fa567b98b [Table] Track by index, save the elements 2017-02-21 16:49:39 -08:00
e61f04663a Merge pull request #1444 from nasa/consistent-css-class
cssclass is now cssClass
2017-02-21 16:36:30 -08:00
4b905fa7d2 Merge pull request #1443 from nasa/remove-old-bundle-loading
Stop loading bundles.json
2017-02-21 16:32:26 -08:00
5e6e7f018a Merge pull request #1442 from nasa/dev-skip-optimize
[Build] Skip optimize in dev environment
2017-02-21 16:26:41 -08:00
53f56b430a Merge pull request #1449 from nasa/identifier-not-key
[API] Use proper key format
2017-02-21 16:24:17 -08:00
50c934820c Merge pull request #1424 from nasa/open1382
[Tables] Do not persist column configuration for non-editable objects
2017-02-21 15:18:56 -08:00
2a10a2cae2 Update specs to match composition policies 2017-02-21 15:14:35 -08:00
65325b90fd Composition policy takes child instance
The composition policy now takes a child instance instead
of the child type, as in all cases we have access to the child
object.

This allows new-style objects to be contained by old-style objects.

Updated all composition policies to use standardized argument names
instead of `context` and `candidate`; this makes it easier to
understand.

Updated AddActionProvider to hardcode the object types supported.
2017-02-21 12:32:49 -08:00
cfecc36ae6 Orphan check uses capability not model
Switch orphan checking to use capability instead of model.

This ensures that new-style composition providers work as intended.
2017-02-21 12:10:17 -08:00
d9f8622459 [Telemetry] Update TelemetryProvider API
Based on feedback from tutorial sprint, update provider API,
formatter API, and legacy adapter code.

Providers can now implement separate checks for providing realtime
and historical data, and providers are not registered with a specific
strategy.  Strategy is instead intended to be an attribute in the
request options.

Removed unused code in the telemetry API and simplify limitEvaluators.
2017-02-21 11:51:32 -08:00
8e13819e1e [API] composition providers receive new-style objects
Ensure that composition providers get new-style objects (with id
included) so that they can properly check for applicability.
2017-02-21 11:21:08 -08:00
aaedf5d576 cssclass is now cssClass
Make property name consistent with standard camelCase naming.
2017-02-21 11:14:46 -08:00
af9ffaf02d Stop loading bundles.json
Stop application from requesting bundles.json at first load.  This was
confusing to some external developers who would see an error in the
log and not know the cause.
2017-02-21 11:11:16 -08:00
970acbd56e [Build] Skip optimize in dev environment
Skip optimize in dev environment to speed up project rebuilds.  Very helpful
when integration testing openmct.js build artifact.
2017-02-21 11:05:20 -08:00
46c7399867 Merge pull request #1410 from joshbaldwin/master
adding MCT name to README
2017-02-21 09:09:51 -08:00
9a6745635d Merge pull request #1431 from dhrubomoy/master
[Documentation] Fixed filename
2017-02-15 10:18:46 -08:00
fa962b42bc [API] Use proper key format 2017-02-13 13:21:55 -08:00
34dc457aff [Tables] Restored telemetry datum field 'name'. Fixed bug with default sort not working 2017-02-10 15:39:39 -08:00
a3311e4c57 [Tables] Tests and style fixes 2017-02-10 14:22:30 -08:00
ef8efbd53d [Tables] Default UTC time system if available and none others defined 2017-02-10 14:22:28 -08:00
6cd99efbb9 [Tables] Added telemetry buffer so that subscription data is not discarded if it's beyond the end bounds 2017-02-10 14:22:28 -08:00
ae2b73a4f5 [Tables] Increase default table size 2017-02-10 14:22:28 -08:00
0c3ff82cfe [Table] Added ticking to combined historical/real-time table
Don't add duplicate telemetry data
2017-02-10 14:22:20 -08:00
50f303bbdc [Tables] limit digests to increase performance 2017-02-10 14:22:20 -08:00
2a4944d6ee [Tables] Refactoring for consolidation of historical and real-time tables
Added batch processing of large historical queries. #1077
2017-02-10 14:21:48 -08:00
3544caf4be [API] Observer path was accessing object key incorrectly 2017-02-10 14:21:47 -08:00
976333d7f7 [Tables] Support for subscriptions from new Telemetry API
Historical and real-time data flowing

Added formatting, and limits. Support telemetry objects themselves and not just composition of telemetry objects

Apply default time range if none supplied (15 minutes)
2017-02-10 14:21:26 -08:00
6d5530ba9c [Tables] Using new composition API to fetch all telemetry objects 2017-02-10 14:12:10 -08:00
77d0134e2e [Build] Added Bourbon deprecation warning suppression system. 2017-02-10 10:03:05 -08:00
d3b4ad41c2 [Documentation] Fixed filename
Fixed file name "Platform.md" to "platform.md". "Platform.md" was giving a 404 error when clicked, in github and in the official site as well.
2017-02-09 20:54:52 -05:00
b28eb049dc Merge pull request #1420 from BogdanAlexandru/tutorial-fix
[Tutorial] Replace glyph mentions with cssclass
2017-02-06 11:57:49 -08:00
3d3baddd23 [Tables] Do not persist column configuration for non-editable objects 2017-02-02 15:44:36 -08:00
e712edba4e [Tutorial] Fix icon set url 2017-01-31 21:35:42 +02:00
35d8024aaa [Tutorial] Better describe cssclass 2017-01-31 21:32:31 +02:00
17564aa489 [Tutorial] Replace glyph mentions with cssclass 2017-01-29 18:16:36 +02:00
9f9d28deef adding MCT name to README 2017-01-21 11:27:20 -05:00
163 changed files with 2700 additions and 3003 deletions

View File

@ -1,6 +1,6 @@
# Open MCT [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)
Open MCT is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
Open MCT (Open Mission Control Technologies) is a next-generation mission control framework for visualization of data on desktop and mobile devices. It is developed at NASA's Ames Research Center, and is being used by NASA for data analysis of spacecraft missions, as well as planning and operation of experimental rover systems. As a generalizable and open source framework, Open MCT could be used as the basis for building applications for planning, operation, and analysis of any systems producing telemetry data.
Please visit our [Official Site](https://nasa.github.io/openmct/) and [Getting Started Guide](https://nasa.github.io/openmct/getting-started/)

View File

@ -131,7 +131,7 @@ Keeping that in mind, there are a few useful patterns supported by the
framework that are useful to keep in mind.
The specific service infrastructure provided by the platform is described
in the [Platform Architecture](Platform.md).
in the [Platform Architecture](platform.md).
## Extension Categories

View File

@ -2261,10 +2261,7 @@ The platform understands the following policy categories (specifiable as the
* `action`: Determines whether or not a given action is allowable. The candidate
argument here is an Action; the context is its action context object.
* `composition`: Determines whether or not domain objects of a given type are
allowed to contain domain objects of another type. The candidate argument here
is the container's `Type`; the context argument is the `Type` of the object to be
contained.
* `composition`: Determines whether or not domain objects of a given type (first argument, `parentType`) can contain a given object (second argument, `child`).
* `view`: Determines whether or not a view is applicable for a domain object.
The candidate argument is the view's extension definition; the context argument
is the `DomainObject` to be viewed.

View File

@ -191,13 +191,12 @@ has been finalized.
[
'example/imagery',
'example/eventGenerator',
'example/generator'
'example/generator',
'platform/features/my-items',
'platform/persistence/local'
].forEach(
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
openmct.install(openmct.plugins.myItems);
openmct.install(openmct.plugins.localStorage);
openmct.install(openmct.plugins.espresso);
openmct.start();
});
</script>
@ -215,7 +214,6 @@ has been finalized.
</body>
</html>
```
__index.html__
@ -260,13 +258,12 @@ __index.html__
'example/imagery',
'example/eventGenerator',
'example/generator',
'platform/features/my-items',
'platform/persistence/local',
+ 'tutorials/todo'
].forEach(
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
openmct.install(openmct.plugins.myItems);
openmct.install(openmct.plugins.localStorage);
openmct.install(openmct.plugins.espresso);
openmct.start();
});
</script>
@ -283,6 +280,7 @@ __index.html__
</div>
</body>
</html>
```
__index.html__
@ -322,7 +320,7 @@ define([
+ {
+ "key": "example.todo",
+ "name": "To-Do List",
+ "cssclass": "icon-check",
+ "cssClass": "icon-check",
+ "description": "A list of things that need to be done.",
+ "features": ["creation"]
+ }
@ -342,8 +340,9 @@ Going through the properties we've defined:
domain objects of this type.
* The `name` of "To-Do List" is the human-readable name for this type, and will
be shown to users.
* The `glyph` refers to a special character in Open MCT's custom font set;
this will be used as an icon.
* The `cssClass` maps to an icon that will be shown for each To-Do List. The icons
are defined in our [custom open MCT icon set](https://github.com/nasa/openmct/blob/master/platform/commonUI/general/res/sass/_glyphs.scss).
A complete list of available icons will be provided in the future.
* The `description` is also human-readable, and will be used whenever a longer
explanation of what this type is should be shown.
* Finally, the `features` property describes some special features of objects of
@ -417,7 +416,7 @@ define([
{
"key": "example.todo",
"name": "To-Do List",
"cssclass": "icon-check",
"cssClass": "icon-check",
"description": "A list of things that need to be done.",
"features": ["creation"]
}
@ -426,7 +425,7 @@ define([
+ {
+ "key": "example.todo",
+ "type": "example.todo",
+ "cssclass": "icon-check",
+ "cssClass": "icon-check",
+ "name": "List",
+ "templateUrl": "templates/todo.html",
+ "editable": true
@ -448,7 +447,7 @@ the domain object type, but could have chosen any unique name.
domain objects of that type. This means that we'll see this view for To-do Lists
that we create, but not for other domain objects (such as Folders.)
* The `glyph` and `name` properties describe the icon and human-readable name
* The `cssClass` and `name` properties describe the icon and human-readable name
for this view to display in the UI where needed (if multiple views are available
for To-do Lists, the user will be able to choose one.)
@ -474,7 +473,7 @@ define([
{
"key": "example.todo",
"name": "To-Do List",
"cssclass": "icon-check",
"cssClass": "icon-check",
"description": "A list of things that need to be done.",
"features": ["creation"],
+ "model": {
@ -489,7 +488,7 @@ define([
{
"key": "example.todo",
"type": "example.todo",
"cssclass": "icon-check",
"cssClass": "icon-check",
"name": "List",
"templateUrl": "templates/todo.html",
"editable": true
@ -648,7 +647,7 @@ define([
{
"key": "example.todo",
"name": "To-Do List",
"cssclass": "icon-check",
"cssClass": "icon-check",
"description": "A list of things that need to be done.",
"features": ["creation"],
"model": {
@ -663,7 +662,7 @@ define([
{
"key": "example.todo",
"type": "example.todo",
"cssclass": "icon-check",
"cssClass": "icon-check",
"name": "List",
"templateUrl": "templates/todo.html",
"editable": true
@ -742,7 +741,7 @@ define([
{
"key": "example.todo",
"name": "To-Do List",
"cssclass": "icon-check",
"cssClass": "icon-check",
"description": "A list of things that need to be done.",
"features": ["creation"],
"model": {
@ -757,7 +756,7 @@ define([
{
"key": "example.todo",
"type": "example.todo",
"cssclass": "icon-check",
"cssClass": "icon-check",
"name": "List",
"templateUrl": "templates/todo.html",
"editable": true,
@ -767,7 +766,7 @@ define([
+ "items": [
+ {
+ "text": "Add Task",
+ "cssclass": "icon-plus",
+ "cssClass": "icon-plus",
+ "method": "addTask",
+ "control": "button"
+ }
@ -776,7 +775,7 @@ define([
+ {
+ "items": [
+ {
+ "cssclass": "icon-trash",
+ "cssClass": "icon-trash",
+ "method": "removeTask",
+ "control": "button"
+ }
@ -972,7 +971,7 @@ define([
{
"key": "example.todo",
"name": "To-Do List",
"cssclass": "icon-check",
"cssClass": "icon-check",
"description": "A list of things that need to be done.",
"features": ["creation"],
"model": {
@ -987,7 +986,7 @@ define([
{
"key": "example.todo",
"type": "example.todo",
"cssclass": "icon-check",
"cssClass": "icon-check",
"name": "List",
"templateUrl": "templates/todo.html",
"editable": true,
@ -997,7 +996,7 @@ define([
"items": [
{
"text": "Add Task",
"cssclass": "icon-plus",
"cssClass": "icon-plus",
"method": "addTask",
"control": "button"
}
@ -1006,7 +1005,7 @@ define([
{
"items": [
{
"cssclass": "icon-trash",
"cssClass": "icon-trash",
"method": "removeTask",
"control": "button"
}
@ -1237,7 +1236,7 @@ define([
{
"key": "example.todo",
"name": "To-Do List",
"cssclass": "icon-check",
"cssClass": "icon-check",
"description": "A list of things that need to be done.",
"features": ["creation"],
"model": {
@ -1249,7 +1248,7 @@ define([
{
"key": "example.todo",
"type": "example.todo",
"cssclass": "icon-check",
"cssClass": "icon-check",
"name": "List",
"templateUrl": "templates/todo.html",
"editable": true,
@ -1259,7 +1258,7 @@ define([
"items": [
{
"text": "Add Task",
"cssclass": "icon-plus",
"cssClass": "icon-plus",
"method": "addTask",
"control": "button"
}
@ -1268,7 +1267,7 @@ define([
{
"items": [
{
"cssclass": "icon-trash",
"cssClass": "icon-trash",
"method": "removeTask",
"control": "button"
}
@ -1375,7 +1374,7 @@ define([
{
"name": "Bar Graph",
"key": "example.bargraph",
"cssclass": "icon-autoflow-tabular",
"cssClass": "icon-autoflow-tabular",
"templateUrl": "templates/bargraph.html",
"needs": [ "telemetry" ],
"delegation": true
@ -1678,7 +1677,7 @@ define([
{
"name": "Bar Graph",
"key": "example.bargraph",
"cssclass": "icon-autoflow-tabular",
"cssClass": "icon-autoflow-tabular",
"templateUrl": "templates/bargraph.html",
"needs": [ "telemetry" ],
"delegation": true
@ -1844,7 +1843,7 @@ define([
{
"name": "Bar Graph",
"key": "example.bargraph",
"cssclass": "icon-autoflow-tabular",
"cssClass": "icon-autoflow-tabular",
"templateUrl": "templates/bargraph.html",
"needs": [ "telemetry" ],
"delegation": true,
@ -2321,7 +2320,7 @@ define([
{
"name": "Spacecraft",
"key": "example.spacecraft",
"cssclass": "icon-object"
"cssClass": "icon-object"
}
],
"roots": [
@ -2394,13 +2393,12 @@ If we include this in our set of active bundles:
'example/imagery',
'example/eventGenerator',
'example/generator',
'platform/features/my-items',
'platform/persistence/local',
'tutorials/telemetry'
].forEach(
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
openmct.install(openmct.plugins.myItems);
openmct.install(openmct.plugins.localStorage);
openmct.install(openmct.plugins.espresso);
openmct.start();
});
</script>
@ -2418,7 +2416,6 @@ If we include this in our set of active bundles:
</body>
</html>
```
__index.html__
@ -2622,65 +2619,69 @@ again later in Steps 3 and 4.
This allows our telemetry dictionary to be expressed as domain object models
(and, in turn, as domain objects), but these objects still aren't reachable. To
fix this, we will need another script which will expose these subsystems from the
fix this, we will need another script which will add these subsystems to the
root-level object we added in Step 1.
```js
/*global define*/
define(
[
'../../../src/api/composition/DefaultCompositionProvider',
'../../../src/api/objects/object-utils'
],
function (
DefaultCompositionProvider,
objectUtils
) {
var TAXONOMY_SPACE = "example",
TAXONOMY_KEY = "sc",
SUBSYSTEM_SPACE = "example_tlm";
function () {
"use strict";
function ExampleTelemetryCompositionProvider(openmct) {
this.openmct = openmct;
DefaultCompositionProvider.call(this, openmct);
var TAXONOMY_ID = "example:sc",
PREFIX = "example_tlm:";
function ExampleTelemetryInitializer(adapter, objectService) {
// Generate a domain object identifier for a dictionary element
function makeId(element) {
return PREFIX + element.identifier;
}
ExampleTelemetryCompositionProvider.prototype = Object.create(DefaultCompositionProvider.prototype);
ExampleTelemetryCompositionProvider.prototype.appliesTo = function (domainObject) {
return domainObject.identifier &&
domainObject.identifier.namespace === TAXONOMY_SPACE &&
domainObject.identifier.key === TAXONOMY_KEY;
};
ExampleTelemetryCompositionProvider.prototype.load = function (domainObject) {
var adapter = this.openmct.$injector.get('example.adapter');
function makeId(subsystem) {
return objectUtils.parseKeyString(SUBSYSTEM_SPACE + ":" + subsystem.identifier);
// When the dictionary is available, add all subsystems
// to the composition of My Spacecraft
function initializeTaxonomy(dictionary) {
// Get the top-level container for dictionary objects
// from a group of domain objects.
function getTaxonomyObject(domainObjects) {
return domainObjects[TAXONOMY_ID];
}
return adapter.dictionary().then(function (dictionary) {
return dictionary.subsystems.map(makeId);
});
};
return ExampleTelemetryCompositionProvider;
// Populate
function populateModel(taxonomyObject) {
return taxonomyObject.useCapability(
"mutation",
function (model) {
model.name =
dictionary.name;
model.composition =
dictionary.subsystems.map(makeId);
}
);
}
// Look up My Spacecraft, and populate it accordingly.
objectService.getObjects([TAXONOMY_ID])
.then(getTaxonomyObject)
.then(populateModel);
}
adapter.dictionary().then(initializeTaxonomy);
}
return ExampleTelemetryInitializer;
}
);
```
__tutorials/telemetry/src/ExampleTelemetryCompositionProvider.js__
__tutorials/telemetry/src/ExampleTelemetryInitializer.js__
At the conclusion of Step 1, the top-level My Spacecraft object was empty. Here
we define a composition provider that allows an object to provide a list of
its children asynchronously. The children are represented with an array of their
identifiers.
You may notice that the code snippet below, the method of registering a composition
provider is a little different to the other components defined in these tutorials.
The registration mechanism for Composition Providers reflects [changes to our API that are currently underway](https://github.com/nasa/openmct#new-api).
More details will be available about Composition Providers and other aspects of
our new API in the near future.
At the conclusion of Step 1, the top-level My Spacecraft object was empty. This
script will wait for the dictionary to be loaded, then load My Spacecraft (by
its identifier), and "mutate" it. The `mutation` capability allows changes to be
made to a domain object's model. Here, we take this top-level object, update its
name to match what was in the dictionary, and set its `composition` to an array
of domain object identifiers for all subsystems contained in the dictionary
(using the same identifier prefix as before.)
Finally, we wire in these changes by modifying our plugin's `bundle.js` to
provide metadata about how these pieces interact (both with each other, and
@ -2690,12 +2691,12 @@ with the platform):
define([
'openmct',
+ './src/ExampleTelemetryServerAdapter',
+ './src/ExampleTelemetryCompositionProvider',
+ './src/ExampleTelemetryInitializer',
+ './src/ExampleTelemetryModelProvider'
], function (
openmct,
+ ExampleTelemetryServerAdapter,
+ ExampleTelemetryCompositionProvider,
+ ExampleTelemetryInitializer,
+ ExampleTelemetryModelProvider
) {
openmct.legacyRegistry.register("tutorials/telemetry", {
@ -2705,18 +2706,18 @@ define([
{
"name": "Spacecraft",
"key": "example.spacecraft",
"cssclass": "icon-object"
"cssClass": "icon-object"
},
+ {
+ "name": "Subsystem",
+ "key": "example.subsystem",
+ "cssclass": "icon-object",
+ "cssClass": "icon-object",
+ "model": { "composition": [] }
+ },
+ {
+ "name": "Measurement",
+ "key": "example.measurement",
+ "cssclass": "icon-telemetry",
+ "cssClass": "icon-telemetry",
+ "model": { "telemetry": {} },
+ "telemetry": {
+ "source": "example.source",
@ -2760,6 +2761,12 @@ define([
+ "value": "ws://localhost:8081"
+ }
+ ],
+ "runs": [
+ {
+ "implementation": ExampleTelemetryInitializer,
+ "depends": [ "example.adapter", "objectService" ]
+ }
+ ],
+ "components": [
+ {
+ "provides": "modelService",
@ -2770,7 +2777,6 @@ define([
+ ]
}
});
+ openmct.composition.addProvider(new ExampleTelemetryCompositionProvider(openmct));
});
```
__tutorials/telemetry/bundle.js__
@ -2796,15 +2802,16 @@ plugin.
`example.server`. Setting `priority` to `fallback` means this constant will be
overridden if defined anywhere else, allowing configuration bundles to specify
different URLs for the WebSocket connection.
* A Composition Provider is defined, which will expose the spacecraft's
subsystems and telemetry as objects in the tree.
* The initializer script is registered using the `runs` category of extension,
to ensure that this executes (and populates the contents of the top-level My
Spacecraft object) once Open MCT is started.
* This depends upon the `example.adapter` service we exposed above, as well
as Angular's `$q`; these services will be made available in the constructor
call.
* Finally, the `modelService` provider which presents dictionary elements as
domain object models is exposed. Since `modelService` is a composite service,
this is registered under the extension category `components`.
* As with the composition provider, this depends upon the `example.adapter` service
* As with the initializer, this depends upon the `example.adapter` service
we exposed above, as well as Angular's `$q`; these services will be made
available in the constructor call.
@ -3009,12 +3016,12 @@ Finally, we expose this `telemetryService` provider declaratively:
define([
'openmct',
'./src/ExampleTelemetryServerAdapter',
'./src/ExampleTelemetryCompositionProvider',
'./src/ExampleTelemetryInitializer',
'./src/ExampleTelemetryModelProvider'
], function (
openmct,
ExampleTelemetryServerAdapter,
ExampleTelemetryCompositionProvider,
ExampleTelemetryInitializer,
ExampleTelemetryModelProvider
) {
openmct.legacyRegistry.register("tutorials/telemetry", {
@ -3024,18 +3031,18 @@ define([
{
"name": "Spacecraft",
"key": "example.spacecraft",
"cssclass": "icon-object"
"cssClass": "icon-object"
},
{
"name": "Subsystem",
"key": "example.subsystem",
"cssclass": "icon-object",
"cssClass": "icon-object",
"model": { "composition": [] }
},
{
"name": "Measurement",
"key": "example.measurement",
"cssclass": "icon-telemetry",
"cssClass": "icon-telemetry",
"model": { "telemetry": {} },
"telemetry": {
"source": "example.source",
@ -3079,6 +3086,12 @@ define([
"value": "ws://localhost:8081"
}
],
"runs": [
{
"implementation": "ExampleTelemetryInitializer.js",
"depends": [ "example.adapter", "objectService" ]
}
],
"components": [
{
"provides": "modelService",
@ -3095,7 +3108,6 @@ define([
]
}
});
openmct.composition.addProvider(new ExampleTelemetryCompositionProvider(openmct));
});
```
__tutorials/telemetry/bundle.js__

View File

@ -49,7 +49,7 @@ define([
{
"key": "eventGenerator",
"name": "Event Message Generator",
"cssclass": "icon-folder-new",
"cssClass": "icon-folder-new",
"description": "For development use. Creates sample event message data that mimics a live data stream.",
"priority": 10,
"features": "creation",

View File

@ -36,7 +36,7 @@ define([
"name": "Export Telemetry as CSV",
"implementation": ExportTelemetryAsCSVAction,
"category": "contextual",
"cssclass": "icon-download",
"cssClass": "icon-download",
"depends": [ "exportService" ]
}
]

View File

@ -41,6 +41,10 @@ define([
return domainObject.type === 'generator';
};
GeneratorProvider.prototype.supportsRequest =
GeneratorProvider.prototype.supportsSubscribe =
GeneratorProvider.prototype.canProvideTelemetry;
GeneratorProvider.prototype.makeWorkerRequest = function (domainObject, request) {
var props = [
'amplitude',

View File

@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/*global define*/
define({
START_TIME: Date.now() - 24 * 60 * 60 * 1000 // Now minus a day.

View File

@ -19,12 +19,11 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define,Promise*/
/*global define*/
define(
['./SinewaveConstants', 'moment'],
function (SinewaveConstants, moment) {
"use strict";
var START_TIME = SinewaveConstants.START_TIME,
FORMAT_REGEX = /^-?\d+:\d+:\d+$/,

View File

@ -1,183 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define([
"./src/SinewaveTelemetryProvider",
"./src/SinewaveLimitCapability",
"./src/SinewaveDeltaFormat",
'legacyRegistry'
], function (
SinewaveTelemetryProvider,
SinewaveLimitCapability,
SinewaveDeltaFormat,
legacyRegistry
) {
"use strict";
legacyRegistry.register("example/generator", {
"name": "Sine Wave Generator",
"description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
"extensions": {
"components": [
{
"implementation": SinewaveTelemetryProvider,
"type": "provider",
"provides": "telemetryService",
"depends": [
"$q",
"$timeout"
]
}
],
"capabilities": [
{
"key": "limit",
"implementation": SinewaveLimitCapability
}
],
"formats": [
{
"key": "example.delta",
"implementation": SinewaveDeltaFormat
}
],
"constants": [
{
"key": "TIME_CONDUCTOR_DOMAINS",
"value": [
{
"key": "time",
"name": "Time"
},
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta",
"format": "example.delta"
}
],
"priority": -1
}
],
"types": [
{
"key": "generator",
"name": "Sine Wave Generator",
"cssclass": "icon-telemetry",
"description": "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
"priority": 10,
"features": "creation",
"model": {
"telemetry": {
"period": 10,
"amplitude": 1,
"offset": 0,
"dataRateInHz": 1
}
},
"telemetry": {
"source": "generator",
"domains": [
{
"key": "utc",
"name": "Time"
},
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta",
"format": "example.delta"
}
],
"ranges": [
{
"key": "sin",
"name": "Sine"
},
{
"key": "cos",
"name": "Cosine"
}
]
},
"properties": [
{
"name": "Period",
"control": "textfield",
"cssclass": "l-input-sm l-numeric",
"key": "period",
"required": true,
"property": [
"telemetry",
"period"
],
"pattern": "^\\d*(\\.\\d*)?$"
},
{
"name": "Amplitude",
"control": "textfield",
"cssclass": "l-input-sm l-numeric",
"key": "amplitude",
"required": true,
"property": [
"telemetry",
"amplitude"
],
"pattern": "^\\d*(\\.\\d*)?$"
},
{
"name": "Offset",
"control": "textfield",
"cssclass": "l-input-sm l-numeric",
"key": "offset",
"required": true,
"property": [
"telemetry",
"offset"
],
"pattern": "^\\d*(\\.\\d*)?$"
},
{
"name": "Data Rate (hz)",
"control": "textfield",
"cssclass": "l-input-sm l-numeric",
"key": "dataRateInHz",
"required": true,
"property": [
"telemetry",
"dataRateInHz"
],
"pattern": "^\\d*(\\.\\d*)?$"
}
]
}
]
}
});
});

View File

@ -24,6 +24,7 @@
(function () {
var FIFTEEN_MINUTES = 15 * 60 * 1000;
var handlers = {
subscribe: onSubscribe,
@ -51,6 +52,7 @@
function onSubscribe(message) {
var data = message.data;
// Keep
var start = Date.now();
var step = 1000 / data.dataRateInHz;
var nextStep = start - (start % step) + step;
@ -82,8 +84,11 @@
function onRequest(message) {
var data = message.data;
if (!data.start || !data.end) {
throw new Error('missing start and end!');
if (data.end == undefined) {
data.end = Date.now();
}
if (data.start == undefined){
data.start = data.end - FIFTEEN_MINUTES;
}
var now = Date.now();

171
example/generator/plugin.js Normal file
View File

@ -0,0 +1,171 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define([
"./GeneratorProvider",
"./SinewaveLimitCapability",
"./SinewaveDeltaFormat"
], function (
GeneratorProvider,
SinewaveLimitCapability,
SinewaveDeltaFormat
) {
var legacyExtensions = {
"capabilities": [
{
"key": "limit",
"implementation": SinewaveLimitCapability
}
],
"formats": [
{
"key": "example.delta",
"implementation": SinewaveDeltaFormat
}
],
"constants": [
{
"key": "TIME_CONDUCTOR_DOMAINS",
"value": [
{
"key": "time",
"name": "Time"
},
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta"
}
],
"priority": -1
}
]
}
return function(openmct){
//Register legacy extensions for things not yet supported by the new API
Object.keys(legacyExtensions).forEach(function (type){
var extensionsOfType = legacyExtensions[type];
extensionsOfType.forEach(function (extension) {
openmct.legacyExtension(type, extension)
})
});
openmct.types.addType("generator", {
label: "Sine Wave Generator",
description: "For development use. Generates example streaming telemetry data using a simple sine wave algorithm.",
cssClass: "icon-telemetry",
creatable: true,
form: [
{
name: "Period",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "period",
required: true,
property: [
"telemetry",
"period"
],
pattern: "^\\d*(\\.\\d*)?$"
},
{
name: "Amplitude",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "amplitude",
required: true,
property: [
"telemetry",
"amplitude"
],
pattern: "^\\d*(\\.\\d*)?$"
},
{
name: "Offset",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "offset",
required: true,
property: [
"telemetry",
"offset"
],
pattern: "^\\d*(\\.\\d*)?$"
},
{
name: "Data Rate (hz)",
control: "textfield",
cssClass: "l-input-sm l-numeric",
key: "dataRateInHz",
required: true,
property: [
"telemetry",
"dataRateInHz"
],
pattern: "^\\d*(\\.\\d*)?$"
}
],
initialize: function (object) {
object.telemetry = {
period: 10,
amplitude: 1,
offset: 0,
dataRateInHz: 1,
domains: [
{
key: "utc",
name: "Time",
format: "utc"
},
{
key: "yesterday",
name: "Yesterday",
format: "utc"
},
{
key: "delta",
name: "Delta",
format: "example.delta"
}
],
ranges: [
{
key: "sin",
name: "Sine"
},
{
key: "cos",
name: "Cosine"
}
]
};
}
});
openmct.telemetry.addProvider(new GeneratorProvider());
};
});

View File

@ -49,7 +49,7 @@ define([
{
"key": "imagery",
"name": "Example Imagery",
"cssclass": "icon-image",
"cssClass": "icon-image",
"features": "creation",
"description": "For development use. Creates example imagery data that mimics a live imagery stream.",
"priority": 10,

View File

@ -31,7 +31,7 @@ define(['../../../platform/features/conductor/core/src/timeSystems/LocalClock'],
this.metadata = {
key: 'test-lad',
mode: 'lad',
cssclass: 'icon-clock',
cssClass: 'icon-clock',
label: 'Latest Available Data',
name: 'Latest available data',
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'

View File

@ -41,18 +41,18 @@ define([
{
"name":"Mars Science Laboratory",
"key": "msl.curiosity",
"cssclass": "icon-object"
"cssClass": "icon-object"
},
{
"name": "Instrument",
"key": "msl.instrument",
"cssclass": "icon-object",
"cssClass": "icon-object",
"model": {"composition": []}
},
{
"name": "Measurement",
"key": "msl.measurement",
"cssclass": "icon-telemetry",
"cssClass": "icon-telemetry",
"model": {"telemetry": {}},
"telemetry": {
"source": "rems.source",

View File

@ -81,7 +81,7 @@ define([
{
"key": "plot",
"name": "Example Telemetry Plot",
"cssclass": "icon-telemetry-panel",
"cssClass": "icon-telemetry-panel",
"description": "For development use. A plot for displaying telemetry.",
"priority": 10,
"delegates": [
@ -129,7 +129,7 @@ define([
{
"name": "Period",
"control": "textfield",
"cssclass": "l-input-sm l-numeric",
"cssClass": "l-input-sm l-numeric",
"key": "period",
"required": true,
"property": [

View File

@ -63,7 +63,7 @@ define(
* Get the CSS class that defines the icon
* to display in this indicator. This will appear
* as a dataflow icon.
* @returns {string} the cssclass of the dataflow icon
* @returns {string} the cssClass of the dataflow icon
*/
getCssClass: function () {
return "icon-connectivity";

View File

@ -69,6 +69,11 @@ var gulp = require('gulp'),
}
};
if (process.env.NODE_ENV === 'development') {
options.requirejsOptimize.optimize = 'none';
}
gulp.task('scripts', function () {
var requirejsOptimize = require('gulp-requirejs-optimize');
var replace = require('gulp-replace-task');

View File

@ -25,20 +25,32 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title></title>
<script src="openmct-tutorial/lib/http.js">
</script>
<script src="bower_components/requirejs/require.js">
</script>
<script src="openmct-tutorial/dictionary-plugin.js">
</script>
<script src="openmct-tutorial/realtime-telemetry-plugin.js">
</script>
<script src="openmct-tutorial/historical-telemetry-plugin.js">
</script>
<script>
require(['openmct'], function (openmct) {
[
'example/imagery',
'example/eventGenerator',
'example/generator'
'example/eventGenerator'
].forEach(
openmct.legacyRegistry.enable.bind(openmct.legacyRegistry)
);
openmct.install(openmct.plugins.myItems);
openmct.install(openmct.plugins.localStorage);
openmct.install(openmct.plugins.espresso);
openmct.install(openmct.plugins.MyItems());
openmct.install(openmct.plugins.LocalStorage());
openmct.install(openmct.plugins.Espresso());
openmct.install(openmct.plugins.Generator());
openmct.install(openmct.plugins.UTCTimeSystem());
openmct.install(DictionaryPlugin());
openmct.install(RealtimeTelemetryPlugin());
openmct.install(HistoricalTelemetryPlugin());
openmct.start();
});
</script>

1
openmct-tutorial Submodule

Submodule openmct-tutorial added at 7076a15d3a

View File

@ -84,5 +84,11 @@ define([
return new Main().run(defaultRegistry);
});
// For now, install conductor by default
openmct.install(openmct.plugins.Conductor({
showConductor: false
}));
return openmct;
});

View File

@ -226,7 +226,7 @@ define([
"$window"
],
"group": "windowing",
"cssclass": "icon-new-window",
"cssClass": "icon-new-window",
"priority": "preferred"
},
{
@ -241,7 +241,7 @@ define([
{
"key": "items",
"name": "Items",
"cssclass": "icon-thumbs-strip",
"cssClass": "icon-thumbs-strip",
"description": "Grid of available items",
"template": itemsTemplate,
"uses": [

View File

@ -43,24 +43,24 @@ define([], function () {
return context.getParent();
}
function isOrphan(domainObject) {
var parent = getParent(domainObject),
composition = parent.getModel().composition,
id = domainObject.getId();
return !composition || (composition.indexOf(id) === -1);
}
function navigateToParent(domainObject) {
function preventOrphanNavigation(domainObject) {
var parent = getParent(domainObject);
return parent.getCapability('action').perform('navigate');
parent.useCapability('composition')
.then(function (composees) {
var isOrphan = composees.every(function (c) {
return c.getId() !== domainObject.getId();
});
if (isOrphan) {
parent.getCapability('action').perform('navigate');
}
});
}
function checkNavigation() {
var navigatedObject = navigationService.getNavigation();
if (navigatedObject.hasCapability('context') &&
isOrphan(navigatedObject)) {
if (navigatedObject.hasCapability('context')) {
if (!navigatedObject.getCapability('editor').isEditContextRoot()) {
navigateToParent(navigatedObject);
preventOrphanNavigation(navigatedObject);
}
}
}

View File

@ -46,12 +46,12 @@ define(
};
FullscreenAction.prototype.getMetadata = function () {
// We override getMetadata, because the icon cssclass and
// We override getMetadata, because the icon cssClass and
// description need to be determined at run-time
// based on whether or not we are currently
// full screen.
var metadata = Object.create(FullscreenAction);
metadata.cssclass = screenfull.isFullscreen ? "icon-fullscreen-expand" : "icon-fullscreen-collapse";
metadata.cssClass = screenfull.isFullscreen ? "icon-fullscreen-expand" : "icon-fullscreen-collapse";
metadata.description = screenfull.isFullscreen ?
EXIT_FULLSCREEN : ENTER_FULLSCREEN;
metadata.group = "windowing";

View File

@ -33,7 +33,7 @@ define([
mockContext,
mockActionCapability,
mockEditor,
testParentModel,
testParentComposition,
testId,
mockThrottledFns;
@ -41,7 +41,6 @@ define([
testId = 'some-identifier';
mockThrottledFns = [];
testParentModel = {};
mockTopic = jasmine.createSpy('topic');
mockThrottle = jasmine.createSpy('throttle');
@ -55,14 +54,12 @@ define([
mockDomainObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'getModel',
'hasCapability'
]);
mockParentObject = jasmine.createSpyObj('domainObject', [
'getId',
'getCapability',
'getModel',
'hasCapability'
'useCapability'
]);
mockContext = jasmine.createSpyObj('context', ['getParent']);
mockActionCapability = jasmine.createSpyObj('action', ['perform']);
@ -75,9 +72,7 @@ define([
mockThrottledFns.push(mockThrottledFn);
return mockThrottledFn;
});
mockTopic.andCallFake(function (k) {
return k === 'mutation' && mockMutationTopic;
});
mockTopic.andReturn(mockMutationTopic);
mockDomainObject.getId.andReturn(testId);
mockDomainObject.getCapability.andCallFake(function (c) {
return {
@ -88,12 +83,13 @@ define([
mockDomainObject.hasCapability.andCallFake(function (c) {
return !!mockDomainObject.getCapability(c);
});
mockParentObject.getModel.andReturn(testParentModel);
mockParentObject.getCapability.andCallFake(function (c) {
return {
action: mockActionCapability
}[c];
});
testParentComposition = [];
mockParentObject.useCapability.andReturn(Promise.resolve(testParentComposition));
mockContext.getParent.andReturn(mockParentObject);
mockNavigationService.getNavigation.andReturn(mockDomainObject);
mockEditor.isEditContextRoot.andReturn(false);
@ -126,7 +122,9 @@ define([
var prefix = isOrphan ? "" : "non-";
describe("for " + prefix + "orphan objects", function () {
beforeEach(function () {
testParentModel.composition = isOrphan ? [] : [testId];
if (!isOrphan) {
testParentComposition.push(mockDomainObject);
}
});
[false, true].forEach(function (isEditRoot) {
@ -136,14 +134,32 @@ define([
function itNavigatesAsExpected() {
if (isOrphan && !isEditRoot) {
it("navigates to the parent", function () {
var done = false;
waitsFor(function () {
return done;
});
setTimeout(function () {
done = true;
}, 5);
runs(function () {
expect(mockActionCapability.perform)
.toHaveBeenCalledWith('navigate');
});
});
} else {
it("does nothing", function () {
var done = false;
waitsFor(function () {
return done;
});
setTimeout(function () {
done = true;
}, 5);
runs(function () {
expect(mockActionCapability.perform)
.not.toHaveBeenCalled();
});
});
}
}
@ -157,7 +173,6 @@ define([
mockNavigationService.addListener.mostRecentCall
.args[0](mockDomainObject);
});
itNavigatesAsExpected();
});

View File

@ -51,7 +51,7 @@ define(
});
it("provides displayable metadata", function () {
expect(action.getMetadata().cssclass).toBeDefined();
expect(action.getMetadata().cssClass).toBeDefined();
});
});

View File

@ -163,7 +163,7 @@ define([
],
"description": "Edit",
"category": "view-control",
"cssclass": "major icon-pencil"
"cssClass": "major icon-pencil"
},
{
"key": "properties",
@ -172,7 +172,7 @@ define([
"view-control"
],
"implementation": PropertiesAction,
"cssclass": "major icon-pencil",
"cssClass": "major icon-pencil",
"name": "Edit Properties...",
"description": "Edit properties of this object.",
"depends": [
@ -183,7 +183,7 @@ define([
"key": "remove",
"category": "contextual",
"implementation": RemoveAction,
"cssclass": "icon-trash",
"cssClass": "icon-trash",
"name": "Remove",
"description": "Remove this object from its containing object.",
"depends": [
@ -195,7 +195,7 @@ define([
"category": "save",
"implementation": SaveAndStopEditingAction,
"name": "Save and Finish Editing",
"cssclass": "icon-save labeled",
"cssClass": "icon-save labeled",
"description": "Save changes made to these objects.",
"depends": [
"dialogService",
@ -207,7 +207,7 @@ define([
"category": "save",
"implementation": SaveAction,
"name": "Save and Continue Editing",
"cssclass": "icon-save labeled",
"cssClass": "icon-save labeled",
"description": "Save changes made to these objects.",
"depends": [
"dialogService",
@ -219,7 +219,7 @@ define([
"category": "save",
"implementation": SaveAsAction,
"name": "Save As...",
"cssclass": "icon-save labeled",
"cssClass": "icon-save labeled",
"description": "Save changes made to these objects.",
"depends": [
"$injector",
@ -237,7 +237,7 @@ define([
// Because we use the name as label for edit buttons and mct-control buttons need
// the label to be set to undefined in order to not apply the labeled CSS rule.
"name": undefined,
"cssclass": "icon-x no-label",
"cssClass": "icon-x no-label",
"description": "Discard changes made to these objects.",
"depends": []
}

View File

@ -25,14 +25,14 @@
<li ng-repeat="createAction in createActions" ng-click="createAction.perform()">
<a ng-mouseover="representation.activeMetadata = createAction.getMetadata()"
ng-mouseleave="representation.activeMetadata = undefined"
class="menu-item-a {{ createAction.getMetadata().cssclass }}">
class="menu-item-a {{ createAction.getMetadata().cssClass }}">
{{createAction.getMetadata().name}}
</a>
</li>
</ul>
</div>
<div class="pane right menu-item-description">
<div class="desc-area icon {{ representation.activeMetadata.cssclass }}"></div>
<div class="desc-area icon {{ representation.activeMetadata.cssClass }}"></div>
<div class="desc-area title">
{{representation.activeMetadata.name}}
</div>

View File

@ -26,7 +26,7 @@
structure="{
text: saveActions[0].getMetadata().name,
click: actionPerformer(saveActions[0]),
cssclass: 'major ' + saveActions[0].getMetadata().cssclass
cssClass: 'major ' + saveActions[0].getMetadata().cssClass
}">
</mct-control>
</span>
@ -36,7 +36,7 @@
structure="{
options: saveActionsAsMenuOptions,
click: saveActionMenuClickHandler,
cssclass: 'btn-bar right icon-save no-label major'
cssClass: 'btn-bar right icon-save no-label major'
}">
</mct-control>
</span>
@ -46,7 +46,7 @@
structure="{
text: currentAction.getMetadata().name,
click: actionPerformer(currentAction),
cssclass: currentAction.getMetadata().cssclass
cssClass: currentAction.getMetadata().cssClass
}">
</mct-control>
</span>

View File

@ -48,9 +48,10 @@ define(
* Decorate PersistenceCapability to queue persistence calls when a
* transaction is in progress.
*/
TransactionCapabilityDecorator.prototype.getCapabilities = function (model) {
TransactionCapabilityDecorator.prototype.getCapabilities = function () {
var self = this,
capabilities = this.capabilityService.getCapabilities(model),
capabilities = this.capabilityService.getCapabilities
.apply(this.capabilityService, arguments),
persistenceCapability = capabilities.persistence;
capabilities.persistence = function (domainObject) {

View File

@ -41,7 +41,7 @@ define(
return {
key: action,
name: action.getMetadata().name,
cssclass: action.getMetadata().cssclass
cssClass: action.getMetadata().cssClass
};
}

View File

@ -51,7 +51,7 @@ define(
function AddAction(type, parent, context, $q, dialogService, policyService) {
this.metadata = {
key: 'add',
cssclass: type.getCssClass(),
cssClass: type.getCssClass(),
name: type.getName(),
type: type.getKey(),
description: type.getDescription(),

View File

@ -54,8 +54,7 @@ define(
AddActionProvider.prototype.getActions = function (actionContext) {
var context = actionContext || {},
key = context.key,
destination = context.domainObject,
self = this;
destination = context.domainObject;
// We only provide Add actions, and we need a
// domain object to serve as the container for the
@ -66,18 +65,16 @@ define(
}
// Introduce one create action per type
return this.typeService.listTypes().filter(function (type) {
return self.policyService.allow("creation", type) && self.policyService.allow("composition", destination.getCapability('type'), type);
}).map(function (type) {
return ['timeline', 'activity'].map(function (type) {
return new AddAction(
type,
this.typeService.getType(type),
destination,
context,
self.$q,
self.dialogService,
self.policyService
this.$q,
this.dialogService,
this.policyService
);
});
}, this);
};
return AddActionProvider;

View File

@ -47,7 +47,7 @@ define(
function CreateAction(type, parent, context) {
this.metadata = {
key: 'create',
cssclass: type.getCssClass(),
cssClass: type.getCssClass(),
name: type.getName(),
type: type.getKey(),
description: type.getDescription(),

View File

@ -56,16 +56,16 @@ define(
*/
CreateWizard.prototype.getFormStructure = function (includeLocation) {
var sections = [],
type = this.type,
domainObject = this.domainObject,
policyService = this.policyService;
function validateLocation(locatingObject) {
var locatingType = locatingObject &&
locatingObject.getCapability('type');
return locatingType && policyService.allow(
function validateLocation(parent) {
var parentType = parent &&
parent.getCapability('type');
return parentType && policyService.allow(
"composition",
locatingType,
type
parentType,
domainObject
);
}
@ -91,7 +91,7 @@ define(
if (includeLocation) {
sections.push({
name: 'Location',
cssclass: "grows",
cssClass: "grows",
rows: [{
name: "Save In",
control: "locator",

View File

@ -28,7 +28,7 @@ define(
describe("The Edit Action controller", function () {
var mockSaveActionMetadata = {
name: "mocked-save-action",
cssclass: "mocked-save-action-css"
cssClass: "mocked-save-action-css"
};
function fakeGetActions(actionContext) {
@ -86,7 +86,7 @@ define(
expect(menuOptions[1].key).toEqual(mockScope.saveActions[1]);
menuOptions.forEach(function (option) {
expect(option.name).toEqual(mockSaveActionMetadata.name);
expect(option.cssclass).toEqual(mockSaveActionMetadata.cssclass);
expect(option.cssClass).toEqual(mockSaveActionMetadata.cssClass);
});
});

View File

@ -31,9 +31,7 @@ define(
var mockTypeService,
mockDialogService,
mockPolicyService,
mockCreationPolicy,
mockCompositionPolicy,
mockPolicyMap = {},
mockTypeMap,
mockTypes,
mockDomainObject,
mockQ,
@ -55,49 +53,33 @@ define(
);
mockType.hasFeature.andReturn(true);
mockType.getName.andReturn(name);
mockType.getKey.andReturn(name);
return mockType;
}
beforeEach(function () {
mockTypeService = jasmine.createSpyObj(
"typeService",
["listTypes"]
);
mockDialogService = jasmine.createSpyObj(
"dialogService",
["getUserInput"]
);
mockPolicyService = jasmine.createSpyObj(
"policyService",
["allow"]
["getType"]
);
mockDialogService = {};
mockPolicyService = {};
mockDomainObject = {};
mockDomainObject = jasmine.createSpyObj(
"domainObject",
["getCapability"]
);
//Mocking getCapability because AddActionProvider uses the
// type capability of the destination object.
mockDomainObject.getCapability.andReturn({});
mockTypes = ["A", "B", "C"].map(createMockType);
mockTypes = [
"timeline",
"activity",
"other"
].map(createMockType);
mockTypeMap = {};
mockTypes.forEach(function (type) {
mockPolicyMap[type.getName()] = true;
mockTypeMap[type.getKey()] = type;
});
mockCreationPolicy = function (type) {
return mockPolicyMap[type.getName()];
};
mockCompositionPolicy = function () {
return true;
};
mockPolicyService.allow.andReturn(true);
mockTypeService.listTypes.andReturn(mockTypes);
mockTypeService.getType.andCallFake(function (key) {
return mockTypeMap[key];
});
provider = new AddActionProvider(
mockQ,
@ -107,29 +89,16 @@ define(
);
});
it("checks for creatability", function () {
provider.getActions({
it("provides actions for timeline and activity", function () {
var actions = provider.getActions({
key: "add",
domainObject: mockDomainObject
});
expect(actions.length).toBe(2);
expect(actions[0].metadata.type).toBe('timeline');
expect(actions[1].metadata.type).toBe('activity');
// Make sure it was creation which was used to check
expect(mockPolicyService.allow)
.toHaveBeenCalledWith("creation", mockTypes[0]);
});
it("checks for composability of type", function () {
provider.getActions({
key: "add",
domainObject: mockDomainObject
});
expect(mockPolicyService.allow).toHaveBeenCalledWith(
"composition",
jasmine.any(Object),
jasmine.any(Object)
);
expect(mockDomainObject.getCapability).toHaveBeenCalledWith('type');
});
});
}

View File

@ -138,7 +138,7 @@ define(
expect(metadata.name).toEqual("Test");
expect(metadata.description).toEqual("a test type");
expect(metadata.cssclass).toEqual("icon-telemetry");
expect(metadata.cssClass).toEqual("icon-telemetry");
});
describe("the perform function", function () {

View File

@ -175,7 +175,7 @@ define(
expect(mockPolicyService.allow).toHaveBeenCalledWith(
'composition',
mockOtherType,
mockType
mockDomainObject
);
});

View File

@ -19,6 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "logo-and-bg";

View File

@ -19,7 +19,7 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<a class="s-button key-{{parameters.action.getMetadata().key}} {{parameters.action.getMetadata().cssclass}}"
<a class="s-button key-{{parameters.action.getMetadata().key}} {{parameters.action.getMetadata().cssClass}}"
ng-class="{ labeled: parameters.labeled }"
title="{{parameters.action.getMetadata().description}}"
ng-click="parameters.action.perform()">

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information.
-->
<span ng-controller="ViewSwitcherController">
<div class="view-switcher menu-element s-menu-button {{ngModel.selected.cssclass}}"
<div class="view-switcher menu-element s-menu-button {{ngModel.selected.cssClass}}"
ng-if="view.length > 1"
ng-controller="ClickAwayController as toggle">
@ -33,7 +33,7 @@
<ul>
<li ng-repeat="option in view"
ng-click="ngModel.selected = option; toggle.setState(false)"
class="{{option.cssclass}}">
class="{{option.cssClass}}">
{{option.name}}
</li>
</ul>

View File

@ -25,7 +25,7 @@
<li ng-repeat="menuAction in menuActions"
ng-click="menuAction.perform()"
title="{{menuAction.getMetadata().description}}"
class="{{menuAction.getMetadata().cssclass}}">
class="{{menuAction.getMetadata().cssClass}}">
{{menuAction.getMetadata().name}}
</li>
</ul>

View File

@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "../../../../general/res/sass/_mixins";

View File

@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "../../../../general/res/sass/_mixins";

View File

@ -40,9 +40,6 @@ define([
{
"category": "composition",
"implementation": CompositionPolicy,
"depends": [
"$injector"
],
"message": "Objects of this type cannot contain objects of that type."
},
{

View File

@ -1,77 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
[],
function () {
/**
* Build a table indicating which types are expected to expose
* which capabilities. This supports composition policy (rules
* for which objects can contain which other objects) which
* sometimes is determined based on the presence of capabilities.
* @constructor
* @memberof platform/containment
*/
function CapabilityTable(typeService, capabilityService) {
var self = this;
// Build an initial model for a type
function buildModel(type) {
var model = Object.create(type.getInitialModel() || {});
model.type = type.getKey();
return model;
}
// Get capabilities expected for this type
function getCapabilities(type) {
return capabilityService.getCapabilities(buildModel(type));
}
// Populate the lookup table for this type's capabilities
function addToTable(type) {
var typeKey = type.getKey();
Object.keys(getCapabilities(type)).forEach(function (key) {
self.table[key] = self.table[key] || {};
self.table[key][typeKey] = true;
});
}
// Build the table
this.table = {};
(typeService.listTypes() || []).forEach(addToTable);
}
/**
* Check if a type is expected to expose a specific capability.
* @param {string} typeKey the type identifier
* @param {string} capabilityKey the capability identifier
* @returns {boolean} true if expected to be exposed
*/
CapabilityTable.prototype.hasCapability = function (typeKey, capabilityKey) {
return (this.table[capabilityKey] || {})[typeKey];
};
return CapabilityTable;
}
);

View File

@ -45,9 +45,7 @@ define(
ComposeActionPolicy.prototype.allowComposition = function (containerObject, selectedObject) {
// Get the object types involved in the compose action
var containerType = containerObject &&
containerObject.getCapability('type'),
selectedType = selectedObject &&
selectedObject.getCapability('type');
containerObject.getCapability('type');
// Get a reference to the policy service if needed...
this.policyService = this.policyService || this.getPolicyService();
@ -57,7 +55,7 @@ define(
this.policyService.allow(
'composition',
containerType,
selectedType
selectedObject
);
};

View File

@ -26,8 +26,8 @@
* @namespace platform/containment
*/
define(
['./ContainmentTable'],
function (ContainmentTable) {
[],
function () {
/**
* Defines composition policy as driven by type metadata.
@ -35,21 +35,33 @@ define(
* @memberof platform/containment
* @implements {Policy.<Type, Type>}
*/
function CompositionPolicy($injector) {
// We're really just wrapping the containment table and rephrasing
// it as a policy decision.
var table;
this.getTable = function () {
return (table = table || new ContainmentTable(
$injector.get('typeService'),
$injector.get('capabilityService')
));
};
function CompositionPolicy() {
}
CompositionPolicy.prototype.allow = function (candidate, context) {
return this.getTable().canContain(candidate, context);
CompositionPolicy.prototype.allow = function (parentType, child) {
var parentDef = parentType.getDefinition();
// A parent without containment rules can contain anything.
if (!parentDef.contains) {
return true;
}
// If any containment rule matches context type, the candidate
// can contain this type.
return parentDef.contains.some(function (c) {
// Simple containment rules are supported typeKeys.
if (typeof c === 'string') {
return c === child.getCapability('type').getKey();
}
// More complicated rules require context to have all specified
// capabilities.
if (!Array.isArray(c.has)) {
c.has = [c.has];
}
return c.has.every(function (capability) {
return child.hasCapability(capability);
});
});
};
return CompositionPolicy;

View File

@ -1,116 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['./CapabilityTable'],
function (CapabilityTable) {
// Symbolic value for the type table for cases when any type
// is allowed to be contained.
var ANY = true;
/**
* Supports composition policy by maintaining a table of
* domain object types, to determine if they can contain
* other domain object types. This is determined at application
* start time (plug-in support means this cannot be determined
* prior to that, but we don't want to redo these calculations
* every time policy is checked.)
* @constructor
* @memberof platform/containment
*/
function ContainmentTable(typeService, capabilityService) {
var self = this,
types = typeService.listTypes(),
capabilityTable = new CapabilityTable(typeService, capabilityService);
// Add types which have all these capabilities to the set
// of allowed types
function addToSetByCapability(set, has) {
has = Array.isArray(has) ? has : [has];
types.forEach(function (type) {
var typeKey = type.getKey();
set[typeKey] = has.map(function (capabilityKey) {
return capabilityTable.hasCapability(typeKey, capabilityKey);
}).reduce(function (a, b) {
return a && b;
}, true);
});
}
// Add this type (or type description) to the set of allowed types
function addToSet(set, type) {
// Is this a simple case of an explicit type identifier?
if (typeof type === 'string') {
// If so, add it to the set of allowed types
set[type] = true;
} else {
// Otherwise, populate that set based on capabilities
addToSetByCapability(set, (type || {}).has || []);
}
}
// Add to the lookup table for this type
function addToTable(type) {
var key = type.getKey(),
definition = type.getDefinition() || {},
contains = definition.contains;
// Check for defined containment restrictions
if (contains === undefined) {
// If not, accept anything
self.table[key] = ANY;
} else {
// Start with an empty set...
self.table[key] = {};
// ...cast accepted types to array if necessary...
contains = Array.isArray(contains) ? contains : [contains];
// ...and add all containment rules to that set
contains.forEach(function (c) {
addToSet(self.table[key], c);
});
}
}
// Build the table
this.table = {};
types.forEach(addToTable);
}
/**
* Check if domain objects of one type can contain domain
* objects of another type.
* @param {Type} containerType type of the containing domain object
* @param {Type} containedType type of the domain object
* to be contained
* @returns {boolean} true if allowable
*/
ContainmentTable.prototype.canContain = function (containerType, containedType) {
var set = this.table[containerType.getKey()] || {};
// Recognize either the symbolic value for "can contain
// anything", or lookup the specific type from the set.
return (set === ANY) || set[containedType.getKey()];
};
return ContainmentTable;
}
);

View File

@ -1,85 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../src/CapabilityTable"],
function (CapabilityTable) {
describe("Composition policy's capability table", function () {
var mockTypeService,
mockCapabilityService,
mockTypes,
table;
beforeEach(function () {
mockTypeService = jasmine.createSpyObj(
'typeService',
['listTypes']
);
mockCapabilityService = jasmine.createSpyObj(
'capabilityService',
['getCapabilities']
);
// Both types can only contain b, let's say
mockTypes = ['a', 'b'].map(function (type) {
var mockType = jasmine.createSpyObj(
'type-' + type,
['getKey', 'getDefinition', 'getInitialModel']
);
mockType.getKey.andReturn(type);
// Return a model to drive apparent capabilities
mockType.getInitialModel.andReturn({ id: type });
return mockType;
});
mockTypeService.listTypes.andReturn(mockTypes);
mockCapabilityService.getCapabilities.andCallFake(function (model) {
var capabilities = {};
capabilities[model.id + '-capability'] = true;
return capabilities;
});
table = new CapabilityTable(
mockTypeService,
mockCapabilityService
);
});
it("provides for lookup of capabilities by type", function () {
// Based on initial model, should report the presence
// of particular capabilities - suffixed above with -capability
expect(table.hasCapability('a', 'a-capability'))
.toBeTruthy();
expect(table.hasCapability('a', 'b-capability'))
.toBeFalsy();
expect(table.hasCapability('a', 'c-capability'))
.toBeFalsy();
expect(table.hasCapability('b', 'a-capability'))
.toBeFalsy();
expect(table.hasCapability('b', 'b-capability'))
.toBeTruthy();
expect(table.hasCapability('b', 'c-capability'))
.toBeFalsy();
});
});
}
);

View File

@ -79,7 +79,7 @@ define(
expect(mockPolicyService.allow).toHaveBeenCalledWith(
'composition',
mockTypes[0],
mockTypes[1]
mockDomainObjects[1]
);
});

View File

@ -24,62 +24,96 @@ define(
["../src/CompositionPolicy"],
function (CompositionPolicy) {
describe("Composition policy", function () {
var mockInjector,
mockTypeService,
mockCapabilityService,
mockTypes,
var typeA,
typeB,
typeC,
mockChildObject,
policy;
beforeEach(function () {
mockInjector = jasmine.createSpyObj('$injector', ['get']);
mockTypeService = jasmine.createSpyObj(
'typeService',
['listTypes']
typeA = jasmine.createSpyObj(
'type A-- the particular kind',
['getKey', 'getDefinition']
);
mockCapabilityService = jasmine.createSpyObj(
'capabilityService',
['getCapabilities']
typeA.getKey.andReturn('a');
typeA.getDefinition.andReturn({
contains: ['a']
});
typeB = jasmine.createSpyObj(
'type B-- anything goes',
['getKey', 'getDefinition']
);
// Both types can only contain b, let's say
mockTypes = ['a', 'b'].map(function (type) {
var mockType = jasmine.createSpyObj(
'type-' + type,
['getKey', 'getDefinition', 'getInitialModel']
typeB.getKey.andReturn('b');
typeB.getDefinition.andReturn({
contains: ['a', 'b']
});
typeC = jasmine.createSpyObj(
'type C-- distinguishing and interested in telemetry',
['getKey', 'getDefinition']
);
mockType.getKey.andReturn(type);
mockType.getDefinition.andReturn({
contains: ['b']
});
mockType.getInitialModel.andReturn({});
return mockType;
typeC.getKey.andReturn('c');
typeC.getDefinition.andReturn({
contains: [{has: 'telemetry'}]
});
mockInjector.get.andCallFake(function (name) {
return {
typeService: mockTypeService,
capabilityService: mockCapabilityService
}[name];
mockChildObject = jasmine.createSpyObj(
'childObject',
['getCapability', 'hasCapability']
);
policy = new CompositionPolicy();
});
mockTypeService.listTypes.andReturn(mockTypes);
mockCapabilityService.getCapabilities.andReturn({});
describe('enforces simple containment rules', function () {
policy = new CompositionPolicy(mockInjector);
});
// Test basic composition policy here; test more closely at
// the unit level in ContainmentTable for 'has' support, et al
it("enforces containment rules defined by types", function () {
expect(policy.allow(mockTypes[0], mockTypes[1]))
it('allows when type matches', function () {
mockChildObject.getCapability.andReturn(typeA);
expect(policy.allow(typeA, mockChildObject))
.toBeTruthy();
expect(policy.allow(mockTypes[1], mockTypes[1]))
expect(policy.allow(typeB, mockChildObject))
.toBeTruthy();
expect(policy.allow(mockTypes[1], mockTypes[0]))
mockChildObject.getCapability.andReturn(typeB);
expect(policy.allow(typeB, mockChildObject))
.toBeTruthy();
});
it('disallows when type doesn\'t match', function () {
mockChildObject.getCapability.andReturn(typeB);
expect(policy.allow(typeA, mockChildObject))
.toBeFalsy();
expect(policy.allow(mockTypes[0], mockTypes[0]))
mockChildObject.getCapability.andReturn(typeC);
expect(policy.allow(typeA, mockChildObject))
.toBeFalsy();
});
});
describe('enforces capability-based containment rules', function () {
it('allows when object has capability', function () {
mockChildObject.hasCapability.andReturn(true);
expect(policy.allow(typeC, mockChildObject))
.toBeTruthy();
expect(mockChildObject.hasCapability)
.toHaveBeenCalledWith('telemetry');
});
it('skips when object doesn\'t have capability', function () {
mockChildObject.hasCapability.andReturn(false);
expect(policy.allow(typeC, mockChildObject))
.toBeFalsy();
expect(mockChildObject.hasCapability)
.toHaveBeenCalledWith('telemetry');
});
});
});
}
);

View File

@ -1,96 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../src/ContainmentTable"],
function (ContainmentTable) {
describe("Composition policy's containment table", function () {
var mockTypeService,
mockCapabilityService,
mockTypes,
table;
beforeEach(function () {
mockTypeService = jasmine.createSpyObj(
'typeService',
['listTypes']
);
mockCapabilityService = jasmine.createSpyObj(
'capabilityService',
['getCapabilities']
);
// Both types can only contain b, let's say
mockTypes = ['a', 'b', 'c'].map(function (type, index) {
var mockType = jasmine.createSpyObj(
'type-' + type,
['getKey', 'getDefinition', 'getInitialModel']
);
mockType.getKey.andReturn(type);
mockType.getDefinition.andReturn({
// First two contain objects with capability 'b';
// third one defines no containership rules
contains: (index < 2) ? [{ has: 'b' }] : undefined
});
// Return a model to drive apparent capabilities
mockType.getInitialModel.andReturn({ id: type });
return mockType;
});
mockTypeService.listTypes.andReturn(mockTypes);
mockCapabilityService.getCapabilities.andCallFake(function (model) {
var capabilities = {};
capabilities[model.id] = true;
return capabilities;
});
table = new ContainmentTable(
mockTypeService,
mockCapabilityService
);
});
// The plain type case is tested in CompositionPolicySpec,
// so just test for special syntax ('has', or no contains rules) here
it("enforces 'has' containment rules related to capabilities", function () {
expect(table.canContain(mockTypes[0], mockTypes[1]))
.toBeTruthy();
expect(table.canContain(mockTypes[1], mockTypes[1]))
.toBeTruthy();
expect(table.canContain(mockTypes[1], mockTypes[0]))
.toBeFalsy();
expect(table.canContain(mockTypes[0], mockTypes[0]))
.toBeFalsy();
});
it("allows anything when no containership rules are defined", function () {
expect(table.canContain(mockTypes[2], mockTypes[0]))
.toBeTruthy();
expect(table.canContain(mockTypes[2], mockTypes[1]))
.toBeTruthy();
expect(table.canContain(mockTypes[2], mockTypes[2]))
.toBeTruthy();
});
});
}
);

View File

@ -241,7 +241,7 @@ define([
"property": "name",
"pattern": "\\S+",
"required": true,
"cssclass": "l-input-lg"
"cssClass": "l-input-lg"
},
{
"name": "Notes",
@ -249,19 +249,19 @@ define([
"property": "notes",
"control": "textarea",
"required": false,
"cssclass": "l-textarea-sm"
"cssClass": "l-textarea-sm"
}
]
},
{
"key": "root",
"name": "Root",
"cssclass": "icon-folder"
"cssClass": "icon-folder"
},
{
"key": "folder",
"name": "Folder",
"cssclass": "icon-folder",
"cssClass": "icon-folder",
"features": "creation",
"description": "Create folders to organize other objects or links to objects.",
"priority": 1000,
@ -272,11 +272,11 @@ define([
{
"key": "unknown",
"name": "Unknown Type",
"cssclass": "icon-object-unknown"
"cssClass": "icon-object-unknown"
},
{
"name": "Unknown Type",
"cssclass": "icon-object-unknown"
"cssClass": "icon-object-unknown"
}
],
"capabilities": [

View File

@ -58,7 +58,7 @@ define(
* @property {string} key machine-readable identifier for this action
* @property {string} name human-readable name for this action
* @property {string} description human-readable description
* @property {string} cssclass CSS class for icon
* @property {string} cssClass CSS class for icon
* @property {ActionContext} context the context in which the action
* will be performed.
*/

View File

@ -53,10 +53,10 @@ define(
*/
function CoreCapabilityProvider(capabilities, $log) {
// Filter by invoking the capability's appliesTo method
function filterCapabilities(model) {
function filterCapabilities(model, id) {
return capabilities.filter(function (capability) {
return capability.appliesTo ?
capability.appliesTo(model) :
capability.appliesTo(model, id) :
true;
});
}
@ -75,8 +75,8 @@ define(
return result;
}
function getCapabilities(model) {
return packageCapabilities(filterCapabilities(model));
function getCapabilities(model, id) {
return packageCapabilities(filterCapabilities(model, id));
}
return {

View File

@ -56,12 +56,12 @@ define(
* @method Type#getDescription
*/
/**
* Get the cssclass associated with this type. cssclass is a
* Get the cssClass associated with this type. cssClass is a
* string which will appear as an icon (when
* displayed in an appropriate font) which visually
* distinguish types from one another.
*
* @returns {string} the cssclass for this type
* @returns {string} the cssClass for this type
* @method Type#getCssClass
*/
/**
@ -145,7 +145,7 @@ define(
};
TypeImpl.prototype.getCssClass = function () {
return this.typeDef.cssclass;
return this.typeDef.cssClass;
};
TypeImpl.prototype.getProperties = function () {

View File

@ -33,7 +33,7 @@ define(
key: 'test-type',
name: 'Test Type',
description: 'A type, for testing',
cssclass: 'icon-telemetry-panel',
cssClass: 'icon-telemetry-panel',
inherits: ['test-parent-1', 'test-parent-2'],
features: ['test-feature-1'],
properties: [{}],

View File

@ -30,18 +30,18 @@ define(
testTypeDefinitions = [
{
key: 'basic',
cssclass: "icon-magnify-in",
cssClass: "icon-magnify-in",
name: "Basic Type"
},
{
key: 'multi1',
cssclass: "icon-trash",
cssClass: "icon-trash",
description: "Multi1 Description",
capabilities: ['a1', 'b1']
},
{
key: 'multi2',
cssclass: "icon-magnify-out",
cssClass: "icon-magnify-out",
capabilities: ['a2', 'b2', 'c2']
},
{

View File

@ -66,7 +66,7 @@ define([
"key": "move",
"name": "Move",
"description": "Move object to another location.",
"cssclass": "icon-move",
"cssClass": "icon-move",
"category": "contextual",
"implementation": MoveAction,
"depends": [
@ -79,7 +79,7 @@ define([
"key": "copy",
"name": "Duplicate",
"description": "Duplicate object to another location.",
"cssclass": "icon-duplicate",
"cssClass": "icon-duplicate",
"category": "contextual",
"implementation": CopyAction,
"depends": [
@ -95,7 +95,7 @@ define([
"key": "link",
"name": "Create Link",
"description": "Create Link to object in another location.",
"cssclass": "icon-link",
"cssClass": "icon-link",
"category": "contextual",
"implementation": LinkAction,
"depends": [
@ -108,7 +108,7 @@ define([
"key": "follow",
"name": "Go To Original",
"description": "Go to the original, un-linked instance of this object.",
"cssclass": "",
"cssClass": "",
"category": "contextual",
"implementation": GoToOriginalAction
},
@ -116,7 +116,7 @@ define([
"key": "locate",
"name": "Set Primary Location",
"description": "Set a domain object's primary location.",
"cssclass": "",
"cssClass": "",
"category": "contextual",
"implementation": SetPrimaryLocationAction
}

View File

@ -48,7 +48,7 @@ define(
return this.policyService.allow(
"composition",
parentCandidate.getCapability('type'),
object.getCapability('type')
object
);
};

View File

@ -52,7 +52,7 @@ define(
return this.policyService.allow(
"composition",
parentCandidate.getCapability('type'),
object.getCapability('type')
object
);
};

View File

@ -58,7 +58,7 @@ define(
sections: [
{
name: 'Location',
cssclass: "grows",
cssClass: "grows",
rows: [
{
name: label,

View File

@ -56,7 +56,7 @@ define(
return this.policyService.allow(
"composition",
parentCandidate.getCapability('type'),
object.getCapability('type')
object
);
};

View File

@ -104,7 +104,7 @@ define(
expect(policyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object.capabilities.type
object
);
});

View File

@ -114,7 +114,7 @@ define(
expect(mockPolicyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object.capabilities.type
object
);
});

View File

@ -124,7 +124,7 @@ define(
expect(policyService.allow).toHaveBeenCalledWith(
"composition",
parentCandidate.capabilities.type,
object.capabilities.type
object
);
});

View File

@ -136,7 +136,7 @@ define([
],
"category": "contextual",
"name": "Start",
"cssclass": "icon-play",
"cssClass": "icon-play",
"priority": "preferred"
},
{
@ -147,7 +147,7 @@ define([
],
"category": "contextual",
"name": "Restart at 0",
"cssclass": "icon-refresh",
"cssClass": "icon-refresh",
"priority": "preferred"
}
],
@ -155,7 +155,7 @@ define([
{
"key": "clock",
"name": "Clock",
"cssclass": "icon-clock",
"cssClass": "icon-clock",
"description": "A UTC-based clock that supports a variety of display formats. Clocks can be added to Display Layouts.",
"priority": 101,
"features": [
@ -183,7 +183,7 @@ define([
"name": "hh:mm:ss"
}
],
"cssclass": "l-inline"
"cssClass": "l-inline"
},
{
"control": "select",
@ -197,7 +197,7 @@ define([
"name": "24hr"
}
],
"cssclass": "l-inline"
"cssClass": "l-inline"
}
]
}
@ -212,7 +212,7 @@ define([
{
"key": "timer",
"name": "Timer",
"cssclass": "icon-timer",
"cssClass": "icon-timer",
"description": "A timer that counts up or down to a datetime. Timers can be started, stopped and reset whenever needed, and support a variety of display formats. Each Timer displays the same value to all users. Timers can be added to Display Layouts.",
"priority": 100,
"features": [

View File

@ -131,11 +131,11 @@ define(
/**
* Get the CSS class to display the right icon
* for the start/restart button.
* @returns {string} cssclass to display
* @returns {string} cssClass to display
*/
TimerController.prototype.buttonCssClass = function () {
return this.relevantAction ?
this.relevantAction.getMetadata().cssclass : "";
this.relevantAction.getMetadata().cssClass : "";
};
/**

View File

@ -85,8 +85,8 @@ define(
'timer.restart': mockRestart
}[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" });
mockRestart.getMetadata.andReturn({ cssClass: "icon-refresh", name: "Restart" });
mockScope.domainObject = mockDomainObject;
testModel = {};
@ -144,7 +144,7 @@ define(
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/restart action", function () {
invokeWatch('domainObject', mockDomainObject);
expect(controller.buttonCssClass()).toEqual("icon-play");
expect(controller.buttonText()).toEqual("Start");

View File

@ -21,11 +21,9 @@
*****************************************************************************/
define([
"./src/ConductorTelemetryDecorator",
"./src/ConductorRepresenter",
'legacyRegistry'
], function (
ConductorTelemetryDecorator,
ConductorRepresenter,
legacyRegistry
) {
@ -39,16 +37,6 @@ define([
"openmct"
]
}
],
"components": [
{
"type": "decorator",
"provides": "telemetryService",
"implementation": ConductorTelemetryDecorator,
"depends": [
"openmct"
]
}
]
}
});

View File

@ -1,87 +0,0 @@
/*****************************************************************************
* Open MCT Web, Copyright (c) 2014-2015, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT Web includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
function () {
/**
* Decorates the `telemetryService` such that requests are
* mediated by the time conductor. This is a modified version of the
* decorator used in the old TimeConductor that integrates with the
* new TimeConductor API.
*
* @constructor
* @memberof platform/features/conductor
* @implements {TelemetryService}
* @param {platform/features/conductor.TimeConductor} conductor
* the service which exposes the global time conductor
* @param {TelemetryService} telemetryService the decorated service
*/
function ConductorTelemetryDecorator(openmct, telemetryService) {
this.conductor = openmct.conductor;
this.telemetryService = telemetryService;
this.amendRequests = ConductorTelemetryDecorator.prototype.amendRequests.bind(this);
}
function amendRequest(request, bounds, timeSystem) {
request = request || {};
request.start = bounds.start;
request.end = bounds.end;
request.domain = timeSystem.metadata.key;
return request;
}
ConductorTelemetryDecorator.prototype.amendRequests = function (requests) {
var bounds = this.conductor.bounds(),
timeSystem = this.conductor.timeSystem();
return (requests || []).map(function (request) {
return amendRequest(request, bounds, timeSystem);
});
};
ConductorTelemetryDecorator.prototype.requestTelemetry = function (requests) {
return this.telemetryService
.requestTelemetry(this.amendRequests(requests));
};
ConductorTelemetryDecorator.prototype.subscribe = function (callback, requests) {
var unsubscribeFunc = this.telemetryService.subscribe(callback, this.amendRequests(requests)),
conductor = this.conductor,
self = this;
function amendRequests() {
return self.amendRequests(requests);
}
conductor.on('bounds', amendRequests);
return function () {
unsubscribeFunc();
conductor.off('bounds', amendRequests);
};
};
return ConductorTelemetryDecorator;
}
);

View File

@ -70,8 +70,9 @@ define([
"$location",
"openmct",
"timeConductorViewService",
"timeSystems[]",
"formatService"
"formatService",
"DEFAULT_TIMECONDUCTOR_MODE",
"SHOW_TIMECONDUCTOR"
]
},
{
@ -150,6 +151,13 @@ define([
"link": "https://github.com/d3/d3/blob/master/LICENSE"
}
],
"constants": [
{
"key": "DEFAULT_TIMECONDUCTOR_MODE",
"value": "realtime",
"priority": "fallback"
}
],
"formats": [
{
"key": "number",

View File

@ -19,6 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "../../../../../commonUI/general/res/sass/constants";
@import "../../../../../commonUI/general/res/sass/mixins";

View File

@ -19,6 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "../../../../../commonUI/general/res/sass/constants";
@import "../../../../../commonUI/general/res/sass/mixins";

View File

@ -26,7 +26,7 @@
ng-click="ngModel.selectedKey=key">
<a ng-mouseover="ngModel.activeMetadata = metadata"
ng-mouseleave="ngModel.activeMetadata = undefined"
class="menu-item-a {{metadata.cssclass}}">
class="menu-item-a {{metadata.cssClass}}">
{{metadata.name}}
</a>
</li>
@ -34,7 +34,7 @@
</div>
<div class="pane right menu-item-description">
<div
class="desc-area ui-symbol icon type-icon {{ngModel.activeMetadata.cssclass}}"></div>
class="desc-area ui-symbol icon type-icon {{ngModel.activeMetadata.cssClass}}"></div>
<div class="desc-area title">
{{ngModel.activeMetadata.name}}
</div>

View File

@ -1,8 +1,7 @@
<!-- Parent holder for time conductor. follow-mode | fixed-mode -->
<div ng-controller="TimeConductorController as tcController"
class="holder grows flex-elem l-flex-row l-time-conductor {{modeModel.selectedKey}}-mode {{timeSystemModel.selected.metadata.key}}-time-system"
ng-class="{'status-panning': tcController.panning}">
ng-class="{'status-panning': tcController.panning}" ng-show="showTimeConductor">
<div class="flex-elem holder time-conductor-icon">
<div class="hand-little"></div>
<div class="hand-big"></div>

View File

@ -31,7 +31,7 @@ define(['./TickSource'], function (TickSource) {
this.metadata = {
key: 'local',
mode: 'realtime',
cssclass: 'icon-clock',
cssClass: 'icon-clock',
label: 'Real-time',
name: 'Real-time Mode',
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'

View File

@ -40,7 +40,16 @@ define(
* @memberof platform.features.conductor
* @constructor
*/
function TimeConductorController($scope, $window, $location, openmct, conductorViewService, timeSystems, formatService) {
function TimeConductorController(
$scope,
$window,
$location,
openmct,
conductorViewService,
formatService,
DEFAULT_MODE,
SHOW_TIMECONDUCTOR
) {
var self = this;
@ -60,10 +69,14 @@ define(
this.validation = new TimeConductorValidation(this.conductor);
this.formatService = formatService;
//Check if the default mode defined is actually available
if (this.modes[DEFAULT_MODE] === undefined) {
DEFAULT_MODE = 'fixed';
}
this.DEFAULT_MODE = DEFAULT_MODE;
// Construct the provided time system definitions
this.timeSystems = timeSystems.map(function (timeSystemConstructor) {
return timeSystemConstructor();
});
this.timeSystems = conductorViewService.systems;
this.initializeScope();
var searchParams = JSON.parse(JSON.stringify(this.$location.search()));
@ -94,6 +107,8 @@ define(
//Respond to any subsequent conductor changes
this.conductor.on('bounds', this.changeBounds);
this.conductor.on('timeSystem', this.changeTimeSystem);
this.$scope.showTimeConductor = SHOW_TIMECONDUCTOR;
}
/**
@ -139,7 +154,7 @@ define(
//Set mode from url if changed
if (searchParams[SEARCH.MODE] === undefined ||
searchParams[SEARCH.MODE] !== this.$scope.modeModel.selectedKey) {
this.setMode(searchParams[SEARCH.MODE] || "fixed");
this.setMode(searchParams[SEARCH.MODE] || this.DEFAULT_MODE);
}
if (searchParams[SEARCH.TIME_SYSTEM] &&

View File

@ -130,8 +130,10 @@ define(['./TimeConductorController'], function (TimeConductorController) {
mockLocation,
{conductor: mockTimeConductor},
mockConductorViewService,
mockTimeSystems,
mockFormatService
mockFormatService,
'fixed',
true
);
tsListener = getListener(mockTimeConductor.on, "timeSystem");
@ -244,7 +246,6 @@ define(['./TimeConductorController'], function (TimeConductorController) {
var ts1Metadata;
var ts2Metadata;
var ts3Metadata;
var mockTimeSystemConstructors;
beforeEach(function () {
mode = "realtime";
@ -276,11 +277,7 @@ define(['./TimeConductorController'], function (TimeConductorController) {
];
//Wrap in mock constructors
mockTimeSystemConstructors = mockTimeSystems.map(function (mockTimeSystem) {
return function () {
return mockTimeSystem;
};
});
mockConductorViewService.systems = mockTimeSystems;
controller = new TimeConductorController(
mockScope,
@ -288,8 +285,9 @@ define(['./TimeConductorController'], function (TimeConductorController) {
mockLocation,
{conductor: mockTimeConductor},
mockConductorViewService,
mockTimeSystemConstructors,
mockFormatService
mockFormatService,
"fixed",
true
);
});
@ -434,12 +432,7 @@ define(['./TimeConductorController'], function (TimeConductorController) {
}
};
mockTimeSystems.push(function () {
return timeSystem;
});
mockTimeSystems.push(function () {
return otherTimeSystem;
});
mockConductorViewService.systems = [timeSystem, otherTimeSystem];
urlBounds = {
start: 100,
@ -467,8 +460,9 @@ define(['./TimeConductorController'], function (TimeConductorController) {
mockLocation,
{conductor: mockTimeConductor},
mockConductorViewService,
mockTimeSystems,
mockFormatService
mockFormatService,
"fixed",
true
);
spyOn(controller, "setMode");

View File

@ -60,7 +60,7 @@ define(
this.availModes = {
'fixed': {
key: 'fixed',
cssclass: 'icon-calendar',
cssClass: 'icon-calendar',
label: 'Fixed',
name: 'Fixed Timespan Mode',
description: 'Query and explore data that falls between two fixed datetimes.'
@ -81,7 +81,7 @@ define(
if (timeSystemsForMode('realtime').length > 0) {
var realtimeMode = {
key: 'realtime',
cssclass: 'icon-clock',
cssClass: 'icon-clock',
label: 'Real-time',
name: 'Real-time Mode',
description: 'Monitor real-time streaming data as it comes in. The Time Conductor and displays will automatically advance themselves based on a UTC clock.'
@ -93,7 +93,7 @@ define(
if (timeSystemsForMode('lad').length > 0) {
var ladMode = {
key: 'lad',
cssclass: 'icon-database',
cssClass: 'icon-database',
label: 'LAD',
name: 'LAD Mode',
description: 'Latest Available Data mode monitors real-time streaming data as it comes in. The Time Conductor and displays will only advance when data becomes available.'

View File

@ -22,7 +22,7 @@
define([
"./src/UTCTimeSystem",
'legacyRegistry'
"legacyRegistry"
], function (
UTCTimeSystem,
legacyRegistry

View File

@ -25,7 +25,7 @@ define([
'../../core/src/timeSystems/LocalClock'
], function (TimeSystem, LocalClock) {
var FIFTEEN_MINUTES = 15 * 60 * 1000,
DEFAULT_PERIOD = 1000;
DEFAULT_PERIOD = 100;
/**
* This time system supports UTC dates and provides a ticking clock source.
@ -38,16 +38,17 @@ define([
/**
* Some metadata, which will be used to identify the time system in
* the UI
* @type {{key: string, name: string, cssclass: string}}
* @type {{key: string, name: string, cssClass: string}}
*/
this.metadata = {
'key': 'utc',
'name': 'UTC',
'cssclass': 'icon-clock'
'cssClass': 'icon-clock'
};
this.fmts = ['utc'];
this.sources = [new LocalClock($timeout, DEFAULT_PERIOD)];
this.defaultValues = undefined;
}
UTCTimeSystem.prototype = Object.create(TimeSystem.prototype);
@ -64,18 +65,25 @@ define([
return this.sources;
};
UTCTimeSystem.prototype.defaults = function () {
UTCTimeSystem.prototype.defaults = function (defaults) {
if (arguments.length > 0) {
this.defaultValues = defaults;
}
if (this.defaultValues === undefined) {
var now = Math.ceil(Date.now() / 1000) * 1000;
var ONE_MINUTE = 60 * 1 * 1000;
var FIFTY_YEARS = 50 * 365 * 24 * 60 * 60 * 1000;
return {
this.defaultValues = {
key: 'utc-default',
name: 'UTC time system defaults',
deltas: {start: FIFTEEN_MINUTES, end: 0},
bounds: {start: now - FIFTEEN_MINUTES, end: now},
zoom: {min: FIFTY_YEARS, max: ONE_MINUTE}
};
}
return this.defaultValues;
};
return UTCTimeSystem;

View File

@ -36,7 +36,7 @@ define([
{
"key": "fixed-display",
"name": "Fixed Position Display",
"cssclass": "icon-box-with-dashed-lines",
"cssClass": "icon-box-with-dashed-lines",
"type": "telemetry.fixed",
"template": fixedTemplate,
"uses": [
@ -49,28 +49,28 @@ define([
"items": [
{
"method": "add",
"cssclass": "icon-plus",
"cssClass": "icon-plus",
"control": "menu-button",
"text": "Add",
"options": [
{
"name": "Box",
"cssclass": "icon-box",
"cssClass": "icon-box",
"key": "fixed.box"
},
{
"name": "Line",
"cssclass": "icon-line-horz",
"cssClass": "icon-line-horz",
"key": "fixed.line"
},
{
"name": "Text",
"cssclass": "icon-T",
"cssClass": "icon-T",
"key": "fixed.text"
},
{
"name": "Image",
"cssclass": "icon-image",
"cssClass": "icon-image",
"key": "fixed.image"
}
]
@ -81,50 +81,50 @@ define([
"items": [
{
"method": "order",
"cssclass": "icon-layers",
"cssClass": "icon-layers",
"control": "menu-button",
"title": "Layering",
"description": "Move the selected object above or below other objects",
"options": [
{
"name": "Move to Top",
"cssclass": "icon-arrow-double-up",
"cssClass": "icon-arrow-double-up",
"key": "top"
},
{
"name": "Move Up",
"cssclass": "icon-arrow-up",
"cssClass": "icon-arrow-up",
"key": "up"
},
{
"name": "Move Down",
"cssclass": "icon-arrow-down",
"cssClass": "icon-arrow-down",
"key": "down"
},
{
"name": "Move to Bottom",
"cssclass": "icon-arrow-double-down",
"cssClass": "icon-arrow-double-down",
"key": "bottom"
}
]
},
{
"property": "fill",
"cssclass": "icon-paint-bucket",
"cssClass": "icon-paint-bucket",
"title": "Fill color",
"description": "Set fill color",
"control": "color"
},
{
"property": "stroke",
"cssclass": "icon-line-horz",
"cssClass": "icon-line-horz",
"title": "Border color",
"description": "Set border color",
"control": "color"
},
{
"property": "color",
"cssclass": "icon-T",
"cssClass": "icon-T",
"title": "Text color",
"description": "Set text color",
"mandatory": true,
@ -132,20 +132,20 @@ define([
},
{
"property": "url",
"cssclass": "icon-image",
"cssClass": "icon-image",
"control": "dialog-button",
"title": "Image Properties",
"description": "Edit image properties",
"dialog": {
"control": "textfield",
"name": "Image URL",
"cssclass": "l-input-lg",
"cssClass": "l-input-lg",
"required": true
}
},
{
"property": "text",
"cssclass": "icon-gear",
"cssClass": "icon-gear",
"control": "dialog-button",
"title": "Text Properties",
"description": "Edit text properties",
@ -157,14 +157,14 @@ define([
},
{
"method": "showTitle",
"cssclass": "icon-two-parts-both",
"cssClass": "icon-two-parts-both",
"control": "button",
"title": "Show title",
"description": "Show telemetry element title"
},
{
"method": "hideTitle",
"cssclass": "icon-two-parts-one-only",
"cssClass": "icon-two-parts-one-only",
"control": "button",
"title": "Hide title",
"description": "Hide telemetry element title"
@ -176,7 +176,7 @@ define([
{
"method": "remove",
"control": "button",
"cssclass": "icon-trash"
"cssClass": "icon-trash"
}
]
}
@ -188,7 +188,7 @@ define([
{
"key": "telemetry.fixed",
"name": "Fixed Position Display",
"cssclass": "icon-box-with-dashed-lines",
"cssClass": "icon-box-with-dashed-lines",
"description": "Collect and display telemetry elements in " +
"alphanumeric format in a simple canvas workspace. " +
"Elements can be positioned and sized. " +
@ -215,12 +215,12 @@ define([
{
"name": "Horizontal grid (px)",
"control": "textfield",
"cssclass": "l-input-sm l-numeric"
"cssClass": "l-input-sm l-numeric"
},
{
"name": "Vertical grid (px)",
"control": "textfield",
"cssclass": "l-input-sm l-numeric"
"cssClass": "l-input-sm l-numeric"
}
],
"pattern": "^(\\d*[1-9]\\d*)?$",

View File

@ -41,7 +41,7 @@ define([
{
"name": "Imagery",
"key": "imagery",
"cssclass": "icon-image",
"cssClass": "icon-image",
"template": imageryTemplate,
"priority": "preferred",
"needs": [

View File

@ -56,7 +56,7 @@ define([
{
"key": "layout",
"name": "Display Layout",
"cssclass": "icon-layout",
"cssClass": "icon-layout",
"type": "layout",
"template": layoutTemplate,
"editable": true,
@ -65,7 +65,7 @@ define([
{
"key": "fixed",
"name": "Fixed Position",
"cssclass": "icon-box-with-dashed-lines",
"cssClass": "icon-box-with-dashed-lines",
"type": "telemetry.panel",
"template": fixedTemplate,
"uses": [
@ -77,7 +77,7 @@ define([
"items": [
{
"method": "add",
"cssclass": "icon-plus",
"cssClass": "icon-plus",
"control": "menu-button",
"text": "Add",
"title": "Add",
@ -85,22 +85,22 @@ define([
"options": [
{
"name": "Box",
"cssclass": "icon-box",
"cssClass": "icon-box",
"key": "fixed.box"
},
{
"name": "Line",
"cssclass": "icon-line-horz",
"cssClass": "icon-line-horz",
"key": "fixed.line"
},
{
"name": "Text",
"cssclass": "icon-T",
"cssClass": "icon-T",
"key": "fixed.text"
},
{
"name": "Image",
"cssclass": "icon-image",
"cssClass": "icon-image",
"key": "fixed.image"
}
]
@ -111,50 +111,50 @@ define([
"items": [
{
"method": "order",
"cssclass": "icon-layers",
"cssClass": "icon-layers",
"control": "menu-button",
"title": "Layering",
"description": "Move the selected object above or below other objects",
"options": [
{
"name": "Move to Top",
"cssclass": "icon-arrow-double-up",
"cssClass": "icon-arrow-double-up",
"key": "top"
},
{
"name": "Move Up",
"cssclass": "icon-arrow-up",
"cssClass": "icon-arrow-up",
"key": "up"
},
{
"name": "Move Down",
"cssclass": "icon-arrow-down",
"cssClass": "icon-arrow-down",
"key": "down"
},
{
"name": "Move to Bottom",
"cssclass": "icon-arrow-double-down",
"cssClass": "icon-arrow-double-down",
"key": "bottom"
}
]
},
{
"property": "fill",
"cssclass": "icon-paint-bucket",
"cssClass": "icon-paint-bucket",
"title": "Fill color",
"description": "Set fill color",
"control": "color"
},
{
"property": "stroke",
"cssclass": "icon-line-horz",
"cssClass": "icon-line-horz",
"title": "Border color",
"description": "Set border color",
"control": "color"
},
{
"property": "color",
"cssclass": "icon-T",
"cssClass": "icon-T",
"title": "Text color",
"description": "Set text color",
"mandatory": true,
@ -162,20 +162,20 @@ define([
},
{
"property": "url",
"cssclass": "icon-image",
"cssClass": "icon-image",
"control": "dialog-button",
"title": "Image Properties",
"description": "Edit image properties",
"dialog": {
"control": "textfield",
"name": "Image URL",
"cssclass": "l-input-lg",
"cssClass": "l-input-lg",
"required": true
}
},
{
"property": "text",
"cssclass": "icon-gear",
"cssClass": "icon-gear",
"control": "dialog-button",
"title": "Text Properties",
"description": "Edit text properties",
@ -187,14 +187,14 @@ define([
},
{
"method": "showTitle",
"cssclass": "icon-two-parts-both",
"cssClass": "icon-two-parts-both",
"control": "button",
"title": "Show title",
"description": "Show telemetry element title"
},
{
"method": "hideTitle",
"cssclass": "icon-two-parts-one-only",
"cssClass": "icon-two-parts-one-only",
"control": "button",
"title": "Hide title",
"description": "Hide telemetry element title"
@ -206,7 +206,7 @@ define([
{
"method": "remove",
"control": "button",
"cssclass": "icon-trash",
"cssClass": "icon-trash",
"title": "Delete",
"description": "Delete the selected item"
}
@ -275,7 +275,7 @@ define([
{
"key": "layout",
"name": "Display Layout",
"cssclass": "icon-layout",
"cssClass": "icon-layout",
"description": "Assemble other objects and components together into a reusable screen layout. Working in a simple canvas workspace, simply drag in the objects you want, position and size them. Save your design and view or edit it at any time.",
"priority": 900,
"features": "creation",
@ -291,12 +291,12 @@ define([
{
"name": "Horizontal grid (px)",
"control": "textfield",
"cssclass": "l-input-sm l-numeric"
"cssClass": "l-input-sm l-numeric"
},
{
"name": "Vertical grid (px)",
"control": "textfield",
"cssclass": "l-input-sm l-numeric"
"cssClass": "l-input-sm l-numeric"
}
],
"key": "layoutGrid",
@ -307,7 +307,7 @@ define([
{
"key": "telemetry.panel",
"name": "Telemetry Panel",
"cssclass": "icon-telemetry-panel",
"cssClass": "icon-telemetry-panel",
"description": "A panel for collecting telemetry elements.",
"priority": 899,
"delegates": [
@ -330,12 +330,12 @@ define([
{
"name": "Horizontal grid (px)",
"control": "textfield",
"cssclass": "l-input-sm l-numeric"
"cssClass": "l-input-sm l-numeric"
},
{
"name": "Vertical grid (px)",
"control": "textfield",
"cssclass": "l-input-sm l-numeric"
"cssClass": "l-input-sm l-numeric"
}
],
"pattern": "^(\\d*[1-9]\\d*)?$",

View File

@ -34,13 +34,14 @@ define(
function LayoutCompositionPolicy() {
}
LayoutCompositionPolicy.prototype.allow = function (candidate, context) {
var isFolderInLayout =
candidate &&
context &&
candidate.instanceOf('layout') &&
context.instanceOf('folder');
return !isFolderInLayout;
LayoutCompositionPolicy.prototype.allow = function (parentType, child) {
if (parentType.instanceOf('layout') &&
child.getCapability('type').instanceOf('folder')) {
return false;
}
return true;
};
return LayoutCompositionPolicy;

View File

@ -55,7 +55,7 @@ define(
key: "url",
control: "textfield",
name: "Image URL",
"cssclass": "l-input-lg",
"cssClass": "l-input-lg",
required: true
}
]

View File

@ -24,18 +24,25 @@ define(
["../src/LayoutCompositionPolicy"],
function (LayoutCompositionPolicy) {
describe("Layout's composition policy", function () {
var mockCandidate,
var mockChild,
mockCandidate,
mockContext,
candidateType,
contextType,
policy;
beforeEach(function () {
mockChild = jasmine.createSpyObj(
'childObject',
['getCapability']
);
mockCandidate =
jasmine.createSpyObj('candidateType', ['instanceOf']);
mockContext =
jasmine.createSpyObj('contextType', ['instanceOf']);
mockChild.getCapability.andReturn(mockContext);
mockCandidate.instanceOf.andCallFake(function (t) {
return t === candidateType;
});
@ -49,19 +56,19 @@ define(
it("disallows folders in layouts", function () {
candidateType = 'layout';
contextType = 'folder';
expect(policy.allow(mockCandidate, mockContext)).toBe(false);
expect(policy.allow(mockCandidate, mockChild)).toBe(false);
});
it("does not disallow folders elsewhere", function () {
candidateType = 'nonlayout';
contextType = 'folder';
expect(policy.allow(mockCandidate, mockContext)).toBe(true);
expect(policy.allow(mockCandidate, mockChild)).toBe(true);
});
it("allows things other than folders in layouts", function () {
candidateType = 'layout';
contextType = 'nonfolder';
expect(policy.allow(mockCandidate, mockContext)).toBe(true);
expect(policy.allow(mockCandidate, mockChild)).toBe(true);
});
});

View File

@ -36,7 +36,7 @@ define([
{
"key": "example.page",
"name": "Web Page",
"cssclass": "icon-page",
"cssClass": "icon-page",
"description": "Embed a web page or web-based image in a resizeable window component. Can be added to Display Layouts. Note that the URL being embedded must allow iframing.",
"priority": 50,
"features": [
@ -49,7 +49,7 @@ define([
"control": "textfield",
"pattern": "^(ftp|https?)\\:\\/\\/\\w+(\\.\\w+)*(\\:\\d+)?(\\/\\S*)*$",
"required": true,
"cssclass": "l-input-lg"
"cssClass": "l-input-lg"
}
]
}

View File

@ -47,7 +47,7 @@ define([
{
"name": "Plot",
"key": "plot",
"cssclass": "icon-sine",
"cssClass": "icon-sine",
"template": plotTemplate,
"needs": [
"telemetry"

View File

@ -121,7 +121,7 @@
ng-show="plot.isZoomed()"
title="Reset pan/zoom">
</a>
<div class="menu-element s-menu-button menus-to-left {{plot.getMode().cssclass}}"
<div class="menu-element s-menu-button menus-to-left {{plot.getMode().cssClass}}"
ng-if="plot.getModeOptions().length > 1"
ng-controller="ClickAwayController as toggle">
<span class="l-click-area" ng-click="toggle.toggle()"></span>
@ -130,7 +130,7 @@
<ul>
<li ng-repeat="option in plot.getModeOptions()"
ng-click="plot.setMode(option); toggle.setState(false)"
class="{{option.cssclass}}">
class="{{option.cssClass}}">
{{option.name}}
</li>
</ul>

View File

@ -217,8 +217,8 @@ define(
if (handle) {
handle.unsubscribe();
handle = undefined;
conductor.off("timeOfInterest", changeTimeOfInterest);
}
conductor.off("timeOfInterest", changeTimeOfInterest);
}
function requery() {
@ -352,7 +352,7 @@ define(
/**
* Get the current mode that is applicable to this plot. This
* will include key, name, and cssclass fields.
* will include key, name, and cssClass fields.
*/
PlotController.prototype.getMode = function () {
return this.modeOptions.getMode();

View File

@ -27,13 +27,13 @@ define(
var STACKED = {
key: "stacked",
name: "Stacked",
cssclass: "icon-plot-stacked",
cssClass: "icon-plot-stacked",
Constructor: PlotStackMode
},
OVERLAID = {
key: "overlaid",
name: "Overlaid",
cssclass: "icon-plot-overlay",
cssClass: "icon-plot-overlay",
Constructor: PlotOverlayMode
};
@ -115,7 +115,7 @@ define(
/**
* Get all mode options available for each plot. Each
* mode contains a `name` and `cssclass` field suitable
* mode contains a `name` and `cssClass` field suitable
* for display in a template.
* @return {Array} the available modes
*/

View File

@ -36,7 +36,7 @@ define([
{
"key": "static.markup",
"name": "Static Markup",
"cssclass": "icon-pencil",
"cssClass": "icon-pencil",
"description": "Static markup sandbox",
"features": [
"creation"

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