Compare commits

...

13 Commits

Author SHA1 Message Date
Charles Hacskaylo
91263b0f6d [Merge] WIP cleanups to markup and SCSS
Fixes #1894
- IN PROGRESS, for Andrew to pick up
and modify to remove need for 'status-block-holder'
in SimpleIndicator.js
2018-01-30 11:45:56 -08:00
Charles Hacskaylo
c458666173 [Merge] Merge in indicators-api branch
Fixes #1894
2018-01-30 10:07:20 -08:00
Charles Hacskaylo
e21906d1fd [Frontend] Markup and SCSS mods continued
Fixes #1894
- Moved *indicator styles into their own SCSS file;
- SCSS consolidation and cleanups;
- Markup simplification continued;
- Fixed configs to target correct vars in JS files;
2018-01-26 18:05:38 -08:00
Charles Hacskaylo
f2193495ec [Frontend] Markup simplification
Fixes #1894
- Markup simplified in element templates;
2018-01-26 15:23:10 -08:00
Charles Hacskaylo
dd05f29210 [Frontend] CSS class normalization, markup simplification
Fixes #1894
- Added s-status-bar class to separate
look from layout;
- Rename status-holder to indicator-holder;
- Moved getGlyphClass to include element;
- Simplify markup, remove .status-block-holder;
2018-01-26 15:21:48 -08:00
Henry
b35be0c559 Address linting issues 2018-01-19 11:39:16 -08:00
Henry
9e6a2de585 Adding Indicator API spec 2018-01-18 15:17:05 -08:00
Henry
c7392db6f5 Updated tests for URL Indicator 2018-01-16 13:07:33 -08:00
Henry
6ee4740f5f [Indicators] Addressed code review items from https://github.com/nasa/openmct/pull/1837 2018-01-14 09:56:21 -08:00
Henry
74401ed2de [Indicators] Changes to some legacy indicators for compatibility with new Indicators API 2018-01-12 18:13:34 -08:00
Henry
c4fd3bc997 [Indicators] Converted URL Indicator to new Indicators API 2018-01-12 18:13:34 -08:00
Henry
55bac65cad [Indicators] Converted Follow Indicator to new Indicators API 2018-01-12 18:13:34 -08:00
Henry
ed327f5211 [API] Added Indicators API 2018-01-12 18:08:04 -08:00
34 changed files with 1167 additions and 537 deletions

108
API.md
View File

@@ -567,7 +567,7 @@ openmct.time.timeSystem(utcTimeSystem, bounds);
Setting the active time system will trigger a [`'timeSystem'`](#time-events)
event. If you supplied bounds, a [`'bounds'`](#time-events) event will be triggered afterwards with your newly supplied bounds.
### Time Bounds
#### Time Bounds
The TimeAPI provides a getter/setter for querying and setting time bounds. Time
bounds are simply an object with a `start` and an end `end` attribute.
@@ -591,7 +591,7 @@ openmct.time.bounds({start: now - ONE_HOUR, now);
To respond to bounds change events, listen for the [`'bounds'`](#time-events)
event.
## Clocks
### Clocks
The Time API can be set to follow a clock source which will cause the bounds
to be updated automatically whenever the clock source "ticks". A clock is simply
@@ -610,7 +610,7 @@ be defined to tick on some remote timing source.
The values provided by clocks are simple `number`s, which are interpreted in the
context of the active [Time System](#defining-and-registering-time-systems).
### Defining and registering clocks
#### Defining and registering clocks
A clock is an object that defines certain required metadata and functions:
@@ -724,7 +724,7 @@ __Note:__ Setting the clock offsets will trigger an immediate bounds change, as
new bounds will be calculated based on the `currentValue()` of the active clock.
Clock offsets are only relevant when a clock source is active.
## Time Events
### Time Events
The Time API is a standard event emitter; you can register callbacks for events using the `on` method and remove callbacks for events with the `off` method.
@@ -766,7 +766,7 @@ The events emitted by the Time API are:
* `clockOffsets`: The new [clock offsets](#clock-offsets).
## The Time Conductor
### The Time Conductor
The Time Conductor provides a user interface for managing time bounds in Open
MCT. It allows a user to select from configured time systems and clocks, and to set bounds and clock offsets.
@@ -856,6 +856,90 @@ openmct.install(openmct.plugins.Conductor({
}));
```
## Indicators
Indicators are small widgets that reside at the bottom of the screen and are visible from
every screen in Open MCT. They can be used to convey system state using an icon and text.
Indicators tend to be collapsed to an icon by default (though this behavior can be customized
by defining a [custom indicator](#custom-indicators)), and hovering over them will reveal
text providing more information.
### The URL Status Indicator
A common use case for indicators is to convey the state of some external system such as a
persistence backend or HTTP server. So long as this system is accessible via http request,
Open MCT provides a general purpose indicator to show whether the server is available and
returing a 2xx status code. The URL Status Indicator is made available as a default plugin. See
[Included Plugins](#included-plugins) below for details on how to install and configure the
URL Status Indicator.
### Creating a New Indicator
A new indicator can be created with a simple API call, eg.
``` javascript
var myIndicator = openmct.indicators.create();
myIndicator.text("Hello World!");
```
This will create a new indicator and add it to the bottom of the screen in Open MCT.
By default, the indicator will appear as an information icon. Hovering over the icon will
reveal the text set via the call to `.text()`. The Indicator object returned by the API
call exposes a number of functions for customizing the content and appearance of the indicator:
* `.text([text])`: Gets or sets the text shown when the user hovers over the indicator.
Accepts an __optional__ `string` argument that, if provided, will be used to set the text.
Hovering over the indicator will expand it to its full size, revealing this text alongside
the icon. Returns the currently set text as a `string`.
* `.description([description])`: Gets or sets the indicator's description. Accepts an
__optional__ `string` argument that, if provided, will be used to set the text. The description
allows for more detail to be provided in a tooltip when the user hovers over the indicator.
Returns the currently set text as a `string`.
* `.iconClass([className])`: Gets or sets the CSS class used to define the icon. Accepts an __optional__
`string` parameter to be used to set the class applied to the indicator. Any of
[the built-in glyphs](https://nasa.github.io/openmct/style-guide/#/browse/styleguide:home/glyphs?view=styleguide.glyphs)
may be used here, or a custom CSS class can be provided. Returns the currently defined CSS
class as a `string`.
* `.statusClass([className])`: Gets or sets the CSS class used to determine status. Accepts an __optional __
`string` parameter to be used to set a status class applied to the indicator. May be used to apply
different colors to indicate status.
### Custom Indicators
A completely custom indicator can be added by registering a factory function that produces
a DOM element for your custom indicator. eg.
``` javascript
openmct.indicators.create(function () {
var domNode = document.createElement('div');
domNode.innerText = new Date().toString();
setInterval(function () {
domNode.innerText = new Date().toString();
}, 1000);
return domNode;
});
```
### Priority
When creating a new indicator or defining a custom indicator, a `number` can optionally be
provided that will determine the order that the indicator appears on the screen. An indicator
with a higher `priority` number will be shown to the left of indicators with lower
priority numbers. The lowest possible `priority` (and the default value if no `priority` is specified)
is `Number.NEGATIVE_INFINTY`, and the highest possible priority is `Number.POSITIVE_INFINITY`.
eg.
``` javascript
var myIndicator = openmct.indicators.create(1);
myIndicator.text("I'm on the left!");
openmct.indicators.create(0, function () {
var domNode = document.createElement('div');
domNode.innerText = "I'm on the right!";
return domNode;
});
```
## Included Plugins
Open MCT is packaged along with a few general-purpose plugins:
@@ -879,18 +963,18 @@ openmct.install(openmct.plugins.CouchDB('http://localhost:9200'))
* `openmct.plugins.Espresso` and `openmct.plugins.Snow` are two different
themes (dark and light) available for Open MCT. Note that at least one
of these themes must be installed for Open MCT to appear correctly.
* `openmct.plugins.URLIndicatorPlugin` adds an indicator which shows the
* `openmct.plugins.URLIndicator` 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)
- `iconClass`: 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.install(openmct.plugins.URLIndicator({
url: 'http://localhost:8080',
iconClass: 'check',
interval: 10000,
label: 'Localhost'
})
);
```

View File

@@ -38,7 +38,8 @@ define([
"provides": "identityService",
"type": "provider",
"depends": [
"dialogService"
"dialogService",
"$q"
]
}
]

View File

@@ -55,21 +55,37 @@ define(
* @implements {IdentityService}
* @memberof platform/identity
*/
function ExampleIdentityProvider(dialogService) {
// Handle rejected dialog messages by treating the
// current user as undefined.
function echo(v) { return v; }
function giveUndefined() { return undefined; }
function ExampleIdentityProvider(dialogService, $q) {
this.dialogService = dialogService;
this.$q = $q;
this.userPromise =
dialogService.getUserInput(DIALOG_STRUCTURE, DEFAULT_IDENTITY)
.then(echo, giveUndefined);
this.returnUser = this.returnUser.bind(this);
this.returnUndefined = this.returnUndefined.bind(this);
}
ExampleIdentityProvider.prototype.getUser = function () {
return this.userPromise;
if (this.user) {
return this.$q.when(this.user);
} else {
return this.dialogService.getUserInput(DIALOG_STRUCTURE, DEFAULT_IDENTITY)
.then(this.returnUser, this.returnUndefined);
}
};
/**
* @private
*/
ExampleIdentityProvider.prototype.returnUser = function (user) {
return this.user = user;
}
/**
* @private
*/
ExampleIdentityProvider.prototype.returnUndefined = function () {
return undefined;
}
return ExampleIdentityProvider;
}
);

View File

@@ -1,9 +1,9 @@
<span class="status block" ng-controller="DialogLaunchController">
<span class="l-indicator s-indicator icon-box-with-arrow" ng-controller="DialogLaunchController">
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<span class="status-indicator icon-box-with-arrow"></span><span class="label">
<span class="label">
<a ng-click="launchProgress(true)">Known</a> |
<a ng-click="launchProgress(false)">Unknown</a> |
<a ng-click="launchError()">Error</a> |
<a ng-click="launchInfo()">Info</a>
</span><span class="count"></span>
</span>
</span>

View File

@@ -1,9 +1,9 @@
<span class="status block" ng-controller="NotificationLaunchController">
<span class="l-indicator s-indicator icon-bell" ng-controller="NotificationLaunchController">
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<span class="status-indicator icon-bell"></span><span class="label">
<span class="label">
<a ng-click="newInfo()">Success</a> |
<a ng-click="newError()">Error</a> |
<a ng-click="newAlert()">Alert</a> |
<a ng-click="newProgress()">Progress</a>
</span><span class="count"></span>
</span>
</span>

View File

@@ -129,6 +129,44 @@
</div>
</div>
<div class="l-section">
<h2>Status Bar Indicators</h2>
<div class="cols cols1-1">
<div class="col">
<p>Indicators content goes here.</p>
</div>
<mct-example><div class="status-holder s-status-bar">
<span class="status-block-holder">
<span class="l-indicator s-indicator">
<span class="status-indicator icon-bell"></span>
<span class="label">Collapsible Indicator</span>
</span>
</span>
<span class="status-block-holder no-collapse caution">
<span class="l-indicator s-indicator">
<span class="status-indicator icon-bell"></span>
<span class="label">No collapse</span>
</span>
</span>
</div>
<div class="status-holder s-status-bar">
<span class="status-block-holder">
<span class="l-indicator s-indicator">
<span class="status-indicator icon-bell"></span>
<span class="label">Collapsible Indicator</span>
</span>
</span>
<span class="status-block-holder">
<span class="status block s-status-info icon-info">
<span class="label">No collapse</span>
</span>
</span>
</div>
</mct-example>
</div>
</div>
<div class="l-section">
<h2>Synchronization</h2>
<div class="cols cols1-1">

View File

@@ -54,7 +54,7 @@ define(
return "icon-object-unknown";
},
getText: function () {
return latest;
return "" + latest;
},
getDescription: function () {
return "";

View File

@@ -34,7 +34,6 @@ define([
"./src/controllers/ContextMenuController",
"./src/controllers/ClickAwayController",
"./src/controllers/ViewSwitcherController",
"./src/controllers/BottomBarController",
"./src/controllers/GetterSetterController",
"./src/controllers/SelectorController",
"./src/controllers/ObjectInspectorController",
@@ -49,11 +48,12 @@ define([
"./src/directives/MCTSplitPane",
"./src/directives/MCTSplitter",
"./src/directives/MCTTree",
"./src/directives/MCTIndicators",
"./src/filters/ReverseFilter",
"text!./res/templates/bottombar.html",
"text!./res/templates/controls/action-button.html",
"text!./res/templates/controls/input-filter.html",
"text!./res/templates/indicator.html",
"text!./res/templates/angular-indicator.html",
"text!./res/templates/message-banner.html",
"text!./res/templates/progress-bar.html",
"text!./res/templates/controls/time-controller.html",
@@ -84,7 +84,6 @@ define([
ContextMenuController,
ClickAwayController,
ViewSwitcherController,
BottomBarController,
GetterSetterController,
SelectorController,
ObjectInspectorController,
@@ -99,6 +98,7 @@ define([
MCTSplitPane,
MCTSplitter,
MCTTree,
MCTIndicators,
ReverseFilter,
bottombarTemplate,
actionButtonTemplate,
@@ -275,13 +275,6 @@ define([
"$timeout"
]
},
{
"key": "BottomBarController",
"implementation": BottomBarController,
"depends": [
"indicators[]"
]
},
{
"key": "GetterSetterController",
"implementation": GetterSetterController,
@@ -395,6 +388,11 @@ define([
"key": "mctTree",
"implementation": MCTTree,
"depends": ['gestureService']
},
{
"key": "mctIndicators",
"implementation": MCTIndicators,
"depends": ['openmct']
}
],
"constants": [

View File

@@ -42,6 +42,7 @@
@import "controls/lists";
@import "controls/menus";
@import "controls/messages";
@import "controls/indicators";
@import "mobile/controls/menus";
/********************************* FORMS */

View File

@@ -20,13 +20,15 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*************************************************** MIXINS */
@mixin formulateStatusColors($c) {
@mixin elementStatusColors($c) {
// Sets bg and icon colors for elements
background: rgba($c, 0.4) !important;
&:before { color: $c !important; }
}
@mixin indicatorStatusColors($c) {
&:before, .count { color: $c; }
}
/*************************************************** GENERAL */
.s-limit-yellow,
@@ -54,14 +56,13 @@
}
}
/*************************************************** LIMITS */
.s-limit-yellow, .s-limit-yellow-icon {
@include formulateStatusColors($colorWarningLo);
@include elementStatusColors($colorWarningLo);
}
.s-limit-red, .s-limit-red-icon {
@include formulateStatusColors($colorWarningHi);
@include elementStatusColors($colorWarningHi);
}
.s-limit-upr:before { content: $glyph-icon-arrow-double-up; }
@@ -70,11 +71,11 @@
.s-limit-red-icon:before { content: $glyph-icon-alert-triangle; }
/*************************************************** STATUS */
.s-status-warning-hi, .s-status-warning-hi-icon { @include formulateStatusColors($colorWarningHi); }
.s-status-warning-lo, .s-status-warning-lo-icon { @include formulateStatusColors($colorWarningLo); }
.s-status-diagnostic, .s-status-diagnostic-icon { @include formulateStatusColors($colorDiagnostic); }
.s-status-info, .s-status-info-icon { @include formulateStatusColors($colorInfo); }
.s-status-ok, .s-status-ok-icon { @include formulateStatusColors($colorOk); }
.s-status-warning-hi, .s-status-warning-hi-icon { @include elementStatusColors($colorWarningHi); }
.s-status-warning-lo, .s-status-warning-lo-icon { @include elementStatusColors($colorWarningLo); }
.s-status-diagnostic, .s-status-diagnostic-icon { @include elementStatusColors($colorDiagnostic); }
.s-status-info, .s-status-info-icon { @include elementStatusColors($colorInfo); }
.s-status-ok, .s-status-ok-icon { @include elementStatusColors($colorOk); }
.s-status-warning-hi-icon:before { content: $glyph-icon-alert-triangle; }
.s-status-warning-lo-icon:before { content: $glyph-icon-alert-rect; }
@@ -82,4 +83,25 @@
.s-status-info-icon:before { content: $glyph-icon-info; }
.s-status-ok-icon:before { content: $glyph-icon-check; }
/*************************************************** INDICATORS */
.s-indicator-status-info {
@include indicatorStatusColors($colorInfo);
}
.s-indicator-status-ok {
@include indicatorStatusColors($colorOk);
}
.s-indicator-status-caution, .s-indicator-status-warning, .s-indicator-status-alert {
@include indicatorStatusColors($colorStatusAlert);
}
.s-indicator-status-error, .s-indicator-status-err {
@include indicatorStatusColors($colorStatusError);
}
.s-indicator-status-available {
@include indicatorStatusColors($colorStatusAvailable);
}
.s-indicator-status-subdued {
opacity: 0.7;
}

View File

@@ -0,0 +1,99 @@
/*****************************************************************************
* 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.
*****************************************************************************/
/* Indicators are generally only displayed in the ue-bottom-bar element of the main interface */
.l-indicator {
$transDelayHover: 1.5s;
$transDelayWhileExpanded: 2.5s;
$transSpeed: .25s;
display: inline-block;
margin-right: $interiorMarginSm;
&:before {
// Icon
display: inline-block;
opacity: 0.85;
}
.label,
.count {
display: inline-block;
vertical-align: top;
}
&.float-right {
float: right;
}
&.no-icon {
&:before {
display: none;
}
}
&:not(.no-collapse) {
.label {
// Max-width silliness is necessary for width transition
@include trans-prop-nice((max-width, margin-right), $transSpeed, $transDelayWhileExpanded);
overflow: hidden;
margin-right: 0;
max-width: 0px;
white-space: nowrap;
}
&:hover {
&:before {
opacity: 1;
}
.label {
@include trans-prop-nice((max-width, margin-right), 0s);
margin-right: $interiorMargin;
max-width: 600px;
width: auto;
}
.count {
@include trans-prop-nice(max-width, 0s);
opacity: 0;
}
}
}
.count {
@include trans-prop-nice(opacity, $transSpeed, $transDelayWhileExpanded);
font-weight: bold;
opacity: 1;
}
.s-button {
background: $colorStatusBtnBg;
padding: 0 $interiorMargin;
height: auto;
line-height: inherit;
}
}
.s-indicator {
color: $colorStatusDefault;
}

View File

@@ -19,7 +19,7 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/******************************************************************* STATUS BLOCK ELEMS */
@mixin statusBannerColors($bg, $fg: $colorStatusFg) {
$bgPb: 30%;
$bgPbD: 10%;
@@ -36,110 +36,6 @@
}
}
// Status coloring
.ok, .info {
.status-indicator {
color: $colorInfo;
}
}
.alert, .caution, .warning {
.status-indicator, .count {
color: $colorStatusAlert;
}
}
.error, .err {
.status-indicator, .count {
color: $colorStatusError;
}
}
.available {
.status-indicator, .count {
color: $colorStatusAvailable;
}
}
.subdued {
.status-indicator {
color: pullForward($colorStatusBarBg, 40%);
}
}
.status-block-holder {
// Applied to mct-include element
// Contains status.block elements
$transDelay: 1.5s;
$transSpeed: .25s;
display: inline-block;
&.clickable { cursor: pointer; }
&:not(.clickable) { cursor: default; }
&.no-icon .status.block {
.status-indicator {
display: none;
}
}
&.float-right {
float: right;
}
&:not(.no-collapse) .status.block {
.label {
// Max-width silliness is necessary for width transition
@include trans-prop-nice(max-width, $transSpeed, $transDelay);
overflow: hidden;
max-width: 0px;
}
&:hover {
.label {
@include trans-prop-nice(max-width, $transSpeed, 0s);
max-width: 600px;
width: auto;
}
.count {
@include trans-prop-nice(max-width, $transSpeed, 0s);
opacity: 0;
}
}
}
}
.status.block {
$transDelay: 1.5s;
$transSpeed: .25s;
color: $colorStatusDefault;
display: inline-block;
margin-right: $interiorMargin;
.status-indicator,
.label,
.count {
display: inline-block;
vertical-align: top;
}
.status-indicator {
background: none !important;
margin-right: $interiorMarginSm;
&[class*='s-status']:before {
font-size: 1em;
}
}
.count {
@include trans-prop-nice(opacity, $transSpeed, $transDelay);
font-weight: bold;
opacity: 1;
}
.s-button {
background: $colorStatusBtnBg;
padding: 0 $interiorMargin;
height: auto;
line-height: inherit;
}
}
/******************************************************************* MESSAGE BANNERS */
.message {
&.block {

View File

@@ -81,16 +81,14 @@
@include absPosDefault(0);// New status bar design
top: auto;
height: $ueFooterH;
line-height: $ueFooterH - ($interiorMargin * 2);
background: $colorStatusBarBg;
color: lighten($colorBodyBg, 30%);
font-size: .7rem;
.status-holder {
mct-indicators { display: block; }
.indicator-holder {
box-sizing: border-box;
@include absPosDefault($interiorMargin);
@include ellipsize();
right: 120px;
text-transform: uppercase;
z-index: 1;
}
.app-logo {
@@ -105,6 +103,14 @@
}
}
.s-status-bar {
background: $colorStatusBarBg;
color: lighten($colorBodyBg, 30%);
font-size: .7rem;
line-height: $ueFooterH - ($interiorMargin * 2);
text-transform: uppercase;
}
.edit-mode {
// Old edit mode
.split-layout {

View File

@@ -20,14 +20,12 @@
at runtime from the About dialog for additional information.
-->
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<div class='status block'
<div class='l-indicator s-indicator s-indicator-status-{{ngModel.getGlyphClass()}}'
title="{{ngModel.getDescription()}}"
ng-click='ngModel.configure()'
ng-show="ngModel.getText().length > 0">
<span class="status-indicator {{ngModel.getCssClass()}}"></span><span class="label"
ng-class='ngModel.getTextClass()'>
ng-show="ngModel.getText().length > 0"
ng-class="ngModel.getCssClass()">
<span class="label" ng-class='ngModel.getTextClass()'>
{{ngModel.getText()}}
<a class="s-button icon-gear" ng-if="ngModel.configure"></a>
</span><span class="count">
</span>
</div>

View File

@@ -19,14 +19,9 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class='abs bottom-bar ue-bottom-bar mobile-disable-select' ng-controller="BottomBarController as bar">
<div id='status' class='status-holder'>
<mct-include ng-repeat="indicator in bar.getIndicators()"
ng-model="indicator.ngModel"
key="indicator.template"
class="status-block-holder"
ng-class='indicator.ngModel.getGlyphClass()'>
</mct-include>
<div class='abs bottom-bar ue-bottom-bar s-status-bar mobile-disable-select'>
<div id='status' class='indicator-holder'>
<mct-indicators></mct-indicators>
</div>
<mct-include key="'message-banner'"></mct-include>
<mct-include key="'about-logo'"></mct-include>

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -23,37 +23,20 @@
define(
[],
function () {
/**
* Controller for the bottombar template. Exposes
* available indicators (of extension category "indicators")
* @memberof platform/commonUI/general
* @constructor
*/
function BottomBarController(indicators) {
// Utility function used to make indicators presentable
// for display.
function present(Indicator) {
return {
template: Indicator.template || "indicator",
ngModel: typeof Indicator === 'function' ?
new Indicator() : Indicator
};
}
this.indicators = indicators.map(present);
function MCTIndicators(openmct) {
return {
restrict: "E",
link: function link(scope, element) {
openmct.indicators.allIndicatorElements().then(function (elements) {
elements.forEach(function (indicatorElement) {
element.append(indicatorElement);
});
});
}
};
}
/**
* Get all indicators to display.
* @returns {Indicator[]} all indicators
* to display in the bottom bar.
* @memberof platform/commonUI/general.BottomBarController#
*/
BottomBarController.prototype.getIndicators = function () {
return this.indicators;
};
return MCTIndicators;
return BottomBarController;
}
);

View File

@@ -1,76 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
["../../src/controllers/BottomBarController"],
function (BottomBarController) {
describe("The bottom bar controller", function () {
var testIndicators,
testIndicatorA,
testIndicatorB,
testIndicatorC,
mockIndicator,
controller;
beforeEach(function () {
mockIndicator = jasmine.createSpyObj(
"indicator",
["getGlyph", "getCssClass", "getText"]
);
testIndicatorA = {};
testIndicatorB = function () {
return mockIndicator;
};
testIndicatorC = { template: "someTemplate" };
testIndicators = [
testIndicatorA,
testIndicatorB,
testIndicatorC
];
controller = new BottomBarController(testIndicators);
});
it("exposes one indicator description per extension", function () {
expect(controller.getIndicators().length)
.toEqual(testIndicators.length);
});
it("uses template field provided, or its own default", function () {
// "indicator" is the default;
// only testIndicatorC overrides this.
var indicators = controller.getIndicators();
expect(indicators[0].template).toEqual("indicator");
expect(indicators[1].template).toEqual("indicator");
expect(indicators[2].template).toEqual("someTemplate");
});
it("instantiates indicators given as constructors", function () {
// testIndicatorB constructs to mockIndicator
expect(controller.getIndicators()[1].ngModel).toBe(mockIndicator);
});
});
}
);

View File

@@ -1,9 +1,7 @@
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<a ng-click="showNotificationsList()" ng-show="notifications.length > 0" class="status block"
ng-class="highest.severity"
<a ng-click="showNotificationsList()" ng-show="notifications.length > 0" class="l-indicator s-indicator s-indicator-status-{{highest.severity}} icon-bell"
ng-controller="NotificationIndicatorController">
<span class="status-indicator icon-bell"></span><span class="label">
{{notifications.length}} Notifications
<span class="label">
{{notifications.length}} Notification<span ng-show="notifications.length > 1">s</span>
</span><span class="count">{{notifications.length}}</span>
</a>

View File

@@ -55,7 +55,7 @@ $colorTransLucBg: #666; // Used as a visual blocking element over variable backg
// Foundation Colors
$colorAlt1: #ffc700;
$colorAlert: #ff3c00;
$colorAlert: #ff6600;
$colorWarningHi: #cc0000;
$colorWarningLo: #ff9900;
$colorDiagnostic: #a4b442;
@@ -114,10 +114,10 @@ $colorInspectorSectionHeaderFg: pullForward($colorInspectorBg, 40%);
// Status colors, mainly used for messaging and item ancillary symbols
$colorStatusFg: #ccc;
$colorStatusDefault: #ccc;
$colorStatusDefault: #999;
$colorStatusInfo: $colorInfo;
$colorStatusAlert: $colorAlert;
$colorStatusError: #d4585c;
$colorStatusError: #ff3300;
$colorStatusAvailable: $colorKey;
$colorStatusBtnBg: $colorBtnBg;
$colorProgressBarOuter: rgba(#000, 0.1);

View File

@@ -55,7 +55,6 @@ define([
timerTemplate,
legacyRegistry
) {
legacyRegistry.register("platform/features/clock", {
"name": "Clocks/Timers",
"descriptions": "Domain objects for displaying current & relative times.",
@@ -86,11 +85,6 @@ define([
"CLOCK_INDICATOR_FORMAT"
],
"priority": "preferred"
},
{
"implementation": FollowIndicator,
"depends": ["timerService"],
"priority": "fallback"
}
],
"services": [
@@ -305,6 +299,10 @@ define([
}
}
],
"runs": [{
"implementation": FollowIndicator,
"depends": ["openmct", "timerService"]
}],
"licenses": [
{
"name": "moment-duration-format",

View File

@@ -45,11 +45,11 @@ define(
}
ClockIndicator.prototype.getGlyphClass = function () {
return "no-collapse float-right subdued";
return "subdued";
};
ClockIndicator.prototype.getCssClass = function () {
return "icon-clock";
return "icon-clock no-collapse float-right";
};
ClockIndicator.prototype.getText = function () {

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* Open MCT, Copyright (c) 2009-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,38 +20,32 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(
['moment'],
function (moment) {
var NO_TIMER = "No timer being followed";
define([], function () {
/**
* Indicator that displays the active timer, as well as its
* current state.
* @implements {Indicator}
* @memberof platform/features/clock
*/
function FollowIndicator(timerService) {
this.timerService = timerService;
/**
* Indicator that displays the active timer, as well as its
* current state.
* @memberof platform/features/clock
*/
return function installFollowIndicator(openmct, timerService) {
var indicator = openmct.indicators.simpleIndicator();
var timer = timerService.getTimer();
setIndicatorStatus(timer);
function setIndicatorStatus(newTimer) {
if (newTimer !== undefined) {
indicator.iconClass('icon-timer');
indicator.statusClass('s-status-ok');
indicator.text('Following timer ' + newTimer.name);
} else {
indicator.iconClass('icon-timer');
indicator.statusClass('');
indicator.text('No timer being followed');
}
}
FollowIndicator.prototype.getGlyphClass = function () {
return "";
};
timerService.on('change', setIndicatorStatus);
FollowIndicator.prototype.getCssClass = function () {
return (this.timerService.getTimer()) ? "icon-timer s-status-ok" : "icon-timer";
};
FollowIndicator.prototype.getText = function () {
var timer = this.timerService.getTimer();
return timer ? ('Following timer ' + timer.name) : NO_TIMER;
};
FollowIndicator.prototype.getDescription = function () {
return "";
};
return FollowIndicator;
}
);
openmct.indicators.add(indicator);
};
});

View File

@@ -44,7 +44,7 @@ define(['EventEmitter'], function (EventEmitter) {
*/
TimerService.prototype.setTimer = function (timer) {
this.timer = timer;
this.emit('change');
this.emit('change', timer);
if (this.stopObserving) {
this.stopObserving();

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2009-2016, United States Government
* Open MCT, Copyright (c) 2009-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -20,39 +20,77 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(["../../src/indicators/FollowIndicator"], function (FollowIndicator) {
var TIMER_SERVICE_METHODS =
['setTimer', 'getTimer', 'clearTimer', 'on', 'off'];
define([
"../../src/indicators/FollowIndicator",
"../../src/services/TimerService",
"../../../../../src/MCT",
'zepto'
], function (
FollowIndicator,
TimerService,
MCT,
$
) {
describe("The timer-following indicator", function () {
var mockTimerService;
var indicator;
var timerService;
var openmct;
beforeEach(function () {
mockTimerService =
jasmine.createSpyObj('timerService', TIMER_SERVICE_METHODS);
indicator = new FollowIndicator(mockTimerService);
openmct = new MCT();
timerService = new TimerService(openmct);
spyOn(openmct.indicators, "add");
});
it("implements the Indicator interface", function () {
expect(indicator.getGlyphClass()).toEqual(jasmine.any(String));
expect(indicator.getCssClass()).toEqual(jasmine.any(String));
expect(indicator.getText()).toEqual(jasmine.any(String));
expect(indicator.getDescription()).toEqual(jasmine.any(String));
it("adds an indicator when installed", function () {
FollowIndicator(openmct, timerService);
expect(openmct.indicators.add).toHaveBeenCalled();
});
it("indicates that no timer is being followed", function () {
FollowIndicator(openmct, timerService);
var simpleIndicator = openmct.indicators.add.mostRecentCall.args[0];
var element = simpleIndicator.element;
var text = $('.indicator-text', element).text().trim();
expect(text).toEqual('No timer being followed');
});
describe("when a timer is set", function () {
var testObject;
var simpleIndicator;
beforeEach(function () {
testObject = { name: "some timer!" };
mockTimerService.getTimer.andReturn(testObject);
testObject = {
identifier: {
namespace: 'namespace',
key: 'key'
},
name: "some timer!"
};
timerService.setTimer(testObject);
FollowIndicator(openmct, timerService);
simpleIndicator = openmct.indicators.add.mostRecentCall.args[0];
});
it("displays the timer's name", function () {
expect(indicator.getText().indexOf(testObject.name))
.not.toEqual(-1);
var element = simpleIndicator.element;
var text = $('.indicator-text', element).text().trim();
expect(text).toEqual('Following timer ' + testObject.name);
});
it("displays the timer's name when it changes", function () {
var secondTimer = {
identifier: {
namespace: 'namespace',
key: 'key2'
},
name: "Some other timer"
};
var element = simpleIndicator.element;
timerService.setTimer(secondTimer);
var text = $('.indicator-text', element).text().trim();
expect(text).toEqual('Following timer ' + secondTimer.name);
});
});
});
});

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -194,6 +194,15 @@ define([
*/
this.telemetry = new api.TelemetryAPI(this);
/**
* An interface for creating new indicators and changing them dynamically.
*
* @type {module:openmct.IndicatorAPI}
* @memberof module:openmct.MCT#
* @name indicators
*/
this.indicators = new api.IndicatorAPI(this);
this.Dialog = api.Dialog;
}

View File

@@ -27,7 +27,8 @@ define([
'./types/TypeRegistry',
'./ui/Dialog',
'./ui/GestureAPI',
'./telemetry/TelemetryAPI'
'./telemetry/TelemetryAPI',
'./indicators/IndicatorAPI'
], function (
TimeAPI,
ObjectAPI,
@@ -35,7 +36,8 @@ define([
TypeRegistry,
Dialog,
GestureAPI,
TelemetryAPI
TelemetryAPI,
IndicatorAPI
) {
return {
TimeAPI: TimeAPI,
@@ -44,6 +46,7 @@ define([
Dialog: Dialog,
TypeRegistry: TypeRegistry,
GestureAPI: GestureAPI,
TelemetryAPI: TelemetryAPI
TelemetryAPI: TelemetryAPI,
IndicatorAPI: IndicatorAPI
};
});

View File

@@ -0,0 +1,129 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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([
'./SimpleIndicator',
'../../../platform/framework/src/Constants',
'lodash'
], function (
SimpleIndicator,
Constants,
_
) {
/* jshint validthis: true */
var LEGACY_INDICATOR_TEMPLATE =
'<mct-include ' +
' ng-model="indicator" ' +
' key="template" ' +
' </mct-include>';
function IndicatorAPI(openmct) {
this.openmct = openmct;
this.indicatorElements = [];
this.promiseForAllElements =
fetchLegacyIndicators.call(this)
.then(addLegacyIndicators.bind(this))
.then(resolveWithAllIndicatorElements.bind(this));
}
IndicatorAPI.prototype.simpleIndicator = function () {
return new SimpleIndicator(this.openmct);
};
/**
* Accepts an indicator object, which is a simple object
* with a single attribute, 'element' which has an HTMLElement
* as its value.
*
* We provide .simpleIndicator() as a convenience function
* which will create a default Open MCT indicator that can
* be passed to .add(indicator). This indicator also exposes
* functions for changing its appearance to support customization
* and dynamic behavior.
*
* Eg.
* var myIndicator = openmct.indicators.simpleIndicator();
* openmct.indicators.add(myIndicator);
*
* myIndicator.text("Hello World!");
* myIndicator.iconClass("icon-info");
*
*/
IndicatorAPI.prototype.add = function (indicator) {
// So that we can consistently position indicator elements,
// guarantee that they are wrapped in an element we control
var wrapperNode = document.createElement('div');
wrapperNode.className = 'l-indicator s-indicator';
wrapperNode.appendChild(indicator.element);
this.indicatorElements.push(wrapperNode);
};
/**
* @private
*/
IndicatorAPI.prototype.allIndicatorElements = function () {
return this.promiseForAllElements;
};
function fetchLegacyIndicators() {
return new Promise(function (resolve) {
this.openmct.legacyExtension('runs', {
depends: ['indicators[]'],
implementation: resolve
});
}.bind(this));
}
function addLegacyIndicators(legacyIndicators) {
legacyIndicators.forEach(function (legacyIndicatorDef) {
var legacyIndicator = initializeIfNeeded(legacyIndicatorDef);
var legacyIndicatorElement = buildLegacyIndicator(this.openmct, legacyIndicator, legacyIndicatorDef.template);
this.indicatorElements.push(legacyIndicatorElement);
}.bind(this));
}
function initializeIfNeeded(LegacyIndicatorDef) {
var legacyIndicator;
if (typeof LegacyIndicatorDef === 'function') {
legacyIndicator = new LegacyIndicatorDef();
} else {
legacyIndicator = LegacyIndicatorDef;
}
return legacyIndicator;
}
function buildLegacyIndicator(openmct, legacyIndicator, template) {
var $compile = openmct.$injector.get('$compile');
var $rootScope = openmct.$injector.get('$rootScope');
var scope = $rootScope.$new(true);
scope.indicator = legacyIndicator;
scope.template = template || 'indicator';
return $compile(LEGACY_INDICATOR_TEMPLATE)(scope)[0];
}
function resolveWithAllIndicatorElements() {
return this.indicatorElements;
}
return IndicatorAPI;
});

View File

@@ -0,0 +1,214 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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(
[
"../../MCT",
"../../../platform/commonUI/general/src/directives/MCTIndicators",
"zepto"
],
function (
MCT,
MCTIndicators,
$
) {
describe("The Indicator API", function () {
var openmct;
var directive;
var holderElement;
var legacyExtensionFunction = MCT.prototype.legacyExtension;
var legacyIndicatorsResolved = false;
var legacyIndicatorsCallback;
beforeEach(function () {
mockLegacyExtensionFunction();
openmct = new MCT();
directive = new MCTIndicators(openmct);
holderElement = document.createElement('div');
mockAngularComponents();
});
afterEach(function () {
MCT.prototype.legacyExtension = legacyExtensionFunction;
});
it("Displays any legacy indicators ", function () {
var legacyIndicators = [{},{},{},{}];
mockLegacyIndicatorsWith(legacyIndicators);
renderIndicators();
waitsFor(legacyIndicatorsToResolve, 1000);
runs(function () {
expectIndicatorsShownToBe(legacyIndicators.length);
});
});
it("If legacy indicator is defined as a constructor function, executes function ", function () {
var mockConstructorFunction = jasmine.createSpy('mockIndicatorConstructor');
var legacyIndicators = [{}, mockConstructorFunction];
mockConstructorFunction.andReturn({});
mockLegacyIndicatorsWith(legacyIndicators);
renderIndicators();
waitsFor(legacyIndicatorsToResolve, 1000);
runs(function () {
expectIndicatorsShownToBe(legacyIndicators.length);
expect(mockConstructorFunction).toHaveBeenCalled();
});
});
describe("The simple indicator", function () {
var simpleIndicator;
beforeEach(function () {
simpleIndicator = openmct.indicators.simpleIndicator();
mockLegacyIndicatorsWith([]);
openmct.indicators.add(simpleIndicator);
renderIndicators();
});
it("applies the set icon class", function () {
simpleIndicator.iconClass('testIconClass');
waitsFor(legacyIndicatorsToResolve);
runs(function () {
expect(getIconElement().hasClass('testIconClass')).toBe(true);
});
});
it("applies the set status class", function () {
simpleIndicator.statusClass('testStatusClass');
waitsFor(legacyIndicatorsToResolve);
runs(function () {
expect(getIconElement().hasClass('testStatusClass')).toBe(true);
});
});
it("displays the set text", function () {
simpleIndicator.text('some test text');
waitsFor(legacyIndicatorsToResolve);
runs(function () {
expect(getTextElement().text().trim()).toEqual('some test text');
});
});
it("sets the indicator's title", function () {
simpleIndicator.description('a test description');
waitsFor(legacyIndicatorsToResolve);
runs(function () {
expect(getIndicatorElement().attr('title')).toEqual('a test description');
});
});
it("Hides indicator text if no text is set", function () {
simpleIndicator.text('');
waitsFor(legacyIndicatorsToResolve);
runs(function () {
expect(getIndicatorElement().hasClass('hidden')).toBe(true);
});
});
});
it("Supports registration of a completely custom indicator", function () {
var customIndicator = $('<div class="customIndicator">A custom indicator</div>')[0];
mockLegacyIndicatorsWith([]);
openmct.indicators.add({element: customIndicator});
renderIndicators();
waitsFor(legacyIndicatorsToResolve);
runs(function () {
expect($('.customIndicator', holderElement).text().trim()).toEqual('A custom indicator');
});
});
function mockLegacyExtensionFunction() {
spyOn(MCT.prototype, "legacyExtension");
MCT.prototype.legacyExtension.andCallFake(function (extensionName, definition) {
if (extensionName === 'runs') {
legacyIndicatorsCallback = definition.implementation;
}
});
}
function mockAngularComponents() {
var mockInjector = jasmine.createSpyObj('$injector', ['get']);
var mockCompile = jasmine.createSpy('$compile');
var mockRootScope = jasmine.createSpyObj('rootScope', ['$new']);
var mockScope = {};
mockRootScope.$new.andReturn(mockScope);
mockInjector.get.andCallFake(function (service) {
return {
'$compile': mockCompile,
'$rootScope': mockRootScope
}[service];
});
openmct.$injector = mockInjector;
mockCompile.andCallFake(function () {
return function () {
return $('<div></div>');
};
});
}
function renderIndicators() {
directive.link({}, holderElement);
}
function mockLegacyIndicatorsWith(legacyIndicators) {
legacyIndicatorsResolved = false;
openmct.indicators.allIndicatorElements().then(function () {
legacyIndicatorsResolved = true;
});
legacyIndicatorsCallback(legacyIndicators);
}
function legacyIndicatorsToResolve() {
return legacyIndicatorsResolved;
}
function expectIndicatorsShownToBe(number) {
expect(holderElement.children.length).toBe(number);
}
function getIconElement() {
return $('.indicator-icon', holderElement);
}
function getIndicatorElement() {
return $('.status', holderElement);
}
function getTextElement() {
return $('.indicator-text', holderElement);
}
});
});

View File

@@ -0,0 +1,100 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['text!./res/indicator-template.html'],
function (indicatorTemplate) {
var DEFAULT_ICON_CLASS = 'icon-info';
function SimpleIndicator(openmct) {
this.openmct = openmct;
this.textValue = 'New Indicator';
this.descriptionValue = 'A simple indicator';
this.iconClassValue = DEFAULT_ICON_CLASS;
this.statusClassValue = '';
// We need to remove this element
this.element = document.createElement('div');
this.element.className = 'status-block-holder';
SimpleIndicator.defaultDisplayFunction.call(this);
}
SimpleIndicator.prototype.text = function (text) {
if (text !== undefined && text !== this.textValue) {
this.textValue = text;
SimpleIndicator.defaultDisplayFunction.call(this);
}
return this.textValue;
};
SimpleIndicator.prototype.description = function (description) {
if (description !== undefined && description !== this.descriptionValue) {
this.descriptionValue = description;
SimpleIndicator.defaultDisplayFunction.call(this);
}
return this.descriptionValue;
};
SimpleIndicator.prototype.iconClass = function (iconClass) {
if (iconClass !== undefined && iconClass !== this.iconClassValue) {
this.iconClassValue = iconClass;
SimpleIndicator.defaultDisplayFunction.call(this);
}
return this.iconClassValue;
};
SimpleIndicator.prototype.statusClass = function (statusClass) {
if (statusClass !== undefined && statusClass !== this.statusClassValue) {
this.statusClassValue = statusClass;
SimpleIndicator.defaultDisplayFunction.call(this);
}
return this.statusClassValue;
};
function hideOrShowText(text) {
if (text && text.length > 0) {
return '';
} else {
return 'hidden';
}
}
SimpleIndicator.defaultDisplayFunction = function () {
var html = indicatorTemplate
.replace('{{indicator.text}}', this.text())
.replace('{{indicator.iconClass}}', this.iconClass())
.replace('{{indicator.statusClass}}', this.statusClass())
.replace('{{indicator.description}}', this.description())
.replace('{{hideOrShowText}}', hideOrShowText(this.text()));
this.element.innerHTML = html;
return this.element;
};
return SimpleIndicator;
}
);

View File

@@ -0,0 +1,24 @@
<!--
Open MCT, Copyright (c) 2014-2018, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<span class="{{hideOrShowText}} {{indicator.iconClass}} {{indicator.statusClass}}" title="{{indicator.description}}">
<span class="label indicator-text">{{indicator.text}}</span>
</span>

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,8 +21,8 @@
*****************************************************************************/
define(
[],
function () {
['zepto'],
function ($) {
// Set of connection states; changing among these states will be
// reflected in the indicator's appearance.
@@ -30,68 +30,86 @@ define(
// DISCONNECTED: HTTP failed; maybe misconfigured, disconnected.
// PENDING: Still trying to connect, and haven't failed yet.
var CONNECTED = {
glyphClass: "ok"
statusClass: "s-status-ok"
},
PENDING = {
glyphClass: 'caution'
statusClass: "s-status-warning-lo"
},
DISCONNECTED = {
glyphClass: "err"
statusClass: "s-status-warning-hi"
};
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 URLIndicator(options, openmct, simpleIndicator) {
this.bindMethods();
this.count = 0;
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);
this.indicator = simpleIndicator;
this.setDefaultsFromOptions(options);
this.setIndicatorToState(PENDING);
this.fetchUrl();
setInterval(this.fetchUrl, this.interval);
}
URLIndicator.prototype.getCssClass = function () {
return this.cssClass;
};
URLIndicator.prototype.getGlyphClass = function () {
return this.state.glyphClass;
};
URLIndicator.prototype.getText = function () {
switch (this.state) {
URLIndicator.prototype.setIndicatorToState = function (state) {
switch (state) {
case CONNECTED: {
return this.label + " is connected";
this.indicator.text(this.label + " is connected");
this.indicator.description(this.label + " is online, checking status every " + this.interval + " milliseconds.");
break;
}
case PENDING: {
return "Checking status of " + this.label + " please stand by...";
this.indicator.text("Checking status of " + this.label + " please stand by...");
this.indicator.description("Checking status of " + this.label + " please stand by...");
break;
}
case DISCONNECTED: {
return this.label + " is offline";
this.indicator.text(this.label + " is offline");
this.indicator.description(this.label + " is offline, checking status every " + this.interval + " milliseconds");
break;
}
}
this.indicator.statusClass(state.statusClass);
};
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";
}
}
URLIndicator.prototype.fetchUrl = function () {
this.get(this.URLpath)
.then(this.handleSuccess)
.catch(this.handleError);
};
URLIndicator.prototype.get = function (url) {
return new Promise(function (resolve, reject) {
$.ajax({
type: 'GET',
url: url,
success: resolve,
error: reject
});
});
};
URLIndicator.prototype.handleError = function (e) {
this.setIndicatorToState(DISCONNECTED);
};
URLIndicator.prototype.handleSuccess = function () {
this.setIndicatorToState(CONNECTED);
};
URLIndicator.prototype.setDefaultsFromOptions = function (options) {
this.URLpath = options.url;
this.label = options.label || options.url;
this.interval = options.interval || 10000;
this.indicator.iconClass(options.iconClass || 'icon-connectivity');
};
URLIndicator.prototype.bindMethods = function () {
this.fetchUrl = this.fetchUrl.bind(this);
this.handleSuccess = this.handleSuccess.bind(this);
this.handleError = this.handleError.bind(this);
this.setIndicatorToState = this.setIndicatorToState.bind(this);
};
return URLIndicator;
});

View File

@@ -1,20 +1,35 @@
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"]
});
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, 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 URLIndicatorPlugin(URLIndicator) {
return function (opts) {
return function install(openmct) {
var simpleIndicator = openmct.indicators.simpleIndicator();
var urlIndicator = new URLIndicator(opts, openmct, simpleIndicator);
openmct.indicators.add(simpleIndicator);
return urlIndicator;
};
};
};
});
}
);

View File

@@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* Open MCT, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@@ -21,138 +21,167 @@
*****************************************************************************/
define(
["./URLIndicator"],
function (URLIndicator) {
[
"./URLIndicator",
"./URLIndicatorPlugin",
"../../MCT",
"zepto"
],
function (
URLIndicator,
URLIndicatorPlugin,
MCT,
$
) {
var defaultPrototypeFunction = URLIndicator.prototype.get;
describe("The URLIndicator", function () {
var mockHttp,
mockInterval,
mockPromise,
opts,
Indicator,
indicatorWrapper;
var openmct;
var indicatorElement;
var urlIndicator;
var mockHttpRequestFunction;
var returned;
var options;
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
);
returned = false;
jasmine.Clock.useMock();
openmct = new MCT();
spyOn(openmct.indicators, 'add');
mockHttpRequest();
});
it("has a database cssClass as default", function () {
expect(indicatorWrapper.getCssClass()).toEqual("icon-database");
afterEach(function () {
URLIndicator.prototype.get = defaultPrototypeFunction;
jasmine.Clock.reset();
});
it("consults the url with the path supplied", function () {
expect(mockHttp.get).toHaveBeenCalledWith(opts.url);
describe("on initialization", function () {
describe("with default options", function () {
beforeEach(function () {
options = {
url: "someURL"
};
urlIndicator = URLIndicatorPlugin(options)(openmct);
indicatorElement = openmct.indicators.add.mostRecentCall.args[0].element;
});
it("has a default icon class if none supplied", function () {
var iconElement = getIconElement();
expect(iconElement.hasClass('icon-connectivity')).toBe(true);
});
it("defaults to the URL if no label supplied", function () {
var textElement = getTextElement();
expect(textElement.text().indexOf(options.url) >= 0).toBe(true);
});
});
describe("with custom options", function () {
beforeEach(function () {
options = {
url: "customURL",
interval: 1814,
iconClass: "iconClass-checked",
label: "custom label"
};
urlIndicator = URLIndicatorPlugin(options)(openmct);
indicatorElement = openmct.indicators.add.mostRecentCall.args[0].element;
});
it("uses the custom iconClass", function () {
var iconElement = getIconElement();
expect(iconElement.hasClass('iconClass-checked')).toBe(true);
});
it("uses custom interval", function () {
expect(mockHttpRequestFunction.calls.length).toEqual(1);
jasmine.Clock.tick(1);
expect(mockHttpRequestFunction.calls.length).toEqual(1);
mockInterval(options.interval + 1);
expect(mockHttpRequestFunction.calls.length).toEqual(2);
});
it("uses custom label if supplied in initialization", function () {
var textElement = getTextElement();
expect(textElement.text().indexOf(options.label) >= 0).toBe(true);
});
});
});
it("changes when the database connection is nominal", function () {
var initialText = indicatorWrapper.getText(),
initialDescrption = indicatorWrapper.getDescription(),
initialGlyphClass = indicatorWrapper.getGlyphClass();
describe("when running", function () {
beforeEach(function () {
options = {
url: "someURL",
interval: 100
};
urlIndicator = URLIndicatorPlugin(options)(openmct);
indicatorElement = openmct.indicators.add.mostRecentCall.args[0].element;
});
// Nominal just means getting back an object, without
// an error field.
mockPromise.then.mostRecentCall.args[0]({ data: {} });
it("requests the provided URL", function () {
mockInterval(options.interval + 1);
expect(mockHttpRequestFunction).toHaveBeenCalledWith(options.url);
});
// 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);
it("indicates success if connection is nominal", function () {
mockSuccess();
mockInterval(options.interval + 1);
// Do check for specific class
expect(indicatorWrapper.getGlyphClass()).toEqual("ok");
waitsFor(httpRequestReturned);
runs(function () {
var iconElement = getIconElement();
expect(iconElement.hasClass('s-status-ok')).toBe(true);
});
});
it("indicates an error when the server cannot be reached", function () {
mockError();
mockInterval(options.interval + 1);
waitsFor(httpRequestReturned);
runs(function () {
var iconElement = getIconElement();
expect(iconElement.hasClass('s-status-warning-hi')).toBe(true);
});
});
});
it("changes when the server cannot be reached", function () {
var initialText = indicatorWrapper.getText(),
initialDescrption = indicatorWrapper.getDescription(),
initialGlyphClass = indicatorWrapper.getGlyphClass();
function mockHttpRequest() {
mockHttpRequestFunction = jasmine.createSpy('get');
URLIndicator.prototype.get = mockHttpRequestFunction;
mockSuccess();
}
// Nominal just means getting back an object, without
// an error field.
mockPromise.then.mostRecentCall.args[1]({ data: {} });
function mockSuccess() {
mockHttpRequestFunction
.andReturn(Promise.resolve().then(function () {
returned = true;
}));
}
// 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);
function mockError() {
mockHttpRequestFunction
.andReturn(Promise.reject().then(function () {
returned = true;
//Throw error to ensure chained catch is invoked
throw undefined;
}));
}
// 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
);
});
function httpRequestReturned() {
return returned;
}
function mockInterval(interval) {
jasmine.Clock.tick(interval);
}
function getIconElement() {
return $('.indicator-icon', indicatorElement);
}
function getTextElement() {
return $('.indicator-text', indicatorElement);
}
});
}
);

View File

@@ -128,7 +128,7 @@ define([
plugins.ExampleImagery = ExampleImagery;
plugins.SummaryWidget = SummaryWidget;
plugins.TelemetryMean = TelemetryMean;
plugins.URLIndicatorPlugin = URLIndicatorPlugin;
plugins.URLIndicator = URLIndicatorPlugin;
return plugins;
});