mirror of
https://github.com/nasa/openmct.git
synced 2025-07-01 04:53:59 +00:00
Compare commits
60 Commits
plot-impor
...
activity-v
Author | SHA1 | Date | |
---|---|---|---|
b7a94a9dd5 | |||
2b11b8d63c | |||
006e99fb07 | |||
63dc4b6253 | |||
85868f690e | |||
b1f34f7cd7 | |||
8785d9a9d7 | |||
3a6e1fd301 | |||
fcef4274e5 | |||
744a5340d3 | |||
d140051054 | |||
8161e4fc89 | |||
8da74f2665 | |||
abf7654027 | |||
8fba707321 | |||
8ad5cca936 | |||
754d484501 | |||
74717b59c3 | |||
ffdb19787b | |||
5dc0d8c7f8 | |||
2390278b97 | |||
8a66731271 | |||
0a9ea48355 | |||
01d93306f3 | |||
0588f9190a | |||
1378b57567 | |||
9e12886c66 | |||
2d352ac574 | |||
284dec4903 | |||
5a0656c700 | |||
425655bae0 | |||
50b4d5cb28 | |||
bc62d7d5ae | |||
c0dcf4495e | |||
a51b9bc63f | |||
ff003c3dab | |||
de7c4d2ce3 | |||
4b07930305 | |||
5a49ac16b1 | |||
91b150c064 | |||
9506d309b0 | |||
c9bd60f50e | |||
cf15ff5c07 | |||
6bbdfcdfbe | |||
06e93ff520 | |||
550e7a15e6 | |||
71c54cd541 | |||
e81b8e53dc | |||
84e6928f54 | |||
ba688fe62c | |||
c533e10352 | |||
04f47b3db6 | |||
717fa5edf4 | |||
14f5f048fb | |||
72929500d3 | |||
471adde923 | |||
6c5d5f3d00 | |||
2262fef29b | |||
bda30f1475 | |||
80582f5e8d |
15
API.md
15
API.md
@ -879,6 +879,21 @@ openmct.install(openmct.plugins.CouchDB('http://localhost:9200'))
|
|||||||
* `openmct.plugins.Espresso` and `openmct.plugins.Snow` are two different
|
* `openmct.plugins.Espresso` and `openmct.plugins.Snow` are two different
|
||||||
themes (dark and light) available for Open MCT. Note that at least one
|
themes (dark and light) available for Open MCT. Note that at least one
|
||||||
of these themes must be installed for Open MCT to appear correctly.
|
of these themes must be installed for Open MCT to appear correctly.
|
||||||
|
* `openmct.plugins.URLIndicatorPlugin` adds an indicator which shows the
|
||||||
|
availability of a URL with the following options:
|
||||||
|
- `url` : URL to indicate the status of
|
||||||
|
- `cssClass`: Icon to show in the status bar, defaults to `icon-database`, [list of all icons](https://nasa.github.io/openmct/style-guide/#/browse/styleguide:home?view=items)
|
||||||
|
- `interval`: Interval between checking the connection, defaults to `10000`
|
||||||
|
- `label` Name showing up as text in the status bar, defaults to url
|
||||||
|
```javascript
|
||||||
|
openmct.install(openmct.plugins.URLIndicatorPlugin({
|
||||||
|
url: 'http://google.com',
|
||||||
|
cssClass: 'check',
|
||||||
|
interval: 10000,
|
||||||
|
label: 'Google'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
```
|
||||||
* `openmct.plugins.LocalStorage` provides persistence of user-created
|
* `openmct.plugins.LocalStorage` provides persistence of user-created
|
||||||
objects in browser-local storage. This is particularly useful in
|
objects in browser-local storage. This is particularly useful in
|
||||||
development environments.
|
development environments.
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"screenfull": "^3.0.0",
|
"screenfull": "^3.0.0",
|
||||||
"node-uuid": "^1.4.7",
|
"node-uuid": "^1.4.7",
|
||||||
"comma-separated-values": "^3.6.4",
|
"comma-separated-values": "^3.6.4",
|
||||||
"FileSaver.js": "^0.0.2",
|
"file-saver": "^1.3.3",
|
||||||
"zepto": "^1.1.6",
|
"zepto": "^1.1.6",
|
||||||
"eventemitter3": "^1.2.0",
|
"eventemitter3": "^1.2.0",
|
||||||
"lodash": "3.10.1",
|
"lodash": "3.10.1",
|
||||||
|
@ -59,7 +59,7 @@ define([
|
|||||||
if (domainObject.telemetry && domainObject.telemetry.hasOwnProperty(prop)) {
|
if (domainObject.telemetry && domainObject.telemetry.hasOwnProperty(prop)) {
|
||||||
workerRequest[prop] = domainObject.telemetry[prop];
|
workerRequest[prop] = domainObject.telemetry[prop];
|
||||||
}
|
}
|
||||||
if (request.hasOwnProperty(prop)) {
|
if (request && request.hasOwnProperty(prop)) {
|
||||||
workerRequest[prop] = request[prop];
|
workerRequest[prop] = request[prop];
|
||||||
}
|
}
|
||||||
if (!workerRequest[prop]) {
|
if (!workerRequest[prop]) {
|
||||||
|
@ -44,9 +44,7 @@ define([
|
|||||||
message = message.data;
|
message = message.data;
|
||||||
var callback = this.callbacks[message.id];
|
var callback = this.callbacks[message.id];
|
||||||
if (callback) {
|
if (callback) {
|
||||||
if (callback(message)) {
|
callback(message);
|
||||||
delete this.callbacks[message.id];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -72,6 +70,7 @@ define([
|
|||||||
deferred.resolve = resolve;
|
deferred.resolve = resolve;
|
||||||
deferred.reject = reject;
|
deferred.reject = reject;
|
||||||
});
|
});
|
||||||
|
var messageId;
|
||||||
|
|
||||||
function callback(message) {
|
function callback(message) {
|
||||||
if (message.error) {
|
if (message.error) {
|
||||||
@ -79,33 +78,27 @@ define([
|
|||||||
} else {
|
} else {
|
||||||
deferred.resolve(message.data);
|
deferred.resolve(message.data);
|
||||||
}
|
}
|
||||||
return true;
|
delete this.callbacks[messageId];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatch('request', request, callback);
|
messageId = this.dispatch('request', request, callback.bind(this));
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
WorkerInterface.prototype.subscribe = function (request, cb) {
|
WorkerInterface.prototype.subscribe = function (request, cb) {
|
||||||
var isCancelled = false;
|
function callback(message) {
|
||||||
|
|
||||||
var callback = function (message) {
|
|
||||||
if (isCancelled) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
cb(message.data);
|
cb(message.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
var messageId = this.dispatch('subscribe', request, callback)
|
var messageId = this.dispatch('subscribe', request, callback);
|
||||||
|
|
||||||
return function () {
|
return function () {
|
||||||
isCancelled = true;
|
|
||||||
this.dispatch('unsubscribe', {
|
this.dispatch('unsubscribe', {
|
||||||
id: messageId
|
id: messageId
|
||||||
});
|
});
|
||||||
|
delete this.callbacks[messageId];
|
||||||
}.bind(this);
|
}.bind(this);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@
|
|||||||
<h2>Palettes</h2>
|
<h2>Palettes</h2>
|
||||||
<div class="cols cols1-1">
|
<div class="cols cols1-1">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<p>Use a palette to provide color choices. Similar to context menus and dropdowns, palettes should be dismissed when a choice is made within them, or if the user clicks outside one.</p>
|
<p>Use a palette to provide color choices. Similar to context menus and dropdowns, palettes should be dismissed when a choice is made within them, or if the user clicks outside one. Selected palette choices should utilize the <code>selected</code> CSS class to visualize indicate that state.</p>
|
||||||
<p>Note that while this example uses static markup for illustrative purposes, don't do this - use a front-end framework with repeaters to build the color choices.</p>
|
<p>Note that while this example uses static markup for illustrative purposes, don't do this - use a front-end framework with repeaters to build the color choices.</p>
|
||||||
</div>
|
</div>
|
||||||
<mct-example><div style="height: 220px" title="Ignore me, I'm just here to provide space for this example.">
|
<mct-example><div style="height: 220px" title="Ignore me, I'm just here to provide space for this example.">
|
||||||
@ -129,9 +129,9 @@
|
|||||||
<div class="s-button s-menu-button menu-element t-color-palette icon-paint-bucket" ng-controller="ClickAwayController as toggle">
|
<div class="s-button s-menu-button menu-element t-color-palette icon-paint-bucket" ng-controller="ClickAwayController as toggle">
|
||||||
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
||||||
<span class="color-swatch" style="background: rgb(255, 0, 0);"></span>
|
<span class="color-swatch" style="background: rgb(255, 0, 0);"></span>
|
||||||
<div class="menu l-color-palette" ng-show="toggle.isActive()">
|
<div class="menu l-palette l-color-palette" ng-show="toggle.isActive()">
|
||||||
<div class="l-palette-row l-option-row">
|
<div class="l-palette-row l-option-row">
|
||||||
<div class="l-palette-item s-palette-item " ng-click="ngModel[field] = 'transparent'"></div>
|
<div class="l-palette-item s-palette-item no-selection"></div>
|
||||||
<span class="l-palette-item-label">None</span>
|
<span class="l-palette-item-label">None</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="l-palette-row">
|
<div class="l-palette-row">
|
||||||
@ -147,7 +147,7 @@
|
|||||||
<div class="l-palette-item s-palette-item" style="background: rgb(255, 255, 255);"></div>
|
<div class="l-palette-item s-palette-item" style="background: rgb(255, 255, 255);"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="l-palette-row">
|
<div class="l-palette-row">
|
||||||
<div class="l-palette-item s-palette-item" style="background: rgb(136, 32, 32);"></div>
|
<div class="l-palette-item s-palette-item selected" style="background: rgb(255, 0, 0);"></div>
|
||||||
<div class="l-palette-item s-palette-item" style="background: rgb(224, 64, 64);"></div>
|
<div class="l-palette-item s-palette-item" style="background: rgb(224, 64, 64);"></div>
|
||||||
<div class="l-palette-item s-palette-item" style="background: rgb(240, 160, 72);"></div>
|
<div class="l-palette-item s-palette-item" style="background: rgb(240, 160, 72);"></div>
|
||||||
<div class="l-palette-item s-palette-item" style="background: rgb(255, 248, 96);"></div>
|
<div class="l-palette-item s-palette-item" style="background: rgb(255, 248, 96);"></div>
|
||||||
|
10
index.html
10
index.html
@ -25,8 +25,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
<title></title>
|
<title></title>
|
||||||
<script src="bower_components/requirejs/require.js">
|
<script src="bower_components/requirejs/require.js"> </script>
|
||||||
</script>
|
|
||||||
<script>
|
<script>
|
||||||
var THIRTY_MINUTES = 30 * 60 * 1000;
|
var THIRTY_MINUTES = 30 * 60 * 1000;
|
||||||
|
|
||||||
@ -44,13 +43,16 @@
|
|||||||
openmct.install(openmct.plugins.ExampleImagery());
|
openmct.install(openmct.plugins.ExampleImagery());
|
||||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||||
openmct.install(openmct.plugins.ImportExport());
|
openmct.install(openmct.plugins.ImportExport());
|
||||||
|
openmct.install(openmct.plugins.AutoflowView({
|
||||||
|
type: "telemetry.panel"
|
||||||
|
}));
|
||||||
openmct.install(openmct.plugins.Conductor({
|
openmct.install(openmct.plugins.Conductor({
|
||||||
menuOptions: [
|
menuOptions: [
|
||||||
{
|
{
|
||||||
name: "Fixed",
|
name: "Fixed",
|
||||||
timeSystem: 'utc',
|
timeSystem: 'utc',
|
||||||
bounds: {
|
bounds: {
|
||||||
start: Date.now() - 30 * 60 * 1000,
|
start: Date.now() - THIRTY_MINUTES,
|
||||||
end: Date.now()
|
end: Date.now()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -65,6 +67,8 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}));
|
}));
|
||||||
|
openmct.install(openmct.plugins.SummaryWidget());
|
||||||
|
openmct.install(openmct.plugins.ActivityModes());
|
||||||
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
|
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
|
||||||
openmct.time.timeSystem('utc');
|
openmct.time.timeSystem('utc');
|
||||||
openmct.start();
|
openmct.start();
|
||||||
|
@ -36,6 +36,7 @@ module.exports = function(config) {
|
|||||||
files: [
|
files: [
|
||||||
{pattern: 'bower_components/**/*.js', included: false},
|
{pattern: 'bower_components/**/*.js', included: false},
|
||||||
{pattern: 'node_modules/d3-*/**/*.js', included: false},
|
{pattern: 'node_modules/d3-*/**/*.js', included: false},
|
||||||
|
{pattern: 'node_modules/vue/**/*.js', included: false},
|
||||||
{pattern: 'src/**/*.js', included: false},
|
{pattern: 'src/**/*.js', included: false},
|
||||||
{pattern: 'example/**/*.html', included: false},
|
{pattern: 'example/**/*.html', included: false},
|
||||||
{pattern: 'example/**/*.js', included: false},
|
{pattern: 'example/**/*.js', included: false},
|
||||||
|
@ -33,10 +33,11 @@ requirejs.config({
|
|||||||
"moment": "bower_components/moment/moment",
|
"moment": "bower_components/moment/moment",
|
||||||
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
"moment-duration-format": "bower_components/moment-duration-format/lib/moment-duration-format",
|
||||||
"moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data",
|
"moment-timezone": "bower_components/moment-timezone/builds/moment-timezone-with-data",
|
||||||
"saveAs": "bower_components/FileSaver.js/FileSaver.min",
|
"saveAs": "bower_components/file-saver/FileSaver.min",
|
||||||
"screenfull": "bower_components/screenfull/dist/screenfull.min",
|
"screenfull": "bower_components/screenfull/dist/screenfull.min",
|
||||||
"text": "bower_components/text/text",
|
"text": "bower_components/text/text",
|
||||||
"uuid": "bower_components/node-uuid/uuid",
|
"uuid": "bower_components/node-uuid/uuid",
|
||||||
|
"vue": "node_modules/vue/dist/vue.min",
|
||||||
"zepto": "bower_components/zepto/zepto.min",
|
"zepto": "bower_components/zepto/zepto.min",
|
||||||
"lodash": "bower_components/lodash/lodash",
|
"lodash": "bower_components/lodash/lodash",
|
||||||
"d3-selection": "node_modules/d3-selection/build/d3-selection.min",
|
"d3-selection": "node_modules/d3-selection/build/d3-selection.min",
|
||||||
@ -48,7 +49,8 @@ requirejs.config({
|
|||||||
"d3-format": "node_modules/d3-format/build/d3-format.min",
|
"d3-format": "node_modules/d3-format/build/d3-format.min",
|
||||||
"d3-interpolate": "node_modules/d3-interpolate/build/d3-interpolate.min",
|
"d3-interpolate": "node_modules/d3-interpolate/build/d3-interpolate.min",
|
||||||
"d3-time": "node_modules/d3-time/build/d3-time.min",
|
"d3-time": "node_modules/d3-time/build/d3-time.min",
|
||||||
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min"
|
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min",
|
||||||
|
"d3-dsv": "node_modules/d3-dsv/build/d3-dsv.min"
|
||||||
},
|
},
|
||||||
"shim": {
|
"shim": {
|
||||||
"angular": {
|
"angular": {
|
||||||
@ -66,6 +68,9 @@ requirejs.config({
|
|||||||
"moment-duration-format": {
|
"moment-duration-format": {
|
||||||
"deps": ["moment"]
|
"deps": ["moment"]
|
||||||
},
|
},
|
||||||
|
"saveAs": {
|
||||||
|
"exports": "saveAs"
|
||||||
|
},
|
||||||
"screenfull": {
|
"screenfull": {
|
||||||
"exports": "screenfull"
|
"exports": "screenfull"
|
||||||
},
|
},
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
"d3-axis": "^1.0.4",
|
"d3-axis": "^1.0.4",
|
||||||
"d3-collection": "^1.0.2",
|
"d3-collection": "^1.0.2",
|
||||||
"d3-color": "^1.0.2",
|
"d3-color": "^1.0.2",
|
||||||
|
"d3-dsv": "^1.0.8",
|
||||||
"d3-format": "^1.0.2",
|
"d3-format": "^1.0.2",
|
||||||
"d3-interpolate": "^1.1.3",
|
"d3-interpolate": "^1.1.3",
|
||||||
"d3-scale": "^1.0.4",
|
"d3-scale": "^1.0.4",
|
||||||
@ -15,7 +16,8 @@
|
|||||||
"d3-time-format": "^2.0.3",
|
"d3-time-format": "^2.0.3",
|
||||||
"express": "^4.13.1",
|
"express": "^4.13.1",
|
||||||
"minimist": "^1.1.1",
|
"minimist": "^1.1.1",
|
||||||
"request": "^2.69.0"
|
"request": "^2.69.0",
|
||||||
|
"vue": "^2.5.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"bower": "^1.7.7",
|
"bower": "^1.7.7",
|
||||||
|
@ -57,7 +57,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<mct-representation key="representation.selected.key"
|
<mct-representation key="representation.selected.key"
|
||||||
mct-object="representation.selected.key && domainObject"
|
mct-object="representation.selected.key && domainObject"
|
||||||
class="abs flex-elem grows object-holder-main scroll">
|
class="abs flex-elem grows object-holder-main scroll"
|
||||||
|
mct-selectable="{
|
||||||
|
item: domainObject.useCapability('adapter'),
|
||||||
|
oldItem: domainObject
|
||||||
|
}"
|
||||||
|
mct-init-select>
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,12 +19,21 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div ng-controller="InspectorController">
|
<div ng-controller="InspectorController as controller">
|
||||||
<div ng-repeat="region in regions">
|
|
||||||
<mct-representation
|
<mct-representation
|
||||||
key="region.content.key"
|
key="'object-properties'"
|
||||||
mct-object="domainObject"
|
mct-object="controller.selectedItem()"
|
||||||
|
ng-model="ngModel">
|
||||||
|
</mct-representation>
|
||||||
|
|
||||||
|
<div ng-if="!controller.hasProviderView()">
|
||||||
|
<mct-representation
|
||||||
|
key="inspectorKey"
|
||||||
|
mct-object="controller.selectedItem()"
|
||||||
ng-model="ngModel">
|
ng-model="ngModel">
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class='inspector-provider-view'>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -38,8 +38,6 @@
|
|||||||
ng-class="{ last:($index + 1) === contextualParents.length }">
|
ng-class="{ last:($index + 1) === contextualParents.length }">
|
||||||
<mct-representation key="'label'"
|
<mct-representation key="'label'"
|
||||||
mct-object="parent"
|
mct-object="parent"
|
||||||
ng-model="ngModel"
|
|
||||||
ng-click="ngModel.selectedObject = parent"
|
|
||||||
class="location-item">
|
class="location-item">
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
</span>
|
</span>
|
||||||
@ -51,8 +49,6 @@
|
|||||||
ng-class="{ last:($index + 1) === primaryParents.length }">
|
ng-class="{ last:($index + 1) === primaryParents.length }">
|
||||||
<mct-representation key="'label'"
|
<mct-representation key="'label'"
|
||||||
mct-object="parent"
|
mct-object="parent"
|
||||||
ng-model="ngModel"
|
|
||||||
ng-click="ngModel.selectedObject = parent"
|
|
||||||
class="location-item">
|
class="location-item">
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
</span>
|
</span>
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="abs top-bar">
|
<div class="abs top-bar">
|
||||||
<div class="title">{{ngModel.title}}</div>
|
<div class="dialog-title">{{ngModel.title}}</div>
|
||||||
<div class="hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
|
<div class="hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='abs editor'>
|
<div class='abs editor'>
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
<div class="l-message"
|
<div class="l-message"
|
||||||
ng-class="'message-severity-' + ngModel.severity">
|
ng-class="'message-severity-' + ngModel.severity">
|
||||||
<div class="ui-symbol type-icon message-type"></div>
|
<div class="w-message-contents">
|
||||||
<div class="message-contents">
|
|
||||||
<div class="top-bar">
|
<div class="top-bar">
|
||||||
<div class="title">{{ngModel.title}}</div>
|
<div class="title">{{ngModel.title}}</div>
|
||||||
<div class="hint" ng-hide="ngModel.hint === undefined">{{ngModel.hint}}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="hint" ng-hide="ngModel.hint === undefined">{{ngModel.hint}}</div>
|
||||||
<div class="message-body">
|
<div class="message-body">
|
||||||
<div class="message-action">
|
<div class="message-action">
|
||||||
{{ngModel.actionText}}
|
{{ngModel.actionText}}
|
||||||
@ -25,8 +24,6 @@
|
|||||||
ng-click="ngModel.primaryOption.callback()">
|
ng-click="ngModel.primaryOption.callback()">
|
||||||
{{ngModel.primaryOption.label}}
|
{{ngModel.primaryOption.label}}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<mct-container key="overlay" class="t-message-list">
|
<mct-container key="overlay">
|
||||||
<div class="message-contents">
|
<div class="t-message-list">
|
||||||
<div class="abs top-bar">
|
<div class="top-bar">
|
||||||
<div class="title">{{ngModel.dialog.title}}</div>
|
<div class="dialog-title">{{ngModel.dialog.title}}</div>
|
||||||
<div class="hint">Displaying {{ngModel.dialog.messages.length}} message<span ng-show="ngModel.dialog.messages.length > 1 ||
|
<div class="hint">Displaying {{ngModel.dialog.messages.length}} message<span ng-show="ngModel.dialog.messages.length > 1 ||
|
||||||
ngModel.dialog.messages.length == 0">s</span>
|
ngModel.dialog.messages.length == 0">s</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="abs message-body">
|
<div class="w-messages">
|
||||||
<mct-include
|
<mct-include
|
||||||
ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'"
|
ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'"
|
||||||
key="'message'" ng-model="msg.model"></mct-include>
|
key="'message'" ng-model="msg.model"></mct-include>
|
||||||
</div>
|
</div>
|
||||||
<div class="abs bottom-bar">
|
<div class="bottom-bar">
|
||||||
<a ng-repeat="dialogAction in ngModel.dialog.actions"
|
<a ng-repeat="dialogAction in ngModel.dialog.actions"
|
||||||
class="s-button major"
|
class="s-button major"
|
||||||
ng-click="dialogAction.action()">
|
ng-click="dialogAction.action()">
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
<mct-container key="overlay">
|
<mct-container key="overlay">
|
||||||
<div class="abs top-bar">
|
<div class="abs top-bar">
|
||||||
<div class="title">{{ngModel.dialog.title}}</div>
|
<div class="dialog-title">{{ngModel.dialog.title}}</div>
|
||||||
<div class="hint">{{ngModel.dialog.hint}}</div>
|
<div class="hint">{{ngModel.dialog.hint}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class='abs editor'>
|
<div class='abs editor'>
|
||||||
|
@ -121,7 +121,8 @@ define([
|
|||||||
"key": "ElementsController",
|
"key": "ElementsController",
|
||||||
"implementation": ElementsController,
|
"implementation": ElementsController,
|
||||||
"depends": [
|
"depends": [
|
||||||
"$scope"
|
"$scope",
|
||||||
|
"openmct"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -299,9 +300,6 @@ define([
|
|||||||
{
|
{
|
||||||
"key": "edit-elements",
|
"key": "edit-elements",
|
||||||
"template": elementsTemplate,
|
"template": elementsTemplate,
|
||||||
"uses": [
|
|
||||||
"composition"
|
|
||||||
],
|
|
||||||
"gestures": [
|
"gestures": [
|
||||||
"drop"
|
"drop"
|
||||||
]
|
]
|
||||||
@ -385,7 +383,10 @@ define([
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"implementation": EditToolbarRepresenter
|
"implementation": EditToolbarRepresenter,
|
||||||
|
"depends": [
|
||||||
|
"openmct"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"constants": [
|
"constants": [
|
||||||
|
@ -61,7 +61,12 @@
|
|||||||
<mct-representation key="representation.selected.key"
|
<mct-representation key="representation.selected.key"
|
||||||
mct-object="representation.selected.key && domainObject"
|
mct-object="representation.selected.key && domainObject"
|
||||||
class="abs flex-elem grows object-holder-main scroll"
|
class="abs flex-elem grows object-holder-main scroll"
|
||||||
toolbar="toolbar">
|
toolbar="toolbar"
|
||||||
|
mct-selectable="{
|
||||||
|
item: domainObject.useCapability('adapter'),
|
||||||
|
oldItem: domainObject
|
||||||
|
}"
|
||||||
|
mct-init-select>
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
</div><!--/ l-object-wrapper-inner -->
|
</div><!--/ l-object-wrapper-inner -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
ng-model="filterBy">
|
ng-model="filterBy">
|
||||||
</mct-include>
|
</mct-include>
|
||||||
<div class="flex-elem grows vscroll">
|
<div class="flex-elem grows vscroll">
|
||||||
<ul class="tree">
|
<ul class="tree" ng-if="composition.length > 0">
|
||||||
<li ng-repeat="containedObject in composition | filter:searchElements">
|
<li ng-repeat="containedObject in composition | filter:searchElements">
|
||||||
<span class="tree-item">
|
<span class="tree-item">
|
||||||
<mct-representation
|
<mct-representation
|
||||||
@ -36,5 +36,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div ng-if="composition.length === 0">No contained elements</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -101,10 +101,15 @@ define(
|
|||||||
*/
|
*/
|
||||||
EditorCapability.prototype.finish = function () {
|
EditorCapability.prototype.finish = function () {
|
||||||
var domainObject = this.domainObject;
|
var domainObject = this.domainObject;
|
||||||
|
|
||||||
|
if (this.transactionService.isActive()) {
|
||||||
return this.transactionService.cancel().then(function () {
|
return this.transactionService.cancel().then(function () {
|
||||||
domainObject.getCapability("status").set("editing", false);
|
domainObject.getCapability("status").set("editing", false);
|
||||||
return domainObject;
|
return domainObject;
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(domainObject);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +29,11 @@ define(
|
|||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function ElementsController($scope) {
|
function ElementsController($scope, openmct) {
|
||||||
|
this.scope = $scope;
|
||||||
|
this.scope.composition = [];
|
||||||
|
var self = this;
|
||||||
|
|
||||||
function filterBy(text) {
|
function filterBy(text) {
|
||||||
if (typeof text === 'undefined') {
|
if (typeof text === 'undefined') {
|
||||||
return $scope.searchText;
|
return $scope.searchText;
|
||||||
@ -47,10 +51,44 @@ define(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setSelection(selection) {
|
||||||
|
self.scope.selection = selection;
|
||||||
|
self.refreshComposition(selection);
|
||||||
|
}
|
||||||
|
|
||||||
$scope.filterBy = filterBy;
|
$scope.filterBy = filterBy;
|
||||||
$scope.searchElements = searchElements;
|
$scope.searchElements = searchElements;
|
||||||
|
|
||||||
|
openmct.selection.on('change', setSelection);
|
||||||
|
setSelection(openmct.selection.get());
|
||||||
|
|
||||||
|
$scope.$on("$destroy", function () {
|
||||||
|
openmct.selection.off("change", setSelection);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the composition for the selected object and populates the scope with it.
|
||||||
|
*
|
||||||
|
* @param selection the selection object
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ElementsController.prototype.refreshComposition = function (selection) {
|
||||||
|
if (!selection[0]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedObjectComposition = selection[0].context.oldItem.useCapability('composition');
|
||||||
|
|
||||||
|
if (selectedObjectComposition) {
|
||||||
|
selectedObjectComposition.then(function (composition) {
|
||||||
|
this.scope.composition = composition;
|
||||||
|
}.bind(this));
|
||||||
|
} else {
|
||||||
|
this.scope.composition = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return ElementsController;
|
return ElementsController;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -38,7 +38,7 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @implements {Representer}
|
* @implements {Representer}
|
||||||
*/
|
*/
|
||||||
function EditToolbarRepresenter(scope, element, attrs) {
|
function EditToolbarRepresenter(openmct, scope, element, attrs) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
// Mark changes as ready to persist
|
// Mark changes as ready to persist
|
||||||
@ -109,6 +109,7 @@ define(
|
|||||||
this.updateSelection = updateSelection;
|
this.updateSelection = updateSelection;
|
||||||
this.toolbar = undefined;
|
this.toolbar = undefined;
|
||||||
this.toolbarObject = {};
|
this.toolbarObject = {};
|
||||||
|
this.openmct = openmct;
|
||||||
|
|
||||||
// If this representation exposes a toolbar, set up watches
|
// If this representation exposes a toolbar, set up watches
|
||||||
// to synchronize with it.
|
// to synchronize with it.
|
||||||
@ -146,7 +147,7 @@ define(
|
|||||||
// Expose the toolbar object to the parent scope
|
// Expose the toolbar object to the parent scope
|
||||||
initialize(definition);
|
initialize(definition);
|
||||||
// Create a selection scope
|
// Create a selection scope
|
||||||
this.setSelection(new EditToolbarSelection());
|
this.setSelection(new EditToolbarSelection(this.openmct));
|
||||||
// Initialize toolbar to an empty selection
|
// Initialize toolbar to an empty selection
|
||||||
this.updateSelection([]);
|
this.updateSelection([]);
|
||||||
};
|
};
|
||||||
|
@ -38,10 +38,18 @@ define(
|
|||||||
* @memberof platform/commonUI/edit
|
* @memberof platform/commonUI/edit
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function EditToolbarSelection() {
|
function EditToolbarSelection(openmct) {
|
||||||
this.selection = [{}];
|
this.selection = [{}];
|
||||||
this.selecting = false;
|
this.selecting = false;
|
||||||
this.selectedObj = undefined;
|
this.selectedObj = undefined;
|
||||||
|
|
||||||
|
openmct.selection.on('change', function (selection) {
|
||||||
|
if (selection[0] && selection[0].context.toolbar) {
|
||||||
|
this.select(selection[0].context.toolbar);
|
||||||
|
} else {
|
||||||
|
this.deselect();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,6 +62,7 @@ define(
|
|||||||
);
|
);
|
||||||
mockTransactionService.commit.andReturn(fastPromise());
|
mockTransactionService.commit.andReturn(fastPromise());
|
||||||
mockTransactionService.cancel.andReturn(fastPromise());
|
mockTransactionService.cancel.andReturn(fastPromise());
|
||||||
|
mockTransactionService.isActive = jasmine.createSpy('isActive');
|
||||||
|
|
||||||
mockStatusCapability = jasmine.createSpyObj(
|
mockStatusCapability = jasmine.createSpyObj(
|
||||||
"statusCapability",
|
"statusCapability",
|
||||||
@ -141,6 +142,7 @@ define(
|
|||||||
|
|
||||||
describe("finish", function () {
|
describe("finish", function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
mockTransactionService.isActive.andReturn(true);
|
||||||
capability.edit();
|
capability.edit();
|
||||||
capability.finish();
|
capability.finish();
|
||||||
});
|
});
|
||||||
@ -152,6 +154,23 @@ define(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("finish", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
mockTransactionService.isActive.andReturn(false);
|
||||||
|
capability.edit();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not cancel transaction when transaction is not active", function () {
|
||||||
|
capability.finish();
|
||||||
|
expect(mockTransactionService.cancel).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns a promise", function () {
|
||||||
|
expect(capability.finish() instanceof Promise).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe("dirty", function () {
|
describe("dirty", function () {
|
||||||
var model = {};
|
var model = {};
|
||||||
|
|
||||||
|
@ -27,11 +27,23 @@ define(
|
|||||||
|
|
||||||
describe("The Elements Pane controller", function () {
|
describe("The Elements Pane controller", function () {
|
||||||
var mockScope,
|
var mockScope,
|
||||||
|
mockOpenMCT,
|
||||||
|
mockSelection,
|
||||||
controller;
|
controller;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockScope = jasmine.createSpy("$scope");
|
mockScope = jasmine.createSpyObj("$scope", ['$on']);
|
||||||
controller = new ElementsController(mockScope);
|
mockSelection = jasmine.createSpyObj("selection", [
|
||||||
|
'on',
|
||||||
|
'off',
|
||||||
|
'get'
|
||||||
|
]);
|
||||||
|
mockSelection.get.andReturn([]);
|
||||||
|
mockOpenMCT = {
|
||||||
|
selection: mockSelection
|
||||||
|
};
|
||||||
|
|
||||||
|
controller = new ElementsController(mockScope, mockOpenMCT);
|
||||||
});
|
});
|
||||||
|
|
||||||
function getModel(model) {
|
function getModel(model) {
|
||||||
|
@ -29,7 +29,9 @@ define(
|
|||||||
mockElement,
|
mockElement,
|
||||||
testAttrs,
|
testAttrs,
|
||||||
mockUnwatch,
|
mockUnwatch,
|
||||||
representer;
|
representer,
|
||||||
|
mockOpenMCT,
|
||||||
|
mockSelection;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockScope = jasmine.createSpyObj(
|
mockScope = jasmine.createSpyObj(
|
||||||
@ -46,7 +48,18 @@ define(
|
|||||||
|
|
||||||
mockScope.$parent.$watchCollection.andReturn(mockUnwatch);
|
mockScope.$parent.$watchCollection.andReturn(mockUnwatch);
|
||||||
|
|
||||||
|
mockSelection = jasmine.createSpyObj("selection", [
|
||||||
|
'on',
|
||||||
|
'off',
|
||||||
|
'get'
|
||||||
|
]);
|
||||||
|
mockSelection.get.andReturn([]);
|
||||||
|
mockOpenMCT = {
|
||||||
|
selection: mockSelection
|
||||||
|
};
|
||||||
|
|
||||||
representer = new EditToolbarRepresenter(
|
representer = new EditToolbarRepresenter(
|
||||||
|
mockOpenMCT,
|
||||||
mockScope,
|
mockScope,
|
||||||
mockElement,
|
mockElement,
|
||||||
testAttrs
|
testAttrs
|
||||||
|
@ -28,13 +28,25 @@ define(
|
|||||||
var testProxy,
|
var testProxy,
|
||||||
testElement,
|
testElement,
|
||||||
otherElement,
|
otherElement,
|
||||||
selection;
|
selection,
|
||||||
|
mockSelection,
|
||||||
|
mockOpenMCT;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
testProxy = { someKey: "some value" };
|
testProxy = { someKey: "some value" };
|
||||||
testElement = { someOtherKey: "some other value" };
|
testElement = { someOtherKey: "some other value" };
|
||||||
otherElement = { yetAnotherKey: 42 };
|
otherElement = { yetAnotherKey: 42 };
|
||||||
selection = new EditToolbarSelection();
|
mockSelection = jasmine.createSpyObj("selection", [
|
||||||
|
// 'select',
|
||||||
|
'on',
|
||||||
|
'off',
|
||||||
|
'get'
|
||||||
|
]);
|
||||||
|
mockSelection.get.andReturn([]);
|
||||||
|
mockOpenMCT = {
|
||||||
|
selection: mockSelection
|
||||||
|
};
|
||||||
|
selection = new EditToolbarSelection(mockOpenMCT);
|
||||||
selection.proxy(testProxy);
|
selection.proxy(testProxy);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -121,6 +121,9 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
UTCTimeFormat.prototype.parse = function (text) {
|
UTCTimeFormat.prototype.parse = function (text) {
|
||||||
|
if (typeof text === 'number') {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
return moment.utc(text, DATE_FORMATS).valueOf();
|
return moment.utc(text, DATE_FORMATS).valueOf();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ define([
|
|||||||
"./src/controllers/BannerController",
|
"./src/controllers/BannerController",
|
||||||
"./src/directives/MCTContainer",
|
"./src/directives/MCTContainer",
|
||||||
"./src/directives/MCTDrag",
|
"./src/directives/MCTDrag",
|
||||||
|
"./src/directives/MCTSelectable",
|
||||||
"./src/directives/MCTClickElsewhere",
|
"./src/directives/MCTClickElsewhere",
|
||||||
"./src/directives/MCTResize",
|
"./src/directives/MCTResize",
|
||||||
"./src/directives/MCTPopup",
|
"./src/directives/MCTPopup",
|
||||||
@ -90,6 +91,7 @@ define([
|
|||||||
BannerController,
|
BannerController,
|
||||||
MCTContainer,
|
MCTContainer,
|
||||||
MCTDrag,
|
MCTDrag,
|
||||||
|
MCTSelectable,
|
||||||
MCTClickElsewhere,
|
MCTClickElsewhere,
|
||||||
MCTResize,
|
MCTResize,
|
||||||
MCTPopup,
|
MCTPopup,
|
||||||
@ -328,6 +330,13 @@ define([
|
|||||||
"$document"
|
"$document"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "mctSelectable",
|
||||||
|
"implementation": MCTSelectable,
|
||||||
|
"depends": [
|
||||||
|
"openmct"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "mctClickElsewhere",
|
"key": "mctClickElsewhere",
|
||||||
"implementation": MCTClickElsewhere,
|
"implementation": MCTClickElsewhere,
|
||||||
|
@ -137,6 +137,11 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
&.holder:not(:last-child) { margin-bottom: $interiorMarginLg; }
|
&.holder:not(:last-child) { margin-bottom: $interiorMarginLg; }
|
||||||
}
|
}
|
||||||
|
&.l-flex-accordion .flex-accordion-holder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
//overflow: hidden !important;
|
||||||
|
}
|
||||||
.flex-container { @include flex-direction(column); }
|
.flex-container { @include flex-direction(column); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.l-fixed-position-item {
|
.l-fixed-position-item {
|
||||||
|
border-width: 1px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
&.s-not-selected {
|
&.s-not-selected {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
@ -180,6 +180,20 @@ a.disabled {
|
|||||||
@include ellipsize();
|
@include ellipsize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-selection {
|
||||||
|
// aka selection = "None". Used in palettes and their menu buttons.
|
||||||
|
$c: red; $s: 48%; $e: 52%;
|
||||||
|
@include background-image(linear-gradient(-45deg,
|
||||||
|
transparent $s - 5%,
|
||||||
|
$c $s,
|
||||||
|
$c $e,
|
||||||
|
transparent $e + 5%
|
||||||
|
));
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.scrolling,
|
.scrolling,
|
||||||
.scroll {
|
.scroll {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
/********************************* CONTROLS */
|
/********************************* CONTROLS */
|
||||||
@import "controls/breadcrumb";
|
@import "controls/breadcrumb";
|
||||||
@import "controls/buttons";
|
@import "controls/buttons";
|
||||||
@import "controls/color-palette";
|
@import "controls/palette";
|
||||||
@import "controls/controls";
|
@import "controls/controls";
|
||||||
@import "controls/lists";
|
@import "controls/lists";
|
||||||
@import "controls/menus";
|
@import "controls/menus";
|
||||||
@ -80,3 +80,4 @@
|
|||||||
@import "autoflow";
|
@import "autoflow";
|
||||||
@import "features/imagery";
|
@import "features/imagery";
|
||||||
@import "features/time-display";
|
@import "features/time-display";
|
||||||
|
@import "widgets";
|
||||||
|
306
platform/commonUI/general/res/sass/_widgets.scss
Normal file
306
platform/commonUI/general/res/sass/_widgets.scss
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/************************************************************* WIDGET OBJECT */
|
||||||
|
.l-summary-widget {
|
||||||
|
// Widget layout classes here
|
||||||
|
@include ellipsize();
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
.widget-label:before {
|
||||||
|
// Widget icon
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin-right: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-summary-widget {
|
||||||
|
// Widget style classes here
|
||||||
|
@include boxShdw($shdwBtns);
|
||||||
|
border-radius: $basicCr;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: default;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: $interiorMarginLg $interiorMarginLg * 2;
|
||||||
|
&[href] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-edit-holder {
|
||||||
|
// Hide edit area when in browse mode
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-rule-header {
|
||||||
|
@extend .l-flex-row;
|
||||||
|
@include align-items(center);
|
||||||
|
margin-bottom: $interiorMargin;
|
||||||
|
> .flex-elem {
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-left: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-rules-wrapper,
|
||||||
|
.widget-rule-content,
|
||||||
|
.w-widget-test-data-content {
|
||||||
|
@include trans-prop-nice($props: (height, min-height, opacity), $dur: 250ms);
|
||||||
|
min-height: 0;
|
||||||
|
height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-rules-wrapper {
|
||||||
|
flex: 1 1 auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-rule-content.expanded {
|
||||||
|
overflow: visible !important;
|
||||||
|
min-height: 50px;
|
||||||
|
height: auto;
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-widget-test-data-content {
|
||||||
|
.l-enable {
|
||||||
|
padding: $interiorMargin 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-widget-test-data-items {
|
||||||
|
max-height: 20vh;
|
||||||
|
overflow-y: scroll !important;
|
||||||
|
padding-right: $interiorMargin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-widget-thumb-wrapper,
|
||||||
|
.l-compact-form label {
|
||||||
|
$ruleLabelW: 40%;
|
||||||
|
$ruleLabelMaxW: 150px;
|
||||||
|
@include display(flex);
|
||||||
|
max-width: $ruleLabelMaxW;
|
||||||
|
width: $ruleLabelW;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-message-widget-no-data {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************** EDITING A WIDGET */
|
||||||
|
.s-status-editing > mct-view > .w-summary-widget {
|
||||||
|
// Classes for editor layout while editing a widget
|
||||||
|
// This selector is ugly and brittle, but needed to prevent interface from showing when widget is in a layout
|
||||||
|
// being edited.
|
||||||
|
@include absPosDefault();
|
||||||
|
@extend .l-flex-col;
|
||||||
|
|
||||||
|
> .l-summary-widget {
|
||||||
|
// Main view of the summary widget
|
||||||
|
// Give some airspace and center the widget in the area
|
||||||
|
margin: 30px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-edit-holder {
|
||||||
|
display: flex; // Overrides `display: none` during Browse mode
|
||||||
|
.flex-accordion-holder {
|
||||||
|
// Needed because otherwise accordion elements "creep" when contents expand and contract
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
&.expanded-widget-test-data {
|
||||||
|
.w-widget-test-data-content {
|
||||||
|
min-height: 50px;
|
||||||
|
height: auto;
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: inherit;
|
||||||
|
}
|
||||||
|
&:not(.expanded-widget-rules) {
|
||||||
|
// Test data is expanded and rules are collapsed
|
||||||
|
// Make text data take up all the vertical space
|
||||||
|
.flex-accordion-holder { display: flex; }
|
||||||
|
.widget-test-data {
|
||||||
|
flex-grow: 999999;
|
||||||
|
}
|
||||||
|
.w-widget-test-data-items {
|
||||||
|
max-height: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.expanded-widget-rules {
|
||||||
|
.widget-rules-wrapper {
|
||||||
|
min-height: 50px;
|
||||||
|
height: auto;
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.s-status-no-data {
|
||||||
|
.widget-edit-holder {
|
||||||
|
opacity: 0.3;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.t-message-widget-no-data {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.l-compact-form {
|
||||||
|
// Overrides on .l-compact-form
|
||||||
|
ul {
|
||||||
|
&:last-child { margin: 0; }
|
||||||
|
li {
|
||||||
|
@include align-items(flex-start);
|
||||||
|
@include flex-wrap(nowrap);
|
||||||
|
line-height: 230%; // Provide enough space when controls wrap
|
||||||
|
padding: 2px 0;
|
||||||
|
&:not(.widget-rule-header) {
|
||||||
|
&:not(.connects-to-previous) {
|
||||||
|
border-top: 1px solid $colorFormLines;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.connects-to-previous {
|
||||||
|
padding: $interiorMargin 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> label {
|
||||||
|
display: block; // Needed to align text to right
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.s-widget-test-data-item {
|
||||||
|
// Single line of ul li label span, etc.
|
||||||
|
ul {
|
||||||
|
li {
|
||||||
|
border: none !important;
|
||||||
|
> label {
|
||||||
|
display: inline-block;
|
||||||
|
width: auto;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-edit-holder {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-rules-wrapper {
|
||||||
|
// Wrapper area that holds n rules
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding-right: $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-widget-rule,
|
||||||
|
.l-widget-test-data-item {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: $interiorMarginSm;
|
||||||
|
padding: $interiorMargin $interiorMarginLg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-widget-thumb-wrapper {
|
||||||
|
@extend .l-flex-row;
|
||||||
|
@include align-items(center);
|
||||||
|
> span { display: block; }
|
||||||
|
.grippy-holder,
|
||||||
|
.view-control {
|
||||||
|
margin-right: $interiorMargin;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-thumb {
|
||||||
|
@include flex(1 1 auto);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-title {
|
||||||
|
@include flex(0 1 auto);
|
||||||
|
color: pullForward($colorBodyFg, 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-description {
|
||||||
|
@include flex(1 1 auto);
|
||||||
|
@include ellipsize();
|
||||||
|
color: pushBack($colorBodyFg, 20%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-widget-rule,
|
||||||
|
.s-widget-test-data-item {
|
||||||
|
background-color: rgba($colorBodyFg, 0.1);
|
||||||
|
border-radius: $basicCr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.widget-thumb {
|
||||||
|
@include ellipsize();
|
||||||
|
@extend .s-summary-widget;
|
||||||
|
@extend .l-summary-widget;
|
||||||
|
padding: $interiorMarginSm $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide and show elements in the rule-header on hover
|
||||||
|
.l-widget-rule,
|
||||||
|
.l-widget-test-data-item {
|
||||||
|
.grippy,
|
||||||
|
.l-rule-action-buttons-wrapper,
|
||||||
|
.l-condition-action-buttons-wrapper,
|
||||||
|
.l-widget-test-data-item-action-buttons-wrapper {
|
||||||
|
@include trans-prop-nice($props: opacity, $dur: 500ms);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
.grippy,
|
||||||
|
.l-rule-action-buttons-wrapper,
|
||||||
|
.l-widget-test-data-item-action-buttons-wrapper {
|
||||||
|
@include trans-prop-nice($props: opacity, $dur: 0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.l-rule-action-buttons-wrapper {
|
||||||
|
.t-delete {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.t-condition {
|
||||||
|
&:hover {
|
||||||
|
.l-condition-action-buttons-wrapper {
|
||||||
|
@include trans-prop-nice($props: opacity, $dur: 0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -261,7 +261,7 @@ input[type="number"] {
|
|||||||
input[type="text"].lg { width: 100% !important; }
|
input[type="text"].lg { width: 100% !important; }
|
||||||
.l-input-med input[type="text"],
|
.l-input-med input[type="text"],
|
||||||
input[type="text"].med { width: 200px !important; }
|
input[type="text"].med { width: 200px !important; }
|
||||||
input[type="text"].sm { width: 50px !important; }
|
input[type="text"].sm, input[type="number"].sm { width: 50px !important; }
|
||||||
.l-numeric input[type="text"],
|
.l-numeric input[type="text"],
|
||||||
input[type="text"].numeric { text-align: right; }
|
input[type="text"].numeric { text-align: right; }
|
||||||
|
|
||||||
@ -317,14 +317,10 @@ input[type="text"].s-input-inline,
|
|||||||
.select {
|
.select {
|
||||||
@include btnSubtle($bg: $colorSelectBg);
|
@include btnSubtle($bg: $colorSelectBg);
|
||||||
@extend .icon-arrow-down; // Context arrow
|
@extend .icon-arrow-down; // Context arrow
|
||||||
@if $shdwBtns != none {
|
|
||||||
margin: 0 0 2px 0; // Needed to avoid dropshadow from being clipped by parent containers
|
|
||||||
}
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0 $interiorMargin;
|
padding: 0 $interiorMargin;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
line-height: $formInputH;
|
|
||||||
select {
|
select {
|
||||||
@include appearance(none);
|
@include appearance(none);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -340,11 +336,13 @@ input[type="text"].s-input-inline,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
&:before {
|
&:before {
|
||||||
pointer-events: none;
|
@include transform(translateY(-50%));
|
||||||
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
|
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
|
||||||
display: block;
|
display: block;
|
||||||
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: $interiorMargin; top: 0;
|
right: $interiorMargin;
|
||||||
|
top: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,8 +394,7 @@ input[type="text"].s-input-inline,
|
|||||||
.l-elem-wrapper {
|
.l-elem-wrapper {
|
||||||
mct-representation {
|
mct-representation {
|
||||||
// Holds the context-available item
|
// Holds the context-available item
|
||||||
// Must have min-width to make flex work properly
|
// Must have min-width to make flex work properly in Safari
|
||||||
// in Safari
|
|
||||||
min-width: 0.7em;
|
min-width: 0.7em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -563,7 +560,6 @@ input[type="text"].s-input-inline,
|
|||||||
height: $h;
|
height: $h;
|
||||||
margin-top: 1 + floor($h/2) * -1;
|
margin-top: 1 + floor($h/2) * -1;
|
||||||
@include btnSubtle(pullForward($colorBtnBg, 10%));
|
@include btnSubtle(pullForward($colorBtnBg, 10%));
|
||||||
//border-radius: 50% !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin sliderKnobRound() {
|
@mixin sliderKnobRound() {
|
||||||
@ -578,7 +574,6 @@ input[type="text"].s-input-inline,
|
|||||||
|
|
||||||
input[type="range"] {
|
input[type="range"] {
|
||||||
// HTML5 range inputs
|
// HTML5 range inputs
|
||||||
|
|
||||||
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
|
-webkit-appearance: none; /* Hides the slider so that custom slider can be made */
|
||||||
background: transparent; /* Otherwise white in Chrome */
|
background: transparent; /* Otherwise white in Chrome */
|
||||||
&:focus {
|
&:focus {
|
||||||
@ -736,6 +731,30 @@ textarea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.view-switcher,
|
||||||
|
.t-btn-view-large {
|
||||||
|
@include trans-prop-nice-fade($controlFadeMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-control {
|
||||||
|
@extend .icon-arrow-right;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.75em;
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
@include trans-prop-nice(transform, 100ms);
|
||||||
|
@include transform-origin(center);
|
||||||
|
}
|
||||||
|
&.expanded:before {
|
||||||
|
@include transform(rotate(90deg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grippy {
|
||||||
|
@extend .icon-grippy;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************** BROWSER ELEMENTS */
|
/******************************************************** BROWSER ELEMENTS */
|
||||||
body.desktop {
|
body.desktop {
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
|
@ -29,23 +29,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
font-size: 16px; //120%;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-label {
|
.title-label {
|
||||||
margin-left: $interiorMarginSm;
|
margin-left: $interiorMarginSm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-swatch,
|
||||||
.color-swatch {
|
.color-swatch {
|
||||||
// Used in color menu buttons in toolbar
|
// Used in color menu buttons in toolbar
|
||||||
$d: 10px;
|
$d: 10px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 1px solid rgba($colorBtnFg, 0.2);
|
border: 1px solid rgba($colorBtnFg, 0.2);
|
||||||
height: $d;
|
height: $d; width: $d;
|
||||||
width: $d;
|
line-height: $d;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-left: $interiorMarginSm;
|
margin-left: $interiorMarginSm;
|
||||||
margin-top: -2px;
|
margin-top: -2px;
|
||||||
|
&:not(.no-selection) {
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
/******************************************************************* STATUS BLOCK ELEMS */
|
||||||
@mixin statusBannerColors($bg, $fg: $colorStatusFg) {
|
@mixin statusBannerColors($bg, $fg: $colorStatusFg) {
|
||||||
$bgPb: 30%;
|
$bgPb: 30%;
|
||||||
$bgPbD: 10%;
|
$bgPbD: 10%;
|
||||||
@ -140,7 +140,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styles for messages and message banners */
|
/******************************************************************* MESSAGE BANNERS */
|
||||||
.message {
|
.message {
|
||||||
&.block {
|
&.block {
|
||||||
border-radius: $basicCr;
|
border-radius: $basicCr;
|
||||||
@ -196,7 +196,6 @@
|
|||||||
padding: 0 $interiorMargin;
|
padding: 0 $interiorMargin;
|
||||||
}
|
}
|
||||||
.close {
|
.close {
|
||||||
//@include test(red, 0.7);
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 7px;
|
font-size: 7px;
|
||||||
width: 8px;
|
width: 8px;
|
||||||
@ -240,132 +239,147 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin messageBlock($iconW: 32px) {
|
/******************************************************************* MESSAGES */
|
||||||
.type-icon.message-type {
|
|
||||||
|
/* Contexts:
|
||||||
|
In .t-message-list
|
||||||
|
In .overlay as a singleton
|
||||||
|
Inline in the view area
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Archetypal message
|
||||||
|
.l-message {
|
||||||
|
$iconW: 32px;
|
||||||
|
@include display(flex);
|
||||||
|
@include flex-direction(row);
|
||||||
|
@include align-items(stretch);
|
||||||
|
padding: $interiorMarginLg;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
// Icon
|
||||||
|
@include flex(0 1 auto);
|
||||||
@include txtShdw($shdwStatusIc);
|
@include txtShdw($shdwStatusIc);
|
||||||
@extend .icon-bell;
|
@extend .icon-bell;
|
||||||
color: $colorStatusDefault;
|
color: $colorStatusDefault;
|
||||||
font-size: $iconW;
|
font-size: $iconW;
|
||||||
padding: 1px;
|
|
||||||
width: $iconW + 2;
|
width: $iconW + 2;
|
||||||
|
margin-right: $interiorMarginLg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-severity-info .type-icon.message-type {
|
&.message-severity-info:before {
|
||||||
@extend .icon-info;
|
@extend .icon-info;
|
||||||
color: $colorInfo;
|
color: $colorInfo;
|
||||||
}
|
}
|
||||||
.message-severity-alert .type-icon.message-type {
|
|
||||||
@extend .icon-bell;
|
&.message-severity-alert:before {
|
||||||
color: $colorWarningLo;
|
color: $colorWarningLo;
|
||||||
}
|
}
|
||||||
.message-severity-error .type-icon.message-type {
|
|
||||||
|
&.message-severity-error:before {
|
||||||
@extend .icon-alert-rect;
|
@extend .icon-alert-rect;
|
||||||
color: $colorWarningHi;
|
color: $colorWarningHi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Paths:
|
|
||||||
t-dialog | t-dialog-sm > t-message-single | t-message-list > overlay > holder > contents > l-message >
|
|
||||||
message-type > (icon)
|
|
||||||
message-contents >
|
|
||||||
top-bar >
|
|
||||||
title
|
|
||||||
hint
|
|
||||||
editor >
|
|
||||||
(if displaying list of messages)
|
|
||||||
ul > li > l-message >
|
|
||||||
... same as above
|
|
||||||
bottom-bar
|
|
||||||
*/
|
|
||||||
|
|
||||||
.l-message {
|
|
||||||
@include display(flex);
|
.w-message-contents {
|
||||||
@include flex-direction(row);
|
|
||||||
@include align-items(stretch);
|
|
||||||
.type-icon.message-type {
|
|
||||||
@include flex(0 1 auto);
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.message-contents {
|
|
||||||
@include flex(1 1 auto);
|
@include flex(1 1 auto);
|
||||||
margin-left: $overlayMargin;
|
@include display(flex);
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.top-bar,
|
|
||||||
.message-body {
|
|
||||||
margin-bottom: $interiorMarginLg * 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Message as singleton
|
|
||||||
.t-message-single {
|
|
||||||
@include messageBlock(80px);
|
|
||||||
}
|
|
||||||
|
|
||||||
body.desktop .t-message-single {
|
|
||||||
.l-message,
|
|
||||||
.bottom-bar {
|
|
||||||
@include absPosDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom-bar {
|
|
||||||
top: auto;
|
|
||||||
height: $ovrFooterH;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include phonePortrait {
|
|
||||||
.t-message-single {
|
|
||||||
.l-message {
|
|
||||||
@include flex-direction(column);
|
@include flex-direction(column);
|
||||||
.message-contents { margin-left: 0; }
|
|
||||||
}
|
> div,
|
||||||
.type-icon.message-type {
|
> span {
|
||||||
margin-bottom: $interiorMarginLg;
|
//@include test(red);
|
||||||
width: 100%;
|
margin-bottom: $interiorMargin;
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-bar {
|
.message-body {
|
||||||
text-align: center !important;
|
@include flex(1 1 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Singleton in an overlay dialog
|
||||||
|
.t-message-single .l-message,
|
||||||
|
.t-message-single.l-message {
|
||||||
|
$iconW: 80px;
|
||||||
|
@include absPosDefault();
|
||||||
|
padding: 0;
|
||||||
|
&:before {
|
||||||
|
font-size: $iconW;
|
||||||
|
width: $iconW + 2;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Singleton inline in a view
|
||||||
|
.t-message-inline .l-message,
|
||||||
|
.t-message-inline.l-message {
|
||||||
|
border-radius: $controlCr;
|
||||||
|
&.message-severity-info { background-color: rgba($colorInfo, 0.3); }
|
||||||
|
&.message-severity-alert { background-color: rgba($colorWarningLo, 0.3); }
|
||||||
|
&.message-severity-error { background-color: rgba($colorWarningHi, 0.3); }
|
||||||
|
|
||||||
|
.w-message-contents.l-message-body-only {
|
||||||
|
.message-body {
|
||||||
|
margin-top: $interiorMargin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Messages in list
|
// In a list
|
||||||
.t-message-list {
|
.t-message-list {
|
||||||
@include messageBlock(32px);
|
@include absPosDefault();
|
||||||
|
@include display(flex);
|
||||||
|
@include flex-direction(column);
|
||||||
|
|
||||||
.message-contents {
|
> div,
|
||||||
|
> span {
|
||||||
|
margin-bottom: $interiorMargin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-messages {
|
||||||
|
@include flex(1 1 100%);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: $interiorMargin;
|
||||||
|
}
|
||||||
|
// Each message
|
||||||
.l-message {
|
.l-message {
|
||||||
border-radius: $controlCr;
|
border-radius: $controlCr;
|
||||||
background: rgba($colorOvrFg, 0.1);
|
background: rgba($colorOvrFg, 0.1);
|
||||||
margin-bottom: $interiorMargin;
|
margin-bottom: $interiorMargin;
|
||||||
padding: $interiorMarginLg;
|
.hint,
|
||||||
|
|
||||||
.message-contents,
|
|
||||||
.bottom-bar {
|
.bottom-bar {
|
||||||
position: relative;
|
text-align: left;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-contents {
|
}
|
||||||
font-size: 0.9em;
|
|
||||||
margin-left: $interiorMarginLg;
|
|
||||||
.message-action { color: pushBack($colorOvrFg, 20%); }
|
|
||||||
.bottom-bar { text-align: left; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-bar,
|
@include phonePortrait {
|
||||||
.message-body {
|
.t-message-single .l-message,
|
||||||
|
.t-message-single.l-message {
|
||||||
|
@include flex-direction(column);
|
||||||
|
&:before {
|
||||||
|
margin-right: 0;
|
||||||
margin-bottom: $interiorMarginLg;
|
margin-bottom: $interiorMarginLg;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-bar {
|
||||||
|
text-align: center;
|
||||||
|
.s-button {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body.desktop .t-message-list {
|
body.desktop .t-message-list {
|
||||||
.message-contents .l-message { margin-right: $interiorMarginLg; }
|
.w-message-contents { padding-right: $interiorMargin; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alert elements in views
|
// Alert elements in views
|
||||||
|
@ -19,11 +19,10 @@
|
|||||||
* this source code distribution or the Licensing information page available
|
* this source code distribution or the Licensing information page available
|
||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
.l-color-palette {
|
.l-palette {
|
||||||
$d: 16px;
|
$d: 16px;
|
||||||
$colorsPerRow: 10;
|
$colorsPerRow: 10;
|
||||||
$m: 1;
|
$m: 1;
|
||||||
$colorSelectedColor: #fff;
|
|
||||||
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding: $interiorMargin !important;
|
padding: $interiorMargin !important;
|
||||||
@ -33,46 +32,41 @@
|
|||||||
line-height: $d;
|
line-height: $d;
|
||||||
width: ($d * $colorsPerRow) + ($m * $colorsPerRow);
|
width: ($d * $colorsPerRow) + ($m * $colorsPerRow);
|
||||||
|
|
||||||
|
&.l-option-row {
|
||||||
|
margin-bottom: $interiorMargin;
|
||||||
|
.s-palette-item {
|
||||||
|
border-color: $colorPaletteFg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.l-palette-item {
|
.l-palette-item {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@include txtShdwSubtle(0.8);
|
|
||||||
@include trans-prop-nice-fade(0.25s);
|
|
||||||
border: 1px solid transparent;
|
|
||||||
color: $colorSelectedColor;
|
|
||||||
display: block;
|
display: block;
|
||||||
float: left;
|
float: left;
|
||||||
height: $d; width: $d;
|
height: $d; width: $d;
|
||||||
line-height: $d * 0.9;
|
line-height: $d * 0.9;
|
||||||
margin: 0 ($m * 1px) ($m * 1px) 0;
|
margin: 0 ($m * 1px) ($m * 1px) 0;
|
||||||
|
position: relative;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
&:before {
|
|
||||||
// Check mark for selected items
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-palette-item {
|
.s-palette-item {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
color: $colorPaletteFg;
|
||||||
|
text-shadow: $shdwPaletteFg;
|
||||||
|
@include trans-prop-nice-fade(0.25s);
|
||||||
&:hover {
|
&:hover {
|
||||||
@include trans-prop-nice-fade(0);
|
@include trans-prop-nice-fade(0);
|
||||||
border-color: $colorSelectedColor !important;
|
border-color: $colorPaletteSelected !important;
|
||||||
|
}
|
||||||
|
&.selected {
|
||||||
|
border-color: $colorPaletteSelected;
|
||||||
|
box-shadow: $shdwPaletteSelected; //Needed to see selection rect on light colored swatches
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.l-palette-item-label {
|
.l-palette-item-label {
|
||||||
margin-left: $interiorMargin;
|
margin-left: $interiorMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.l-option-row {
|
|
||||||
margin-bottom: $interiorMargin;
|
|
||||||
.s-palette-item {
|
|
||||||
border-color: $colorBodyFg;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -80,23 +80,32 @@
|
|||||||
|
|
||||||
// Editing Grids
|
// Editing Grids
|
||||||
.l-grid-holder {
|
.l-grid-holder {
|
||||||
display: block;
|
|
||||||
.l-grid {
|
.l-grid {
|
||||||
&.l-grid-x { @include bgTicks($colorGridLines, 'x'); }
|
&.l-grid-x { @include bgTicks($colorGridLines, 'x'); }
|
||||||
&.l-grid-y { @include bgTicks($colorGridLines, 'y'); }
|
&.l-grid-y { @include bgTicks($colorGridLines, 'y'); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent nested frames from showing their grids
|
// Display grid when selected or selection parent.
|
||||||
.t-frame-outer .l-grid-holder { display: none !important; }
|
.s-selected .l-grid-holder,
|
||||||
|
.s-selected-parent .l-grid-holder {
|
||||||
// Prevent nested elements from showing s-hover-border
|
display: block;
|
||||||
.t-frame-outer .s-hover-border {
|
|
||||||
border: none !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent nested frames from being selectable until we have proper sub-object editing
|
// Display in nested frames...
|
||||||
.t-frame-outer .t-frame-outer {
|
.t-frame-outer {
|
||||||
pointer-events: none;
|
// ...when drilled in or selection parent...
|
||||||
|
&.s-drilled-in, &.s-selected-parent {
|
||||||
|
.l-grid-holder {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.t-frame-outer:not(.s-drilled-in) .l-grid-holder {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ...but hide otherwise.
|
||||||
|
.l-grid-holder {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,19 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
.section-header {
|
.section-header {
|
||||||
|
border-radius: $basicCr;
|
||||||
|
background: $colorFormSectionHeader;
|
||||||
|
color: lighten($colorBodyFg, 20%);
|
||||||
|
font-size: inherit;
|
||||||
|
margin: $interiorMargin 0;
|
||||||
|
padding: $formTBPad $formLRPad;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
.view-control {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: $interiorMargin;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form {
|
.form {
|
||||||
@ -41,15 +53,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-header {
|
|
||||||
border-radius: $basicCr;
|
|
||||||
background: $colorFormSectionHeader;
|
|
||||||
$c: lighten($colorBodyFg, 20%);
|
|
||||||
color: $c;
|
|
||||||
font-size: 0.8em;
|
|
||||||
padding: $formTBPad $formLRPad;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-row {
|
.form-row {
|
||||||
$m: $interiorMargin;
|
$m: $interiorMargin;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -57,9 +60,6 @@
|
|||||||
margin-bottom: $interiorMarginLg * 2;
|
margin-bottom: $interiorMarginLg * 2;
|
||||||
padding: $formTBPad 0;
|
padding: $formTBPad 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
//&ng-form {
|
|
||||||
// display: block;
|
|
||||||
//}
|
|
||||||
|
|
||||||
&.first {
|
&.first {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
@ -171,3 +171,106 @@
|
|||||||
padding: $interiorMargin;
|
padding: $interiorMargin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**************************************************************************** COMPACT FORM */
|
||||||
|
// ul > li > label, control
|
||||||
|
// Make a new UL for each form section
|
||||||
|
// Allow control-first, controls-below
|
||||||
|
// TO-DO: migrate work in branch ch-plot-styling that users .inspector-config to use classes below instead
|
||||||
|
|
||||||
|
.l-compact-form .tree ul li,
|
||||||
|
.l-compact-form ul li {
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.l-compact-form {
|
||||||
|
$labelW: 40%;
|
||||||
|
$minW: $labelW;
|
||||||
|
ul {
|
||||||
|
margin-bottom: $interiorMarginLg;
|
||||||
|
li {
|
||||||
|
@include display(flex);
|
||||||
|
@include flex-wrap(wrap);
|
||||||
|
@include align-items(center);
|
||||||
|
label,
|
||||||
|
.control {
|
||||||
|
@include display(flex);
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
line-height: inherit;
|
||||||
|
width: $labelW;
|
||||||
|
}
|
||||||
|
.controls {
|
||||||
|
@include flex-grow(1);
|
||||||
|
margin-left: $interiorMargin;
|
||||||
|
input[type="text"],
|
||||||
|
input[type="search"],
|
||||||
|
input[type="number"],
|
||||||
|
.select {
|
||||||
|
height: $btnStdH;
|
||||||
|
line-height: $btnStdH;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.e-control {
|
||||||
|
// Individual form controls
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-left: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.connects-to-previous {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.section-header {
|
||||||
|
margin-top: $interiorMarginLg;
|
||||||
|
border-top: 1px solid $colorFormLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.controls-first {
|
||||||
|
.control {
|
||||||
|
@include flex-grow(0);
|
||||||
|
margin-right: $interiorMargin;
|
||||||
|
min-width: 0;
|
||||||
|
order: 1;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
@include flex-grow(1);
|
||||||
|
order: 2;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.controls-under {
|
||||||
|
display: block;
|
||||||
|
.control, label {
|
||||||
|
display: block;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li {
|
||||||
|
border-top: none !important;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-error {
|
||||||
|
// Block element that visually flags an error and contains a message
|
||||||
|
background-color: $colorFormFieldErrorBg;
|
||||||
|
color: $colorFormFieldErrorFg;
|
||||||
|
border-radius: $basicCr;
|
||||||
|
display: block;
|
||||||
|
padding: 1px 6px;
|
||||||
|
&:before {
|
||||||
|
content: $glyph-icon-alert-triangle;
|
||||||
|
display: inline;
|
||||||
|
font-family: symbolsfont;
|
||||||
|
margin-right: $interiorMarginSm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -79,6 +79,7 @@
|
|||||||
|
|
||||||
// Dialog boxes, size constrained and centered in desktop/tablet
|
// Dialog boxes, size constrained and centered in desktop/tablet
|
||||||
&.l-dialog {
|
&.l-dialog {
|
||||||
|
font-size: 0.8rem;
|
||||||
.s-button {
|
.s-button {
|
||||||
&:not(.major) {
|
&:not(.major) {
|
||||||
@include btnSubtle($bg: $colorOvrBtnBg, $bgHov: pullForward($colorOvrBtnBg, 10%), $fg: $colorOvrBtnFg, $fgHov: $colorOvrBtnFg, $ic: $colorOvrBtnFg, $icHov: $colorOvrBtnFg);
|
@include btnSubtle($bg: $colorOvrBtnBg, $bgHov: pullForward($colorOvrBtnBg, 10%), $fg: $colorOvrBtnFg, $fgHov: $colorOvrBtnFg, $ic: $colorOvrBtnFg, $icHov: $colorOvrBtnFg);
|
||||||
@ -125,9 +126,9 @@
|
|||||||
@include containerSubtle($colorOvrBg, $colorOvrFg);
|
@include containerSubtle($colorOvrBg, $colorOvrFg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.dialog-title {
|
||||||
@include ellipsize();
|
@include ellipsize();
|
||||||
font-size: 1.2em;
|
font-size: 1.5em;
|
||||||
line-height: 120%;
|
line-height: 120%;
|
||||||
margin-bottom: $interiorMargin;
|
margin-bottom: $interiorMargin;
|
||||||
}
|
}
|
||||||
|
@ -52,21 +52,13 @@ ul.tree {
|
|||||||
|
|
||||||
.view-control {
|
.view-control {
|
||||||
color: $colorItemTreeVC;
|
color: $colorItemTreeVC;
|
||||||
font-size: 0.75em;
|
|
||||||
margin-right: $interiorMargin;
|
margin-right: $interiorMargin;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
width: $treeVCW;
|
width: $treeVCW;
|
||||||
|
&:before { display: none; }
|
||||||
&.has-children {
|
&.has-children {
|
||||||
&:before {
|
&:before { display: block; }
|
||||||
position: absolute;
|
|
||||||
@include trans-prop-nice(transform, 100ms);
|
|
||||||
content: "\e904";
|
|
||||||
@include transform-origin(center);
|
|
||||||
}
|
|
||||||
&.expanded:before {
|
|
||||||
@include transform(rotate(90deg));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,15 +23,14 @@
|
|||||||
$ohH: $btnFrameH;
|
$ohH: $btnFrameH;
|
||||||
$bc: $colorInteriorBorder;
|
$bc: $colorInteriorBorder;
|
||||||
&.child-frame.panel {
|
&.child-frame.panel {
|
||||||
|
border: 1px solid transparent;
|
||||||
z-index: 0; // Needed to prevent child-frame controls from showing through when another child-frame is above
|
z-index: 0; // Needed to prevent child-frame controls from showing through when another child-frame is above
|
||||||
&:not(.no-frame) {
|
&:not(.no-frame) {
|
||||||
background: $colorBodyBg;
|
background: $colorBodyBg;
|
||||||
border: 1px solid $bc;
|
border-color: $bc;
|
||||||
&:hover {
|
|
||||||
border-color: lighten($bc, 10%);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.object-browse-bar {
|
.object-browse-bar {
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
height: $ohH;
|
height: $ohH;
|
||||||
@ -44,7 +43,8 @@
|
|||||||
|
|
||||||
&.t-object-type-timer,
|
&.t-object-type-timer,
|
||||||
&.t-object-type-clock,
|
&.t-object-type-clock,
|
||||||
&.t-object-type-hyperlink {
|
&.t-object-type-hyperlink,
|
||||||
|
&.t-object-type-summary-widget {
|
||||||
// Hide the right side buttons for objects where they don't make sense
|
// Hide the right side buttons for objects where they don't make sense
|
||||||
// Note that this will hide the view Switcher button if applied
|
// Note that this will hide the view Switcher button if applied
|
||||||
// to an object that has it.
|
// to an object that has it.
|
||||||
@ -91,9 +91,9 @@
|
|||||||
|
|
||||||
&.no-frame {
|
&.no-frame {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
border: none !important;
|
border: none;
|
||||||
.object-browse-bar .right {
|
.object-browse-bar .right {
|
||||||
$m: 0; // $interiorMarginSm;
|
$m: 0;
|
||||||
background: rgba(black, 0.3);
|
background: rgba(black, 0.3);
|
||||||
border-radius: $basicCr;
|
border-radius: $basicCr;
|
||||||
padding: $interiorMarginSm;
|
padding: $interiorMarginSm;
|
||||||
@ -103,7 +103,7 @@
|
|||||||
}
|
}
|
||||||
&.t-frame-outer > .t-rep-frame {
|
&.t-frame-outer > .t-rep-frame {
|
||||||
&.contents {
|
&.contents {
|
||||||
$m: 2px;
|
$m: 0px;
|
||||||
top: $m;
|
top: $m;
|
||||||
right: $m;
|
right: $m;
|
||||||
bottom: $m;
|
bottom: $m;
|
||||||
@ -114,6 +114,7 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
> .object-holder.abs {
|
> .object-holder.abs {
|
||||||
|
overflow: hidden;
|
||||||
top: 0 !important;
|
top: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,13 +127,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/********************************************************** OBJECT TYPES */
|
/********************************************************** OBJECT TYPES */
|
||||||
.t-object-type-hyperlink {
|
.t-object-type-hyperlink,
|
||||||
|
.t-object-type-summary-widget {
|
||||||
.object-holder {
|
.object-holder {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
.w-summary-widget,
|
||||||
|
.l-summary-widget,
|
||||||
.l-hyperlink.s-button {
|
.l-hyperlink.s-button {
|
||||||
// When a hyperlink is a button in a frame, make it expand to fill out to the object-holder
|
// Some object types expand to the full size of the object-holder.
|
||||||
@extend .abs;
|
@extend .abs;
|
||||||
|
}
|
||||||
|
|
||||||
|
.l-summary-widget,
|
||||||
|
.l-hyperlink.s-button {
|
||||||
.label {
|
.label {
|
||||||
@include ellipsize();
|
@include ellipsize();
|
||||||
@include transform(translateY(-50%));
|
@include transform(translateY(-50%));
|
||||||
|
@ -20,21 +20,40 @@
|
|||||||
* at runtime from the About dialog for additional information.
|
* at runtime from the About dialog for additional information.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
.s-hover-border {
|
.s-hover-border {
|
||||||
border: 1px dotted transparent;
|
&:hover {
|
||||||
|
border-color: rgba($colorSelectableSelectedPrimary, 0.5) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-status-editing {
|
.s-status-editing {
|
||||||
// Limit to editing mode until we have sub-object selection
|
// Limit to editing mode
|
||||||
|
$o: 0.5;
|
||||||
|
$oHover: 0.8;
|
||||||
|
$bc: $colorSelectableSelectedPrimary;
|
||||||
.s-hover-border {
|
.s-hover-border {
|
||||||
// Show a border by default so user can see object bounds and empty objects
|
// Show a border by default so user can see object bounds and empty objects
|
||||||
border: 1px dotted rgba($colorSelectableSelectedPrimary, 0.3) !important;
|
border-color: rgba($bc, $o) !important;
|
||||||
|
border-style: dotted !important;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: rgba($colorSelectableSelectedPrimary, 0.7) !important;
|
border-color: rgba($bc, $oHover) !important;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.s-selected > .s-hover-border,
|
&.t-object-type-layout {
|
||||||
.s-selected.s-hover-border {
|
border-style: dashed !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.s-selected {
|
||||||
|
&.s-moveable {
|
||||||
|
&:not(.s-drilled-in) {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.s-selected > .s-hover-border,
|
||||||
|
.s-selected.s-hover-border {
|
||||||
// Styles for a selected object. Also used by legacy Fixed Position/Panel objects.
|
// Styles for a selected object. Also used by legacy Fixed Position/Panel objects.
|
||||||
border-color: $colorSelectableSelectedPrimary !important;
|
border-color: $colorSelectableSelectedPrimary !important;
|
||||||
@include boxShdwLarge();
|
@include boxShdwLarge();
|
||||||
@ -45,10 +64,7 @@
|
|||||||
background-color: rgba($colorKey, 1);
|
background-color: rgba($colorKey, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.s-selected > .s-moveable,
|
|
||||||
.s-selected.s-moveable {
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<span ng-controller="DateTimeFieldController">
|
<span ng-controller="DateTimeFieldController">
|
||||||
<input type="text"
|
<input type="text" autocorrect="off" spellcheck="false"
|
||||||
ng-model="textValue"
|
ng-model="textValue"
|
||||||
ng-blur="restoreTextValue(); ngBlur()"
|
ng-blur="restoreTextValue(); ngBlur()"
|
||||||
ng-mouseup="ngMouseup()"
|
ng-mouseup="ngMouseup()"
|
||||||
|
@ -40,7 +40,7 @@ define(
|
|||||||
|
|
||||||
// Gets an array of the contextual parents/ancestors of the selected object
|
// Gets an array of the contextual parents/ancestors of the selected object
|
||||||
function getContextualPath() {
|
function getContextualPath() {
|
||||||
var currentObj = $scope.ngModel.selectedObject,
|
var currentObj = $scope.domainObject,
|
||||||
currentParent,
|
currentParent,
|
||||||
parents = [];
|
parents = [];
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ define(
|
|||||||
|
|
||||||
// If this the the initial call of this recursive function
|
// If this the the initial call of this recursive function
|
||||||
if (!current) {
|
if (!current) {
|
||||||
current = $scope.ngModel.selectedObject;
|
current = $scope.domainObject;
|
||||||
$scope.primaryParents = [];
|
$scope.primaryParents = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,16 +87,16 @@ define(
|
|||||||
|
|
||||||
// Gets the metadata for the selected object
|
// Gets the metadata for the selected object
|
||||||
function getMetadata() {
|
function getMetadata() {
|
||||||
$scope.metadata = $scope.ngModel.selectedObject &&
|
$scope.metadata = $scope.domainObject &&
|
||||||
$scope.ngModel.selectedObject.hasCapability('metadata') &&
|
$scope.domainObject.hasCapability('metadata') &&
|
||||||
$scope.ngModel.selectedObject.useCapability('metadata');
|
$scope.domainObject.useCapability('metadata');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set scope variables when the selected object changes
|
// Set scope variables when the selected object changes
|
||||||
$scope.$watch('ngModel.selectedObject', function () {
|
$scope.$watch('domainObject', function () {
|
||||||
$scope.isLink = $scope.ngModel.selectedObject &&
|
$scope.isLink = $scope.domainObject &&
|
||||||
$scope.ngModel.selectedObject.hasCapability('location') &&
|
$scope.domainObject.hasCapability('location') &&
|
||||||
$scope.ngModel.selectedObject.getCapability('location').isLink();
|
$scope.domainObject.getCapability('location').isLink();
|
||||||
|
|
||||||
if ($scope.isLink) {
|
if ($scope.isLink) {
|
||||||
getPrimaryPath();
|
getPrimaryPath();
|
||||||
@ -109,7 +109,7 @@ define(
|
|||||||
getMetadata();
|
getMetadata();
|
||||||
});
|
});
|
||||||
|
|
||||||
var mutation = $scope.ngModel.selectedObject.getCapability('mutation');
|
var mutation = $scope.domainObject.getCapability('mutation');
|
||||||
var unlisten = mutation.listen(getMetadata);
|
var unlisten = mutation.listen(getMetadata);
|
||||||
$scope.$on('$destroy', unlisten);
|
$scope.$on('$destroy', unlisten);
|
||||||
}
|
}
|
||||||
|
60
platform/commonUI/general/src/directives/MCTSelectable.js
Normal file
60
platform/commonUI/general/src/directives/MCTSelectable.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mct-selectable directive allows selection functionality
|
||||||
|
* (click) to be attached to specific elements.
|
||||||
|
*
|
||||||
|
* @memberof platform/commonUI/general
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function MCTSelectable(openmct) {
|
||||||
|
|
||||||
|
// Link; install event handlers.
|
||||||
|
function link(scope, element, attrs) {
|
||||||
|
var removeSelectable = openmct.selection.selectable(
|
||||||
|
element[0],
|
||||||
|
scope.$eval(attrs.mctSelectable),
|
||||||
|
attrs.hasOwnProperty('mctInitSelect') && scope.$eval(attrs.mctInitSelect) !== false
|
||||||
|
);
|
||||||
|
|
||||||
|
scope.$on("$destroy", function () {
|
||||||
|
removeSelectable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// mct-selectable only makes sense as an attribute
|
||||||
|
restrict: "A",
|
||||||
|
// Link function, to install event handlers
|
||||||
|
link: link
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return MCTSelectable;
|
||||||
|
}
|
||||||
|
);
|
@ -41,16 +41,6 @@ define(
|
|||||||
"$scope",
|
"$scope",
|
||||||
["$watch", "$on"]
|
["$watch", "$on"]
|
||||||
);
|
);
|
||||||
mockScope.ngModel = {};
|
|
||||||
mockScope.ngModel.selectedObject = {
|
|
||||||
getCapability: function () {
|
|
||||||
return {
|
|
||||||
listen: function () {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
mockObjectService = jasmine.createSpyObj(
|
mockObjectService = jasmine.createSpyObj(
|
||||||
"objectService",
|
"objectService",
|
||||||
@ -77,22 +67,27 @@ define(
|
|||||||
"location capability",
|
"location capability",
|
||||||
["isLink"]
|
["isLink"]
|
||||||
);
|
);
|
||||||
|
|
||||||
mockDomainObject.getCapability.andCallFake(function (param) {
|
mockDomainObject.getCapability.andCallFake(function (param) {
|
||||||
if (param === 'location') {
|
if (param === 'location') {
|
||||||
return mockLocationCapability;
|
return mockLocationCapability;
|
||||||
} else if (param === 'context') {
|
} else if (param === 'context') {
|
||||||
return mockContextCapability;
|
return mockContextCapability;
|
||||||
|
} else if (param === 'mutation') {
|
||||||
|
return {
|
||||||
|
listen: function () {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
mockScope.domainObject = mockDomainObject;
|
||||||
controller = new ObjectInspectorController(mockScope, mockObjectService);
|
controller = new ObjectInspectorController(mockScope, mockObjectService);
|
||||||
|
|
||||||
// Change the selected object to trigger the watch call
|
|
||||||
mockScope.ngModel.selectedObject = mockDomainObject;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("watches for changes to the selected object", function () {
|
it("watches for changes to the selected object", function () {
|
||||||
expect(mockScope.$watch).toHaveBeenCalledWith('ngModel.selectedObject', jasmine.any(Function));
|
expect(mockScope.$watch).toHaveBeenCalledWith('domainObject', jasmine.any(Function));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("looks for contextual parent objects", function () {
|
it("looks for contextual parent objects", function () {
|
||||||
|
@ -38,7 +38,8 @@ define([
|
|||||||
"implementation": InspectorController,
|
"implementation": InspectorController,
|
||||||
"depends": [
|
"depends": [
|
||||||
"$scope",
|
"$scope",
|
||||||
"policyService"
|
"openmct",
|
||||||
|
"$document"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -21,44 +21,69 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
['../../browse/src/InspectorRegion'],
|
[],
|
||||||
function (InspectorRegion) {
|
function () {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The InspectorController adds region data for a domain object's type
|
* The InspectorController listens for the selection changes and adds the selection
|
||||||
* to the scope.
|
* object to the scope.
|
||||||
*
|
*
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function InspectorController($scope, policyService) {
|
function InspectorController($scope, openmct, $document) {
|
||||||
var domainObject = $scope.domainObject,
|
var self = this;
|
||||||
typeCapability = domainObject.getCapability('type'),
|
self.$scope = $scope;
|
||||||
statusListener;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filters region parts to only those allowed by region policies
|
* Callback handler for the selection change event.
|
||||||
* @param regions
|
* Adds the selection object to the scope. If the selected item has an inspector view,
|
||||||
* @returns {{}}
|
* it puts the key in the scope. If provider view exists, it shows the view.
|
||||||
*/
|
*/
|
||||||
function filterRegions(inspector) {
|
function setSelection(selection) {
|
||||||
//Dupe so we're not modifying the type definition.
|
if (selection[0]) {
|
||||||
return inspector.regions && inspector.regions.filter(function (region) {
|
var view = openmct.inspectorViews.get(selection);
|
||||||
return policyService.allow('region', region, domainObject);
|
var container = $document[0].querySelectorAll('.inspector-provider-view')[0];
|
||||||
});
|
container.innerHTML = "";
|
||||||
|
|
||||||
|
if (view) {
|
||||||
|
self.providerView = true;
|
||||||
|
view.show(container);
|
||||||
|
} else {
|
||||||
|
self.providerView = false;
|
||||||
|
$scope.inspectorKey = selection[0].context.oldItem.getCapability("type").typeDef.inspector;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setRegions() {
|
self.$scope.selection = selection;
|
||||||
$scope.regions = filterRegions(typeCapability.getDefinition().inspector || new InspectorRegion());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
statusListener = domainObject.getCapability("status").listen(setRegions);
|
openmct.selection.on("change", setSelection);
|
||||||
|
|
||||||
|
setSelection(openmct.selection.get());
|
||||||
|
|
||||||
$scope.$on("$destroy", function () {
|
$scope.$on("$destroy", function () {
|
||||||
statusListener();
|
openmct.selection.off("change", setSelection);
|
||||||
});
|
});
|
||||||
|
|
||||||
setRegions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the selected item.
|
||||||
|
*
|
||||||
|
* @returns a domain object
|
||||||
|
*/
|
||||||
|
InspectorController.prototype.selectedItem = function () {
|
||||||
|
return this.$scope.selection[0].context.oldItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a provider view exists.
|
||||||
|
*
|
||||||
|
* @returns 'true' if provider view exists, 'false' otherwise
|
||||||
|
*/
|
||||||
|
InspectorController.prototype.hasProviderView = function () {
|
||||||
|
return this.providerView;
|
||||||
|
};
|
||||||
|
|
||||||
return InspectorController;
|
return InspectorController;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -27,82 +27,93 @@ define(
|
|||||||
describe("The inspector controller ", function () {
|
describe("The inspector controller ", function () {
|
||||||
var mockScope,
|
var mockScope,
|
||||||
mockDomainObject,
|
mockDomainObject,
|
||||||
mockTypeCapability,
|
mockOpenMCT,
|
||||||
mockTypeDefinition,
|
mockSelection,
|
||||||
mockPolicyService,
|
mockInspectorViews,
|
||||||
mockStatusCapability,
|
mockTypeDef,
|
||||||
capabilities = {},
|
controller,
|
||||||
controller;
|
container,
|
||||||
|
$document = [],
|
||||||
|
selectable = [];
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockTypeDefinition = {
|
mockTypeDef = {
|
||||||
inspector:
|
typeDef: {
|
||||||
{
|
inspector: "some-key"
|
||||||
'regions': [
|
|
||||||
{'name': 'Part One'},
|
|
||||||
{'name': 'Part Two'}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mockTypeCapability = jasmine.createSpyObj('typeCapability', [
|
|
||||||
'getDefinition'
|
|
||||||
]);
|
|
||||||
mockTypeCapability.getDefinition.andReturn(mockTypeDefinition);
|
|
||||||
capabilities.type = mockTypeCapability;
|
|
||||||
|
|
||||||
mockStatusCapability = jasmine.createSpyObj('statusCapability', [
|
|
||||||
'listen'
|
|
||||||
]);
|
|
||||||
capabilities.status = mockStatusCapability;
|
|
||||||
|
|
||||||
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
mockDomainObject = jasmine.createSpyObj('domainObject', [
|
||||||
'getCapability'
|
'getCapability'
|
||||||
]);
|
]);
|
||||||
mockDomainObject.getCapability.andCallFake(function (name) {
|
mockDomainObject.getCapability.andReturn(mockTypeDef);
|
||||||
return capabilities[name];
|
|
||||||
});
|
|
||||||
|
|
||||||
mockPolicyService = jasmine.createSpyObj('policyService', [
|
|
||||||
'allow'
|
|
||||||
]);
|
|
||||||
|
|
||||||
mockScope = jasmine.createSpyObj('$scope',
|
mockScope = jasmine.createSpyObj('$scope',
|
||||||
['$on']
|
['$on', 'selection']
|
||||||
);
|
);
|
||||||
|
|
||||||
mockScope.domainObject = mockDomainObject;
|
selectable[0] = {
|
||||||
|
context: {
|
||||||
|
oldItem: mockDomainObject
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mockSelection = jasmine.createSpyObj("selection", [
|
||||||
|
'on',
|
||||||
|
'off',
|
||||||
|
'get'
|
||||||
|
]);
|
||||||
|
mockSelection.get.andReturn(selectable);
|
||||||
|
|
||||||
|
mockInspectorViews = jasmine.createSpyObj('inspectorViews', ['get']);
|
||||||
|
mockOpenMCT = {
|
||||||
|
selection: mockSelection,
|
||||||
|
inspectorViews: mockInspectorViews
|
||||||
|
};
|
||||||
|
|
||||||
|
container = jasmine.createSpy('container', ['innerHTML']);
|
||||||
|
$document[0] = jasmine.createSpyObj("$document", ['querySelectorAll']);
|
||||||
|
$document[0].querySelectorAll.andReturn([container]);
|
||||||
|
|
||||||
|
controller = new InspectorController(mockScope, mockOpenMCT, $document);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("filters out regions disallowed by region policy", function () {
|
it("listens for selection change event", function () {
|
||||||
mockPolicyService.allow.andReturn(false);
|
expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
|
||||||
controller = new InspectorController(mockScope, mockPolicyService);
|
'change',
|
||||||
expect(mockScope.regions.length).toBe(0);
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(controller.selectedItem()).toEqual(mockDomainObject);
|
||||||
|
|
||||||
|
var mockItem = jasmine.createSpyObj('domainObject', [
|
||||||
|
'getCapability'
|
||||||
|
]);
|
||||||
|
mockItem.getCapability.andReturn(mockTypeDef);
|
||||||
|
selectable[0].context.oldItem = mockItem;
|
||||||
|
|
||||||
|
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
|
||||||
|
|
||||||
|
expect(controller.selectedItem()).toEqual(mockItem);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not filter out regions allowed by region policy", function () {
|
it("cleans up on scope destroy", function () {
|
||||||
mockPolicyService.allow.andReturn(true);
|
expect(mockScope.$on).toHaveBeenCalledWith(
|
||||||
controller = new InspectorController(mockScope, mockPolicyService);
|
'$destroy',
|
||||||
expect(mockScope.regions.length).toBe(2);
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
|
||||||
|
mockScope.$on.calls[0].args[1]();
|
||||||
|
|
||||||
|
expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
|
||||||
|
'change',
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Responds to status changes", function () {
|
it("adds selection object to scope", function () {
|
||||||
mockPolicyService.allow.andReturn(true);
|
expect(mockScope.selection).toEqual(selectable);
|
||||||
controller = new InspectorController(mockScope, mockPolicyService);
|
expect(controller.selectedItem()).toEqual(mockDomainObject);
|
||||||
expect(mockScope.regions.length).toBe(2);
|
|
||||||
expect(mockStatusCapability.listen).toHaveBeenCalled();
|
|
||||||
mockPolicyService.allow.andReturn(false);
|
|
||||||
mockStatusCapability.listen.mostRecentCall.args[0]();
|
|
||||||
expect(mockScope.regions.length).toBe(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Unregisters status listener", function () {
|
|
||||||
var mockListener = jasmine.createSpy('listener');
|
|
||||||
mockStatusCapability.listen.andReturn(mockListener);
|
|
||||||
controller = new InspectorController(mockScope, mockPolicyService);
|
|
||||||
expect(mockScope.$on).toHaveBeenCalledWith("$destroy", jasmine.any(Function));
|
|
||||||
mockScope.$on.mostRecentCall.args[1]();
|
|
||||||
expect(mockListener).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -243,6 +243,12 @@ $colorCalCellSelectedBg: $colorItemTreeSelectedBg;
|
|||||||
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
|
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
|
||||||
$colorCalCellInMonthBg: pushBack($colorMenuBg, 5%);
|
$colorCalCellInMonthBg: pushBack($colorMenuBg, 5%);
|
||||||
|
|
||||||
|
// Palettes
|
||||||
|
$colorPaletteFg: pullForward($colorMenuBg, 30%);
|
||||||
|
$colorPaletteSelected: #fff;
|
||||||
|
$shdwPaletteFg: black 0 0 2px;
|
||||||
|
$shdwPaletteSelected: inset 0 0 0 1px #000;
|
||||||
|
|
||||||
// About Screen
|
// About Screen
|
||||||
$colorAboutLink: #84b3ff;
|
$colorAboutLink: #84b3ff;
|
||||||
|
|
||||||
|
@ -243,6 +243,12 @@ $colorCalCellSelectedBg: $colorItemTreeSelectedBg;
|
|||||||
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
|
$colorCalCellSelectedFg: $colorItemTreeSelectedFg;
|
||||||
$colorCalCellInMonthBg: pullForward($colorMenuBg, 5%);
|
$colorCalCellInMonthBg: pullForward($colorMenuBg, 5%);
|
||||||
|
|
||||||
|
// Palettes
|
||||||
|
$colorPaletteFg: pullForward($colorMenuBg, 30%);
|
||||||
|
$colorPaletteSelected: #333;
|
||||||
|
$shdwPaletteFg: none;
|
||||||
|
$shdwPaletteSelected: inset 0 0 0 1px #fff;
|
||||||
|
|
||||||
// About Screen
|
// About Screen
|
||||||
$colorAboutLink: #84b3ff;
|
$colorAboutLink: #84b3ff;
|
||||||
|
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
define([
|
|
||||||
'text!./res/templates/autoflow-tabular.html',
|
|
||||||
'./src/AutoflowTabularController',
|
|
||||||
'./src/MCTAutoflowTable'
|
|
||||||
], function (
|
|
||||||
autoflowTabularTemplate,
|
|
||||||
AutoflowTabularController,
|
|
||||||
MCTAutoflowTable
|
|
||||||
) {
|
|
||||||
return function (options) {
|
|
||||||
return function (openmct) {
|
|
||||||
openmct.legacyRegistry.register("platform/features/autoflow", {
|
|
||||||
"name": "WARP Telemetry Adapter",
|
|
||||||
"description": "Retrieves telemetry from the WARP Server and provides related types and views.",
|
|
||||||
"resources": "res",
|
|
||||||
"extensions": {
|
|
||||||
"views": [
|
|
||||||
{
|
|
||||||
"key": "autoflow",
|
|
||||||
"name": "Autoflow Tabular",
|
|
||||||
"cssClass": "icon-packet",
|
|
||||||
"description": "A tabular view of packet contents.",
|
|
||||||
"template": autoflowTabularTemplate,
|
|
||||||
"type": options && options.type,
|
|
||||||
"needs": [
|
|
||||||
"telemetry"
|
|
||||||
],
|
|
||||||
"delegation": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"controllers": [
|
|
||||||
{
|
|
||||||
"key": "AutoflowTabularController",
|
|
||||||
"implementation": AutoflowTabularController,
|
|
||||||
"depends": [
|
|
||||||
"$scope",
|
|
||||||
"$timeout",
|
|
||||||
"telemetrySubscriber"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"directives": [
|
|
||||||
{
|
|
||||||
"key": "mctAutoflowTable",
|
|
||||||
"implementation": MCTAutoflowTable
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
openmct.legacyRegistry.enable("platform/features/autoflow");
|
|
||||||
};
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
|||||||
<div class="items-holder abs contents autoflow obj-value-format"
|
|
||||||
ng-controller="AutoflowTabularController as autoflow">
|
|
||||||
<div class="abs l-flex-row holder t-autoflow-header l-autoflow-header">
|
|
||||||
<mct-include key="'input-filter'"
|
|
||||||
ng-model="autoflow.filter"
|
|
||||||
class="flex-elem">
|
|
||||||
</mct-include>
|
|
||||||
<div class="flex-elem grows t-last-update" title="Last Update">{{autoflow.updated()}}</div>
|
|
||||||
<a title="Change column width"
|
|
||||||
class="s-button flex-elem icon-arrows-right-left change-column-width"
|
|
||||||
ng-click="autoflow.increaseColumnWidth()"></a>
|
|
||||||
</div>
|
|
||||||
<div class="abs t-autoflow-items l-autoflow-items"
|
|
||||||
mct-resize="autoflow.setBounds(bounds)"
|
|
||||||
mct-resize-interval="50">
|
|
||||||
<mct-autoflow-table values="autoflow.rangeValues()"
|
|
||||||
objects="autoflow.getTelemetryObjects()"
|
|
||||||
rows="autoflow.getRows()"
|
|
||||||
classes="autoflow.classes()"
|
|
||||||
updated="autoflow.updated()"
|
|
||||||
column-width="autoflow.columnWidth()"
|
|
||||||
counter="autoflow.counter()"
|
|
||||||
>
|
|
||||||
</mct-autoflow-table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,169 +0,0 @@
|
|||||||
/*global angular*/
|
|
||||||
define(
|
|
||||||
[],
|
|
||||||
function () {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The link step for the `mct-autoflow-table` directive;
|
|
||||||
* watches scope and updates the DOM appropriately.
|
|
||||||
* See documentation in `MCTAutoflowTable.js` for the rationale
|
|
||||||
* for including this directive, as well as for an explanation
|
|
||||||
* of which values are placed in scope.
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
* @param {Scope} scope the scope for this usage of the directive
|
|
||||||
* @param element the jqLite-wrapped element which used this directive
|
|
||||||
*/
|
|
||||||
function AutoflowTableLinker(scope, element) {
|
|
||||||
var objects, // Domain objects at last structure refresh
|
|
||||||
rows, // Number of rows from last structure refresh
|
|
||||||
priorClasses = {},
|
|
||||||
valueSpans = {}; // Span elements to put data values in
|
|
||||||
|
|
||||||
// Create a new name-value pair in the specified column
|
|
||||||
function createListItem(domainObject, ul) {
|
|
||||||
// Create a new li, and spans to go in it.
|
|
||||||
var li = angular.element('<li>'),
|
|
||||||
titleSpan = angular.element('<span>'),
|
|
||||||
valueSpan = angular.element('<span>');
|
|
||||||
|
|
||||||
// Place spans in the li, and li into the column.
|
|
||||||
// valueSpan must precede titleSpan in the DOM due to new CSS float approach
|
|
||||||
li.append(valueSpan).append(titleSpan);
|
|
||||||
ul.append(li);
|
|
||||||
|
|
||||||
// Style appropriately
|
|
||||||
li.addClass('l-autoflow-row');
|
|
||||||
titleSpan.addClass('l-autoflow-item l');
|
|
||||||
valueSpan.addClass('l-autoflow-item r l-obj-val-format');
|
|
||||||
|
|
||||||
// Set text/tooltip for the name-value row
|
|
||||||
titleSpan.text(domainObject.getModel().name);
|
|
||||||
titleSpan.attr("title", domainObject.getModel().name);
|
|
||||||
|
|
||||||
// Keep a reference to the span which will hold the
|
|
||||||
// data value, to populate in the next refreshValues call
|
|
||||||
valueSpans[domainObject.getId()] = valueSpan;
|
|
||||||
|
|
||||||
return li;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new column of name-value pairs in this table.
|
|
||||||
function createColumn(el) {
|
|
||||||
// Create a ul
|
|
||||||
var ul = angular.element('<ul>');
|
|
||||||
|
|
||||||
// Add it into the mct-autoflow-table
|
|
||||||
el.append(ul);
|
|
||||||
|
|
||||||
// Style appropriately
|
|
||||||
ul.addClass('l-autoflow-col');
|
|
||||||
|
|
||||||
// Get the current col width and apply at time of column creation
|
|
||||||
// Important to do this here, as new columns could be created after
|
|
||||||
// the user has changed the width.
|
|
||||||
ul.css('width', scope.columnWidth + 'px');
|
|
||||||
|
|
||||||
// Return it, so some li elements can be added
|
|
||||||
return ul;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change the width of the columns when user clicks the resize button.
|
|
||||||
function resizeColumn() {
|
|
||||||
element.find('ul').css('width', scope.columnWidth + 'px');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rebuild the DOM associated with this table.
|
|
||||||
function rebuild(domainObjects, rowCount) {
|
|
||||||
var activeColumn;
|
|
||||||
|
|
||||||
// Empty out our cached span elements
|
|
||||||
valueSpans = {};
|
|
||||||
|
|
||||||
// Start with an empty DOM beneath this directive
|
|
||||||
element.html("");
|
|
||||||
|
|
||||||
// Add DOM elements for each domain object being displayed
|
|
||||||
// in this table.
|
|
||||||
domainObjects.forEach(function (object, index) {
|
|
||||||
// Start a new column if we'd run out of room
|
|
||||||
if (index % rowCount === 0) {
|
|
||||||
activeColumn = createColumn(element);
|
|
||||||
}
|
|
||||||
// Add the DOM elements for that object to whichever
|
|
||||||
// column (a `ul` element) is current.
|
|
||||||
createListItem(object, activeColumn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update spans with values, as made available via the
|
|
||||||
// `values` attribute of this directive.
|
|
||||||
function refreshValues() {
|
|
||||||
// Get the available values
|
|
||||||
var values = scope.values || {},
|
|
||||||
classes = scope.classes || {};
|
|
||||||
|
|
||||||
// Populate all spans with those values (or clear
|
|
||||||
// those spans if no value is available)
|
|
||||||
(objects || []).forEach(function (object) {
|
|
||||||
var id = object.getId(),
|
|
||||||
span = valueSpans[id],
|
|
||||||
value;
|
|
||||||
|
|
||||||
if (span) {
|
|
||||||
// Look up the value...
|
|
||||||
value = values[id];
|
|
||||||
// ...and convert to empty string if it's undefined
|
|
||||||
value = value === undefined ? "" : value;
|
|
||||||
span.attr("data-value", value);
|
|
||||||
|
|
||||||
// Update the span
|
|
||||||
span.text(value);
|
|
||||||
span.attr("title", value);
|
|
||||||
span.removeClass(priorClasses[id]);
|
|
||||||
span.addClass(classes[id]);
|
|
||||||
priorClasses[id] = classes[id];
|
|
||||||
}
|
|
||||||
// Also need stale/alert/ok class
|
|
||||||
// on span
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh the DOM for this table, if necessary
|
|
||||||
function refreshStructure() {
|
|
||||||
// Only rebuild if number of rows or set of objects
|
|
||||||
// has changed; otherwise, our structure is still valid.
|
|
||||||
if (scope.objects !== objects ||
|
|
||||||
scope.rows !== rows) {
|
|
||||||
|
|
||||||
// Track those values to support future refresh checks
|
|
||||||
objects = scope.objects;
|
|
||||||
rows = scope.rows;
|
|
||||||
|
|
||||||
// Rebuild the DOM
|
|
||||||
rebuild(objects || [], rows || 1);
|
|
||||||
|
|
||||||
// Refresh all data values shown
|
|
||||||
refreshValues();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Changing the domain objects in use or the number
|
|
||||||
// of rows should trigger a structure change (DOM rebuild)
|
|
||||||
scope.$watch("objects", refreshStructure);
|
|
||||||
scope.$watch("rows", refreshStructure);
|
|
||||||
|
|
||||||
// When the current column width has been changed, resize the column
|
|
||||||
scope.$watch('columnWidth', resizeColumn);
|
|
||||||
|
|
||||||
// When the last-updated time ticks,
|
|
||||||
scope.$watch("updated", refreshValues);
|
|
||||||
|
|
||||||
// Update displayed values when the counter changes.
|
|
||||||
scope.$watch("counter", refreshValues);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return AutoflowTableLinker;
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,324 +0,0 @@
|
|||||||
|
|
||||||
define(
|
|
||||||
['moment'],
|
|
||||||
function (moment) {
|
|
||||||
|
|
||||||
var ROW_HEIGHT = 16,
|
|
||||||
SLIDER_HEIGHT = 10,
|
|
||||||
INITIAL_COLUMN_WIDTH = 225,
|
|
||||||
MAX_COLUMN_WIDTH = 525,
|
|
||||||
COLUMN_WIDTH_STEP = 25,
|
|
||||||
DEBOUNCE_INTERVAL = 100,
|
|
||||||
DATE_FORMAT = "YYYY-DDD HH:mm:ss.SSS\\Z",
|
|
||||||
NOT_UPDATED = "No updates",
|
|
||||||
EMPTY_ARRAY = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responsible for supporting the autoflow tabular view.
|
|
||||||
* Implements the all-over logic which drives that view,
|
|
||||||
* mediating between template-provided areas, the included
|
|
||||||
* `mct-autoflow-table` directive, and the underlying
|
|
||||||
* domain object model.
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
function AutflowTabularController(
|
|
||||||
$scope,
|
|
||||||
$timeout,
|
|
||||||
telemetrySubscriber
|
|
||||||
) {
|
|
||||||
var filterValue = "",
|
|
||||||
filterValueLowercase = "",
|
|
||||||
subscription,
|
|
||||||
filteredObjects = [],
|
|
||||||
lastUpdated = {},
|
|
||||||
updateText = NOT_UPDATED,
|
|
||||||
rangeValues = {},
|
|
||||||
classes = {},
|
|
||||||
limits = {},
|
|
||||||
updatePending = false,
|
|
||||||
lastBounce = Number.NEGATIVE_INFINITY,
|
|
||||||
columnWidth = INITIAL_COLUMN_WIDTH,
|
|
||||||
rows = 1,
|
|
||||||
counter = 0;
|
|
||||||
|
|
||||||
// Trigger an update of the displayed table by incrementing
|
|
||||||
// the counter that it watches.
|
|
||||||
function triggerDisplayUpdate() {
|
|
||||||
counter += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whether or not an object's name matches the
|
|
||||||
// user-entered filter value.
|
|
||||||
function filterObject(domainObject) {
|
|
||||||
return (domainObject.getModel().name || "")
|
|
||||||
.toLowerCase()
|
|
||||||
.indexOf(filterValueLowercase) !== -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comparator for sorting points back into packet order
|
|
||||||
function compareObject(objectA, objectB) {
|
|
||||||
var indexA = objectA.getModel().index || 0,
|
|
||||||
indexB = objectB.getModel().index || 0;
|
|
||||||
return indexA - indexB;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the list of currently-displayed objects; these
|
|
||||||
// will be the subset of currently subscribed-to objects
|
|
||||||
// which match a user-entered filter.
|
|
||||||
function doUpdateFilteredObjects() {
|
|
||||||
// Generate the list
|
|
||||||
filteredObjects = (
|
|
||||||
subscription ?
|
|
||||||
subscription.getTelemetryObjects() :
|
|
||||||
[]
|
|
||||||
).filter(filterObject).sort(compareObject);
|
|
||||||
|
|
||||||
// Clear the pending flag
|
|
||||||
updatePending = false;
|
|
||||||
|
|
||||||
// Track when this occurred, so that we can wait
|
|
||||||
// a whole before updating again.
|
|
||||||
lastBounce = Date.now();
|
|
||||||
|
|
||||||
triggerDisplayUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request an update to the list of current objects; this may
|
|
||||||
// run on a timeout to avoid excessive calls, e.g. while the user
|
|
||||||
// is typing a filter.
|
|
||||||
function updateFilteredObjects() {
|
|
||||||
// Don't do anything if an update is already scheduled
|
|
||||||
if (!updatePending) {
|
|
||||||
if (Date.now() > lastBounce + DEBOUNCE_INTERVAL) {
|
|
||||||
// Update immediately if it's been long enough
|
|
||||||
doUpdateFilteredObjects();
|
|
||||||
} else {
|
|
||||||
// Otherwise, update later, and track that we have
|
|
||||||
// an update pending so that subsequent calls can
|
|
||||||
// be ignored.
|
|
||||||
updatePending = true;
|
|
||||||
$timeout(doUpdateFilteredObjects, DEBOUNCE_INTERVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track the latest data values for this domain object
|
|
||||||
function recordData(telemetryObject) {
|
|
||||||
// Get latest domain/range values for this object.
|
|
||||||
var id = telemetryObject.getId(),
|
|
||||||
domainValue = subscription.getDomainValue(telemetryObject),
|
|
||||||
rangeValue = subscription.getRangeValue(telemetryObject);
|
|
||||||
|
|
||||||
// Track the most recent timestamp change observed...
|
|
||||||
if (domainValue !== undefined && domainValue !== lastUpdated[id]) {
|
|
||||||
lastUpdated[id] = domainValue;
|
|
||||||
// ... and update the displayable text for that timestamp
|
|
||||||
updateText = isNaN(domainValue) ? "" :
|
|
||||||
moment.utc(domainValue).format(DATE_FORMAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store data values into the rangeValues structure, which
|
|
||||||
// will be used to populate the table itself.
|
|
||||||
// Note that we want full precision here.
|
|
||||||
rangeValues[id] = rangeValue;
|
|
||||||
|
|
||||||
// Update limit states as well
|
|
||||||
classes[id] = limits[id] && (limits[id].evaluate({
|
|
||||||
// This relies on external knowledge that the
|
|
||||||
// range value of a telemetry point is encoded
|
|
||||||
// in its datum as "value."
|
|
||||||
value: rangeValue
|
|
||||||
}) || {}).cssClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Look at telemetry objects from the subscription; this is watched
|
|
||||||
// to detect changes from the subscription.
|
|
||||||
function subscribedTelemetry() {
|
|
||||||
return subscription ?
|
|
||||||
subscription.getTelemetryObjects() : EMPTY_ARRAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the data values which will be used to populate the table
|
|
||||||
function updateValues() {
|
|
||||||
subscribedTelemetry().forEach(recordData);
|
|
||||||
triggerDisplayUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getter-setter function for user-entered filter text.
|
|
||||||
function filter(value) {
|
|
||||||
// If value was specified, we're a setter
|
|
||||||
if (value !== undefined) {
|
|
||||||
// Store the new value
|
|
||||||
filterValue = value;
|
|
||||||
filterValueLowercase = value.toLowerCase();
|
|
||||||
// Change which objects appear in the table
|
|
||||||
updateFilteredObjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always act as a getter
|
|
||||||
return filterValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the bounds (width and height) of this view;
|
|
||||||
// called from the mct-resize directive. Recalculates how
|
|
||||||
// many rows should appear in the contained table.
|
|
||||||
function setBounds(bounds) {
|
|
||||||
var availableSpace = bounds.height - SLIDER_HEIGHT;
|
|
||||||
rows = Math.max(1, Math.floor(availableSpace / ROW_HEIGHT));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment the current column width, up to the defined maximum.
|
|
||||||
// When the max is hit, roll back to the default.
|
|
||||||
function increaseColumnWidth() {
|
|
||||||
columnWidth += COLUMN_WIDTH_STEP;
|
|
||||||
// Cycle down to the initial width instead of exceeding max
|
|
||||||
columnWidth = columnWidth > MAX_COLUMN_WIDTH ?
|
|
||||||
INITIAL_COLUMN_WIDTH : columnWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get displayable text for last-updated value
|
|
||||||
function updated() {
|
|
||||||
return updateText;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsubscribe, if a subscription is active.
|
|
||||||
function releaseSubscription() {
|
|
||||||
if (subscription) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
subscription = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update set of telemetry objects managed by this view
|
|
||||||
function updateTelemetryObjects(telemetryObjects) {
|
|
||||||
updateFilteredObjects();
|
|
||||||
limits = {};
|
|
||||||
telemetryObjects.forEach(function (telemetryObject) {
|
|
||||||
var id = telemetryObject.getId();
|
|
||||||
limits[id] = telemetryObject.getCapability('limit');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a subscription for the represented domain object.
|
|
||||||
// This will resolve capability delegation as necessary.
|
|
||||||
function makeSubscription(domainObject) {
|
|
||||||
// Unsubscribe, if there is an existing subscription
|
|
||||||
releaseSubscription();
|
|
||||||
|
|
||||||
// Clear updated timestamp
|
|
||||||
lastUpdated = {};
|
|
||||||
updateText = NOT_UPDATED;
|
|
||||||
|
|
||||||
// Create a new subscription; telemetrySubscriber gets
|
|
||||||
// to do the meaningful work here.
|
|
||||||
subscription = domainObject && telemetrySubscriber.subscribe(
|
|
||||||
domainObject,
|
|
||||||
updateValues
|
|
||||||
);
|
|
||||||
|
|
||||||
// Our set of in-view telemetry objects may have changed,
|
|
||||||
// so update the set that is being passed down to the table.
|
|
||||||
updateFilteredObjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch for changes to the set of objects which have telemetry
|
|
||||||
$scope.$watch(subscribedTelemetry, updateTelemetryObjects);
|
|
||||||
|
|
||||||
// Watch for the represented domainObject (this field will
|
|
||||||
// be populated by mct-representation)
|
|
||||||
$scope.$watch("domainObject", makeSubscription);
|
|
||||||
|
|
||||||
// Make sure we unsubscribe when this view is destroyed.
|
|
||||||
$scope.$on("$destroy", releaseSubscription);
|
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Get the number of rows which should be shown in this table.
|
|
||||||
* @return {number} the number of rows to show
|
|
||||||
*/
|
|
||||||
getRows: function () {
|
|
||||||
return rows;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Get the objects which should currently be displayed in
|
|
||||||
* this table. This will be watched, so the return value
|
|
||||||
* should be stable when this list is unchanging. Only
|
|
||||||
* objects which match the user-entered filter value should
|
|
||||||
* be returned here.
|
|
||||||
* @return {DomainObject[]} the domain objects to include in
|
|
||||||
* this table.
|
|
||||||
*/
|
|
||||||
getTelemetryObjects: function () {
|
|
||||||
return filteredObjects;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Set the bounds (width/height) of this autoflow tabular view.
|
|
||||||
* The template must ensure that these bounds are tracked on
|
|
||||||
* the table area only.
|
|
||||||
* @param bounds the bounds; and object with `width` and
|
|
||||||
* `height` properties, both as numbers, in pixels.
|
|
||||||
*/
|
|
||||||
setBounds: setBounds,
|
|
||||||
/**
|
|
||||||
* Increments the width of the autoflow column.
|
|
||||||
* Setting does not yet persist.
|
|
||||||
*/
|
|
||||||
increaseColumnWidth: increaseColumnWidth,
|
|
||||||
/**
|
|
||||||
* Get-or-set the user-supplied filter value.
|
|
||||||
* @param {string} [value] the new filter value; omit to use
|
|
||||||
* as a getter
|
|
||||||
* @returns {string} the user-supplied filter value
|
|
||||||
*/
|
|
||||||
filter: filter,
|
|
||||||
/**
|
|
||||||
* Get all range values for use in this table. These will be
|
|
||||||
* returned as an object of key-value pairs, where keys are
|
|
||||||
* domain object IDs, and values are the most recently observed
|
|
||||||
* data values associated with those objects, formatted for
|
|
||||||
* display.
|
|
||||||
* @returns {object.<string,string>} most recent values
|
|
||||||
*/
|
|
||||||
rangeValues: function () {
|
|
||||||
return rangeValues;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Get CSS classes to apply to specific rows, representing limit
|
|
||||||
* states and/or stale states. These are returned as key-value
|
|
||||||
* pairs where keys are domain object IDs, and values are CSS
|
|
||||||
* classes to display for domain objects with those IDs.
|
|
||||||
* @returns {object.<string,string>} CSS classes
|
|
||||||
*/
|
|
||||||
classes: function () {
|
|
||||||
return classes;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Get the "last updated" text for this view; this will be
|
|
||||||
* the most recent timestamp observed for any telemetry-
|
|
||||||
* providing object, formatted for display.
|
|
||||||
* @returns {string} the time of the most recent update
|
|
||||||
*/
|
|
||||||
updated: updated,
|
|
||||||
/**
|
|
||||||
* Get the current column width, in pixels.
|
|
||||||
* @returns {number} column width
|
|
||||||
*/
|
|
||||||
columnWidth: function () {
|
|
||||||
return columnWidth;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Keep a counter and increment this whenever the display
|
|
||||||
* should be updated; this will be watched by the
|
|
||||||
* `mct-autoflow-table`.
|
|
||||||
* @returns {number} a counter value
|
|
||||||
*/
|
|
||||||
counter: function () {
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return AutflowTabularController;
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,60 +0,0 @@
|
|||||||
|
|
||||||
define(
|
|
||||||
["./AutoflowTableLinker"],
|
|
||||||
function (AutoflowTableLinker) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The `mct-autoflow-table` directive specifically supports
|
|
||||||
* autoflow tabular views; it is not intended for use outside
|
|
||||||
* of that view.
|
|
||||||
*
|
|
||||||
* This directive is responsible for creating the structure
|
|
||||||
* of the table in this view, and for updating its values.
|
|
||||||
* While this is achievable using a regular Angular template,
|
|
||||||
* this is undesirable from the perspective of performance
|
|
||||||
* due to the number of watches that can be involved for large
|
|
||||||
* tables. Instead, this directive will maintain a small number
|
|
||||||
* of watches, rebuilding table structure only when necessary,
|
|
||||||
* and updating displayed values in the more common case of
|
|
||||||
* new data arriving.
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
function MCTAutoflowTable() {
|
|
||||||
return {
|
|
||||||
// Only applicable at the element level
|
|
||||||
restrict: "E",
|
|
||||||
|
|
||||||
// The link function; handles DOM update/manipulation
|
|
||||||
link: AutoflowTableLinker,
|
|
||||||
|
|
||||||
// Parameters to pass from attributes into scope
|
|
||||||
scope: {
|
|
||||||
// Set of domain objects to show in the table
|
|
||||||
objects: "=",
|
|
||||||
|
|
||||||
// Values for those objects, by ID
|
|
||||||
values: "=",
|
|
||||||
|
|
||||||
// CSS classes to show for objects, by ID
|
|
||||||
classes: "=",
|
|
||||||
|
|
||||||
// Number of rows to show before autoflowing
|
|
||||||
rows: "=",
|
|
||||||
|
|
||||||
// Time of last update; watched to refresh values
|
|
||||||
updated: "=",
|
|
||||||
|
|
||||||
// Current width of the autoflow column
|
|
||||||
columnWidth: "=",
|
|
||||||
|
|
||||||
// A counter used to trigger display updates
|
|
||||||
counter: "="
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return MCTAutoflowTable;
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,178 +0,0 @@
|
|||||||
|
|
||||||
define(
|
|
||||||
["../src/AutoflowTableLinker"],
|
|
||||||
function (AutoflowTableLinker) {
|
|
||||||
|
|
||||||
describe("The mct-autoflow-table linker", function () {
|
|
||||||
var cachedAngular,
|
|
||||||
mockAngular,
|
|
||||||
mockScope,
|
|
||||||
mockElement,
|
|
||||||
mockElements,
|
|
||||||
linker;
|
|
||||||
|
|
||||||
// Utility function to generate more mock elements
|
|
||||||
function createMockElement(html) {
|
|
||||||
var mockEl = jasmine.createSpyObj(
|
|
||||||
"element-" + html,
|
|
||||||
[
|
|
||||||
"append",
|
|
||||||
"addClass",
|
|
||||||
"removeClass",
|
|
||||||
"text",
|
|
||||||
"attr",
|
|
||||||
"html",
|
|
||||||
"css",
|
|
||||||
"find"
|
|
||||||
]
|
|
||||||
);
|
|
||||||
mockEl.testHtml = html;
|
|
||||||
mockEl.append.andReturn(mockEl);
|
|
||||||
mockElements.push(mockEl);
|
|
||||||
return mockEl;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMockDomainObject(id) {
|
|
||||||
var mockDomainObject = jasmine.createSpyObj(
|
|
||||||
"domainObject-" + id,
|
|
||||||
["getId", "getModel"]
|
|
||||||
);
|
|
||||||
mockDomainObject.getId.andReturn(id);
|
|
||||||
mockDomainObject.getModel.andReturn({name: id.toUpperCase()});
|
|
||||||
return mockDomainObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fireWatch(watchExpression, value) {
|
|
||||||
mockScope.$watch.calls.forEach(function (call) {
|
|
||||||
if (call.args[0] === watchExpression) {
|
|
||||||
call.args[1](value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// AutoflowTableLinker accesses Angular in the global
|
|
||||||
// scope, since it is not injectable; we simulate that
|
|
||||||
// here by adding/removing it to/from the window object.
|
|
||||||
beforeEach(function () {
|
|
||||||
mockElements = [];
|
|
||||||
|
|
||||||
mockAngular = jasmine.createSpyObj("angular", ["element"]);
|
|
||||||
mockScope = jasmine.createSpyObj("scope", ["$watch"]);
|
|
||||||
mockElement = createMockElement('<div>');
|
|
||||||
|
|
||||||
mockAngular.element.andCallFake(createMockElement);
|
|
||||||
|
|
||||||
if (window.angular !== undefined) {
|
|
||||||
cachedAngular = window.angular;
|
|
||||||
}
|
|
||||||
window.angular = mockAngular;
|
|
||||||
|
|
||||||
linker = new AutoflowTableLinker(mockScope, mockElement);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function () {
|
|
||||||
if (cachedAngular !== undefined) {
|
|
||||||
window.angular = cachedAngular;
|
|
||||||
} else {
|
|
||||||
delete window.angular;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("watches for changes in inputs", function () {
|
|
||||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
|
||||||
"objects",
|
|
||||||
jasmine.any(Function)
|
|
||||||
);
|
|
||||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
|
||||||
"rows",
|
|
||||||
jasmine.any(Function)
|
|
||||||
);
|
|
||||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
|
||||||
"counter",
|
|
||||||
jasmine.any(Function)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("changes structure when domain objects change", function () {
|
|
||||||
// Set up scope
|
|
||||||
mockScope.rows = 4;
|
|
||||||
mockScope.objects = ['a', 'b', 'c', 'd', 'e', 'f']
|
|
||||||
.map(createMockDomainObject);
|
|
||||||
|
|
||||||
// Fire an update to the set of objects
|
|
||||||
fireWatch("objects");
|
|
||||||
|
|
||||||
// Should have rebuilt with two columns of
|
|
||||||
// four and two rows each; first, by clearing...
|
|
||||||
expect(mockElement.html).toHaveBeenCalledWith("");
|
|
||||||
|
|
||||||
// Should have appended two columns...
|
|
||||||
expect(mockElement.append.calls.length).toEqual(2);
|
|
||||||
|
|
||||||
// ...which should have received two and four rows each
|
|
||||||
expect(mockElement.append.calls[0].args[0].append.calls.length)
|
|
||||||
.toEqual(4);
|
|
||||||
expect(mockElement.append.calls[1].args[0].append.calls.length)
|
|
||||||
.toEqual(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates values", function () {
|
|
||||||
var mockSpans;
|
|
||||||
|
|
||||||
mockScope.objects = ['a', 'b', 'c', 'd', 'e', 'f']
|
|
||||||
.map(createMockDomainObject);
|
|
||||||
mockScope.values = { a: 0 };
|
|
||||||
|
|
||||||
// Fire an update to the set of values
|
|
||||||
fireWatch("objects");
|
|
||||||
fireWatch("updated");
|
|
||||||
|
|
||||||
// Get all created spans
|
|
||||||
mockSpans = mockElements.filter(function (mockElem) {
|
|
||||||
return mockElem.testHtml === '<span>';
|
|
||||||
});
|
|
||||||
|
|
||||||
// First span should be a, should have gotten this value.
|
|
||||||
// This test detects, in particular, WTD-749
|
|
||||||
expect(mockSpans[0].text).toHaveBeenCalledWith('A');
|
|
||||||
expect(mockSpans[1].text).toHaveBeenCalledWith(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("listens for changes in column width", function () {
|
|
||||||
var mockUL = createMockElement("<ul>");
|
|
||||||
mockElement.find.andReturn(mockUL);
|
|
||||||
mockScope.columnWidth = 200;
|
|
||||||
fireWatch("columnWidth", mockScope.columnWidth);
|
|
||||||
expect(mockUL.css).toHaveBeenCalledWith("width", "200px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("updates CSS classes", function () {
|
|
||||||
var mockSpans;
|
|
||||||
|
|
||||||
mockScope.objects = ['a', 'b', 'c', 'd', 'e', 'f']
|
|
||||||
.map(createMockDomainObject);
|
|
||||||
mockScope.values = { a: "a value to find" };
|
|
||||||
mockScope.classes = { a: 'class-a' };
|
|
||||||
|
|
||||||
// Fire an update to the set of values
|
|
||||||
fireWatch("objects");
|
|
||||||
fireWatch("updated");
|
|
||||||
|
|
||||||
// Figure out which span holds the relevant value...
|
|
||||||
mockSpans = mockElements.filter(function (mockElem) {
|
|
||||||
return mockElem.testHtml === '<span>';
|
|
||||||
}).filter(function (mockSpan) {
|
|
||||||
var attrCalls = mockSpan.attr.calls;
|
|
||||||
return attrCalls.some(function (call) {
|
|
||||||
return call.args[0] === 'title' &&
|
|
||||||
call.args[1] === mockScope.values.a;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// ...and make sure it also has had its class applied
|
|
||||||
expect(mockSpans[0].addClass)
|
|
||||||
.toHaveBeenCalledWith(mockScope.classes.a);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,341 +0,0 @@
|
|||||||
|
|
||||||
define(
|
|
||||||
["../src/AutoflowTabularController"],
|
|
||||||
function (AutoflowTabularController) {
|
|
||||||
|
|
||||||
describe("The autoflow tabular controller", function () {
|
|
||||||
var mockScope,
|
|
||||||
mockTimeout,
|
|
||||||
mockSubscriber,
|
|
||||||
mockDomainObject,
|
|
||||||
mockSubscription,
|
|
||||||
controller;
|
|
||||||
|
|
||||||
// Fire watches that are registered as functions.
|
|
||||||
function fireFnWatches() {
|
|
||||||
mockScope.$watch.calls.forEach(function (call) {
|
|
||||||
if (typeof call.args[0] === 'function') {
|
|
||||||
call.args[1](call.args[0]());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
mockScope = jasmine.createSpyObj(
|
|
||||||
"$scope",
|
|
||||||
["$on", "$watch"]
|
|
||||||
);
|
|
||||||
mockTimeout = jasmine.createSpy("$timeout");
|
|
||||||
mockSubscriber = jasmine.createSpyObj(
|
|
||||||
"telemetrySubscriber",
|
|
||||||
["subscribe"]
|
|
||||||
);
|
|
||||||
mockDomainObject = jasmine.createSpyObj(
|
|
||||||
"domainObject",
|
|
||||||
["getId", "getModel", "getCapability"]
|
|
||||||
);
|
|
||||||
mockSubscription = jasmine.createSpyObj(
|
|
||||||
"subscription",
|
|
||||||
[
|
|
||||||
"unsubscribe",
|
|
||||||
"getTelemetryObjects",
|
|
||||||
"getDomainValue",
|
|
||||||
"getRangeValue"
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
mockSubscriber.subscribe.andReturn(mockSubscription);
|
|
||||||
mockDomainObject.getModel.andReturn({name: "something"});
|
|
||||||
|
|
||||||
controller = new AutoflowTabularController(
|
|
||||||
mockScope,
|
|
||||||
mockTimeout,
|
|
||||||
mockSubscriber
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("listens for the represented domain object", function () {
|
|
||||||
expect(mockScope.$watch).toHaveBeenCalledWith(
|
|
||||||
"domainObject",
|
|
||||||
jasmine.any(Function)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("provides a getter-setter function for filtering", function () {
|
|
||||||
expect(controller.filter()).toEqual("");
|
|
||||||
controller.filter("something");
|
|
||||||
expect(controller.filter()).toEqual("something");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("tracks bounds and adjust number of rows accordingly", function () {
|
|
||||||
// Rows are 15px high, and need room for an 10px slider
|
|
||||||
controller.setBounds({ width: 700, height: 120 });
|
|
||||||
expect(controller.getRows()).toEqual(6); // 110 usable height / 16px
|
|
||||||
controller.setBounds({ width: 700, height: 240 });
|
|
||||||
expect(controller.getRows()).toEqual(14); // 230 usable height / 16px
|
|
||||||
});
|
|
||||||
|
|
||||||
it("subscribes to a represented object's telemetry", function () {
|
|
||||||
// Set up subscription, scope
|
|
||||||
mockSubscription.getTelemetryObjects
|
|
||||||
.andReturn([mockDomainObject]);
|
|
||||||
mockScope.domainObject = mockDomainObject;
|
|
||||||
|
|
||||||
// Invoke the watcher with represented domain object
|
|
||||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
|
||||||
|
|
||||||
// Should have subscribed to it
|
|
||||||
expect(mockSubscriber.subscribe).toHaveBeenCalledWith(
|
|
||||||
mockDomainObject,
|
|
||||||
jasmine.any(Function)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Should report objects as reported from subscription
|
|
||||||
expect(controller.getTelemetryObjects())
|
|
||||||
.toEqual([mockDomainObject]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("releases subscriptions on destroy", function () {
|
|
||||||
// Set up subscription...
|
|
||||||
mockSubscription.getTelemetryObjects
|
|
||||||
.andReturn([mockDomainObject]);
|
|
||||||
mockScope.domainObject = mockDomainObject;
|
|
||||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
|
||||||
|
|
||||||
// Verify precondition
|
|
||||||
expect(mockSubscription.unsubscribe).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
// Make sure we're listening for $destroy
|
|
||||||
expect(mockScope.$on).toHaveBeenCalledWith(
|
|
||||||
"$destroy",
|
|
||||||
jasmine.any(Function)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fire a destroy event
|
|
||||||
mockScope.$on.mostRecentCall.args[1]();
|
|
||||||
|
|
||||||
// Should have unsubscribed
|
|
||||||
expect(mockSubscription.unsubscribe).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("presents latest values and latest update state", function () {
|
|
||||||
// Make sure values are available
|
|
||||||
mockSubscription.getDomainValue.andReturn(402654321123);
|
|
||||||
mockSubscription.getRangeValue.andReturn(789);
|
|
||||||
mockDomainObject.getId.andReturn('testId');
|
|
||||||
|
|
||||||
// Set up subscription...
|
|
||||||
mockSubscription.getTelemetryObjects
|
|
||||||
.andReturn([mockDomainObject]);
|
|
||||||
mockScope.domainObject = mockDomainObject;
|
|
||||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
|
||||||
|
|
||||||
// Fire subscription callback
|
|
||||||
mockSubscriber.subscribe.mostRecentCall.args[1]();
|
|
||||||
|
|
||||||
// ...and exposed the results for template to consume
|
|
||||||
expect(controller.updated()).toEqual("1982-278 08:25:21.123Z");
|
|
||||||
expect(controller.rangeValues().testId).toEqual(789);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sorts domain objects by index", function () {
|
|
||||||
var testIndexes = { a: 2, b: 1, c: 3, d: 0 },
|
|
||||||
mockDomainObjects = Object.keys(testIndexes).sort().map(function (id) {
|
|
||||||
var mockDomainObj = jasmine.createSpyObj(
|
|
||||||
"domainObject",
|
|
||||||
["getId", "getModel"]
|
|
||||||
);
|
|
||||||
|
|
||||||
mockDomainObj.getId.andReturn(id);
|
|
||||||
mockDomainObj.getModel.andReturn({ index: testIndexes[id] });
|
|
||||||
|
|
||||||
return mockDomainObj;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Expose those domain objects...
|
|
||||||
mockSubscription.getTelemetryObjects.andReturn(mockDomainObjects);
|
|
||||||
mockScope.domainObject = mockDomainObject;
|
|
||||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
|
||||||
|
|
||||||
// Fire subscription callback
|
|
||||||
mockSubscriber.subscribe.mostRecentCall.args[1]();
|
|
||||||
|
|
||||||
// Controller should expose same objects, but sorted by index from model
|
|
||||||
expect(controller.getTelemetryObjects()).toEqual([
|
|
||||||
mockDomainObjects[3], // d, index=0
|
|
||||||
mockDomainObjects[1], // b, index=1
|
|
||||||
mockDomainObjects[0], // a, index=2
|
|
||||||
mockDomainObjects[2] // c, index=3
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("uses a timeout to throttle update", function () {
|
|
||||||
// Set up subscription...
|
|
||||||
mockSubscription.getTelemetryObjects
|
|
||||||
.andReturn([mockDomainObject]);
|
|
||||||
mockScope.domainObject = mockDomainObject;
|
|
||||||
|
|
||||||
// Set the object in view; should not need a timeout
|
|
||||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
|
||||||
expect(mockTimeout.calls.length).toEqual(0);
|
|
||||||
|
|
||||||
// Next call should schedule an update on a timeout
|
|
||||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
|
||||||
expect(mockTimeout.calls.length).toEqual(1);
|
|
||||||
|
|
||||||
// ...but this last one should not, since existing
|
|
||||||
// timeout will cover it
|
|
||||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
|
||||||
expect(mockTimeout.calls.length).toEqual(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows changing column width", function () {
|
|
||||||
var initialWidth = controller.columnWidth();
|
|
||||||
controller.increaseColumnWidth();
|
|
||||||
expect(controller.columnWidth()).toBeGreaterThan(initialWidth);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("filter", function () {
|
|
||||||
var doFilter,
|
|
||||||
filteredObjects,
|
|
||||||
filteredObjectNames;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
var telemetryObjects,
|
|
||||||
updateFilteredObjects;
|
|
||||||
|
|
||||||
telemetryObjects = [
|
|
||||||
'DEF123',
|
|
||||||
'abc789',
|
|
||||||
'456abc',
|
|
||||||
'4ab3cdef',
|
|
||||||
'hjs[12].*(){}^\\'
|
|
||||||
].map(function (objectName, index) {
|
|
||||||
var mockTelemetryObject = jasmine.createSpyObj(
|
|
||||||
objectName,
|
|
||||||
["getId", "getModel"]
|
|
||||||
);
|
|
||||||
|
|
||||||
mockTelemetryObject.getId.andReturn(objectName);
|
|
||||||
mockTelemetryObject.getModel.andReturn({
|
|
||||||
name: objectName,
|
|
||||||
index: index
|
|
||||||
});
|
|
||||||
|
|
||||||
return mockTelemetryObject;
|
|
||||||
});
|
|
||||||
|
|
||||||
mockSubscription
|
|
||||||
.getTelemetryObjects
|
|
||||||
.andReturn(telemetryObjects);
|
|
||||||
|
|
||||||
// Trigger domainObject change to create subscription.
|
|
||||||
mockScope.$watch.mostRecentCall.args[1](mockDomainObject);
|
|
||||||
|
|
||||||
updateFilteredObjects = function () {
|
|
||||||
filteredObjects = controller.getTelemetryObjects();
|
|
||||||
filteredObjectNames = filteredObjects.map(function (o) {
|
|
||||||
return o.getModel().name;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
doFilter = function (term) {
|
|
||||||
controller.filter(term);
|
|
||||||
// Filter is debounced so we have to force it to occur.
|
|
||||||
mockTimeout.mostRecentCall.args[0]();
|
|
||||||
updateFilteredObjects();
|
|
||||||
};
|
|
||||||
|
|
||||||
updateFilteredObjects();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("initially shows all objects", function () {
|
|
||||||
expect(filteredObjectNames).toEqual([
|
|
||||||
'DEF123',
|
|
||||||
'abc789',
|
|
||||||
'456abc',
|
|
||||||
'4ab3cdef',
|
|
||||||
'hjs[12].*(){}^\\'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("by blank string matches all objects", function () {
|
|
||||||
doFilter('');
|
|
||||||
expect(filteredObjectNames).toEqual([
|
|
||||||
'DEF123',
|
|
||||||
'abc789',
|
|
||||||
'456abc',
|
|
||||||
'4ab3cdef',
|
|
||||||
'hjs[12].*(){}^\\'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("exactly matches an object name", function () {
|
|
||||||
doFilter('4ab3cdef');
|
|
||||||
expect(filteredObjectNames).toEqual(['4ab3cdef']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("partially matches object names", function () {
|
|
||||||
doFilter('abc');
|
|
||||||
expect(filteredObjectNames).toEqual([
|
|
||||||
'abc789',
|
|
||||||
'456abc'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("matches case insensitive names", function () {
|
|
||||||
doFilter('def');
|
|
||||||
expect(filteredObjectNames).toEqual([
|
|
||||||
'DEF123',
|
|
||||||
'4ab3cdef'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("works as expected with special characters", function () {
|
|
||||||
doFilter('[12]');
|
|
||||||
expect(filteredObjectNames).toEqual(['hjs[12].*(){}^\\']);
|
|
||||||
doFilter('.*');
|
|
||||||
expect(filteredObjectNames).toEqual(['hjs[12].*(){}^\\']);
|
|
||||||
doFilter('.*()');
|
|
||||||
expect(filteredObjectNames).toEqual(['hjs[12].*(){}^\\']);
|
|
||||||
doFilter('.*?');
|
|
||||||
expect(filteredObjectNames).toEqual([]);
|
|
||||||
doFilter('.+');
|
|
||||||
expect(filteredObjectNames).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("exposes CSS classes from limits", function () {
|
|
||||||
var id = mockDomainObject.getId(),
|
|
||||||
testClass = "some-css-class",
|
|
||||||
mockLimitCapability =
|
|
||||||
jasmine.createSpyObj('limit', ['evaluate']);
|
|
||||||
|
|
||||||
mockDomainObject.getCapability.andCallFake(function (key) {
|
|
||||||
return key === 'limit' && mockLimitCapability;
|
|
||||||
});
|
|
||||||
mockLimitCapability.evaluate
|
|
||||||
.andReturn({ cssClass: testClass });
|
|
||||||
|
|
||||||
mockSubscription.getTelemetryObjects
|
|
||||||
.andReturn([mockDomainObject]);
|
|
||||||
|
|
||||||
fireFnWatches();
|
|
||||||
mockSubscriber.subscribe.mostRecentCall.args[1]();
|
|
||||||
|
|
||||||
expect(controller.classes()[id]).toEqual(testClass);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("exposes a counter that changes with each update", function () {
|
|
||||||
var i, prior;
|
|
||||||
|
|
||||||
for (i = 0; i < 10; i += 1) {
|
|
||||||
prior = controller.counter();
|
|
||||||
expect(controller.counter()).toEqual(prior);
|
|
||||||
mockSubscriber.subscribe.mostRecentCall.args[1]();
|
|
||||||
expect(controller.counter()).not.toEqual(prior);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
define(
|
|
||||||
["../src/MCTAutoflowTable"],
|
|
||||||
function (MCTAutoflowTable) {
|
|
||||||
|
|
||||||
describe("The mct-autoflow-table directive", function () {
|
|
||||||
var mctAutoflowTable;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
mctAutoflowTable = new MCTAutoflowTable();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Real functionality is contained/tested in the linker,
|
|
||||||
// so just check to make sure we're exposing the directive
|
|
||||||
// appropriately.
|
|
||||||
it("is applicable at the element level", function () {
|
|
||||||
expect(mctAutoflowTable.restrict).toEqual("E");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("two-ways binds needed scope variables", function () {
|
|
||||||
expect(mctAutoflowTable.scope).toEqual({
|
|
||||||
objects: "=",
|
|
||||||
values: "=",
|
|
||||||
rows: "=",
|
|
||||||
updated: "=",
|
|
||||||
classes: "=",
|
|
||||||
columnWidth: "=",
|
|
||||||
counter: "="
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("provides a link function", function () {
|
|
||||||
expect(mctAutoflowTable.link).toEqual(jasmine.any(Function));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
@ -45,7 +45,7 @@ define(
|
|||||||
|
|
||||||
FollowIndicator.prototype.getText = function () {
|
FollowIndicator.prototype.getText = function () {
|
||||||
var timer = this.timerService.getTimer();
|
var timer = this.timerService.getTimer();
|
||||||
return (timer) ? 'Following timer ' + timer.getModel().name : NO_TIMER;
|
return timer ? ('Following timer ' + timer.name) : NO_TIMER;
|
||||||
};
|
};
|
||||||
|
|
||||||
FollowIndicator.prototype.getDescription = function () {
|
FollowIndicator.prototype.getDescription = function () {
|
||||||
|
@ -42,18 +42,15 @@ define(["../../src/indicators/FollowIndicator"], function (FollowIndicator) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("when a timer is set", function () {
|
describe("when a timer is set", function () {
|
||||||
var testModel;
|
var testObject;
|
||||||
var mockDomainObject;
|
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
testModel = { name: "some timer!" };
|
testObject = { name: "some timer!" };
|
||||||
mockDomainObject = jasmine.createSpyObj('timer', ['getModel']);
|
mockTimerService.getTimer.andReturn(testObject);
|
||||||
mockDomainObject.getModel.andReturn(testModel);
|
|
||||||
mockTimerService.getTimer.andReturn(mockDomainObject);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("displays the timer's name", function () {
|
it("displays the timer's name", function () {
|
||||||
expect(indicator.getText().indexOf(testModel.name))
|
expect(indicator.getText().indexOf(testObject.name))
|
||||||
.not.toEqual(-1);
|
.not.toEqual(-1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -255,6 +255,8 @@ define(
|
|||||||
if (this.nextDatum) {
|
if (this.nextDatum) {
|
||||||
this.updateValues(this.nextDatum);
|
this.updateValues(this.nextDatum);
|
||||||
delete this.nextDatum;
|
delete this.nextDatum;
|
||||||
|
} else {
|
||||||
|
this.updateValues(this.$scope.imageHistory[this.$scope.imageHistory.length - 1]);
|
||||||
}
|
}
|
||||||
this.autoScroll = true;
|
this.autoScroll = true;
|
||||||
}
|
}
|
||||||
|
@ -183,6 +183,17 @@ define(
|
|||||||
expect(controller.getImageUrl()).toEqual(newUrl);
|
expect(controller.getImageUrl()).toEqual(newUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("forwards large image view to latest image in history on un-pause", function () {
|
||||||
|
$scope.imageHistory = [
|
||||||
|
{ utc: 1434600258122, url: 'some/url1', selected: false},
|
||||||
|
{ utc: 1434600258123, url: 'some/url2', selected: false}
|
||||||
|
];
|
||||||
|
controller.paused(true);
|
||||||
|
controller.paused(false);
|
||||||
|
|
||||||
|
expect(controller.getImageUrl()).toEqual(controller.getImageUrl($scope.imageHistory[1]));
|
||||||
|
});
|
||||||
|
|
||||||
it("subscribes to telemetry", function () {
|
it("subscribes to telemetry", function () {
|
||||||
expect(openmct.telemetry.subscribe).toHaveBeenCalledWith(
|
expect(openmct.telemetry.subscribe).toHaveBeenCalledWith(
|
||||||
newDomainObject,
|
newDomainObject,
|
||||||
@ -227,7 +238,7 @@ define(
|
|||||||
expect(controller.updateHistory(mockDatum)).toBe(false);
|
expect(controller.updateHistory(mockDatum)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("user clicks on imagery thumbnail", function () {
|
describe("when user clicks on imagery thumbnail", function () {
|
||||||
var mockDatum = { utc: 1434600258123, url: 'some/url', selected: false};
|
var mockDatum = { utc: 1434600258123, url: 'some/url', selected: false};
|
||||||
|
|
||||||
it("pauses and adds selected class to imagery thumbnail", function () {
|
it("pauses and adds selected class to imagery thumbnail", function () {
|
||||||
@ -248,6 +259,7 @@ define(
|
|||||||
expect(controller.getTime()).toEqual(controller.timeFormat.format(mockDatum.utc));
|
expect(controller.getTime()).toEqual(controller.timeFormat.format(mockDatum.utc));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("initially shows an empty string for date/time", function () {
|
it("initially shows an empty string for date/time", function () {
|
||||||
|
@ -260,7 +260,9 @@ define([
|
|||||||
"key": "LayoutController",
|
"key": "LayoutController",
|
||||||
"implementation": LayoutController,
|
"implementation": LayoutController,
|
||||||
"depends": [
|
"depends": [
|
||||||
"$scope"
|
"$scope",
|
||||||
|
"$element",
|
||||||
|
"openmct"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
's-selected': controller.selected(element)
|
's-selected': controller.selected(element)
|
||||||
}"
|
}"
|
||||||
ng-style="element.style"
|
ng-style="element.style"
|
||||||
ng-click="controller.select(element)">
|
ng-click="controller.select(element, $event)">
|
||||||
<mct-include key="element.template"
|
<mct-include key="element.template"
|
||||||
parameters="{ gridSize: controller.getGridSize() }"
|
parameters="{ gridSize: controller.getGridSize() }"
|
||||||
ng-model="element">
|
ng-model="element">
|
||||||
@ -53,14 +53,16 @@
|
|||||||
mct-drag-down="controller.moveHandle().startDrag(controller.selected())"
|
mct-drag-down="controller.moveHandle().startDrag(controller.selected())"
|
||||||
mct-drag="controller.moveHandle().continueDrag(delta)"
|
mct-drag="controller.moveHandle().continueDrag(delta)"
|
||||||
mct-drag-up="controller.moveHandle().endDrag()"
|
mct-drag-up="controller.moveHandle().endDrag()"
|
||||||
ng-style="controller.selected().style">
|
ng-style="controller.selected().style"
|
||||||
|
ng-click="$event.stopPropagation()">
|
||||||
</div>
|
</div>
|
||||||
<div ng-repeat="handle in controller.handles()"
|
<div ng-repeat="handle in controller.handles()"
|
||||||
class="l-fixed-position-item-handle edit-corner"
|
class="l-fixed-position-item-handle edit-corner"
|
||||||
ng-style="handle.style()"
|
ng-style="handle.style()"
|
||||||
mct-drag-down="handle.startDrag()"
|
mct-drag-down="handle.startDrag()"
|
||||||
mct-drag="handle.continueDrag(delta)"
|
mct-drag="handle.continueDrag(delta)"
|
||||||
mct-drag-up="handle.endDrag()">
|
mct-drag-up="handle.endDrag()"
|
||||||
|
ng-click="$event.stopPropagation()">
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
|
<div class="frame frame-template t-frame-inner abs t-object-type-{{ domainObject.getModel().type }}">
|
||||||
<div class="abs object-browse-bar l-flex-row">
|
<div class="abs object-browse-bar l-flex-row">
|
||||||
<div class="left flex-elem l-flex-row grows">
|
<div class="left flex-elem l-flex-row grows">
|
||||||
<mct-representation
|
<mct-representation
|
||||||
|
@ -22,10 +22,12 @@
|
|||||||
|
|
||||||
<div class="abs l-layout"
|
<div class="abs l-layout"
|
||||||
ng-controller="LayoutController as controller"
|
ng-controller="LayoutController as controller"
|
||||||
ng-click="controller.clearSelection()">
|
ng-click="controller.bypassSelection($event)">
|
||||||
|
|
||||||
<!-- Background grid -->
|
<!-- Background grid -->
|
||||||
<div class="l-grid-holder" ng-click="controller.clearSelection()">
|
<div class="l-grid-holder"
|
||||||
|
ng-show="!controller.drilledIn"
|
||||||
|
ng-click="controller.bypassSelection($event)">
|
||||||
<div class="l-grid l-grid-x"
|
<div class="l-grid l-grid-x"
|
||||||
ng-if="!controller.getGridSize()[0] < 3"
|
ng-if="!controller.getGridSize()[0] < 3"
|
||||||
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
|
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
|
||||||
@ -34,10 +36,12 @@
|
|||||||
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
|
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='abs frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border'
|
<div class="abs frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border {{childObject.getId() + '-' + $id}} t-object-type-{{ childObject.getModel().type }}"
|
||||||
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-selected':controller.selected(childObject) }"
|
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-drilled-in': controller.isDrilledIn(childObject) }"
|
||||||
ng-repeat="childObject in composition"
|
ng-repeat="childObject in composition"
|
||||||
ng-click="controller.select($event, childObject.getId())"
|
ng-init="controller.selectIfNew(childObject.getId() + '-' + $id, childObject)"
|
||||||
|
mct-selectable="controller.getContext(childObject, true)"
|
||||||
|
ng-dblclick="controller.drill($event, childObject)"
|
||||||
ng-style="controller.getFrameStyle(childObject.getId())">
|
ng-style="controller.getFrameStyle(childObject.getId())">
|
||||||
|
|
||||||
<mct-representation key="'frame'"
|
<mct-representation key="'frame'"
|
||||||
@ -45,7 +49,7 @@
|
|||||||
mct-object="childObject">
|
mct-object="childObject">
|
||||||
</mct-representation>
|
</mct-representation>
|
||||||
<!-- Drag handles -->
|
<!-- Drag handles -->
|
||||||
<span class="abs t-edit-handle-holder s-hover-border" ng-if="controller.selected(childObject)">
|
<span class="abs t-edit-handle-holder" ng-if="controller.selected(childObject) && !controller.isDrilledIn(childObject)">
|
||||||
<span class="edit-handle edit-move"
|
<span class="edit-handle edit-move"
|
||||||
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
|
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
|
||||||
mct-drag="controller.continueDrag(delta)"
|
mct-drag="controller.continueDrag(delta)"
|
||||||
@ -73,7 +77,6 @@
|
|||||||
mct-drag-up="controller.endDrag()">
|
mct-drag-up="controller.endDrag()">
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -360,22 +360,47 @@ define(
|
|||||||
*/
|
*/
|
||||||
FixedController.prototype.updateView = function (telemetryObject, datum) {
|
FixedController.prototype.updateView = function (telemetryObject, datum) {
|
||||||
var metadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
var metadata = this.openmct.telemetry.getMetadata(telemetryObject);
|
||||||
var rangeMetadata = metadata.valuesForHints(['range'])[0];
|
var telemetryKeyToDisplay = this.chooseTelemetryKeyToDisplay(metadata);
|
||||||
var rangeKey = rangeMetadata.source || rangeMetadata.key;
|
var formattedTelemetryValue = this.getFormattedTelemetryValueForKey(telemetryKeyToDisplay, datum, metadata);
|
||||||
var valueMetadata = metadata.value(rangeKey);
|
|
||||||
var limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
|
var limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
|
||||||
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
|
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, telemetryKeyToDisplay);
|
||||||
var value = datum[valueMetadata.key];
|
|
||||||
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, rangeKey);
|
|
||||||
|
|
||||||
this.setDisplayedValue(
|
this.setDisplayedValue(
|
||||||
telemetryObject,
|
telemetryObject,
|
||||||
formatter.format(value),
|
formattedTelemetryValue,
|
||||||
alarm && alarm.cssClass
|
alarm && alarm.cssClass
|
||||||
);
|
);
|
||||||
this.digest();
|
this.digest();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
FixedController.prototype.getFormattedTelemetryValueForKey = function (telemetryKeyToDisplay, datum, metadata) {
|
||||||
|
var valueMetadata = metadata.value(telemetryKeyToDisplay);
|
||||||
|
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
|
||||||
|
|
||||||
|
return formatter.format(datum[valueMetadata.key]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
FixedController.prototype.chooseTelemetryKeyToDisplay = function (metadata) {
|
||||||
|
// If there is a range value, show that preferentially
|
||||||
|
var telemetryKeyToDisplay = metadata.valuesForHints(['range'])[0];
|
||||||
|
|
||||||
|
// If no range is defined, default to the highest priority non time-domain data.
|
||||||
|
if (telemetryKeyToDisplay === undefined) {
|
||||||
|
var valuesOrderedByPriority = metadata.values();
|
||||||
|
telemetryKeyToDisplay = valuesOrderedByPriority.filter(function (valueMetadata) {
|
||||||
|
return !(valueMetadata.hints.domain);
|
||||||
|
})[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return telemetryKeyToDisplay.source;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request the last historical data point for the given domain objects
|
* Request the last historical data point for the given domain objects
|
||||||
* @param {object[]} objects
|
* @param {object[]} objects
|
||||||
@ -388,7 +413,9 @@ define(
|
|||||||
objects.forEach(function (object) {
|
objects.forEach(function (object) {
|
||||||
self.openmct.telemetry.request(object, {start: bounds.start, end: bounds.end, size: 1})
|
self.openmct.telemetry.request(object, {start: bounds.start, end: bounds.end, size: 1})
|
||||||
.then(function (data) {
|
.then(function (data) {
|
||||||
|
if (data.length > 0) {
|
||||||
self.updateView(object, data[data.length - 1]);
|
self.updateView(object, data[data.length - 1]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return objects;
|
return objects;
|
||||||
@ -479,7 +506,11 @@ define(
|
|||||||
* Set the active user selection in this view.
|
* Set the active user selection in this view.
|
||||||
* @param element the element to select
|
* @param element the element to select
|
||||||
*/
|
*/
|
||||||
FixedController.prototype.select = function select(element) {
|
FixedController.prototype.select = function select(element, event) {
|
||||||
|
if (event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
if (this.selection) {
|
if (this.selection) {
|
||||||
// Update selection...
|
// Update selection...
|
||||||
this.selection.select(element);
|
this.selection.select(element);
|
||||||
|
@ -27,9 +27,11 @@
|
|||||||
*/
|
*/
|
||||||
define(
|
define(
|
||||||
[
|
[
|
||||||
|
'zepto',
|
||||||
'./LayoutDrag'
|
'./LayoutDrag'
|
||||||
],
|
],
|
||||||
function (
|
function (
|
||||||
|
$,
|
||||||
LayoutDrag
|
LayoutDrag
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -50,10 +52,12 @@ define(
|
|||||||
* @constructor
|
* @constructor
|
||||||
* @param {Scope} $scope the controller's Angular scope
|
* @param {Scope} $scope the controller's Angular scope
|
||||||
*/
|
*/
|
||||||
function LayoutController($scope) {
|
function LayoutController($scope, $element, openmct) {
|
||||||
var self = this,
|
var self = this,
|
||||||
callbackCount = 0;
|
callbackCount = 0;
|
||||||
|
|
||||||
|
this.$element = $element;
|
||||||
|
|
||||||
// Update grid size when it changed
|
// Update grid size when it changed
|
||||||
function updateGridSize(layoutGrid) {
|
function updateGridSize(layoutGrid) {
|
||||||
var oldSize = self.gridSize;
|
var oldSize = self.gridSize;
|
||||||
@ -123,12 +127,11 @@ define(
|
|||||||
self.layoutPanels(ids);
|
self.layoutPanels(ids);
|
||||||
self.setFrames(ids);
|
self.setFrames(ids);
|
||||||
|
|
||||||
// If there is a newly-dropped object, select it.
|
if (self.selectedId &&
|
||||||
if (self.droppedIdToSelectAfterRefresh) {
|
self.selectedId !== $scope.domainObject.getId() &&
|
||||||
self.select(null, self.droppedIdToSelectAfterRefresh);
|
composition.indexOf(self.selectedId) === -1) {
|
||||||
delete self.droppedIdToSelectAfterRefresh;
|
// Click triggers selection of layout parent.
|
||||||
} else if (composition.indexOf(self.selectedId) === -1) {
|
self.$element[0].click();
|
||||||
self.clearSelection();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -160,22 +163,39 @@ define(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Sets the selectable object in response to the selection change event.
|
||||||
|
function setSelection(selectable) {
|
||||||
|
var selection = selectable[0];
|
||||||
|
|
||||||
|
if (!selection) {
|
||||||
|
delete self.selectedId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.selectedId = selection.context.oldItem.getId();
|
||||||
|
self.drilledIn = undefined;
|
||||||
|
self.selectable = selectable;
|
||||||
|
}
|
||||||
|
|
||||||
this.positions = {};
|
this.positions = {};
|
||||||
this.rawPositions = {};
|
this.rawPositions = {};
|
||||||
this.gridSize = DEFAULT_GRID_SIZE;
|
this.gridSize = DEFAULT_GRID_SIZE;
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
|
this.drilledIn = undefined;
|
||||||
|
this.openmct = openmct;
|
||||||
|
|
||||||
// Watch for changes to the grid size in the model
|
// Watch for changes to the grid size in the model
|
||||||
$scope.$watch("model.layoutGrid", updateGridSize);
|
$scope.$watch("model.layoutGrid", updateGridSize);
|
||||||
|
|
||||||
$scope.$watch("selection", function (selection) {
|
|
||||||
this.selection = selection;
|
|
||||||
}.bind(this));
|
|
||||||
|
|
||||||
// Update composed objects on screen, and position panes
|
// Update composed objects on screen, and position panes
|
||||||
$scope.$watchCollection("model.composition", refreshComposition);
|
$scope.$watchCollection("model.composition", refreshComposition);
|
||||||
|
|
||||||
// Position panes where they are dropped
|
openmct.selection.on('change', setSelection);
|
||||||
|
|
||||||
|
$scope.$on("$destroy", function () {
|
||||||
|
openmct.selection.off("change", setSelection);
|
||||||
|
});
|
||||||
|
|
||||||
$scope.$on("mctDrop", handleDrop);
|
$scope.$on("mctDrop", handleDrop);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,37 +377,14 @@ define(
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the object is currently selected.
|
* Checks if the object is currently selected.
|
||||||
*
|
*
|
||||||
* @param {string} obj the object to check for selection
|
* @param {string} obj the object to check for selection
|
||||||
* @returns {boolean} true if selected, otherwise false
|
* @returns {boolean} true if selected, otherwise false
|
||||||
*/
|
*/
|
||||||
LayoutController.prototype.selected = function (obj) {
|
LayoutController.prototype.selected = function (obj) {
|
||||||
return !!this.selectedId && this.selectedId === obj.getId();
|
var sobj = this.openmct.selection.get()[0];
|
||||||
};
|
return (sobj && sobj.context.oldItem.getId() === obj.getId()) ? true : false;
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the active user selection in this view.
|
|
||||||
*
|
|
||||||
* @param event the mouse event
|
|
||||||
* @param {string} id the object id
|
|
||||||
*/
|
|
||||||
LayoutController.prototype.select = function (event, id) {
|
|
||||||
if (event) {
|
|
||||||
event.stopPropagation();
|
|
||||||
if (this.selection) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectedId = id;
|
|
||||||
|
|
||||||
var selectedObj = {};
|
|
||||||
selectedObj[this.frames[id] ? 'hideFrame' : 'showFrame'] = this.toggleFrame.bind(this, id);
|
|
||||||
|
|
||||||
if (this.selection) {
|
|
||||||
this.selection.select(selectedObj);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -396,7 +393,7 @@ define(
|
|||||||
* @param {string} id the object id
|
* @param {string} id the object id
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
LayoutController.prototype.toggleFrame = function (id) {
|
LayoutController.prototype.toggleFrame = function (id, domainObject) {
|
||||||
var configuration = this.$scope.configuration;
|
var configuration = this.$scope.configuration;
|
||||||
|
|
||||||
if (!configuration.panels[id]) {
|
if (!configuration.panels[id]) {
|
||||||
@ -404,21 +401,75 @@ define(
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.frames[id] = configuration.panels[id].hasFrame = !this.frames[id];
|
this.frames[id] = configuration.panels[id].hasFrame = !this.frames[id];
|
||||||
this.select(undefined, id); // reselect so toolbar updates
|
|
||||||
|
var selection = this.openmct.selection.get();
|
||||||
|
selection[0].context.toolbar = this.getToolbar(id, domainObject);
|
||||||
|
this.openmct.selection.select(selection); // reselect so toolbar updates
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the current user selection.
|
* Gets the toolbar object for the given domain object.
|
||||||
|
*
|
||||||
|
* @param id the domain object id
|
||||||
|
* @param domainObject the domain object
|
||||||
|
* @returns {object}
|
||||||
|
* @private
|
||||||
*/
|
*/
|
||||||
LayoutController.prototype.clearSelection = function () {
|
LayoutController.prototype.getToolbar = function (id, domainObject) {
|
||||||
|
var toolbarObj = {};
|
||||||
|
toolbarObj[this.frames[id] ? 'hideFrame' : 'showFrame'] = this.toggleFrame.bind(this, id, domainObject);
|
||||||
|
return toolbarObj;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bypasses selection if drag is in progress.
|
||||||
|
*
|
||||||
|
* @param event the angular event object
|
||||||
|
*/
|
||||||
|
LayoutController.prototype.bypassSelection = function (event) {
|
||||||
if (this.dragInProgress) {
|
if (this.dragInProgress) {
|
||||||
|
if (event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the domain object is drilled in.
|
||||||
|
*
|
||||||
|
* @param domainObject the domain object
|
||||||
|
* @return true if the object is drilled in, false otherwise
|
||||||
|
*/
|
||||||
|
LayoutController.prototype.isDrilledIn = function (domainObject) {
|
||||||
|
return this.drilledIn === domainObject.getId();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts the given object in the drilled-in mode.
|
||||||
|
*
|
||||||
|
* @param event the angular event object
|
||||||
|
* @param domainObject the domain object
|
||||||
|
*/
|
||||||
|
LayoutController.prototype.drill = function (event, domainObject) {
|
||||||
|
if (event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!domainObject.getCapability('editor').inEditContext()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selection) {
|
if (!domainObject.hasCapability('composition')) {
|
||||||
this.selection.deselect();
|
return;
|
||||||
delete this.selectedId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable since fixed position doesn't use the selection API yet
|
||||||
|
if (domainObject.getModel().type === 'telemetry.fixed') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.drilledIn = domainObject.getId();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -440,6 +491,36 @@ define(
|
|||||||
return this.gridSize;
|
return this.gridSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the selection context.
|
||||||
|
*
|
||||||
|
* @param domainObject the domain object
|
||||||
|
* @returns {object} the context object which includes
|
||||||
|
* item, oldItem and toolbar
|
||||||
|
*/
|
||||||
|
LayoutController.prototype.getContext = function (domainObject, toolbar) {
|
||||||
|
return {
|
||||||
|
item: domainObject.useCapability('adapter'),
|
||||||
|
oldItem: domainObject,
|
||||||
|
toolbar: toolbar ? this.getToolbar(domainObject.getId(), domainObject) : undefined
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects a newly-dropped object.
|
||||||
|
*
|
||||||
|
* @param classSelector the css class selector
|
||||||
|
* @param domainObject the domain object
|
||||||
|
*/
|
||||||
|
LayoutController.prototype.selectIfNew = function (selector, domainObject) {
|
||||||
|
if (domainObject.getId() === this.droppedIdToSelectAfterRefresh) {
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.' + selector)[0].click();
|
||||||
|
delete this.droppedIdToSelectAfterRefresh;
|
||||||
|
}.bind(this), 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return LayoutController;
|
return LayoutController;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -178,7 +178,6 @@ define(
|
|||||||
Promise.resolve(mockChildren)
|
Promise.resolve(mockChildren)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
mockScope.model = testModel;
|
mockScope.model = testModel;
|
||||||
mockScope.configuration = testConfiguration;
|
mockScope.configuration = testConfiguration;
|
||||||
mockScope.selection = jasmine.createSpyObj(
|
mockScope.selection = jasmine.createSpyObj(
|
||||||
@ -194,7 +193,8 @@ define(
|
|||||||
|
|
||||||
mockMetadata = jasmine.createSpyObj('mockMetadata', [
|
mockMetadata = jasmine.createSpyObj('mockMetadata', [
|
||||||
'valuesForHints',
|
'valuesForHints',
|
||||||
'value'
|
'value',
|
||||||
|
'values'
|
||||||
]);
|
]);
|
||||||
mockMetadata.value.andReturn({
|
mockMetadata.value.andReturn({
|
||||||
key: 'value'
|
key: 'value'
|
||||||
@ -653,6 +653,39 @@ define(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("selects an range value to display, if available", function () {
|
||||||
|
mockMetadata.valuesForHints.andReturn([
|
||||||
|
{
|
||||||
|
key: 'range',
|
||||||
|
source: 'range'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
|
||||||
|
expect(key).toEqual('range');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("selects the first non-domain value to display, if no range available", function () {
|
||||||
|
mockMetadata.valuesForHints.andReturn([]);
|
||||||
|
mockMetadata.values.andReturn([
|
||||||
|
{
|
||||||
|
key: 'domain',
|
||||||
|
source: 'domain',
|
||||||
|
hints: {
|
||||||
|
domain: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'image',
|
||||||
|
source: 'image',
|
||||||
|
hints: {
|
||||||
|
image: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
|
||||||
|
expect(key).toEqual('image');
|
||||||
|
});
|
||||||
|
|
||||||
it("reflects limit status", function () {
|
it("reflects limit status", function () {
|
||||||
mockLimitEvaluator.evaluate.andReturn({cssClass: "alarm-a"});
|
mockLimitEvaluator.evaluate.andReturn({cssClass: "alarm-a"});
|
||||||
controller.updateView(mockTelemetryObject, [{
|
controller.updateView(mockTelemetryObject, [{
|
||||||
|
@ -21,8 +21,14 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
["../src/LayoutController"],
|
[
|
||||||
function (LayoutController) {
|
"../src/LayoutController",
|
||||||
|
"zepto"
|
||||||
|
],
|
||||||
|
function (
|
||||||
|
LayoutController,
|
||||||
|
$
|
||||||
|
) {
|
||||||
|
|
||||||
describe("The Layout controller", function () {
|
describe("The Layout controller", function () {
|
||||||
var mockScope,
|
var mockScope,
|
||||||
@ -32,7 +38,12 @@ define(
|
|||||||
controller,
|
controller,
|
||||||
mockCompositionCapability,
|
mockCompositionCapability,
|
||||||
mockComposition,
|
mockComposition,
|
||||||
mockCompositionObjects;
|
mockCompositionObjects,
|
||||||
|
mockOpenMCT,
|
||||||
|
mockSelection,
|
||||||
|
mockDomainObjectCapability,
|
||||||
|
$element = [],
|
||||||
|
selectable = [];
|
||||||
|
|
||||||
function mockPromise(value) {
|
function mockPromise(value) {
|
||||||
return {
|
return {
|
||||||
@ -58,21 +69,18 @@ define(
|
|||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
getCapability: function () {
|
||||||
|
return mockDomainObjectCapability;
|
||||||
|
},
|
||||||
|
hasCapability: function (param) {
|
||||||
|
if (param === 'composition') {
|
||||||
|
return id !== 'b';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility function to find a watch for a given expression
|
|
||||||
function findWatch(expr) {
|
|
||||||
var watch;
|
|
||||||
mockScope.$watch.calls.forEach(function (call) {
|
|
||||||
if (call.args[0] === expr) {
|
|
||||||
watch = call.args[1];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return watch;
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
mockScope = jasmine.createSpyObj(
|
mockScope = jasmine.createSpyObj(
|
||||||
"$scope",
|
"$scope",
|
||||||
@ -88,7 +96,6 @@ define(
|
|||||||
mockComposition = ["a", "b", "c"];
|
mockComposition = ["a", "b", "c"];
|
||||||
mockCompositionObjects = mockComposition.map(mockDomainObject);
|
mockCompositionObjects = mockComposition.map(mockDomainObject);
|
||||||
|
|
||||||
|
|
||||||
testConfiguration = {
|
testConfiguration = {
|
||||||
panels: {
|
panels: {
|
||||||
a: {
|
a: {
|
||||||
@ -97,27 +104,70 @@ define(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
mockDomainObjectCapability = jasmine.createSpyObj('capability',
|
||||||
|
['inEditContext']
|
||||||
|
);
|
||||||
mockCompositionCapability = mockPromise(mockCompositionObjects);
|
mockCompositionCapability = mockPromise(mockCompositionObjects);
|
||||||
|
|
||||||
mockScope.domainObject = mockDomainObject("mockDomainObject");
|
mockScope.domainObject = mockDomainObject("mockDomainObject");
|
||||||
mockScope.model = testModel;
|
mockScope.model = testModel;
|
||||||
mockScope.configuration = testConfiguration;
|
mockScope.configuration = testConfiguration;
|
||||||
mockScope.selection = jasmine.createSpyObj(
|
|
||||||
'selection',
|
selectable[0] = {
|
||||||
['select', 'get', 'selected', 'deselect']
|
context: {
|
||||||
);
|
oldItem: mockScope.domainObject
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mockSelection = jasmine.createSpyObj("selection", [
|
||||||
|
'select',
|
||||||
|
'on',
|
||||||
|
'off',
|
||||||
|
'get'
|
||||||
|
]);
|
||||||
|
mockSelection.get.andReturn(selectable);
|
||||||
|
mockOpenMCT = {
|
||||||
|
selection: mockSelection
|
||||||
|
};
|
||||||
|
|
||||||
|
$element = $('<div></div>');
|
||||||
|
$(document).find('body').append($element);
|
||||||
|
spyOn($element[0], 'click');
|
||||||
|
|
||||||
spyOn(mockScope.domainObject, "useCapability").andCallThrough();
|
spyOn(mockScope.domainObject, "useCapability").andCallThrough();
|
||||||
|
|
||||||
controller = new LayoutController(mockScope);
|
controller = new LayoutController(mockScope, $element, mockOpenMCT);
|
||||||
spyOn(controller, "layoutPanels").andCallThrough();
|
spyOn(controller, "layoutPanels").andCallThrough();
|
||||||
|
|
||||||
findWatch("selection")(mockScope.selection);
|
|
||||||
|
|
||||||
jasmine.Clock.useMock();
|
jasmine.Clock.useMock();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
$element.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it("listens for selection change events", function () {
|
||||||
|
expect(mockOpenMCT.selection.on).toHaveBeenCalledWith(
|
||||||
|
'change',
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("cleans up on scope destroy", function () {
|
||||||
|
expect(mockScope.$on).toHaveBeenCalledWith(
|
||||||
|
'$destroy',
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
|
||||||
|
mockScope.$on.calls[0].args[1]();
|
||||||
|
|
||||||
|
expect(mockOpenMCT.selection.off).toHaveBeenCalledWith(
|
||||||
|
'change',
|
||||||
|
jasmine.any(Function)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Model changes will indicate that panel positions
|
// Model changes will indicate that panel positions
|
||||||
// may have changed, for instance.
|
// may have changed, for instance.
|
||||||
it("watches for changes to composition", function () {
|
it("watches for changes to composition", function () {
|
||||||
@ -320,67 +370,35 @@ define(
|
|||||||
.not.toEqual(oldStyle);
|
.not.toEqual(oldStyle);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows panels to be selected", function () {
|
it("allows objects to be selected", function () {
|
||||||
|
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||||
|
var childObj = mockCompositionObjects[0];
|
||||||
|
selectable[0].context.oldItem = childObj;
|
||||||
|
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
|
||||||
|
|
||||||
|
expect(controller.selected(childObj)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("prevents event bubbling while drag is in progress", function () {
|
||||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||||
var childObj = mockCompositionObjects[0];
|
var childObj = mockCompositionObjects[0];
|
||||||
|
|
||||||
controller.select(mockEvent, childObj.getId());
|
// Do a drag
|
||||||
|
controller.startDrag(childObj.getId(), [1, 1], [0, 0]);
|
||||||
|
controller.continueDrag([100, 100]);
|
||||||
|
controller.endDrag();
|
||||||
|
|
||||||
|
// Because mouse position could cause the parent object to be selected, this should be ignored.
|
||||||
|
controller.bypassSelection(mockEvent);
|
||||||
|
|
||||||
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
expect(mockEvent.stopPropagation).toHaveBeenCalled();
|
||||||
|
|
||||||
expect(controller.selected(childObj)).toBe(true);
|
// Shoud be able to select another object when dragging is done.
|
||||||
});
|
|
||||||
|
|
||||||
it("allows selection to be cleared", function () {
|
|
||||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
|
||||||
var childObj = mockCompositionObjects[0];
|
|
||||||
|
|
||||||
controller.select(null, childObj.getId());
|
|
||||||
controller.clearSelection();
|
|
||||||
|
|
||||||
expect(controller.selected(childObj)).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("prevents clearing selection while drag is in progress", function () {
|
|
||||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
|
||||||
var childObj = mockCompositionObjects[0];
|
|
||||||
var id = childObj.getId();
|
|
||||||
|
|
||||||
controller.select(mockEvent, id);
|
|
||||||
|
|
||||||
// Do a drag
|
|
||||||
controller.startDrag(id, [1, 1], [0, 0]);
|
|
||||||
controller.continueDrag([100, 100]);
|
|
||||||
controller.endDrag();
|
|
||||||
|
|
||||||
// Because mouse position could cause clearSelection to be called, this should be ignored.
|
|
||||||
controller.clearSelection();
|
|
||||||
|
|
||||||
expect(controller.selected(childObj)).toBe(true);
|
|
||||||
|
|
||||||
// Shoud be able to clear the selection after dragging is done.
|
|
||||||
jasmine.Clock.tick(0);
|
jasmine.Clock.tick(0);
|
||||||
controller.clearSelection();
|
mockEvent.stopPropagation.reset();
|
||||||
|
controller.bypassSelection(mockEvent);
|
||||||
|
|
||||||
expect(controller.selected(childObj)).toBe(false);
|
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
|
||||||
});
|
|
||||||
|
|
||||||
it("clears selection after moving/resizing", function () {
|
|
||||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
|
||||||
var childObj = mockCompositionObjects[0];
|
|
||||||
var id = childObj.getId();
|
|
||||||
|
|
||||||
controller.select(mockEvent, id);
|
|
||||||
|
|
||||||
// Do a drag
|
|
||||||
controller.startDrag(id, [1, 1], [0, 0]);
|
|
||||||
controller.continueDrag([100, 100]);
|
|
||||||
controller.endDrag();
|
|
||||||
|
|
||||||
jasmine.Clock.tick(0);
|
|
||||||
controller.clearSelection();
|
|
||||||
|
|
||||||
expect(controller.selected(childObj)).toBe(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows frames by default", function () {
|
it("shows frames by default", function () {
|
||||||
@ -398,43 +416,74 @@ define(
|
|||||||
it("hides frame when selected object has frame ", function () {
|
it("hides frame when selected object has frame ", function () {
|
||||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||||
var childObj = mockCompositionObjects[0];
|
var childObj = mockCompositionObjects[0];
|
||||||
controller.select(mockEvent, childObj.getId());
|
selectable[0].context.oldItem = childObj;
|
||||||
|
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
|
||||||
expect(mockScope.selection.select).toHaveBeenCalled();
|
var toolbarObj = controller.getToolbar(childObj.getId(), childObj);
|
||||||
|
|
||||||
var selectedObj = mockScope.selection.select.mostRecentCall.args[0];
|
|
||||||
|
|
||||||
expect(controller.hasFrame(childObj)).toBe(true);
|
expect(controller.hasFrame(childObj)).toBe(true);
|
||||||
expect(selectedObj.hideFrame).toBeDefined();
|
expect(toolbarObj.hideFrame).toBeDefined();
|
||||||
expect(selectedObj.hideFrame).toEqual(jasmine.any(Function));
|
expect(toolbarObj.hideFrame).toEqual(jasmine.any(Function));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows frame when selected object has no frame", function () {
|
it("shows frame when selected object has no frame", function () {
|
||||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||||
|
|
||||||
var childObj = mockCompositionObjects[1];
|
var childObj = mockCompositionObjects[1];
|
||||||
controller.select(mockEvent, childObj.getId());
|
selectable[0].context.oldItem = childObj;
|
||||||
|
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
|
||||||
expect(mockScope.selection.select).toHaveBeenCalled();
|
var toolbarObj = controller.getToolbar(childObj.getId(), childObj);
|
||||||
|
|
||||||
var selectedObj = mockScope.selection.select.mostRecentCall.args[0];
|
|
||||||
|
|
||||||
expect(controller.hasFrame(childObj)).toBe(false);
|
expect(controller.hasFrame(childObj)).toBe(false);
|
||||||
expect(selectedObj.showFrame).toBeDefined();
|
expect(toolbarObj.showFrame).toBeDefined();
|
||||||
expect(selectedObj.showFrame).toEqual(jasmine.any(Function));
|
expect(toolbarObj.showFrame).toEqual(jasmine.any(Function));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("deselects the object that is no longer in the composition", function () {
|
it("selects the parent object when selected object is removed", function () {
|
||||||
mockScope.$watchCollection.mostRecentCall.args[1]();
|
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||||
var childObj = mockCompositionObjects[0];
|
var childObj = mockCompositionObjects[0];
|
||||||
controller.select(mockEvent, childObj.getId());
|
selectable[0].context.oldItem = childObj;
|
||||||
|
mockOpenMCT.selection.on.mostRecentCall.args[1](selectable);
|
||||||
|
|
||||||
var composition = ["b", "c"];
|
var composition = ["b", "c"];
|
||||||
mockScope.$watchCollection.mostRecentCall.args[1](composition);
|
mockScope.$watchCollection.mostRecentCall.args[1](composition);
|
||||||
|
|
||||||
expect(controller.selected(childObj)).toBe(false);
|
expect($element[0].click).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("allows objects to be drilled-in only when editing", function () {
|
||||||
|
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||||
|
var childObj = mockCompositionObjects[0];
|
||||||
|
childObj.getCapability().inEditContext.andReturn(false);
|
||||||
|
controller.drill(mockEvent, childObj);
|
||||||
|
|
||||||
|
expect(controller.isDrilledIn(childObj)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows objects to be drilled-in only if it has sub objects", function () {
|
||||||
|
mockScope.$watchCollection.mostRecentCall.args[1]();
|
||||||
|
var childObj = mockCompositionObjects[1];
|
||||||
|
childObj.getCapability().inEditContext.andReturn(true);
|
||||||
|
controller.drill(mockEvent, childObj);
|
||||||
|
|
||||||
|
expect(controller.isDrilledIn(childObj)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("selects a newly-dropped object", function () {
|
||||||
|
mockScope.$on.mostRecentCall.args[1](
|
||||||
|
mockEvent,
|
||||||
|
'd',
|
||||||
|
{ x: 300, y: 100 }
|
||||||
|
);
|
||||||
|
|
||||||
|
var childObj = mockDomainObject("d");
|
||||||
|
var testElement = $("<div class='some-class'></div>");
|
||||||
|
$element.append(testElement);
|
||||||
|
spyOn(testElement[0], 'click');
|
||||||
|
|
||||||
|
controller.selectIfNew('some-class', childObj);
|
||||||
|
jasmine.Clock.tick(0);
|
||||||
|
|
||||||
|
expect(testElement[0].click).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -69,7 +69,7 @@ define([
|
|||||||
"delegates": [
|
"delegates": [
|
||||||
"telemetry"
|
"telemetry"
|
||||||
],
|
],
|
||||||
"inspector": tableInspector,
|
"inspector": "table-options-edit",
|
||||||
"contains": [
|
"contains": [
|
||||||
{
|
{
|
||||||
"has": "telemetry"
|
"has": "telemetry"
|
||||||
|
@ -19,7 +19,10 @@
|
|||||||
this source code distribution or the Licensing information page available
|
this source code distribution or the Licensing information page available
|
||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div ng-controller="TableOptionsController" class="l-controls-first flex-elem grows l-inspector-part">
|
|
||||||
|
<div ng-if="domainObject.getCapability('editor').inEditContext()"
|
||||||
|
ng-controller="TableOptionsController"
|
||||||
|
class="l-controls-first flex-elem grows l-inspector-part">
|
||||||
<em class="t-inspector-part-header" title="Display properties for this object">Table Options</em>
|
<em class="t-inspector-part-header" title="Display properties for this object">Table Options</em>
|
||||||
<mct-form
|
<mct-form
|
||||||
ng-model="configuration.table.columns"
|
ng-model="configuration.table.columns"
|
||||||
|
@ -32,6 +32,7 @@ define(
|
|||||||
*/
|
*/
|
||||||
function TelemetryCollection() {
|
function TelemetryCollection() {
|
||||||
EventEmitter.call(this, arguments);
|
EventEmitter.call(this, arguments);
|
||||||
|
this.dupeCheck = false;
|
||||||
this.telemetry = [];
|
this.telemetry = [];
|
||||||
this.highBuffer = [];
|
this.highBuffer = [];
|
||||||
this.sortField = undefined;
|
this.sortField = undefined;
|
||||||
@ -161,7 +162,7 @@ define(
|
|||||||
var startIx = _.sortedIndex(array, item, this.sortField);
|
var startIx = _.sortedIndex(array, item, this.sortField);
|
||||||
var endIx;
|
var endIx;
|
||||||
|
|
||||||
if (startIx !== array.length) {
|
if (this.dupeCheck && startIx !== array.length) {
|
||||||
endIx = _.sortedLastIndex(array, item, this.sortField);
|
endIx = _.sortedLastIndex(array, item, this.sortField);
|
||||||
|
|
||||||
// Create an array of potential dupes, based on having the
|
// Create an array of potential dupes, based on having the
|
||||||
@ -189,6 +190,7 @@ define(
|
|||||||
TelemetryCollection.prototype.add = function (items) {
|
TelemetryCollection.prototype.add = function (items) {
|
||||||
var added = items.filter(this.addOne);
|
var added = items.filter(this.addOne);
|
||||||
this.emit('added', added);
|
this.emit('added', added);
|
||||||
|
this.dupeCheck = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -436,9 +436,31 @@ define(
|
|||||||
* @param {Object} searchElement Object to find the insertion point for
|
* @param {Object} searchElement Object to find the insertion point for
|
||||||
*/
|
*/
|
||||||
MCTTableController.prototype.findInsertionPoint = function (searchArray, searchElement) {
|
MCTTableController.prototype.findInsertionPoint = function (searchArray, searchElement) {
|
||||||
//First, use a binary search to find the correct insertion point
|
var index;
|
||||||
var index = this.binarySearch(searchArray, searchElement, 0, searchArray.length - 1);
|
var testIndex;
|
||||||
var testIndex = index;
|
var first = searchArray[0];
|
||||||
|
var last = searchArray[searchArray.length - 1];
|
||||||
|
|
||||||
|
if (first) {
|
||||||
|
first = first[this.$scope.sortColumn].text;
|
||||||
|
}
|
||||||
|
if (last) {
|
||||||
|
last = last[this.$scope.sortColumn].text;
|
||||||
|
}
|
||||||
|
// Shortcut check for append/prepend
|
||||||
|
if (first && this.sortComparator(first, searchElement) >= 0) {
|
||||||
|
index = testIndex = 0;
|
||||||
|
} else if (last && this.sortComparator(last, searchElement) <= 0) {
|
||||||
|
index = testIndex = searchArray.length;
|
||||||
|
} else {
|
||||||
|
// use a binary search to find the correct insertion point
|
||||||
|
index = testIndex = this.binarySearch(
|
||||||
|
searchArray,
|
||||||
|
searchElement,
|
||||||
|
0,
|
||||||
|
searchArray.length - 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
//It's possible that the insertion point is a duplicate of the element to be inserted
|
//It's possible that the insertion point is a duplicate of the element to be inserted
|
||||||
var isDupe = function () {
|
var isDupe = function () {
|
||||||
|
@ -170,6 +170,9 @@ define(
|
|||||||
* @param rows
|
* @param rows
|
||||||
*/
|
*/
|
||||||
TelemetryTableController.prototype.addRowsToTable = function (rows) {
|
TelemetryTableController.prototype.addRowsToTable = function (rows) {
|
||||||
|
rows.forEach(function (row) {
|
||||||
|
this.$scope.rows.push(row);
|
||||||
|
}, this);
|
||||||
this.$scope.$broadcast('add:rows', rows);
|
this.$scope.$broadcast('add:rows', rows);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -436,5 +436,28 @@ define(
|
|||||||
expect(mockScope.$broadcast).toHaveBeenCalledWith("remove:rows", discardedRows);
|
expect(mockScope.$broadcast).toHaveBeenCalledWith("remove:rows", discardedRows);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when telemetry is added', function () {
|
||||||
|
var testRows;
|
||||||
|
var expectedRows;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
testRows = [{ a: 0 }, { a: 1 }, { a: 2 }];
|
||||||
|
mockScope.rows = [{ a: -1 }];
|
||||||
|
expectedRows = mockScope.rows.concat(testRows);
|
||||||
|
|
||||||
|
spyOn(controller.telemetry, "on").andCallThrough();
|
||||||
|
controller.registerChangeListeners();
|
||||||
|
|
||||||
|
controller.telemetry.on.calls.forEach(function (call) {
|
||||||
|
if (call.args[0] === 'added') {
|
||||||
|
call.args[1](testRows);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds it to rows in scope", function () {
|
||||||
|
expect(mockScope.rows).toEqual(expectedRows);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -32,15 +32,18 @@ define([
|
|||||||
"./src/controllers/TimelineTOIController",
|
"./src/controllers/TimelineTOIController",
|
||||||
"./src/controllers/ActivityModeValuesController",
|
"./src/controllers/ActivityModeValuesController",
|
||||||
"./src/capabilities/ActivityTimespanCapability",
|
"./src/capabilities/ActivityTimespanCapability",
|
||||||
|
"./src/capabilities/ActivityValueCapability",
|
||||||
"./src/capabilities/TimelineTimespanCapability",
|
"./src/capabilities/TimelineTimespanCapability",
|
||||||
"./src/capabilities/UtilizationCapability",
|
"./src/capabilities/UtilizationCapability",
|
||||||
"./src/capabilities/GraphCapability",
|
"./src/capabilities/GraphCapability",
|
||||||
"./src/capabilities/CostCapability",
|
"./src/capabilities/CostCapability",
|
||||||
"./src/directives/MCTSwimlaneDrop",
|
"./src/directives/MCTSwimlaneDrop",
|
||||||
"./src/directives/MCTSwimlaneDrag",
|
"./src/directives/MCTSwimlaneDrag",
|
||||||
|
"./src/directives/MCTResourceGraphDrop",
|
||||||
"./src/services/ObjectLoader",
|
"./src/services/ObjectLoader",
|
||||||
"./src/chart/MCTTimelineChart",
|
"./src/chart/MCTTimelineChart",
|
||||||
"text!./res/templates/values.html",
|
"text!./res/templates/values.html",
|
||||||
|
"text!./res/templates/activity-view.html",
|
||||||
"text!./res/templates/timeline.html",
|
"text!./res/templates/timeline.html",
|
||||||
"text!./res/templates/activity-gantt.html",
|
"text!./res/templates/activity-gantt.html",
|
||||||
"text!./res/templates/tabular-swimlane-cols-tree.html",
|
"text!./res/templates/tabular-swimlane-cols-tree.html",
|
||||||
@ -63,15 +66,18 @@ define([
|
|||||||
TimelineTOIController,
|
TimelineTOIController,
|
||||||
ActivityModeValuesController,
|
ActivityModeValuesController,
|
||||||
ActivityTimespanCapability,
|
ActivityTimespanCapability,
|
||||||
|
ActivityValueCapability,
|
||||||
TimelineTimespanCapability,
|
TimelineTimespanCapability,
|
||||||
UtilizationCapability,
|
UtilizationCapability,
|
||||||
GraphCapability,
|
GraphCapability,
|
||||||
CostCapability,
|
CostCapability,
|
||||||
MCTSwimlaneDrop,
|
MCTSwimlaneDrop,
|
||||||
MCTSwimlaneDrag,
|
MCTSwimlaneDrag,
|
||||||
|
MCTResourceGraphDrop,
|
||||||
ObjectLoader,
|
ObjectLoader,
|
||||||
MCTTimelineChart,
|
MCTTimelineChart,
|
||||||
valuesTemplate,
|
valuesTemplate,
|
||||||
|
activityTemplate,
|
||||||
timelineTemplate,
|
timelineTemplate,
|
||||||
activityGanttTemplate,
|
activityGanttTemplate,
|
||||||
tabularSwimlaneColsTreeTemplate,
|
tabularSwimlaneColsTreeTemplate,
|
||||||
@ -202,7 +208,9 @@ define([
|
|||||||
"composition": [],
|
"composition": [],
|
||||||
"start": {
|
"start": {
|
||||||
"timestamp": 0
|
"timestamp": 0
|
||||||
}
|
},
|
||||||
|
"activityStart": {},
|
||||||
|
"activityDuration": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -302,6 +310,17 @@ define([
|
|||||||
],
|
],
|
||||||
"editable": false
|
"editable": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "activityValues",
|
||||||
|
"name": "Activity Values",
|
||||||
|
"cssClass": "icon-activity",
|
||||||
|
"template": activityTemplate,
|
||||||
|
"type": "activity",
|
||||||
|
"uses": [
|
||||||
|
"activityValue"
|
||||||
|
],
|
||||||
|
"editable": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "timeline",
|
"key": "timeline",
|
||||||
"name": "Timeline",
|
"name": "Timeline",
|
||||||
@ -553,6 +572,10 @@ define([
|
|||||||
{
|
{
|
||||||
"key": "cost",
|
"key": "cost",
|
||||||
"implementation": CostCapability
|
"implementation": CostCapability
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "activityValue",
|
||||||
|
"implementation": ActivityValueCapability
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"directives": [
|
"directives": [
|
||||||
@ -577,6 +600,13 @@ define([
|
|||||||
"$interval",
|
"$interval",
|
||||||
"$log"
|
"$log"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "mctResourceGraphDrop",
|
||||||
|
"implementation": MCTResourceGraphDrop,
|
||||||
|
"depends": [
|
||||||
|
"dndService"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"services": [
|
"services": [
|
||||||
|
@ -38,6 +38,10 @@
|
|||||||
.l-timeline-pane {
|
.l-timeline-pane {
|
||||||
@include absPosDefault();
|
@include absPosDefault();
|
||||||
|
|
||||||
|
&.drop-over {
|
||||||
|
background-color: lighten($colorEditAreaBg, 5%);
|
||||||
|
}
|
||||||
|
|
||||||
.l-width-control {
|
.l-width-control {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
27
platform/features/timeline/res/templates/activity-view.html
Normal file
27
platform/features/timeline/res/templates/activity-view.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<!--
|
||||||
|
Open MCT, Copyright (c) 2009-2016, United States Government
|
||||||
|
as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
Administration. All rights reserved.
|
||||||
|
|
||||||
|
Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
License for the specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
|
||||||
|
Open MCT includes source code licensed under additional open source
|
||||||
|
licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
this source code distribution or the Licensing information page available
|
||||||
|
at runtime from the About dialog for additional information.
|
||||||
|
-->
|
||||||
|
<ul ng-controller="ActivityModeValuesController as controller" class="cols cols-2-ff properties">
|
||||||
|
<li ng-repeat="(key, value) in activityValue" class="l-row s-row">
|
||||||
|
<span class="col col-100px s-title"></span>
|
||||||
|
<span class="col s-value">{{value}}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
@ -20,6 +20,7 @@
|
|||||||
at runtime from the About dialog for additional information.
|
at runtime from the About dialog for additional information.
|
||||||
-->
|
-->
|
||||||
<div class="s-timeline l-timeline-holder split-layout vertical splitter-sm"
|
<div class="s-timeline l-timeline-holder split-layout vertical splitter-sm"
|
||||||
|
ng-click="$event.stopPropagation()"
|
||||||
ng-controller="TimelineController as timelineController">
|
ng-controller="TimelineController as timelineController">
|
||||||
|
|
||||||
<mct-split-pane anchor="left" class="abs" position="pane.x">
|
<mct-split-pane anchor="left" class="abs" position="pane.x">
|
||||||
@ -77,7 +78,8 @@
|
|||||||
<mct-splitter></mct-splitter>
|
<mct-splitter></mct-splitter>
|
||||||
|
|
||||||
<!-- BOTTOM PANE RESOURCE LEGEND -->
|
<!-- BOTTOM PANE RESOURCE LEGEND -->
|
||||||
<div class="split-pane-component abs l-timeline-pane t-pane-h l-pane-btm s-timeline-resource-legend l-timeline-resource-legend">
|
<div mct-resource-graph-drop
|
||||||
|
class="split-pane-component abs l-timeline-pane t-pane-h l-pane-btm s-timeline-resource-legend l-timeline-resource-legend">
|
||||||
<div class="l-title s-title">{{ngModel.title}}Resource Graph Legend</div>
|
<div class="l-title s-title">{{ngModel.title}}Resource Graph Legend</div>
|
||||||
<div class="l-legend-items legend">
|
<div class="l-legend-items legend">
|
||||||
<mct-include key="'timeline-legend-item'"
|
<mct-include key="'timeline-legend-item'"
|
||||||
|
@ -21,27 +21,48 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
define(
|
define(
|
||||||
[],
|
['EventEmitter'],
|
||||||
function () {
|
function (EventEmitter) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the time span of an activity object.
|
* Describes the time span of an activity object.
|
||||||
* @param model the activity's object model
|
* @param model the activity's object model
|
||||||
*/
|
*/
|
||||||
function ActivityTimespan(model, mutation) {
|
function ActivityTimespan(model, mutation, parentTimeline) {
|
||||||
|
var parentTimelineModel = parentTimeline.getModel(),
|
||||||
|
parentMutation = parentTimeline.getCapability('mutation');
|
||||||
|
|
||||||
|
function getTimelineActivityStart (domainObjectModel) {
|
||||||
|
if (domainObjectModel.activityStart && domainObjectModel.activityStart[model.id]) {
|
||||||
|
return domainObjectModel.activityStart[model.id];
|
||||||
|
} else {
|
||||||
|
return model.start.timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimelineActivityDuration (domainObjectModel) {
|
||||||
|
if (domainObjectModel.activityDuration && domainObjectModel.activityDuration[model.id]) {
|
||||||
|
return domainObjectModel.activityDuration[model.id];
|
||||||
|
} else {
|
||||||
|
return model.duration.timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get the start time for this timeline
|
// Get the start time for this timeline
|
||||||
function getStart() {
|
function getStart() {
|
||||||
return model.start.timestamp;
|
return getTimelineActivityStart(parentTimelineModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the end time for this timeline
|
// Get the end time for this timeline
|
||||||
function getEnd() {
|
function getEnd() {
|
||||||
return model.start.timestamp + model.duration.timestamp;
|
var start = getTimelineActivityStart(parentTimelineModel),
|
||||||
|
duration = getTimelineActivityDuration(parentTimelineModel);
|
||||||
|
|
||||||
|
return start + duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the duration of this timeline
|
// Get the duration of this timeline
|
||||||
function getDuration() {
|
function getDuration() {
|
||||||
return model.duration.timestamp;
|
return getTimelineActivityDuration(parentTimelineModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the epoch used by this timeline
|
// Get the epoch used by this timeline
|
||||||
@ -52,26 +73,41 @@ define(
|
|||||||
// Set the start time associated with this object
|
// Set the start time associated with this object
|
||||||
function setStart(value) {
|
function setStart(value) {
|
||||||
var end = getEnd();
|
var end = getEnd();
|
||||||
mutation.mutate(function (m) {
|
|
||||||
m.start.timestamp = Math.max(value, 0);
|
parentMutation.mutate(function (m) {
|
||||||
// Update duration to keep end time
|
m.activityStart[model.id] = Math.max(value,0);
|
||||||
m.duration.timestamp = Math.max(end - value, 0);
|
m.activityDuration[model.id] = Math.max(end - value, 0);
|
||||||
}, model.modified);
|
});
|
||||||
|
|
||||||
|
// mutation.mutate(function (m) {
|
||||||
|
// m.start.timestamp = Math.max(value, 0);
|
||||||
|
// // Update duration to keep end time
|
||||||
|
// m.duration.timestamp = Math.max(end - value, 0);
|
||||||
|
// }, model.modified);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the duration associated with this object
|
// Set the duration associated with this object
|
||||||
function setDuration(value) {
|
function setDuration(value) {
|
||||||
mutation.mutate(function (m) {
|
parentMutation.mutate(function (m) {
|
||||||
m.duration.timestamp = Math.max(value, 0);
|
m.activityDuration[model.id] = Math.max(value, 0);
|
||||||
}, model.modified);
|
});
|
||||||
|
|
||||||
|
// mutation.mutate(function (m) {
|
||||||
|
// m.duration.timestamp = Math.max(value, 0);
|
||||||
|
// }, model.modified);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the end time associated with this object
|
// Set the end time associated with this object
|
||||||
function setEnd(value) {
|
function setEnd(value) {
|
||||||
var start = getStart();
|
var start = getStart();
|
||||||
mutation.mutate(function (m) {
|
|
||||||
m.duration.timestamp = Math.max(value - start, 0);
|
parentMutation.mutate(function (m) {
|
||||||
}, model.modified);
|
m.activityDuration[model.id] = Math.max(value - start, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// mutation.mutate(function (m) {
|
||||||
|
// m.duration.timestamp = Math.max(value - start, 0);
|
||||||
|
// }, model.modified);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -32,11 +32,26 @@ define(
|
|||||||
* @param {DomainObject} domainObject the Activity
|
* @param {DomainObject} domainObject the Activity
|
||||||
*/
|
*/
|
||||||
function ActivityTimespanCapability($q, domainObject) {
|
function ActivityTimespanCapability($q, domainObject) {
|
||||||
|
|
||||||
|
function findTimeline (object) {
|
||||||
|
var parent = domainObject.getCapability('context').parentObject;
|
||||||
|
|
||||||
|
while (parent.getModel().type !== 'timeline') {
|
||||||
|
parent = parent.getCapability('context').parentObject;
|
||||||
|
findTimeline(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parent = findTimeline(domainObject);
|
||||||
|
|
||||||
// Promise time span
|
// Promise time span
|
||||||
function promiseTimeSpan() {
|
function promiseTimeSpan() {
|
||||||
return $q.when(new ActivityTimespan(
|
return $q.when(new ActivityTimespan(
|
||||||
domainObject.getModel(),
|
domainObject.getModel(),
|
||||||
domainObject.getCapability('mutation')
|
domainObject.getCapability('mutation'),
|
||||||
|
parent
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
[],
|
||||||
|
function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes costs associated with a subsystem mode.
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function ActivityValueCapability(domainObject) {
|
||||||
|
var model = domainObject.getModel();
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Get a list of resource types which have associated
|
||||||
|
* costs for this object. Returned values are machine-readable
|
||||||
|
* keys, and should be paired with external metadata for
|
||||||
|
* presentation (see category of extension `resources`).
|
||||||
|
* @returns {string[]} resource types
|
||||||
|
*/
|
||||||
|
resources: function () {
|
||||||
|
return Object.keys(model.resources || {}).sort();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get the cost associated with a resource of an identified
|
||||||
|
* type (typically, one of the types reported from a
|
||||||
|
* `resources` call.)
|
||||||
|
* @param {string} key the resource type
|
||||||
|
* @returns {number} the associated cost
|
||||||
|
*/
|
||||||
|
cost: function (key) {
|
||||||
|
return (model.resources || {})[key] || 0;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Get an object containing key-value pairs describing
|
||||||
|
* resource utilization as described by this object.
|
||||||
|
* Keys are resource types; values are levels of associated
|
||||||
|
* resource utilization.
|
||||||
|
* @returns {object} resource utilizations
|
||||||
|
*/
|
||||||
|
invoke: function () {
|
||||||
|
return {key: 'deep is the best'};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only applies to subsystem modes.
|
||||||
|
ActivityValueCapability.appliesTo = function (model) {
|
||||||
|
return (model || {}).type === 'activity';
|
||||||
|
};
|
||||||
|
|
||||||
|
return ActivityValueCapability;
|
||||||
|
}
|
||||||
|
);
|
@ -0,0 +1,81 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['./SwimlaneDragConstants'],
|
||||||
|
function (SwimlaneDragConstants) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the `mct-resource-graph-drop` directive. When a drop occurs
|
||||||
|
* on an element with this attribute, the swimlane targeted by the drop
|
||||||
|
* will receive the dropped domain object (at which point it can handle
|
||||||
|
* the drop, typically by toggling the swimlane graph.)
|
||||||
|
* @param {DndService} dndService drag-and-drop service
|
||||||
|
*/
|
||||||
|
function MCTResourceGraphDrop(dndService) {
|
||||||
|
|
||||||
|
function link(scope, element, attrs) {
|
||||||
|
// Handle dragover
|
||||||
|
element.on('dragover', function (e) {
|
||||||
|
var swimlane = dndService.getData(
|
||||||
|
SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof swimlane !== "undefined" && !swimlane.graph()) {
|
||||||
|
element.addClass('drop-over');
|
||||||
|
scope.$apply();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Handle drops
|
||||||
|
element.on('drop', function (e) {
|
||||||
|
var swimlane = dndService.getData(
|
||||||
|
SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE
|
||||||
|
);
|
||||||
|
|
||||||
|
element.removeClass('drop-over');
|
||||||
|
|
||||||
|
// Only toggle if the graph isn't already set
|
||||||
|
if (typeof swimlane !== "undefined" && !swimlane.graph()) {
|
||||||
|
swimlane.toggleGraph();
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Clear highlights when drag leaves this swimlane
|
||||||
|
element.on('dragleave', function (e) {
|
||||||
|
element.removeClass('drop-over');
|
||||||
|
scope.$apply();
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Applies to attributes
|
||||||
|
restrict: "A",
|
||||||
|
// Link using above function
|
||||||
|
link: link
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return MCTResourceGraphDrop;
|
||||||
|
}
|
||||||
|
);
|
@ -0,0 +1,159 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2009-2016, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define(
|
||||||
|
['../../src/directives/MCTResourceGraphDrop', '../../src/directives/SwimlaneDragConstants'],
|
||||||
|
function (MCTResourceGraphDrop, SwimlaneDragConstants) {
|
||||||
|
|
||||||
|
describe("The mct-resource-graph-drop directive", function () {
|
||||||
|
var mockDndService,
|
||||||
|
mockScope,
|
||||||
|
mockElement,
|
||||||
|
testAttrs,
|
||||||
|
mockSwimlane,
|
||||||
|
testEvent,
|
||||||
|
handlers,
|
||||||
|
directive;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
handlers = {};
|
||||||
|
|
||||||
|
mockDndService = jasmine.createSpyObj(
|
||||||
|
'dndService',
|
||||||
|
['setData', 'getData', 'removeData']
|
||||||
|
);
|
||||||
|
mockScope = jasmine.createSpyObj('$scope', ['$eval', '$apply']);
|
||||||
|
mockElement = jasmine.createSpyObj('element', ['on', 'addClass', 'removeClass']);
|
||||||
|
testAttrs = { mctSwimlaneDrop: "mockSwimlane" };
|
||||||
|
mockSwimlane = jasmine.createSpyObj(
|
||||||
|
"swimlane",
|
||||||
|
['graph', 'toggleGraph']
|
||||||
|
);
|
||||||
|
|
||||||
|
testEvent = {
|
||||||
|
dataTransfer: { getData: jasmine.createSpy() },
|
||||||
|
preventDefault: jasmine.createSpy(),
|
||||||
|
stopPropagation: jasmine.createSpy()
|
||||||
|
};
|
||||||
|
|
||||||
|
testEvent.dataTransfer.getData.andReturn('abc');
|
||||||
|
mockDndService.getData.andCallFake(function (key) {
|
||||||
|
return key === SwimlaneDragConstants.TIMELINE_SWIMLANE_DRAG_TYPE ?
|
||||||
|
mockSwimlane : undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
mockSwimlane.graph.andReturn(false);
|
||||||
|
|
||||||
|
directive = new MCTResourceGraphDrop(mockDndService);
|
||||||
|
directive.link(mockScope, mockElement, testAttrs);
|
||||||
|
|
||||||
|
mockElement.on.calls.forEach(function (call) {
|
||||||
|
handlers[call.args[0]] = call.args[1];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is available as an attribute", function () {
|
||||||
|
expect(directive.restrict).toEqual("A");
|
||||||
|
});
|
||||||
|
|
||||||
|
[false, true].forEach(function (graphing) {
|
||||||
|
describe("when swimlane graph is " + (graphing ? "" : "not ") + "enabled", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
mockSwimlane.graph.andReturn(graphing);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe("on dragover", function () {
|
||||||
|
var prefix = !graphing ? "does" : "does not";
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
handlers.dragover(testEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(prefix + " add a drop-over class", function () {
|
||||||
|
var expectAddClass = expect(mockElement.addClass);
|
||||||
|
(!graphing ? expectAddClass : expectAddClass.not)
|
||||||
|
.toHaveBeenCalledWith('drop-over');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(prefix + " call $apply on scope", function () {
|
||||||
|
var expectApply = expect(mockScope.$apply);
|
||||||
|
(!graphing ? expectApply : expectApply.not)
|
||||||
|
.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(prefix + " prevent default", function () {
|
||||||
|
var expectPreventDefault = expect(testEvent.preventDefault);
|
||||||
|
(!graphing ? expectPreventDefault : expectPreventDefault.not)
|
||||||
|
.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("on drop", function () {
|
||||||
|
var prefix = !graphing ? "does" : "does not";
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
handlers.drop(testEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes any drop-over class", function () {
|
||||||
|
expect(mockElement.removeClass)
|
||||||
|
.toHaveBeenCalledWith('drop-over');
|
||||||
|
});
|
||||||
|
|
||||||
|
it(prefix + " toggle the swimlane's resource graph", function () {
|
||||||
|
var expectToggle = expect(mockSwimlane.toggleGraph);
|
||||||
|
(!graphing ? expectToggle : expectToggle.not)
|
||||||
|
.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(prefix + " prevent default", function () {
|
||||||
|
var expectPreventDefault = expect(testEvent.preventDefault);
|
||||||
|
(!graphing ? expectPreventDefault : expectPreventDefault.not)
|
||||||
|
.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("on dragleave", function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
handlers.dragleave(testEvent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes any drop-over class", function () {
|
||||||
|
expect(mockElement.removeClass)
|
||||||
|
.toHaveBeenCalledWith('drop-over');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls $apply on scope", function () {
|
||||||
|
expect(mockScope.$apply).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls preventDefault on events", function () {
|
||||||
|
expect(testEvent.preventDefault).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
@ -24,21 +24,22 @@
|
|||||||
|
|
||||||
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
<span class="l-click-area" ng-click="toggle.toggle()"></span>
|
||||||
<span class="color-swatch"
|
<span class="color-swatch"
|
||||||
|
ng-class="{'no-selection':ngModel[field] === 'transparent'}"
|
||||||
ng-style="{
|
ng-style="{
|
||||||
background: ngModel[field]
|
'background-color': ngModel[field]
|
||||||
}">
|
}">
|
||||||
</span>
|
</span>
|
||||||
<span class="title-label" ng-if="structure.text">
|
<span class="title-label" ng-if="structure.text">
|
||||||
{{structure.text}}
|
{{structure.text}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="menu l-color-palette"
|
<div class="menu l-palette l-color-palette"
|
||||||
ng-controller="ColorController as colors"
|
ng-controller="ColorController as colors"
|
||||||
ng-show="toggle.isActive()">
|
ng-show="toggle.isActive()">
|
||||||
<div
|
<div
|
||||||
class="l-palette-row l-option-row"
|
class="l-palette-row l-option-row"
|
||||||
ng-if="!structure.mandatory">
|
ng-if="!structure.mandatory">
|
||||||
<div class="l-palette-item s-palette-item {{ngModel[field] === 'transparent' ? 'icon-check' : '' }}"
|
<div class="l-palette-item s-palette-item no-selection {{ngModel[field] === 'transparent' ? 'selected' : '' }}"
|
||||||
ng-click="ngModel[field] = 'transparent'">
|
ng-click="ngModel[field] = 'transparent'">
|
||||||
</div>
|
</div>
|
||||||
<span class="l-palette-item-label">None</span>
|
<span class="l-palette-item-label">None</span>
|
||||||
@ -46,7 +47,7 @@
|
|||||||
<div
|
<div
|
||||||
class="l-palette-row"
|
class="l-palette-row"
|
||||||
ng-repeat="group in colors.groups()">
|
ng-repeat="group in colors.groups()">
|
||||||
<div class="l-palette-item s-palette-item {{ngModel[field] === color ? 'icon-check' : '' }}"
|
<div class="l-palette-item s-palette-item {{ngModel[field] === color ? 'selected' : '' }}"
|
||||||
ng-repeat="color in group"
|
ng-repeat="color in group"
|
||||||
ng-style="{ background: color }"
|
ng-style="{ background: color }"
|
||||||
ng-click="ngModel[field] = color">
|
ng-click="ngModel[field] = color">
|
||||||
|
17
src/MCT.js
17
src/MCT.js
@ -28,7 +28,8 @@ define([
|
|||||||
'./selection/Selection',
|
'./selection/Selection',
|
||||||
'./api/objects/object-utils',
|
'./api/objects/object-utils',
|
||||||
'./plugins/plugins',
|
'./plugins/plugins',
|
||||||
'./ui/ViewRegistry'
|
'./ui/ViewRegistry',
|
||||||
|
'./ui/InspectorViewRegistry'
|
||||||
], function (
|
], function (
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
legacyRegistry,
|
legacyRegistry,
|
||||||
@ -37,7 +38,8 @@ define([
|
|||||||
Selection,
|
Selection,
|
||||||
objectUtils,
|
objectUtils,
|
||||||
plugins,
|
plugins,
|
||||||
ViewRegistry
|
ViewRegistry,
|
||||||
|
InspectorViewRegistry
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Open MCT is an extensible web application for building mission
|
* Open MCT is an extensible web application for building mission
|
||||||
@ -112,15 +114,13 @@ define([
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry for views which should appear in the Inspector area.
|
* Registry for views which should appear in the Inspector area.
|
||||||
* These views will be chosen based on selection state, so
|
* These views will be chosen based on the selection state.
|
||||||
* providers should be prepared to test arbitrary objects for
|
|
||||||
* viewability.
|
|
||||||
*
|
*
|
||||||
* @type {module:openmct.ViewRegistry}
|
* @type {module:openmct.InspectorViewRegistry}
|
||||||
* @memberof module:openmct.MCT#
|
* @memberof module:openmct.MCT#
|
||||||
* @name inspectors
|
* @name inspectorViews
|
||||||
*/
|
*/
|
||||||
this.inspectors = new ViewRegistry();
|
this.inspectorViews = new InspectorViewRegistry();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registry for views which should appear in Edit Properties
|
* Registry for views which should appear in Edit Properties
|
||||||
@ -196,7 +196,6 @@ define([
|
|||||||
|
|
||||||
this.Dialog = api.Dialog;
|
this.Dialog = api.Dialog;
|
||||||
|
|
||||||
this.on('navigation', this.selection.clear.bind(this.selection));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MCT.prototype = Object.create(EventEmitter.prototype);
|
MCT.prototype = Object.create(EventEmitter.prototype);
|
||||||
|
@ -48,6 +48,7 @@ define([
|
|||||||
this.unlisteners.forEach(function (unlisten) {
|
this.unlisteners.forEach(function (unlisten) {
|
||||||
unlisten();
|
unlisten();
|
||||||
});
|
});
|
||||||
|
this.unlisteners = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,11 +50,18 @@ define([
|
|||||||
this.rootProvider = new RootObjectProvider(this.rootRegistry);
|
this.rootProvider = new RootObjectProvider(this.rootRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set fallback provider, this is an internal API for legacy reasons.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
ObjectAPI.prototype.supersecretSetFallbackProvider = function (p) {
|
ObjectAPI.prototype.supersecretSetFallbackProvider = function (p) {
|
||||||
this.fallbackProvider = p;
|
this.fallbackProvider = p;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Retrieve the provider for a given key.
|
/**
|
||||||
|
* Retrieve the provider for a given identifier.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
ObjectAPI.prototype.getProvider = function (identifier) {
|
ObjectAPI.prototype.getProvider = function (identifier) {
|
||||||
if (identifier.key === 'ROOT') {
|
if (identifier.key === 'ROOT') {
|
||||||
return this.rootProvider;
|
return this.rootProvider;
|
||||||
@ -135,27 +142,28 @@ define([
|
|||||||
* @returns {Promise} a promise which will resolve when the domain object
|
* @returns {Promise} a promise which will resolve when the domain object
|
||||||
* has been saved, or be rejected if it cannot be saved
|
* has been saved, or be rejected if it cannot be saved
|
||||||
*/
|
*/
|
||||||
|
ObjectAPI.prototype.get = function (identifier) {
|
||||||
[
|
identifier = utils.parseKeyString(identifier);
|
||||||
'save',
|
var provider = this.getProvider(identifier);
|
||||||
'delete',
|
|
||||||
'get'
|
|
||||||
].forEach(function (method) {
|
|
||||||
ObjectAPI.prototype[method] = function () {
|
|
||||||
var identifier = arguments[0],
|
|
||||||
provider = this.getProvider(identifier);
|
|
||||||
|
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
throw new Error('No Provider Matched');
|
throw new Error('No Provider Matched');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!provider[method]) {
|
if (!provider.get) {
|
||||||
throw new Error('Provider does not support [' + method + '].');
|
throw new Error('Provider does not support get!');
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider[method].apply(provider, arguments);
|
return provider.get(identifier);
|
||||||
|
};
|
||||||
|
|
||||||
|
ObjectAPI.prototype.delete = function () {
|
||||||
|
throw new Error('Delete not implemented');
|
||||||
|
};
|
||||||
|
|
||||||
|
ObjectAPI.prototype.save = function () {
|
||||||
|
throw new Error('Save not implemented');
|
||||||
};
|
};
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a root-level object.
|
* Add a root-level object.
|
||||||
|
@ -41,8 +41,6 @@ define([
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.valueMetadata = valueMetadata;
|
this.valueMetadata = valueMetadata;
|
||||||
this.parseCache = new WeakMap();
|
|
||||||
this.formatCache = new WeakMap();
|
|
||||||
try {
|
try {
|
||||||
this.formatter = formatService
|
this.formatter = formatService
|
||||||
.getFormat(valueMetadata.format, valueMetadata);
|
.getFormat(valueMetadata.format, valueMetadata);
|
||||||
@ -72,26 +70,14 @@ define([
|
|||||||
|
|
||||||
TelemetryValueFormatter.prototype.parse = function (datum) {
|
TelemetryValueFormatter.prototype.parse = function (datum) {
|
||||||
if (_.isObject(datum)) {
|
if (_.isObject(datum)) {
|
||||||
if (!this.parseCache.has(datum)) {
|
return this.formatter.parse(datum[this.valueMetadata.source]);
|
||||||
this.parseCache.set(
|
|
||||||
datum,
|
|
||||||
this.formatter.parse(datum[this.valueMetadata.source])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this.parseCache.get(datum);
|
|
||||||
}
|
}
|
||||||
return this.formatter.parse(datum);
|
return this.formatter.parse(datum);
|
||||||
};
|
};
|
||||||
|
|
||||||
TelemetryValueFormatter.prototype.format = function (datum) {
|
TelemetryValueFormatter.prototype.format = function (datum) {
|
||||||
if (_.isObject(datum)) {
|
if (_.isObject(datum)) {
|
||||||
if (!this.formatCache.has(datum)) {
|
return this.formatter.format(datum[this.valueMetadata.source]);
|
||||||
this.formatCache.set(
|
|
||||||
datum,
|
|
||||||
this.formatter.format(datum[this.valueMetadata.source])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this.formatCache.get(datum);
|
|
||||||
}
|
}
|
||||||
return this.formatter.format(datum);
|
return this.formatter.format(datum);
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<a class="close icon-x-in-circle"></a>
|
<a class="close icon-x-in-circle"></a>
|
||||||
<div class="abs inner-holder contents">
|
<div class="abs inner-holder contents">
|
||||||
<div class="abs top-bar">
|
<div class="abs top-bar">
|
||||||
<div class="title"></div>
|
<div class="dialog-title"></div>
|
||||||
<div class="hint"></div>
|
<div class="hint"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class='abs editor'>
|
<div class='abs editor'>
|
||||||
|
97
src/plugins/URLIndicatorPlugin/URLIndicator.js
Normal file
97
src/plugins/URLIndicatorPlugin/URLIndicator.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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 () {
|
||||||
|
|
||||||
|
// Set of connection states; changing among these states will be
|
||||||
|
// reflected in the indicator's appearance.
|
||||||
|
// CONNECTED: Everything nominal, expect to be able to read/write.
|
||||||
|
// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
|
||||||
|
// PENDING: Still trying to connect, and haven't failed yet.
|
||||||
|
var CONNECTED = {
|
||||||
|
glyphClass: "ok"
|
||||||
|
},
|
||||||
|
PENDING = {
|
||||||
|
glyphClass: 'caution'
|
||||||
|
},
|
||||||
|
DISCONNECTED = {
|
||||||
|
glyphClass: "err"
|
||||||
|
};
|
||||||
|
function URLIndicator($http, $interval) {
|
||||||
|
var self = this;
|
||||||
|
this.cssClass = this.options.cssClass ? this.options.cssClass : "icon-database";
|
||||||
|
this.URLpath = this.options.url;
|
||||||
|
this.label = this.options.label ? this.options.label : this.options.url;
|
||||||
|
this.interval = this.options.interval || 10000;
|
||||||
|
this.state = PENDING;
|
||||||
|
|
||||||
|
function handleError(e) {
|
||||||
|
self.state = DISCONNECTED;
|
||||||
|
}
|
||||||
|
function handleResponse() {
|
||||||
|
self.state = CONNECTED;
|
||||||
|
}
|
||||||
|
function updateIndicator() {
|
||||||
|
$http.get(self.URLpath).then(handleResponse, handleError);
|
||||||
|
}
|
||||||
|
updateIndicator();
|
||||||
|
$interval(updateIndicator, self.interval, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
URLIndicator.prototype.getCssClass = function () {
|
||||||
|
return this.cssClass;
|
||||||
|
};
|
||||||
|
URLIndicator.prototype.getGlyphClass = function () {
|
||||||
|
return this.state.glyphClass;
|
||||||
|
};
|
||||||
|
URLIndicator.prototype.getText = function () {
|
||||||
|
switch (this.state) {
|
||||||
|
case CONNECTED: {
|
||||||
|
return this.label + " is connected";
|
||||||
|
}
|
||||||
|
case PENDING: {
|
||||||
|
return "Checking status of " + this.label + " please stand by...";
|
||||||
|
}
|
||||||
|
case DISCONNECTED: {
|
||||||
|
return this.label + " is offline";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
URLIndicator.prototype.getDescription = function () {
|
||||||
|
switch (this.state) {
|
||||||
|
case CONNECTED: {
|
||||||
|
return this.label + " is online, checking status every " +
|
||||||
|
this.interval + " milliseconds.";
|
||||||
|
}
|
||||||
|
case PENDING: {
|
||||||
|
return "Checking status of " + this.label + " please stand by...";
|
||||||
|
}
|
||||||
|
case DISCONNECTED: {
|
||||||
|
return this.label + " is offline, checking status every " +
|
||||||
|
this.interval + " milliseconds";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return URLIndicator;
|
||||||
|
});
|
20
src/plugins/URLIndicatorPlugin/URLIndicatorPlugin.js
Normal file
20
src/plugins/URLIndicatorPlugin/URLIndicatorPlugin.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
define(
|
||||||
|
[
|
||||||
|
'./URLIndicator'
|
||||||
|
],
|
||||||
|
function URLIndicatorPlugin(URLIndicator) {
|
||||||
|
return function (opts) {
|
||||||
|
// Wrap the plugin in a function so we can apply the arguments.
|
||||||
|
function URLIndicatorWrapper() {
|
||||||
|
this.options = opts;
|
||||||
|
URLIndicator.apply(this, arguments);
|
||||||
|
}
|
||||||
|
URLIndicatorWrapper.prototype = Object.create(URLIndicator.prototype);
|
||||||
|
return function install(openmct) {
|
||||||
|
openmct.legacyExtension('indicators', {
|
||||||
|
"implementation": URLIndicatorWrapper,
|
||||||
|
"depends": ["$http", "$interval"]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
158
src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js
Normal file
158
src/plugins/URLIndicatorPlugin/URLIndicatorSpec.js
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* 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(
|
||||||
|
["./URLIndicator"],
|
||||||
|
function (URLIndicator) {
|
||||||
|
|
||||||
|
describe("The URLIndicator", function () {
|
||||||
|
var mockHttp,
|
||||||
|
mockInterval,
|
||||||
|
mockPromise,
|
||||||
|
opts,
|
||||||
|
Indicator,
|
||||||
|
indicatorWrapper;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mockHttp = jasmine.createSpyObj("$http", ["get"]);
|
||||||
|
mockInterval = jasmine.createSpy("$interval");
|
||||||
|
mockPromise = jasmine.createSpyObj("promise", ["then"]);
|
||||||
|
opts = {
|
||||||
|
url: "http://localhost:8080",
|
||||||
|
interval: 1337 //some number
|
||||||
|
};
|
||||||
|
mockHttp.get.andReturn(mockPromise);
|
||||||
|
Indicator = function () {
|
||||||
|
this.options = opts;
|
||||||
|
URLIndicator.call(this, mockHttp, mockInterval);
|
||||||
|
};
|
||||||
|
Indicator.prototype = Object.create(URLIndicator.prototype);
|
||||||
|
indicatorWrapper = new Indicator();
|
||||||
|
});
|
||||||
|
it("polls for changes", function () {
|
||||||
|
expect(mockInterval).toHaveBeenCalledWith(
|
||||||
|
jasmine.any(Function),
|
||||||
|
opts.interval,
|
||||||
|
0,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has a database cssClass as default", function () {
|
||||||
|
expect(indicatorWrapper.getCssClass()).toEqual("icon-database");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("consults the url with the path supplied", function () {
|
||||||
|
expect(mockHttp.get).toHaveBeenCalledWith(opts.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("changes when the database connection is nominal", function () {
|
||||||
|
var initialText = indicatorWrapper.getText(),
|
||||||
|
initialDescrption = indicatorWrapper.getDescription(),
|
||||||
|
initialGlyphClass = indicatorWrapper.getGlyphClass();
|
||||||
|
|
||||||
|
// Nominal just means getting back an object, without
|
||||||
|
// an error field.
|
||||||
|
mockPromise.then.mostRecentCall.args[0]({ data: {} });
|
||||||
|
|
||||||
|
// Verify that these values changed;
|
||||||
|
// don't test for specific text.
|
||||||
|
expect(indicatorWrapper.getText()).not.toEqual(initialText);
|
||||||
|
expect(indicatorWrapper.getGlyphClass()).not.toEqual(initialGlyphClass);
|
||||||
|
expect(indicatorWrapper.getDescription()).not.toEqual(initialDescrption);
|
||||||
|
|
||||||
|
// Do check for specific class
|
||||||
|
expect(indicatorWrapper.getGlyphClass()).toEqual("ok");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("changes when the server cannot be reached", function () {
|
||||||
|
var initialText = indicatorWrapper.getText(),
|
||||||
|
initialDescrption = indicatorWrapper.getDescription(),
|
||||||
|
initialGlyphClass = indicatorWrapper.getGlyphClass();
|
||||||
|
|
||||||
|
// Nominal just means getting back an object, without
|
||||||
|
// an error field.
|
||||||
|
mockPromise.then.mostRecentCall.args[1]({ data: {} });
|
||||||
|
|
||||||
|
// Verify that these values changed;
|
||||||
|
// don't test for specific text.
|
||||||
|
expect(indicatorWrapper.getText()).not.toEqual(initialText);
|
||||||
|
expect(indicatorWrapper.getGlyphClass()).not.toEqual(initialGlyphClass);
|
||||||
|
expect(indicatorWrapper.getDescription()).not.toEqual(initialDescrption);
|
||||||
|
|
||||||
|
// Do check for specific class
|
||||||
|
expect(indicatorWrapper.getGlyphClass()).toEqual("err");
|
||||||
|
});
|
||||||
|
it("has a customized cssClass if supplied in initialization", function () {
|
||||||
|
opts = {
|
||||||
|
url: "http://localhost:8080",
|
||||||
|
cssClass: "cssClass-checked",
|
||||||
|
interval: 10000
|
||||||
|
};
|
||||||
|
indicatorWrapper = new Indicator();
|
||||||
|
expect(indicatorWrapper.getCssClass()).toEqual("cssClass-checked");
|
||||||
|
});
|
||||||
|
it("has a customized interval if supplied in initialization", function () {
|
||||||
|
opts = {
|
||||||
|
url: "http://localhost:8080",
|
||||||
|
interval: 1814
|
||||||
|
};
|
||||||
|
indicatorWrapper = new Indicator();
|
||||||
|
expect(mockInterval).toHaveBeenCalledWith(
|
||||||
|
jasmine.any(Function),
|
||||||
|
1814,
|
||||||
|
0,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("has a custom label if supplied in initialization", function () {
|
||||||
|
opts = {
|
||||||
|
url: "http://localhost:8080",
|
||||||
|
label: "Localhost"
|
||||||
|
};
|
||||||
|
indicatorWrapper = new Indicator();
|
||||||
|
expect(indicatorWrapper.getText()).toEqual("Checking status of Localhost please stand by...");
|
||||||
|
});
|
||||||
|
it("has a default label if not supplied in initialization", function () {
|
||||||
|
opts = {
|
||||||
|
url: "http://localhost:8080"
|
||||||
|
};
|
||||||
|
indicatorWrapper = new Indicator();
|
||||||
|
expect(indicatorWrapper.getText()).toEqual(
|
||||||
|
"Checking status of http://localhost:8080 please stand by..."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it("has a default interval if not supplied in initialization", function () {
|
||||||
|
opts = {
|
||||||
|
url: "http://localhost:8080"
|
||||||
|
};
|
||||||
|
indicatorWrapper = new Indicator();
|
||||||
|
expect(mockInterval).toHaveBeenCalledWith(
|
||||||
|
jasmine.any(Function),
|
||||||
|
10000,
|
||||||
|
0,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
47
src/plugins/activityModes/plugin.js
Normal file
47
src/plugins/activityModes/plugin.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
define(['./src/actions/activityModesImportAction'], function (ActivityModes) {
|
||||||
|
function plugin() {
|
||||||
|
|
||||||
|
return function install(openmct) {
|
||||||
|
|
||||||
|
openmct.legacyRegistry.register("src/plugins/activityModes", {
|
||||||
|
"name": "Activity Import",
|
||||||
|
"description": "Defines a root named My Items",
|
||||||
|
"extensions": {
|
||||||
|
"roots": [
|
||||||
|
{
|
||||||
|
"id": "activity-import"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"id": "activity-import",
|
||||||
|
"model": {
|
||||||
|
"name": "Activity Import",
|
||||||
|
"type": "folder",
|
||||||
|
"composition": [],
|
||||||
|
"location": "ROOT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
openmct.legacyRegistry.enable("src/plugins/activityModes");
|
||||||
|
|
||||||
|
openmct.legacyExtension('actions', {
|
||||||
|
key: "import-csv",
|
||||||
|
category: ["contextual"],
|
||||||
|
implementation: ActivityModes,
|
||||||
|
cssClass: "major icon-import",
|
||||||
|
name: "Import Activity Definitions from CSV",
|
||||||
|
description: "Import activities from a CSV file",
|
||||||
|
depends: [
|
||||||
|
"dialogService",
|
||||||
|
"openmct"
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugin;
|
||||||
|
});
|
@ -0,0 +1,127 @@
|
|||||||
|
define(['d3-dsv'], function (d3Dsv) {
|
||||||
|
|
||||||
|
function ActivityModesImportAction(dialogService, openmct, context) {
|
||||||
|
this.dialogService = dialogService;
|
||||||
|
this.openmct = openmct;
|
||||||
|
this.context = context;
|
||||||
|
this.parent = this.context.domainObject;
|
||||||
|
this.instantiate = this.openmct.$injector.get("instantiate");
|
||||||
|
|
||||||
|
this.instantiateActivities = this.instantiateActivities.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivityModesImportAction.prototype.perform = function () {
|
||||||
|
this.dialogService.getUserInput(this.getFormModel(), function () {})
|
||||||
|
.then(function (form) {
|
||||||
|
if(form.selectFile.name.slice(-3) !== 'csv'){
|
||||||
|
this.displayError();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.csvParse(form.selectFile.body).then(this.instantiateActivities);
|
||||||
|
}.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
ActivityModesImportAction.prototype.csvParse = function (csvString) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var parsedObject = d3Dsv.csvParse(csvString);
|
||||||
|
|
||||||
|
return parsedObject ? resolve(parsedObject) : reject('Could not parse provided file');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ActivityModesImportAction.prototype.instantiateActivities = function (csvObjects) {
|
||||||
|
var parent = this.context.domainObject,
|
||||||
|
parentId = parent.getId(),
|
||||||
|
parentComposition = parent.getCapability("composition"),
|
||||||
|
activitiesObjects = [],
|
||||||
|
activityModesObjects = [];
|
||||||
|
|
||||||
|
csvObjects.forEach(function (activity) {
|
||||||
|
var newActivity = {},
|
||||||
|
newActivityMode = {};
|
||||||
|
|
||||||
|
newActivity.name = activity.name;
|
||||||
|
newActivity.start = {timestamp: 0, epoch: "SET"};
|
||||||
|
newActivity.duration = {timestamp: Number(activity.duration), epoch: "SET"};
|
||||||
|
newActivity.type = "activity";
|
||||||
|
newActivity.relationships = {modes: []};
|
||||||
|
|
||||||
|
activitiesObjects.push(newActivity);
|
||||||
|
|
||||||
|
newActivityMode.name = activity.name + ' Resources';
|
||||||
|
newActivityMode.resources = {comms: Number(activity.comms), power: Number(activity.power)};
|
||||||
|
newActivityMode.type = 'mode';
|
||||||
|
|
||||||
|
activityModesObjects.push(newActivityMode);
|
||||||
|
});
|
||||||
|
|
||||||
|
var folderComposition = this.createActivityModesFolder().getCapability('composition');
|
||||||
|
|
||||||
|
activityModesObjects.forEach(function (activityMode, index) {
|
||||||
|
var newActivityModeInstance = this.instantiate(activityMode, 'activity-mode-' + index);
|
||||||
|
|
||||||
|
newActivityModeInstance.getCapability('location').setPrimaryLocation('activity-modes-folder');
|
||||||
|
folderComposition.add(newActivityModeInstance);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
activitiesObjects.forEach(function (activity, index) {
|
||||||
|
activity.relationships.modes.push('activity-mode-' + index);
|
||||||
|
activity.id = 'activity-' + index;
|
||||||
|
|
||||||
|
var newActivityInstance = this.instantiate(activity, 'activity-' + index);
|
||||||
|
|
||||||
|
newActivityInstance.getCapability('location').setPrimaryLocation(parentId);
|
||||||
|
parentComposition.add(newActivityInstance);
|
||||||
|
}.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
ActivityModesImportAction.prototype.createActivityModesFolder = function () {
|
||||||
|
var folderInstance = this.instantiate({name: 'Activity-Modes', type: 'folder', composition: []}, 'activity-modes-folder');
|
||||||
|
folderInstance.getCapability('location').setPrimaryLocation(this.parent.getId());
|
||||||
|
this.parent.getCapability('composition').add(folderInstance);
|
||||||
|
|
||||||
|
return folderInstance;
|
||||||
|
};
|
||||||
|
|
||||||
|
ActivityModesImportAction.prototype.displayError = function () {
|
||||||
|
var dialog,
|
||||||
|
perform = this.perform.bind(this),
|
||||||
|
model = {
|
||||||
|
title: "Invalid File",
|
||||||
|
actionText: "The selected file was not a valid CSV file",
|
||||||
|
severity: "error",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: "Ok",
|
||||||
|
callback: function () {
|
||||||
|
dialog.dismiss();
|
||||||
|
perform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
dialog = this.dialogService.showBlockingMessage(model);
|
||||||
|
};
|
||||||
|
|
||||||
|
ActivityModesImportAction.prototype.getFormModel = function () {
|
||||||
|
return {
|
||||||
|
name: 'Import activities from CSV',
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
name: 'Import A File',
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
name: 'Select File',
|
||||||
|
key: 'selectFile',
|
||||||
|
control: 'file-input',
|
||||||
|
required: true,
|
||||||
|
text: 'Select File'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return ActivityModesImportAction;
|
||||||
|
});
|
34
src/plugins/autoflow/AutoflowTabularConstants.js
Normal file
34
src/plugins/autoflow/AutoflowTabularConstants.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Open MCT, Copyright (c) 2014-2017, United States Government
|
||||||
|
* as represented by the Administrator of the National Aeronautics and Space
|
||||||
|
* Administration. All rights reserved.
|
||||||
|
*
|
||||||
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
* Open MCT includes source code licensed under additional open source
|
||||||
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||||
|
* this source code distribution or the Licensing information page available
|
||||||
|
* at runtime from the About dialog for additional information.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
define([], function () {
|
||||||
|
/**
|
||||||
|
* Constant values used by the Autoflow Tabular View.
|
||||||
|
*/
|
||||||
|
return {
|
||||||
|
ROW_HEIGHT: 16,
|
||||||
|
SLIDER_HEIGHT: 10,
|
||||||
|
INITIAL_COLUMN_WIDTH: 225,
|
||||||
|
MAX_COLUMN_WIDTH: 525,
|
||||||
|
COLUMN_WIDTH_STEP: 25
|
||||||
|
};
|
||||||
|
});
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user