Compare commits

...

26 Commits

Author SHA1 Message Date
ca88996d86 Persist table sort options 2019-03-22 17:44:29 -07:00
4111c12895 Added background property to table __headers-w element (#2325) 2019-03-21 15:37:09 -07:00
b6ec023920 format created and modified time to utc (#2324) 2019-03-21 15:14:45 -07:00
e8e7067993 Fix legacy messages (#2323)
* Fix legacy message styling

- Code cleanup;
- Enable constants-mobile;
- Add _legacy-messages.scss;
- Add status color fg colors to theme constants;

* CSS refactoring, significant migration for messages classes

- Many messages classes migrated;
- c-click-icon > c-icon-button;
- c-click-icon rewritten;
- __close-btn refined in dialogs;

* About and splash screen styling

- Fixed splash in About screen;

* Update _mixins.scss

* Convert c-overlay__close-button to button

- <a> -> <button>;
- Set color of button;
- Normalized naming from close-btn to close-button;

* Fixed brightness filter issue on elements in overlays in VISTA;

* Fix dismiss button
2019-03-21 15:07:16 -07:00
0e9319e97b Plot options 4.1 (#2303)
* working crosshairs -- todo -- add toggle option

* add ability to toggle crosshair

* add zoom in by onscreen button to plot

* add zoom out functionality

* switch positions of zoom in and out buttons

* working zoom with mousewheel

* add better logic to storing plot history on mouse wheel zoom

* Local controls styling in plots and imagery

- WIP

* Plot local controls, cursor guides

- Local control classes much refined;
- Cursor guides styled, theme constants added;

* Fix local controls in imagery

- LC styles refined;
- New theme constant;

* Better approach to loading

- New .c-loading--overlay that doesn't block the cursor;
- Applied to plot views (telem, overlay and stacked plots);

* Plot styles, local controls refined

- Moved plot classes into their own scss file;
- More refinement on local control styles;
- Plot local controls layout finalized;
- Buttons smallified in layout frame context;

* Convert export buttons from <a> tags

* Stubbed in cursor guide buttons in templates

- WIP!

* add toggle-cursor-guide-button to stacked plots

* move cursor guide button to top left in all plots for consistency

* Plot local controls layout refined

- Also: global styling for *[disabled]

* Change c-button 'is-enabled' to 'is-active'

* Added check for if crosshairs enabled before tracking
2019-03-21 13:57:41 -07:00
df53af7b4d Inspector location (#2317)
* working original location - todo link location

* remove link location for now

* remove legacy getPath and implement new getOriginalPath API call

* simplify getOriginalPath, and use path to calculate parent paths in location.vue
2019-03-21 12:41:40 -07:00
bcbf244fd2 Fix foreground color of option elements in selects (#2300)
* Fix foreground color of option elements in selects

- Windows users in Snow theme reporting white foreground color on system
 background color (light gray) when interacting with select elements.
 This fix hard-codes the option's `color` attribute to black.

* Add background property to style select options for Windows
2019-03-21 12:39:29 -07:00
7ff5febae0 Tables - Maintain stable sort. Requery for historical data on time system change. Parse telemetry time values before comparing to bounds. (#2322) 2019-03-21 11:00:48 -07:00
019d108bb2 Reorder api update (#2319)
* Added 'reorder' function to composition API

* Re-implemented reordering in Elements

* Make LAD table editable

* Remove test spec focus

* Fixing bugs with event listeners

* Clean up listeners properly in Elements pool

* Fixed race condition on drag-and-drop to initiate edit

* Implement reordering in LAD tables

* Reorder events emit full reorder plan

* Fixed failing specs
2019-03-21 10:59:08 -07:00
a14f628ca3 fix regression regarding edit views not showing in main object view (#2318) 2019-03-20 09:31:51 -07:00
6116351dad Reorder api (#2316)
* Added 'reorder' function to composition API

* Re-implemented reordering in Elements

* Make LAD table editable

* Remove test spec focus

* Fixing bugs with event listeners

* Clean up listeners properly in Elements pool

* Fixed race condition on drag-and-drop to initiate edit

* Implement reordering in LAD tables
2019-03-19 10:31:56 -07:00
23efef4469 fix auto closing issue when changing months, and remove stray event listener (#2305) 2019-03-18 11:59:41 -07:00
95549f7be2 Should not show editable views for objects in flexible layout and display layout (#2304)
* fix issue where editable summary widget view shows up in display layout and flexible layout

* keep consistent kebab case for props
2019-03-18 11:59:09 -07:00
6338bd1168 About dialog (#2306)
* Added legacy about dialog launcher

* Added build information and licenses dialog

* Made discussed changes to About API. Is now Branding API and a little more user friendly

* Added fullscreen overlay option

* About dialog and licenses overlay screens migrated

- Migrated CSS and refined styling;
- Unit tested in Open only - not able to test other 'brands';
2019-03-18 10:54:51 -07:00
f7d0d2c166 remove root from navigation (#2309) 2019-03-18 09:50:23 -07:00
7c2e10ba0e Remove url validation from summary widgets, webpage and hyperlink (#2312)
LGTM
2019-03-18 09:45:38 -07:00
350d3c92e7 remove is editing checks from toolbar providers since is editing is being checked by layout (#2314) 2019-03-17 21:01:03 -07:00
0f2918efaf Fix telemetry metadata issues (#2308)
* Do not try to convert undefined to a string

* Fixed metadata sorting. Iteratees that return arrays are treated as object paths.

* Added test specs for telemetry API reordering change

* Added telemetry filters to the API

* Support multiple inspector views

* Renamed InspectorView.vue to InspectorViews.vue

* VISTA compatibility issues (#2291)

* Build config changes necessary to work with VISTA
* Fixes to TelemetryTableRow to address bug in VISTA
* Fixed sass-fast-loader version to avoid https://github.com/yibn2008/fast-sass-loader/issues/47
* Reverted default theme
2019-03-14 13:49:37 -07:00
b72ad529aa small compatibility fix to make legacy dialogs (mostly) work (#2310) 2019-03-14 13:31:42 -07:00
f77c6c821c Migrate styles (#2307)
* Legacy style migration in progress

- Working bottom up, many legacy items commented out. Stopped at
controls/indicators;

* Further migrations and deprecating
- Legacy indicator styles moved;

* WIP Styles migration
- s-button converted to c-button, WIP;
- Other

* Significant progress on migration, but still very WIP

- Mostly constants and overlay styling;
- Also bubbles and splitter;
- TODO: fix tree in overlay and splitter in imagery!

* Fix Summary Widgets UI WIP

- Remove non-working status 'editing' checks;

* Fix Summary Widgets UI WIP

- Remove non-working status 'editing' checks;
- view-control > c-disclosure-triangle;

* Fix Summary Widgets UI WIP

- Markup changes;
- Migrate CSS to styles-new, remove old;

* Fix Summary Widgets UI WIP

- Rule formatting and layout;
- Refinement to _controls / select {} padding;

* Fix Summary Widgets UI WIP

- Toolbar styles made more portable;
- Palette style  migration;
- Very WIP;

* Fix Summary Widgets UI WIP

- Palettes all fixed and functional;
- Conditions layout;
- New c-button--swatched styles;

* Fix Summary Widgets UI WIP

- Clean up code;

* Fix Summary Widgets UI WIP

- Fix button in Test Data area;

* Fix layout in shell left pane due to elements being moved

- Styles fixed and refined;

* Fixed palettes

- Fixed icon palette;
- Significant refinement to general palette styles;

* Significant fixes for Summary Widgets

- Widget editing UI fixed;
- JS cleanups and improvements;
- CSS, JS code cleanup;

* Migrate tree view used in Locator

- Mods to legacy markup;
- Mods to current CSS;
- Removed import of legacy tree CSS in legacy-styles.scss;

* Migrate archetypes

- l-flex-row, l-flex-col, etc. moved to legacy;
- grid-* styles cleaned up and moved, @extends removed;
- WIP on c-object-label, move styles from mct-tree.vue into ObjectLabel
.vue;
- TODO: finish up c-object-label, cleanups in mct-tree.vue;

* Migrate effects and animation mixins

* Object labels, legacy cleanup

- Add and apply .c-object-label for tree node elements;
- Remove legacy class "tree" from markup;
- Tweak color of tree item hover for better contrast in Inspector;

* Fix palettes in Inspector

* Various

- Fix hover color in tree for better mechanics on a variety of bgs;
- Fix object label in Locator tree;
- Remove overlay blocker test color;

* Significant work for Summary Widgets, mctForm, compact form

- Forms in overlay dialogs fixed;
- form, compact-form, other classes migrated into new _forms.scss;
- Fixes for Summary Widgets;
- Theme constants files synced, add form values;
- Removed import of legacy forms/elems SCSS file;

* Migrate various

- Autoflow tabular;
- Datetime;
- Channel selector;
- Form validation;

* Migrate wait spinners, final cleanup

* Remove old src/styles directory

- Remove old Snow and Espresso plugins;
- Remove refs to old Snow and Espresso config'd aliases;

* Update Palette.js

* Update Palette.js

* Removed commented code

* Removed commented code

* Migrate About, startup and splash screen styles
2019-03-14 13:27:13 -07:00
248f160e73 Filters Inspector View (#2296)
* Added telemetry filters to the API

* Support multiple inspector views

* Renamed InspectorView.vue to InspectorViews.vue

* first cut of filter inspector plugin

* abstract for better readability

* third times the charm

* working persist checkbox selections

* fix typo

* working persisted filters from inspector

* add prop validations for FitlerValue and FilterObject

* enable filter inspector view for overlay and stacked plots

* remove object from persisted filter when object is removed from composition

* update filterValue to filterField for clarity

* Added filter code to tables

* add filter support to Telemetry Tables

* fix errors when old domainObject does not have configuration property

* working filters on overlay plots

* make requested changes

* Add filters as 'options' object on subscribe

* Significant mods to Filtering
- Styling;
- Added Browse view in Inspector;
- Added .c-checkbox-list class;
- "PLOT SERIES" header changed to "PLOT SERIES OPTIONS" for clarity;

* make filter update pass updated filters to telemetry adapter

* Tolerate undefined configuration

* Conditionally destroy filters listener

* use @change event instead of @blur'
2019-03-12 12:20:03 -07:00
5151f90bb8 Reimplemented Remove Action. Removed confirmation dialog from legacy remove action, which move still relies on (#2290) 2019-03-11 14:53:32 -07:00
402062110d TCR fixes 2 (#2286)
* prevent default on dragover in dropHint, to allow drop event to fire

* add notebook snapshot to preview

* fix for preview image overlay

* pin fast-sass-loader version to 1.4.6

* fix saveAs in plot image export

* fix elements search in inspector

* fix current Search error

* fix anonymous render error in layout

* navigate and edit on create

* remove domainObjects from composition when deleting frames and containers, and also fix a bug whereby a user can add domainObject via drag and drop(composition) but because of the lack of containers, it will not be added to the flexible layout

* fix index undefined error when reordering containers

* throw an error when user cancels instead of returning false

* fixes for toolbar not updating on selection change

* fix errors when objects without context are returned by the search aggregator

* prompt user before cancelling edit

* check transactions before prompting user

* add save and continue editing option to save menu

* prompt user if in edit mode and is navigating away
2019-03-11 11:47:53 -07:00
1c8f23dea1 Object migration (#2282)
I implementation of data migrations for display layouts and fixed position elements.
2019-03-11 08:56:56 -07:00
5ee22b3481 Error in conductor validation (#2293) 2019-03-05 12:20:34 -08:00
322a7bd5a8 VISTA compatibility issues (#2291)
* Build config changes necessary to work with VISTA
* Fixes to TelemetryTableRow to address bug in VISTA
* Fixed sass-fast-loader version to avoid https://github.com/yibn2008/fast-sass-loader/issues/47
* Reverted default theme
2019-02-27 17:08:50 -08:00
227 changed files with 5450 additions and 12215 deletions

View File

@ -27,7 +27,7 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<title></title>
<script src="dist/openmct.js"></script>
<link rel="stylesheet" href="dist/openmct.css">
<link rel="stylesheet" href="dist/styles/openmct.css">
<link rel="icon" type="image/png" href="dist/favicons/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="dist/favicons/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="dist/favicons/favicon-16x16.png" sizes="16x16">
@ -81,6 +81,8 @@
openmct.install(openmct.plugins.Tabs());
openmct.install(openmct.plugins.FlexibleLayout());
openmct.install(openmct.plugins.LADTable());
openmct.install(openmct.plugins.Filters(['table', 'telemetry.plot.overlay']));
openmct.install(openmct.plugins.ObjectMigration());
openmct.start();
</script>
</html>

View File

@ -25,7 +25,7 @@
"eventemitter3": "^1.2.0",
"exports-loader": "^0.7.0",
"express": "^4.13.1",
"fast-sass-loader": "^1.4.5",
"fast-sass-loader": "1.4.6",
"file-loader": "^1.1.11",
"file-saver": "^1.3.8",
"git-rev-sync": "^1.4.0",

View File

@ -28,6 +28,7 @@ define([
"./res/templates/dialog.html",
"./res/templates/overlay-blocking-message.html",
"./res/templates/message.html",
"./res/templates/notification-message.html",
"./res/templates/overlay-message-list.html",
"./res/templates/overlay.html",
'legacyRegistry'
@ -39,6 +40,7 @@ define([
dialogTemplate,
overlayBlockingMessageTemplate,
messageTemplate,
notificationMessageTemplate,
overlayMessageListTemplate,
overlayTemplate,
legacyRegistry
@ -88,6 +90,10 @@ define([
"key": "message",
"template": messageTemplate
},
{
"key": "notification-message",
"template": notificationMessageTemplate
},
{
"key": "overlay-message-list",
"template": overlayMessageListTemplate

View File

@ -19,24 +19,24 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="abs top-bar">
<div class="dialog-title">{{ngModel.title}}</div>
<div class="hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
<div class="c-overlay__top-bar">
<div class="c-overlay__dialog-title">{{ngModel.title}}</div>
<div class="c-overlay__dialog-hint hint">All fields marked <span class="req icon-asterisk"></span> are required.</div>
</div>
<div class='abs editor'>
<div class='c-overlay__contents-main'>
<mct-form ng-model="ngModel.value"
structure="ngModel.structure"
class="validates"
name="createForm">
</mct-form>
</div>
<div class="abs bottom-bar">
<a class='s-button major'
<div class="c-overlay__button-bar">
<a class='c-button c-button--major'
ng-class="{ disabled: !createForm.$valid }"
ng-click="ngModel.confirm()">
OK
</a>
<a class='s-button'
<a class='c-button '
ng-click="ngModel.cancel()">
Cancel
</a>

View File

@ -1,25 +1,32 @@
<div class="l-message"
<div class="c-message"
ng-class="'message-severity-' + ngModel.severity">
<div class="w-message-contents">
<div class="top-bar">
<div class="title">{{ngModel.message}}</div>
<div class="c-message__top-bar">
<div class="c-message__title">{{ngModel.title}}</div>
</div>
<div class="c-message__hint" ng-hide="ngModel.hint === undefined">
{{ngModel.hint}}
<span ng-if="ngModel.timestamp !== undefined">[{{ngModel.timestamp}}]</span>
</div>
<div class="message-body">
<div class="message-action">
{{ngModel.actionText}}
</div>
<mct-include key="'progress-bar'"
ng-model="ngModel"
ng-show="ngModel.progressPerc !== undefined"></mct-include>
ng-show="ngModel.progress !== undefined || ngModel.unknownProgress"></mct-include>
</div>
<div class="bottom-bar">
<a ng-repeat="dialogOption in ngModel.options"
class="s-button"
ng-click="dialogOption.callback()">
{{dialogOption.label}}
</a>
<a class="s-button major"
ng-if="ngModel.primaryOption"
ng-click="ngModel.primaryOption.callback()">
{{ngModel.primaryOption.label}}
</a>
<div class="c-overlay__button-bar">
<button ng-repeat="dialogOption in ngModel.options"
class="c-button"
ng-click="dialogOption.callback()">
{{dialogOption.label}}
</button>
<button class="c-button c-button--major"
ng-if="ngModel.primaryOption"
ng-click="ngModel.primaryOption.callback()">
{{ngModel.primaryOption.label}}
</button>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,25 @@
<div class="c-message"
ng-class="'message-severity-' + ngModel.severity">
<div class="w-message-contents">
<div class="c-message__top-bar">
<div class="c-message__title">{{ngModel.message}}</div>
</div>
<div class="message-body">
<mct-include key="'progress-bar'"
ng-model="ngModel"
ng-show="ngModel.progressPerc !== undefined"></mct-include>
</div>
<div class="c-overlay__button-bar">
<button ng-repeat="dialogOption in ngModel.options"
class="c-button"
ng-click="dialogOption.callback()">
{{dialogOption.label}}
</button>
<button class="c-button c-button--major"
ng-if="ngModel.primaryOption"
ng-click="ngModel.primaryOption.callback()">
{{ngModel.primaryOption.label}}
</button>
</div>
</div>
</div>

View File

@ -1,22 +1,23 @@
<mct-container key="overlay">
<div class="t-message-list">
<div class="top-bar">
<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 ||
ngModel.dialog.messages.length == 0">s</span>
<div class="t-message-list c-overlay__contents">
<div class="c-overlay__top-bar">
<div class="c-overlay__dialog-title">{{ngModel.dialog.title}}</div>
<div class="c-overlay__dialog-hint">Displaying {{ngModel.dialog.messages.length}} message<span
ng-show="ngModel.dialog.messages.length > 1 ||
ngModel.dialog.messages.length == 0">s</span>
</div>
</div>
<div class="w-messages">
<div class="w-messages c-overlay__messages">
<mct-include
ng-repeat="msg in ngModel.dialog.messages | orderBy: '-'"
key="'message'" ng-model="msg.model"></mct-include>
key="'notification-message'" ng-model="msg.model"></mct-include>
</div>
<div class="bottom-bar">
<a ng-repeat="dialogAction in ngModel.dialog.actions"
class="s-button major"
<div class="c-overlay__bottom-bar">
<button ng-repeat="dialogAction in ngModel.dialog.actions"
class="c-button c-button--major"
ng-click="dialogAction.action()">
{{dialogAction.label}}
</a>
</button>
</div>
</div>
</mct-container>

View File

@ -19,18 +19,18 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<mct-container key="overlay">
<div class="abs top-bar">
<div class="dialog-title">{{ngModel.dialog.title}}</div>
<div class="hint">{{ngModel.dialog.hint}}</div>
<mct-container key="c-overlay__contents">
<div class=c-overlay__top-bar">
<div class="c-overlay__dialog-title">{{ngModel.dialog.title}}</div>
<div class="c-overlay__dialog-hint hint">{{ngModel.dialog.hint}}</div>
</div>
<div class='abs editor'>
<div class='c-overlay__contents-main'>
<mct-include key="ngModel.dialog.template"
parameters="ngModel.dialog.parameters"
ng-model="ngModel.dialog.model">
</mct-include>
</div>
<div class="abs bottom-bar">
<div class="c-overlay__button-bar">
<a ng-repeat="option in ngModel.dialog.options"
href=''
class="s-button lg"

View File

@ -19,12 +19,12 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<div class="abs overlay l-dialog" ng-class="{'delayEntry100ms' : ngModel.delay}">
<div class="abs blocker"></div>
<div class="abs outer-holder">
<a ng-click="ngModel.cancel()"
<div class="c-overlay l-overlay-small" ng-class="{'delayEntry100ms' : ngModel.delay}">
<div class="c-overlay__blocker"></div>
<div class="c-overlay__outer">
<button ng-click="ngModel.cancel()"
ng-if="ngModel.cancel"
class="close icon-x-in-circle"></a>
<div class="abs inner-holder contents" ng-transclude></div>
class="c-click-icon c-overlay__close-button icon-x-in-circle"></button>
<div class="c-overlay__contents" ng-transclude></div>
</div>
</div>

View File

@ -160,7 +160,7 @@ define([
},
{
"key": "remove",
"category": "contextual",
"category": "legacy",
"implementation": RemoveAction,
"cssClass": "icon-trash",
"name": "Remove",

View File

@ -23,11 +23,7 @@
/**
* Module defining RemoveAction. Created by vwoeltje on 11/17/14.
*/
define([
'./RemoveDialog'
], function (
RemoveDialog
) {
define([], function () {
/**
* Construct an action which will remove the provided object manifestation.
@ -114,12 +110,7 @@ define([
return parent.useCapability('mutation', doMutate);
}
/*
* Pass in the function to remove the domain object so it can be
* associated with an 'OK' button press
*/
dialog = new RemoveDialog(this.openmct, domainObject, removeFromContext);
dialog.show();
removeFromContext();
};
// Object needs to have a parent for Remove to be applicable

View File

@ -1,72 +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([], function () {
/**
* @callback removeCallback
* @param {DomainObject} domainObject the domain object to be removed
*/
/**
* Construct a new Remove dialog.
*
* @param {DialogService} dialogService the service that shows the dialog
* @param {DomainObject} domainObject the domain object to be removed
* @param {removeCallback} removeCallback callback that handles removal of the domain object
* @memberof platform/commonUI/edit
* @constructor
*/
function RemoveDialog(openmct, domainObject, removeCallback) {
this.openmct = openmct;
this.domainObject = domainObject;
this.removeCallback = removeCallback;
}
/**
* Display a dialog to confirm the removal of a domain object.
*/
RemoveDialog.prototype.show = function () {
let dialog = this.openmct.overlays.dialog({
title: 'Remove ' + this.domainObject.getModel().name,
iconClass: 'alert',
message: 'Warning! This action will permanently remove this object. Are you sure you want to continue?',
buttons: [
{
label: 'OK',
callback: () => {
this.removeCallback();
dialog.dismiss();
}
},
{
label: 'Cancel',
callback: () => {
dialog.dismiss();
}
}
]
});
};
return RemoveDialog;
});

View File

@ -92,16 +92,7 @@ function (
* @memberof platform/commonUI/edit.SaveAction#
*/
SaveAsAction.prototype.perform = function () {
// Discard the current root view (which will be the editing
// UI, which will have been pushed atop the Browse UI.)
function returnToBrowse(object) {
if (object) {
object.getCapability("action").perform("navigate");
}
return object;
}
return this.save().then(returnToBrowse);
return this.save();
};
/**
@ -192,7 +183,7 @@ function (
if (reason !== "user canceled") {
self.notificationService.error("Save Failed");
}
return false;
throw reason;
}
return getParent(domainObject)

View File

@ -67,20 +67,30 @@ define(
openmct = this.openmct,
newObject;
function onSave() {
// openmct.editor.save();
}
function onCancel() {
openmct.editor.cancel();
}
function navigateAndEdit(object) {
let objectPath = object.getCapability('context').getPath(),
url = '#/browse/' + objectPath
.slice(1)
.map(function (o) {
return o && openmct.objects.makeKeyString(o.getId());
})
.join('/');
window.location.href = url;
openmct.editor.edit();
}
newModel.type = this.type.getKey();
newModel.location = this.parent.getId();
newObject = this.parent.useCapability('instantiation', newModel);
openmct.editor.edit();
newObject.getCapability("action").perform("save-as").then(onSave, onCancel);
newObject.getCapability("action").perform("save-as").then(navigateAndEdit, onCancel);
// TODO: support editing object without saving object first.
// Which means we have to toggle createwizard afterwards. For now,
// We will disable this.

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.
-->
<div class="t-object-label l-flex-row flex-elem grows">
<div class="t-item-icon flex-elem {{type.getCssClass()}}" ng-class="{ 'l-icon-link':location.isLink() }"></div>
<div class='t-title-label flex-elem grows'>{{model.name}}</div>
<div class="c-object-label">
<div class="c-object-label__type-icon {{type.getCssClass()}}" ng-class="{ 'l-icon-link':location.isLink() }"></div>
<div class='c-object-label__name'>{{model.name}}</div>
</div>

View File

@ -1,13 +1,13 @@
<div ng-controller="BannerController" ng-show="active.notification"
class="l-message-banner s-message-banner {{active.notification.model.severity}}" ng-class="{
class="c-message-banner {{active.notification.model.severity}}" ng-class="{
'minimized': active.notification.model.minimized,
'new': !active.notification.model.minimized}"
ng-click="maximize(active.notification)">
<span class="banner-elem label">
<span class="c-message-banner__message">
{{active.notification.model.title}}
</span>
<span ng-show="active.notification.model.progress !== undefined || active.notification.model.unknownProgress">
<mct-include key="'progress-bar'" class="banner-elem"
<mct-include key="'progress-bar'" class="c-message-banner__progress-bar"
ng-model="active.notification.model">
</mct-include>
</span>
@ -16,5 +16,5 @@
ng-click="action(active.notification.model.primaryOption.callback, $event)">
{{active.notification.model.primaryOption.label}}
</a>
<a class="banner-elem close icon-x" ng-click="dismiss(active.notification, $event)"></a>
<button class="c-message-banner__close-button c-click-icon icon-x-in-circle" ng-click="dismiss(active.notification, $event)"></button>
</div>

View File

@ -20,14 +20,11 @@
at runtime from the About dialog for additional information.
-->
<span ng-controller="ToggleController as toggle">
<span ng-controller="TreeNodeController as treeNode">
<span
class="tree-item menus-to-left"
ng-class="{selected: treeNode.isSelected()}"
>
<span
class='ui-symbol view-control flex-elem'
ng-class="{ 'has-children': model.composition !== undefined, expanded: toggle.isActive() }"
<div class="u-contents" ng-controller="TreeNodeController as treeNode">
<div class="c-tree__item menus-to-left"
ng-class="{selected: treeNode.isSelected()}">
<span class='c-disclosure-triangle c-tree__item__view-control'
ng-class="{ 'is-enabled': model.composition !== undefined, 'c-disclosure-triangle--expanded': toggle.isActive() }"
ng-click="toggle.toggle(); treeNode.trackExpansion()"
>
</span>
@ -39,19 +36,15 @@
ng-click="treeNode.select()"
>
</mct-representation>
</span>
<span
class="tree-item-subtree"
</div>
<div class="u-contents"
ng-show="toggle.isActive()"
ng-if="model.composition !== undefined"
>
ng-if="model.composition !== undefined">
<mct-representation key="'subtree'"
ng-model="ngModel"
parameters="parameters"
mct-object="treeNode.hasBeenExpanded() && domainObject">
</mct-representation>
</span>
</span>
</div>
</div>
</span>

View File

@ -19,8 +19,8 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<ul class="tree">
<li>
<ul class="c-tree">
<li class="c-tree__item-h">
<mct-representation key="'tree-node'"
mct-object="domainObject"
ng-model="ngModel"

View File

@ -1,4 +1,2 @@
<span class="tree-item menus-to-left">
</span>
<span class="tree-item-subtree">
</span>
<span class="c-tree__item js-tree__item"></span>
<span class="c-tree__item-subtree"></span>

View File

@ -1,2 +1 @@
<span class='ui-symbol view-control flex-elem'>
</span>
<span class='c-disclosure-triangle c-tree__item__view-control'></span>

View File

@ -1,6 +1,4 @@
<span class="rep-object-label">
<div class="t-object-label l-flex-row flex-elem grows">
<div class="t-item-icon flex-elem"></div>
<div class='t-title-label flex-elem grows'></div>
</div>
</span>
<div class="rep-object-label c-object-label c-tree__item__label">
<div class="c-object-label__type-icon c-tree__item__type-icon t-item-icon"></div>
<div class="c-object-label__name c-tree__item__name t-title-label"></div>
</div>

View File

@ -37,9 +37,9 @@ define([
this.expanded = state;
if (state) {
this.el.addClass('expanded');
this.el.addClass('c-disclosure-triangle--expanded');
} else {
this.el.removeClass('expanded');
this.el.removeClass('c-disclosure-triangle--expanded');
}
this.callbacks.forEach(function (callback) {

View File

@ -28,7 +28,7 @@ define([
], function ($, nodeTemplate, ToggleView, TreeLabelView) {
function TreeNodeView(gestureService, subtreeFactory, selectFn, openmct) {
this.li = $('<li>');
this.li = $('<li class="c-tree__item-h">');
this.openmct = openmct;
this.statusClasses = [];
@ -38,7 +38,7 @@ define([
if (!this.subtreeView) {
this.subtreeView = subtreeFactory();
this.subtreeView.model(this.activeObject);
this.li.find('.tree-item-subtree').eq(0)
this.li.find('.c-tree__item-subtree').eq(0)
.append($(this.subtreeView.elements()));
}
$(this.subtreeView.elements()).removeClass('hidden');
@ -85,9 +85,9 @@ define([
var obj = domainObject.useCapability('adapter');
var hasComposition = this.openmct.composition.get(obj) !== undefined;
if (hasComposition) {
$(this.toggleView.elements()).removeClass('no-children');
$(this.toggleView.elements()).addClass('is-enabled');
} else {
$(this.toggleView.elements()).addClass('no-children');
$(this.toggleView.elements()).removeClass('is-enabled');
}
}
@ -120,7 +120,7 @@ define([
selectedIdPath = getIdPath(domainObject);
if (this.onSelectionPath) {
this.li.find('.tree-item').eq(0).removeClass('selected');
this.li.find('.js-tree__item').eq(0).removeClass('is-selected');
if (this.subtreeView) {
this.subtreeView.value(undefined);
}
@ -136,7 +136,7 @@ define([
if (this.onSelectionPath) {
if (activeIdPath.length === selectedIdPath.length) {
this.li.find('.tree-item').eq(0).addClass('selected');
this.li.find('.js-tree__item').eq(0).addClass('is-selected');
} else {
// Expand to reveal the selection
this.toggleView.value(true);

View File

@ -27,7 +27,7 @@ define([
], function ($, TreeNodeView, spinnerTemplate) {
function TreeView(gestureService, openmct, selectFn) {
this.ul = $('<ul class="tree"></ul>');
this.ul = $('<ul class="c-tree"></ul>');
this.nodeViews = [];
this.callbacks = [];
this.selectFn = selectFn || this.value.bind(this);

View File

@ -24,10 +24,10 @@
<button ng-click="timer.clickStopButton()"
ng-hide="timer.timerState == 'stopped'"
title="Reset"
class="c-timer__ctrl-reset c-click-icon c-click-icon--major icon-reset"></button>
class="c-timer__ctrl-reset c-icon-button c-icon-button--major icon-reset"></button>
<button ng-click="timer.clickButton()"
title="{{timer.buttonText()}}"
class="c-timer__ctrl-pause-play c-click-icon c-click-icon--major {{timer.buttonCssClass()}}"></button>
class="c-timer__ctrl-pause-play c-icon-button c-icon-button--major {{timer.buttonCssClass()}}"></button>
</div>
<div class="c-timer__direction {{timer.signClass()}}"
ng-hide="!timer.signClass()"></div>

View File

@ -45,7 +45,6 @@ define([
"key": "url",
"name": "URL",
"control": "textfield",
"pattern": "^(ftp|https?)\\:\\/\\/",
"required": true,
"cssClass": "l-input-lg"
},

View File

@ -1,22 +1,18 @@
<div class="t-imagery" ng-controller="ImageryController as imagery">
<div class="t-imagery c-imagery" ng-controller="ImageryController as imagery">
<mct-split-pane class='abs' anchor="bottom" alias="imagery">
<div class="split-pane-component has-local-controls l-image-main-wrapper l-flex-col"
ng-mouseenter="showLocalControls = true;"
ng-mouseleave="showLocalControls = false;">
<div class="h-local-controls h-local-controls-overlay-content h-local-controls-trans s-local-controls local-controls-hidden l-flex-row">
<span class="holder flex-elem grows">
<div class="split-pane-component has-local-controls l-image-main-wrapper l-flex-col">
<div class="h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover l-flex-row c-imagery__lc">
<span class="holder flex-elem grows c-imagery__lc__sliders">
<input class="icon-brightness" type="range"
min="0"
max="500"
ng-model="filters.brightness">
</input>
ng-model="filters.brightness" />
<input class="icon-contrast" type="range"
min="0"
max="500"
ng-model="filters.contrast">
</input>
ng-model="filters.contrast" />
</span>
<span class="holder flex-elem t-reset-btn-holder">
<span class="holder flex-elem t-reset-btn-holder c-imagery__lc__reset-btn">
<a class="s-icon-button icon-reset t-btn-reset"
ng-click="filters = { brightness: 100, contrast: 100 }"></a>
</span>
@ -33,14 +29,14 @@
<div class="l-image-main-controlbar flex-elem l-flex-row">
<div class="l-datetime-w flex-elem grows">
<a class="s-button show-thumbs sm hidden icon-thumbs-strip"
<a class="c-button show-thumbs sm hidden icon-thumbs-strip"
ng-click="showThumbsBubble = (showThumbsBubble) ? false:true"></a>
<span class="l-time">{{imagery.getTime()}}</span>
</div>
<div class="h-local-controls flex-elem">
<a class="s-button pause-play"
<a class="c-button icon-pause pause-play"
ng-click="imagery.paused(!imagery.paused())"
ng-class="{ paused: imagery.paused() }"></a>
ng-class="{ 'is-paused': imagery.paused() }"></a>
<a href=""
class="s-button l-mag s-mag vsm icon-reset"
ng-click="clipped = false"

View File

@ -47,7 +47,6 @@ define([
"key": "url",
"name": "URL",
"control": "textfield",
"pattern": "^(ftp|https?)\\:\\/\\/",
"required": true,
"cssClass": "l-input-lg"
}

View File

@ -19,13 +19,13 @@
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<form name="mctForm" novalidate class="form l-flex-col">
<form name="mctForm" novalidate class="form c-form" autocomplete="off">
<span ng-repeat="section in structure.sections"
class="l-form-section l-flex-col flex-elem {{ section.cssClass }}">
<h2 class="section-header flex-elem" ng-if="section.name">
class="l-form-section c-form__section {{ section.cssClass }}">
<h2 class="c-form__header" ng-if="section.name">
{{section.name}}
</h2>
<ng-form class="form-row validates l-flex-row flex-elem {{ section.cssClass }}"
<ng-form class="form-row c-form__row validates {{ section.cssClass }}"
ng-class="{
first:$index < 1,
req: row.required,
@ -37,11 +37,11 @@
}"
name="mctFormInner"
ng-repeat="row in section.rows">
<div class='label flex-elem' title="{{row.description}}">
<div class='c-form__row__label label flex-elem' title="{{row.description}}">
{{row.name}}
</div>
<div class='controls flex-elem'>
<div class="wrapper" ng-if="row.control">
<div class='c-form__row__controls controls flex-elem'>
<div class="c-form__controls-wrapper wrapper" ng-if="row.control">
<mct-control key="row.control"
ng-model="ngModel"
ng-required="row.required"

View File

@ -29,12 +29,13 @@ define(
function SnapshotPreviewController($scope, openmct) {
$scope.previewImage = function (imageUrl) {
let image = document.createElement('img');
image.src = imageUrl;
let imageDiv = document.createElement('div');
imageDiv.classList = 'image-main s-image-main';
imageDiv.style.backgroundImage = `url(${imageUrl})`;
let previewImageOverlay = openmct.overlays.overlay(
{
element: image,
element: imageDiv,
size: 'large',
buttons: [
{

View File

@ -45,7 +45,7 @@
</mct-include>
</div>
<a class="s-button c-search__btn-cancel"
<a class="c-button c-search__btn-cancel"
ng-show="!(ngModel.input === '' || ngModel.input === undefined)"
ng-click="ngModel.input = ''; ngModel.checkAll = true; menuController.checkAll(); controller.search()">
Cancel</a>

View File

@ -44,6 +44,9 @@ define([
'../platform/core/src/objects/DomainObjectImpl',
'../platform/core/src/capabilities/ContextualDomainObject',
'./ui/preview/plugin',
'./api/Branding',
'./plugins/licenses/plugin',
'./plugins/remove/plugin',
'vue'
], function (
EventEmitter,
@ -69,6 +72,9 @@ define([
DomainObjectImpl,
ContextualDomainObject,
PreviewPlugin,
BrandingAPI,
LicensesPlugin,
RemoveActionPlugin,
Vue
) {
/**
@ -89,6 +95,13 @@ define([
*/
function MCT() {
EventEmitter.call(this);
this.buildInfo = {
version: __OPENMCT_VERSION__,
buildDate: __OPENMCT_BUILD_DATE__,
revision: __OPENMCT_REVISION__,
branch: __OPENMCT_BUILD_BRANCH__
};
this.legacyBundle = { extensions: {
services: [
{
@ -228,11 +241,18 @@ define([
this.contextMenu = new api.ContextMenuRegistry();
this.router = new ApplicationRouter();
this.branding = BrandingAPI.default;
this.legacyRegistry = defaultRegistry;
this.install(this.plugins.Plot());
this.install(this.plugins.TelemetryTable());
this.install(this.plugins.DisplayLayout());
this.install(PreviewPlugin.default());
this.install(LegacyIndicatorsPlugin());
this.install(LicensesPlugin.default());
this.install(RemoveActionPlugin.default());
if (typeof BUILD_CONSTANTS !== 'undefined') {
this.install(buildInfoPlugin(BUILD_CONSTANTS));
@ -331,10 +351,6 @@ define([
legacyRegistry.register('adapter', this.legacyBundle);
legacyRegistry.enable('adapter');
this.install(LegacyIndicatorsPlugin());
this.router = new ApplicationRouter();
this.router.route(/^\/$/, () => {
this.router.setPath('/browse/mine');
});

View File

@ -158,7 +158,7 @@ define([
return {
evaluate: function (datum, property) {
return limitEvaluator.evaluate(datum, property.key);
return limitEvaluator.evaluate(datum, property && property.key);
}
};
};

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* Open MCT, Copyright (c) 2014-2019, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -19,31 +19,27 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
// Styles for extended text copy
.abs.l-standalone {
$d: 20%;
padding: $d/4 $d;
}
let brandingOptions = {};
.s-text {
font-size: 0.8em;
ol, ul {
list-style: square;
margin-left: 1.5em;
li {
/**
* @typedef {Object} BrandingOptions
* @memberOf openmct/branding
* @property {string} smallLogoImage URL to the image to use as the applications logo.
* This logo will appear on every screen and when clicked will launch the about dialog.
* @property {string} aboutHtml Custom content for the about screen. When defined the
* supplied content will be inserted at the start of the about dialog, and the default
* Open MCT splash logo will be suppressed.
*/
}
}
h1, h2, h3 {
font-weight: 200 !important;
}
table {
td { font-size: inherit; }
tr.header {
background-color: rgba($colorBodyFg, 0.2);
}
/**
* Set branding options for the application. These will override certain visual elements
* of the application and allow for customization of the application.
* @param {BrandingOptions} options
*/
export default function Branding(options) {
if (arguments.length === 1) {
brandingOptions = options;
}
return brandingOptions;
}

View File

@ -21,7 +21,11 @@ define([
topicService.and.returnValue(mutationTopic);
publicAPI = {};
publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [
'get'
'get',
'mutate'
]);
publicAPI.objects.eventEmitter = jasmine.createSpyObj('eventemitter', [
'on'
]);
publicAPI.objects.get.and.callFake(function (identifier) {
return Promise.resolve({identifier: identifier});
@ -52,6 +56,14 @@ define([
{
namespace: 'test',
key: 'a'
},
{
namespace: 'test',
key: 'b'
},
{
namespace: 'test',
key: 'c'
}
]
};
@ -68,12 +80,45 @@ define([
composition.on('add', listener);
return composition.load().then(function () {
expect(listener.calls.count()).toBe(1);
expect(listener.calls.count()).toBe(3);
expect(listener).toHaveBeenCalledWith({
identifier: {namespace: 'test', key: 'a'}
});
});
});
describe('supports reordering of composition', function () {
var listener;
beforeEach(function () {
listener = jasmine.createSpy('reorderListener');
composition.on('reorder', listener);
return composition.load();
});
it('', function () {
composition.reorder(1, 0);
let newComposition =
publicAPI.objects.mutate.calls.mostRecent().args[2];
let reorderPlan = listener.calls.mostRecent().args[0][0];
expect(reorderPlan.oldIndex).toBe(1);
expect(reorderPlan.newIndex).toBe(0);
expect(newComposition[0].key).toEqual('b');
expect(newComposition[1].key).toEqual('a');
expect(newComposition[2].key).toEqual('c');
});
it('', function () {
composition.reorder(0, 2);
let newComposition =
publicAPI.objects.mutate.calls.mostRecent().args[2];
let reorderPlan = listener.calls.mostRecent().args[0][0];
expect(reorderPlan.oldIndex).toBe(0);
expect(reorderPlan.newIndex).toBe(2);
expect(newComposition[0].key).toEqual('b');
expect(newComposition[1].key).toEqual('c');
expect(newComposition[2].key).toEqual('a');
})
});
// TODO: Implement add/removal in new default provider.
xit('synchronizes changes between instances', function () {

View File

@ -56,7 +56,8 @@ define([
this.listeners = {
add: [],
remove: [],
load: []
load: [],
reorder: []
};
this.onProviderAdd = this.onProviderAdd.bind(this);
this.onProviderRemove = this.onProviderRemove.bind(this);
@ -91,6 +92,13 @@ define([
this.onProviderRemove,
this
);
} if (event === 'reorder') {
this.provider.on(
this.domainObject,
'reorder',
this.onProviderReorder,
this
)
}
}
@ -141,6 +149,13 @@ define([
this.onProviderRemove,
this
);
} else if (event === 'reorder') {
this.provider.off(
this.domainObject,
'reorder',
this.onProviderReorder,
this
);
}
}
}
@ -209,6 +224,29 @@ define([
}
};
/**
* Reorder the domain objects in this composition.
*
* A call to [load]{@link module:openmct.CompositionCollection#load}
* must have resolved before using this method.
*
* @param {number} oldIndex
* @param {number} newIndex
* @memberof module:openmct.CompositionCollection#
* @name remove
*/
CompositionCollection.prototype.reorder = function (oldIndex, newIndex, skipMutate) {
this.provider.reorder(this.domainObject, oldIndex, newIndex);
};
/**
* Handle reorder from provider.
* @private
*/
CompositionCollection.prototype.onProviderReorder = function (reorderMap) {
this.emit('reorder', reorderMap);
};
/**
* Handle adds from provider.
* @private
@ -232,12 +270,12 @@ define([
* Emit events.
* @private
*/
CompositionCollection.prototype.emit = function (event, payload) {
CompositionCollection.prototype.emit = function (event, ...payload) {
this.listeners[event].forEach(function (l) {
if (l.context) {
l.callback.call(l.context, payload);
l.callback.apply(l.context, payload);
} else {
l.callback(payload);
l.callback(...payload);
}
});
};

View File

@ -126,6 +126,7 @@ define([
objectListeners = this.listeningTo[keyString] = {
add: [],
remove: [],
reorder: [],
composition: [].slice.apply(domainObject.composition)
};
}
@ -160,7 +161,7 @@ define([
});
objectListeners[event].splice(index, 1);
if (!objectListeners.add.length && !objectListeners.remove.length) {
if (!objectListeners.add.length && !objectListeners.remove.length && !objectListeners.reorder.length) {
delete this.listeningTo[keyString];
}
};
@ -178,8 +179,12 @@ define([
* @method remove
*/
DefaultCompositionProvider.prototype.remove = function (domainObject, childId) {
// TODO: this needs to be synchronized via mutation.
throw new Error('Default Provider does not implement removal.');
let composition = domainObject.composition.filter(function (child) {
return !(childId.namespace === child.namespace &&
childId.key === child.key);
});
this.publicAPI.objects.mutate(domainObject, 'composition', composition);
};
/**
@ -199,6 +204,54 @@ define([
// TODO: this needs to be synchronized via mutation
};
DefaultCompositionProvider.prototype.reorder = function (domainObject, oldIndex, newIndex) {
let newComposition = domainObject.composition.slice();
let removeId = oldIndex > newIndex ? oldIndex + 1 : oldIndex;
let insertPosition = oldIndex < newIndex ? newIndex + 1 : newIndex;
//Insert object in new position
newComposition.splice(insertPosition, 0, domainObject.composition[oldIndex]);
newComposition.splice(removeId, 1);
let reorderPlan = [{
oldIndex,
newIndex
}];
if (oldIndex > newIndex) {
for (let i = newIndex; i < oldIndex; i++) {
reorderPlan.push({
oldIndex: i,
newIndex: i + 1
});
}
} else {
for (let i = oldIndex + 1; i <= newIndex; i++) {
reorderPlan.push({
oldIndex: i,
newIndex: i - 1
});
}
}
this.publicAPI.objects.mutate(domainObject, 'composition', newComposition);
let id = objectUtils.makeKeyString(domainObject.identifier);
var listeners = this.listeningTo[id];
if (!listeners) {
return;
}
listeners.reorder.forEach(notify);
function notify(listener) {
if (listener.context) {
listener.callback.call(listener.context, reorderPlan);
} else {
listener.callback(reorderPlan);
}
}
};
/**
* Listens on general mutation topic, using injector to fetch to avoid
* circular dependencies.

View File

@ -226,7 +226,20 @@ define([
(identifier.namespace === identifiers[0].namespace &&
identifier.key === identifiers[0].key);
});
}
};
ObjectAPI.prototype.getOriginalPath = function (identifier, path = []) {
return this.get(identifier).then((domainObject) => {
path.push(domainObject);
let location = domainObject.location;
if (location) {
return this.getOriginalPath(utils.parseKeyString(location), path);
} else {
return path;
}
});
};
/**
* Uniquely identifies a domain object.

View File

@ -5,7 +5,8 @@ import Vue from 'vue';
const cssClasses = {
large: 'l-overlay-large',
small: 'l-overlay-small',
fit: 'l-overlay-fit'
fit: 'l-overlay-fit',
fullscreen: 'l-overlay-fullscreen'
};
class Overlay extends EventEmitter {

View File

@ -27,10 +27,16 @@
<style lang="scss">
@import "~styles/sass-base";
@mixin legacyMessage() {
flex: 0 1 auto;
font-family: symbolsfont;
font-size: $messageIconD; // Singleton message in a dialog
margin-right: $interiorMarginLg;
}
.c-message {
display: flex;
align-items: center;
padding: $interiorMarginLg;
align-items: flex-start;
> * + * {
margin-left: $interiorMarginLg;
@ -58,7 +64,33 @@
&__title,
&__action-text {
font-size: 1.2em; // TEMP
}
/************************** LEGACY */
&.message-severity-info:before {
@include legacyMessage();
content: $glyph-icon-info;
color: $colorInfo;
}
&.message-severity-alert:before {
@include legacyMessage();
content: $glyph-icon-alert-rect;
color: $colorWarningLo;
}
&.message-severity-error:before {
@include legacyMessage();
content: $glyph-icon-alert-triangle;
color: $colorWarningLo;
}
// Messages in a list
.c-overlay__messages & {
padding: $interiorMarginLg;
&:before {
font-size: $messageListIconD;
}
}
}
</style>

View File

@ -56,16 +56,44 @@
}
&__close-button {
$p: $interiorMarginSm;
$p: $interiorMargin;
border-radius: 100% !important;
color: $overlayColorFg;
display: inline-block;
font-size: 1.25em;
position: absolute;
top: $p; right: $p;
}
&__contents {
flex: 1 1 auto;
display: flex;
flex-direction: column;
}
&__top-bar {
flex: 0 0 auto;
flex-direction: column;
display: flex;
> * {
flex: 0 0 auto;
margin-bottom: $interiorMargin;
}
}
&__dialog-title {
@include ellipsize();
font-size: 1.5em;
line-height: 120%;
}
&__contents-main {
display: flex;
flex-direction: column;
flex: 1 1 auto;
overflow: auto;
padding-right: $interiorMargin; // fend off scroll bar
}
&__button-bar {
@ -89,18 +117,29 @@
.c-overlay {
&__blocker {
@include abs();
background: rgba(black, 0.7);
background: $colorOvrBlocker;
cursor: pointer;
display: block;
}
&__outer {
border-radius: $overlayCr;
box-shadow: rgba(black, 0.5) 0 2px 25px;
}
}
// Overlay types, styling for desktop. Appended to .l-overlay-wrapper element.
.l-overlay-large,
.l-overlay-small,
.l-overlay-fit {
.c-overlay__outer {
border-radius: $overlayCr;
box-shadow: rgba(black, 0.5) 0 2px 25px;
}
}
.l-overlay-fullscreen {
// Used by About > Licenses display
.c-overlay__outer {
@include overlaySizing($overlayOuterMarginFullscreen);
}
}
.l-overlay-large {
// Default
.c-overlay__outer {
@ -114,6 +153,7 @@
}
}
.t-dialog-sm .l-overlay-small, // Legacy dialog support
.l-overlay-fit {
.c-overlay__outer {
@include overlaySizing(auto);

View File

@ -297,7 +297,7 @@ define([
* @returns {Function} a function which may be called to terminate
* the subscription
*/
TelemetryAPI.prototype.subscribe = function (domainObject, callback) {
TelemetryAPI.prototype.subscribe = function (domainObject, callback, options) {
var provider = this.findSubscriptionProvider(domainObject);
if (!this.subscribeCache) {
@ -316,7 +316,7 @@ define([
subscriber.callbacks.forEach(function (cb) {
cb(value);
});
});
}, options);
} else {
subscriber.unsubscribe = function () {};
}

View File

@ -28,14 +28,22 @@ define([
describe('Telemetry API', function () {
var openmct;
var telemetryAPI;
var mockTypeService;
beforeEach(function () {
openmct = {
time: jasmine.createSpyObj('timeAPI', [
'timeSystem',
'bounds'
]),
$injector: jasmine.createSpyObj('injector', [
'get'
])
};
mockTypeService = jasmine.createSpyObj('typeService', [
'getType'
]);
openmct.$injector.get.and.returnValue(mockTypeService);
openmct.time.timeSystem.and.returnValue({key: 'system'});
openmct.time.bounds.and.returnValue({start: 0, end: 1});
telemetryAPI = new TelemetryAPI(openmct);
@ -296,5 +304,233 @@ define([
);
});
});
describe('metadata', function () {
let mockMetadata = {};
let mockObjectType = {
typeDef: {}
};
beforeEach(function () {
telemetryAPI.addProvider({
key: 'mockMetadataProvider',
supportsMetadata() {
return true;
},
getMetadata() {
return mockMetadata;
}
});
mockTypeService.getType.and.returnValue(mockObjectType);
})
it('respects explicit priority', function () {
mockMetadata.values = [
{
key: "name",
name: "Name",
hints: {
priority: 2
}
},
{
key: "timestamp",
name: "Timestamp",
hints: {
priority: 1
}
},
{
key: "sin",
name: "Sine",
hints: {
priority: 4
}
},
{
key: "cos",
name: "Cosine",
hints: {
priority: 3
}
}
];
let metadata = telemetryAPI.getMetadata({});
let values = metadata.values();
values.forEach((value, index) => {
expect(value.hints.priority).toBe(index + 1);
});
});
it('if no explicit priority, defaults to order defined', function () {
mockMetadata.values = [
{
key: "name",
name: "Name"
},
{
key: "timestamp",
name: "Timestamp"
},
{
key: "sin",
name: "Sine"
},
{
key: "cos",
name: "Cosine"
}
];
let metadata = telemetryAPI.getMetadata({});
let values = metadata.values();
values.forEach((value, index) => {
expect(value.key).toBe(mockMetadata.values[index].key);
});
});
it('respects domain priority', function () {
mockMetadata.values = [
{
key: "name",
name: "Name"
},
{
key: "timestamp-utc",
name: "Timestamp UTC",
hints: {
domain: 2
}
},
{
key: "timestamp-local",
name: "Timestamp Local",
hints: {
domain: 1
}
},
{
key: "sin",
name: "Sine",
hints: {
range: 2
}
},
{
key: "cos",
name: "Cosine",
hints: {
range: 1
}
}
];
let metadata = telemetryAPI.getMetadata({});
let values = metadata.valuesForHints(['domain']);
expect(values[0].key).toBe('timestamp-local');
expect(values[1].key).toBe('timestamp-utc');
});
it('respects range priority', function () {
mockMetadata.values = [
{
key: "name",
name: "Name"
},
{
key: "timestamp-utc",
name: "Timestamp UTC",
hints: {
domain: 2
}
},
{
key: "timestamp-local",
name: "Timestamp Local",
hints: {
domain: 1
}
},
{
key: "sin",
name: "Sine",
hints: {
range: 2
}
},
{
key: "cos",
name: "Cosine",
hints: {
range: 1
}
}
];
let metadata = telemetryAPI.getMetadata({});
let values = metadata.valuesForHints(['range']);
expect(values[0].key).toBe('cos');
expect(values[1].key).toBe('sin');
});
it('respects priority and domain ordering', function () {
mockMetadata.values = [
{
key: "id",
name: "ID",
hints: {
priority: 2
}
},
{
key: "name",
name: "Name",
hints: {
priority: 1
}
},
{
key: "timestamp-utc",
name: "Timestamp UTC",
hints: {
domain: 2,
priority: 1
}
},
{
key: "timestamp-local",
name: "Timestamp Local",
hints: {
domain: 1,
priority: 2
}
},
{
key: "timestamp-pst",
name: "Timestamp PST",
hints: {
domain: 3,
priority: 2
}
},
{
key: "sin",
name: "Sine"
},
{
key: "cos",
name: "Cosine"
}
];
let metadata = telemetryAPI.getMetadata({});
let values = metadata.valuesForHints(['priority', 'domain']);
[
'timestamp-utc',
'timestamp-local',
'timestamp-pst'
].forEach((key, index) => {
expect(values[index].key).toBe(key);
});
});
})
});
});

View File

@ -116,14 +116,18 @@ define([
return hints.every(hasHint, metadata);
}
var matchingMetadata = this.valueMetadatas.filter(hasHints);
var sortedMetadata = _.sortBy(matchingMetadata, function (metadata) {
return hints.map(function (hint) {
let iteratees = hints.map(hint => {
return (metadata) => {
return metadata.hints[hint];
});
}
});
return sortedMetadata;
return _.sortByAll(matchingMetadata, ...iteratees);
};
TelemetryMetadataManager.prototype.getFilterableValues = function () {
return this.valueMetadatas.filter(metadatum => metadatum.filters && metadatum.filters.length > 0);
}
TelemetryMetadataManager.prototype.getDefaultDisplayValue = function () {
let valueMetadata = this.valuesForHints(['range'])[0];

View File

@ -35,6 +35,9 @@ define([
canView: function (domainObject) {
return domainObject.type === 'LadTableSet';
},
canEdit: function (domainObject) {
return domainObject.type === 'LadTableSet';
},
view: function (domainObject) {
let component;

View File

@ -35,6 +35,9 @@ define([
canView: function (domainObject) {
return domainObject.type === 'LadTable';
},
canEdit: function (domainObject) {
return domainObject.type === 'LadTable';
},
view: function (domainObject) {
let component;

View File

@ -65,17 +65,25 @@ export default {
let index = _.findIndex(this.items, (item) => this.openmct.objects.makeKeyString(identifier) === item.key);
this.items.splice(index, 1);
},
reorder(reorderPlan) {
let oldItems = this.items.slice();
reorderPlan.forEach((reorderEvent) => {
this.$set(this.items, reorderEvent.newIndex, oldItems[reorderEvent.oldIndex]);
});
}
},
mounted() {
this.composition = this.openmct.composition.get(this.domainObject);
this.composition.on('add', this.addItem);
this.composition.on('remove', this.removeItem);
this.composition.on('reorder', this.reorder);
this.composition.load();
},
destroyed() {
this.composition.off('add', this.addItem);
this.composition.off('remove', this.removeItem);
this.composition.off('reorder', this.reorder);
}
}
</script>

View File

@ -93,6 +93,12 @@
this.primaryTelemetryObjects.splice(index,1);
primary = undefined;
},
reorderPrimary(reorderPlan) {
let oldComposition = this.primaryTelemetryObjects.slice();
reorderPlan.forEach(reorderEvent => {
this.$set(this.primaryTelemetryObjects, reorderEvent.newIndex, oldComposition[reorderEvent.oldIndex]);
});
},
addSecondary(primary) {
return (domainObject) => {
let secondary = {};
@ -120,11 +126,13 @@
this.composition = this.openmct.composition.get(this.domainObject);
this.composition.on('add', this.addPrimary);
this.composition.on('remove', this.removePrimary);
this.composition.on('reorder', this.reorderPrimary);
this.composition.load();
},
destroyed() {
this.composition.off('add', this.addPrimary);
this.composition.off('remove', this.removePrimary);
this.composition.off('reorder', this.reorderPrimary);
this.compositions.forEach(c => {
c.composition.off('add', c.addCallback);
c.composition.off('remove', c.removeCallback);

View File

@ -28,9 +28,9 @@ define([], function () {
key: "layout",
description: "A toolbar for objects inside a display layout.",
forSelection: function (selection) {
// Apply the layout toolbar if the edit mode is on, and the selected object
// Apply the layout toolbar if the selected object
// is inside a layout, or the main layout is selected.
return (openmct.editor.isEditing() && selection &&
return (selection &&
((selection[1] && selection[1].context.item && selection[1].context.item.type === 'layout') ||
(selection[0].context.item && selection[0].context.item.type === 'layout')));
},

View File

@ -27,6 +27,7 @@
:domain-object="domainObject"
:object-path="objectPath"
:has-frame="item.hasFrame"
:show-edit-view="false"
ref="objectFrame">
</object-frame>
</layout-frame>

View File

@ -0,0 +1,113 @@
<template>
<div class="u-contents c-filter-settings">
<li class="grid-row c-filter-settings__setting"
v-for="(filter, index) in filterField.filters"
:key="index">
<div class="grid-cell label">
{{ filterField.name }} =
</div>
<div class="grid-cell value">
<!-- EDITING -->
<!-- String input, editing -->
<template v-if="!filter.possibleValues && isEditing">
<input class="c-input--flex"
type="text"
placeholder="Enter Value"
:id="`${filter}filterControl`"
:value="persistedValue(filter)"
@change="updateFilterValue($event, filter)">
</template>
<!-- Checkbox list, editing -->
<template v-if="filter.possibleValues && isEditing">
<div class="c-checkbox-list__row"
v-for="value in filter.possibleValues"
:key="value">
<input class="c-checkbox-list__input"
type="checkbox"
:id="`${value}filterControl`"
@change="onUserSelect($event, filter.comparator, value)"
:checked="isChecked(filter.comparator, value)">
<span class="c-checkbox-list__value">
{{ value }}
</span>
</div>
</template>
<!-- BROWSING -->
<!-- String input, NOT editing -->
<template v-if="!filter.possibleValues && !isEditing">
{{ persistedValue(filter) }}
</template>
<!-- Checkbox list, NOT editing -->
<template v-if="filter.possibleValues && !isEditing">
<span>{{persistedFilters[filter.comparator].join(', ')}}</span>
</template>
</div>
</li>
</div>
</template>
<style lang="scss">
@import "~styles/sass-base";
.c-filter-settings {
&__setting {
.grid-cell.label {
white-space: nowrap;
}
}
}
</style>
<script>
export default {
inject: [
'openmct'
],
props: {
filterField: Object,
persistedFilters: {
type: Object,
default: () => {
return {}
}
}
},
data() {
return {
expanded: false,
isEditing: this.openmct.editor.isEditing()
}
},
methods: {
toggleIsEditing(isEditing) {
this.isEditing = isEditing;
},
onUserSelect(event, comparator, value){
this.$emit('onUserSelect', this.filterField.key, comparator, value, event.target.checked);
},
isChecked(comparator, value) {
if (this.persistedFilters[comparator] && this.persistedFilters[comparator].includes(value)) {
return true;
} else {
return false;
}
},
persistedValue(comparator) {
return this.persistedFilters && this.persistedFilters[comparator];
},
updateFilterValue(event, comparator) {
this.$emit('onTextEnter', this.filterField.key, comparator, event.target.value);
}
},
mounted() {
this.openmct.editor.on('isEditing', this.toggleIsEditing);
},
beforeDestroy() {
this.openmct.editor.off('isEditing', this.toggleIsEditing);
}
}
</script>

View File

@ -0,0 +1,93 @@
<template>
<li>
<div class="c-tree__item menus-to-left"
@click="toggleExpanded">
<span class="c-disclosure-triangle is-enabled flex-elem"
:class="{'c-disclosure-triangle--expanded': expanded}"></span>
<div class="c-tree__item__label">
<div class="t-object-label l-flex-row flex-elem grows">
<div class="t-item-icon flex-elem"
:class="objectCssClass">
</div>
<div class="t-title-label flex-elem grows">{{ filterObject.name }}</div>
</div>
</div>
</div>
<ul class="grid-properties" v-if="expanded">
<filter-field
v-for="field in filterObject.valuesWithFilters"
:key="field.key"
:filterField="field"
:persistedFilters="persistedFilters[field.key]"
@onUserSelect="collectUserSelects"
@onTextEnter="updateTextFilter">
</filter-field>
</ul>
</li>
</template>
<style lang="scss">
</style>
<script>
import FilterField from './FilterField.vue';
export default {
inject: ['openmct'],
components: {
FilterField
},
props: {
filterObject: Object,
persistedFilters: {
type: Object,
default: () => {
return {};
}
}
},
data() {
return {
expanded: false,
objectCssClass: undefined,
updatedFilters: this.persistedFilters
}
},
methods: {
toggleExpanded() {
this.expanded = !this.expanded;
},
collectUserSelects(key, comparator, valueName, value) {
let filterValue = this.updatedFilters[key];
if (filterValue && filterValue[comparator]) {
if (value === false) {
filterValue[comparator] = filterValue[comparator].filter(v => v !== valueName);
} else {
filterValue[comparator].push(valueName);
}
} else {
if (!this.updatedFilters[key]) {
this.updatedFilters[key] = {};
}
this.updatedFilters[key][comparator] = [value ? valueName : undefined];
}
this.$emit('updateFilters', this.keyString, this.updatedFilters);
},
updateTextFilter(key, comparator, value) {
if (!this.updatedFilters[key]) {
this.updatedFilters[key] = {};
}
this.updatedFilters[key][comparator] = value;
this.$emit('updateFilters', this.keyString, this.updatedFilters);
}
},
mounted() {
let type = this.openmct.types.get(this.filterObject.domainObject.type) || {};
this.keyString = this.openmct.objects.makeKeyString(this.filterObject.domainObject.identifier);
this.objectCssClass = type.definition.cssClass;
}
}
</script>

View File

@ -0,0 +1,85 @@
<template>
<ul class="tree c-tree c-properties__section" v-if="Object.keys(children).length">
<h2 class="c-properties__header">Filters</h2>
<filter-object
v-for="(child, key) in children"
:key="key"
:filterObject="child"
:persistedFilters="persistedFilters[key]"
@updateFilters="persistFilters">
</filter-object>
</ul>
</template>
<style lang="scss">
</style>
<script>
import FilterObject from './FilterObject.vue';
export default {
components: {
FilterObject
},
inject: [
'openmct',
'providedObject'
],
data() {
let persistedFilters = {};
if (this.providedObject.configuration && this.providedObject.configuration.filters) {
persistedFilters = this.providedObject.configuration.filters;
}
return {
persistedFilters,
children: {}
}
},
methods: {
addChildren(child) {
let keyString = this.openmct.objects.makeKeyString(child.identifier),
metadata = this.openmct.telemetry.getMetadata(child),
valuesWithFilters = metadata.valueMetadatas.filter((value) => value.filters),
childObject = {
name: child.name,
domainObject: child,
valuesWithFilters
};
if (childObject.valuesWithFilters.length) {
this.$set(this.children, keyString, childObject);
} else {
return;
}
},
removeChildren(identifier) {
let keyString = this.openmct.objects.makeKeyString(identifier);
this.$delete(this.children, keyString);
this.persistFilters(keyString);
},
persistFilters(keyString, userSelects) {
this.persistedFilters[keyString] = userSelects;
this.openmct.objects.mutate(this.providedObject, 'configuration.filters', this.persistedFilters);
},
updatePersistedFilters(filters) {
this.persistedFilters = filters;
}
},
mounted(){
this.composition = this.openmct.composition.get(this.providedObject);
this.composition.on('add', this.addChildren);
this.composition.on('remove', this.removeChildren);
this.composition.load();
this.unobserve = this.openmct.objects.observe(this.providedObject, 'configuration.filters', this.updatePersistedFilters);
},
beforeDestroy() {
this.composition.off('add', this.addChildren);
this.composition.off('remove', this.removeChildren);
this.unobserve();
}
}
</script>

View File

@ -0,0 +1,73 @@
/*****************************************************************************
* 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([
'./components/FiltersView.vue',
'vue'
], function (
FiltersView,
Vue
) {
function FiltersInspectorViewProvider(openmct, supportedObjectTypesArray) {
return {
key: 'filters-inspector',
name: 'Filters Inspector View',
canView: function (selection) {
if (selection.length === 0) {
return false;
}
let object = selection[0].context.item;
return object && supportedObjectTypesArray.some(type => object.type === type);
},
view: function (selection) {
let component;
let providedObject = selection[0].context.item;
return {
show: function (element) {
component = new Vue({
provide: {
openmct,
providedObject
},
components: {
FiltersView: FiltersView.default
},
template: '<filters-view></filters-view>',
el: element
});
},
destroy: function () {
component.$destroy();
component = undefined;
}
}
},
priority: function () {
return 1;
}
}
}
return FiltersInspectorViewProvider;
});

View File

@ -20,6 +20,14 @@
* at runtime from the About dialog for additional information.
*****************************************************************************/
@import "vendor/normalize.min.css";
@import "startup-base";
@import "openmct";
define([
'./filtersInspectorViewProvider'
], function (
FiltersInspectorViewProvider
) {
return function plugin(supportedObjectTypesArray) {
return function install(openmct) {
openmct.inspectorViews.addProvider(new FiltersInspectorViewProvider(openmct, supportedObjectTypesArray));
};
};
});

View File

@ -506,9 +506,27 @@ export default {
this.persist();
},
deleteContainer(containerId) {
let container = this.containers.filter(c => c.id === containerId)[0];
let containerIndex = this.containers.indexOf(container);
let container = this.containers.filter(c => c.id === containerId)[0],
containerIndex = this.containers.indexOf(container);
/*
remove associated domainObjects from composition
*/
container.frames.forEach(f => {
this.composition.remove({identifier: f.domainObjectIdentifier});
});
this.containers.splice(containerIndex, 1);
/*
add a container when there are no containers in the FL,
to prevent user from not being able to add a frame via
drag and drop.
*/
if (this.containers.length === 0) {
this.containers.push(new Container(100));
}
sizeToFill(this.containers);
this.persist();
},
@ -548,6 +566,12 @@ export default {
.frames
.filter((f => f.id === frameId))[0];
let frameIndex = container.frames.indexOf(frame);
/*
remove associated domainObject from composition
*/
this.composition.remove({identifier: frame.domainObjectIdentifier});
container.frames.splice(frameIndex, 1);
sizeToFill(container.frames);
this.persist(containerIndex);
@ -620,7 +644,7 @@ export default {
} else {
this.containers.splice(toIndex, 0, container);
}
this.persist(index);
this.persist();
},
removeChildObject(identifier) {
let removeIdentifier = this.openmct.objects.makeKeyString(identifier);

View File

@ -36,6 +36,7 @@
:domain-object="domainObject"
:object-path="objectPath"
:has-frame="hasFrame"
:show-edit-view="false"
ref="objectFrame">
</object-frame>

View File

@ -57,7 +57,7 @@ define([
layoutObject: domainObject
},
el: element,
template: '<flexible-layout-component ref="flexibleLayout"></flexible-layout-component>'
template: '<flexible-layout-component ref="flexibleLayout" :isEditing="isEditing"></flexible-layout-component>'
});
},
getSelectionContext: function () {

View File

@ -29,7 +29,7 @@ function ToolbarProvider(openmct) {
forSelection: function (selection) {
let context = selection[0].context;
return (openmct.editor.isEditing() && context && context.type &&
return (context && context.type &&
(context.type === 'flexible-layout' || context.type === 'container' || context.type === 'frame'));
},
toolbar: function (selection) {
@ -204,7 +204,7 @@ function ToolbarProvider(openmct) {
addContainer,
toggleFrame ? separator: undefined,
toggleFrame,
deleteFrame || deleteContainer ? separator: undefined,
deleteFrame || deleteContainer ? separator : undefined,
deleteFrame,
deleteContainer
];

View File

@ -16,7 +16,7 @@
</div>
<div class="c-grid-item__controls">
<div class="icon-people" title='Shared'></div>
<button class="c-click-icon icon-info c-info-button" title='More Info'></button>
<button class="c-icon-button icon-info c-info-button" title='More Info'></button>
<div class="icon-pointer-right c-pointer-icon"></div>
</div>
</a>

View File

@ -0,0 +1,52 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2019, 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.
*****************************************************************************/
<template>
<div class="c-about c-about--licenses">
<h1>Open MCT Third Party Licenses</h1>
<p>This software includes components released under the following licenses:</p>
<div v-for="(pkg, key) in packages" :key="key" class="c-license">
<h2 class="c-license__name">{{key}}</h2>
<div class="c-license__details">
<span class="c-license__author"><em>Author</em> {{pkg.publisher}}</span> |
<span class="c-license__license"><em>License(s)</em> {{pkg.licenses}}</span> |
<span class="c-license__repo"><em>Repository</em> <a :href="pkg.repository" target="_blank">{{pkg.repository}}</a></span>
</div>
<div class="c-license__text">
<p>{{pkg.licenseText}}</p>
</div>
</div>
</div>
</template>
<style lang="sass">
</style>
<script>
import packages from './third-party-licenses.json';
export default {
data() {
return {
packages: packages
}
}
}
</script>

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* Open MCT, Copyright (c) 2014-2019, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -19,23 +19,20 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
import Licenses from './Licenses.vue';
import Vue from 'vue';
// Override the Create menu for mobile
@include phoneandtablet {
.super-menu {
$d: 250px;
width: $d;
height: $d;
export default function () {
return function install(openmct) {
openmct.router.route(/^\/licenses$/, () => {
let licensesVm = new Vue(Licenses).$mount();
.pane {
&.left {
border-right: none;
padding-right: 0;
width: 100%;
}
&.right {
display: none;
}
}
}
openmct.overlays.overlay({
element: licensesVm.$el,
size: 'fullscreen',
dismissable: false,
onDestroy: () => licensesVm.$destroy()
});
});
};
}

View File

@ -0,0 +1,268 @@
{
"angular-route@1.4.14": {
"licenses": "MIT",
"repository": "https://github.com/angular/angular.js",
"publisher": "Angular Core Team",
"email": "angular-core+npm@google.com",
"path": "/Users/akhenry/Code/licenses/node_modules/angular-route",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/angular-route/LICENSE.md",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 Angular\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.",
"copyright": "Copyright (c) 2016 Angular"
},
"angular@1.4.14": {
"licenses": "MIT",
"repository": "https://github.com/angular/angular.js",
"publisher": "Angular Core Team",
"email": "angular-core+npm@google.com",
"path": "/Users/akhenry/Code/licenses/node_modules/angular",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/angular/LICENSE.md",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2016 Angular\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.",
"copyright": "Copyright (c) 2016 Angular"
},
"base64-arraybuffer@0.1.5": {
"licenses": "MIT",
"repository": "https://github.com/niklasvh/base64-arraybuffer",
"publisher": "Niklas von Hertzen",
"email": "niklasvh@gmail.com",
"url": "http://hertzen.com",
"path": "/Users/akhenry/Code/licenses/node_modules/base64-arraybuffer",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/base64-arraybuffer/LICENSE-MIT",
"licenseText": "Copyright (c) 2012 Niklas von Hertzen\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.",
"copyright": "Copyright (c) 2012 Niklas von Hertzen"
},
"comma-separated-values@3.6.4": {
"licenses": "MIT",
"repository": "https://github.com/knrz/CSV.js",
"publisher": "=",
"email": "hi@knrz.co",
"url": "http://knrz.co/",
"path": "/Users/akhenry/Code/licenses/node_modules/comma-separated-values",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/comma-separated-values/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Kash Nouroozi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.",
"copyright": "Copyright (c) 2014 Kash Nouroozi"
},
"css-line-break@1.0.1": {
"licenses": "MIT",
"repository": "https://github.com/niklasvh/css-line-break",
"publisher": "Niklas von Hertzen",
"email": "niklasvh@gmail.com",
"url": "https://hertzen.com",
"path": "/Users/akhenry/Code/licenses/node_modules/css-line-break",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/css-line-break/LICENSE",
"licenseText": "Copyright (c) 2017 Niklas von Hertzen\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.",
"copyright": "Copyright (c) 2017 Niklas von Hertzen"
},
"d3-array@1.2.4": {
"licenses": "BSD-3-Clause",
"repository": "https://github.com/d3/d3-array",
"publisher": "Mike Bostock",
"url": "http://bost.ocks.org/mike",
"path": "/Users/akhenry/Code/licenses/node_modules/d3-array",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/d3-array/LICENSE",
"licenseText": "Copyright 2010-2016 Mike Bostock\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the author nor the names of contributors may be used to\n endorse or promote products derived from this software without specific prior\n written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"copyright": "Copyright 2010-2016 Mike Bostock. All rights reserved."
},
"d3-axis@1.0.12": {
"licenses": "BSD-3-Clause",
"repository": "https://github.com/d3/d3-axis",
"publisher": "Mike Bostock",
"url": "http://bost.ocks.org/mike",
"path": "/Users/akhenry/Code/licenses/node_modules/d3-axis",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/d3-axis/LICENSE",
"licenseText": "Copyright 2010-2016 Mike Bostock\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the author nor the names of contributors may be used to\n endorse or promote products derived from this software without specific prior\n written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"copyright": "Copyright 2010-2016 Mike Bostock. All rights reserved."
},
"d3-collection@1.0.7": {
"licenses": "BSD-3-Clause",
"repository": "https://github.com/d3/d3-collection",
"publisher": "Mike Bostock",
"url": "http://bost.ocks.org/mike",
"path": "/Users/akhenry/Code/licenses/node_modules/d3-collection",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/d3-collection/LICENSE",
"licenseText": "Copyright 2010-2016, Mike Bostock\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the author nor the names of contributors may be used to\n endorse or promote products derived from this software without specific prior\n written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"copyright": "Copyright 2010-2016, Mike Bostock. All rights reserved."
},
"d3-color@1.0.4": {
"licenses": "BSD-3-Clause",
"repository": "https://github.com/d3/d3-color",
"publisher": "Mike Bostock",
"url": "http://bost.ocks.org/mike",
"path": "/Users/akhenry/Code/licenses/node_modules/d3-color",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/d3-color/LICENSE",
"licenseText": "Copyright 2010-2016 Mike Bostock\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the author nor the names of contributors may be used to\n endorse or promote products derived from this software without specific prior\n written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"copyright": "Copyright 2010-2016 Mike Bostock. All rights reserved."
},
"d3-format@1.2.2": {
"licenses": "BSD-3-Clause",
"repository": "https://github.com/d3/d3-format",
"publisher": "Mike Bostock",
"url": "http://bost.ocks.org/mike",
"path": "/Users/akhenry/Code/licenses/node_modules/d3-format",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/d3-format/LICENSE",
"licenseText": "Copyright 2010-2015 Mike Bostock\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the author nor the names of contributors may be used to\n endorse or promote products derived from this software without specific prior\n written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"copyright": "Copyright 2010-2015 Mike Bostock. All rights reserved."
},
"d3-interpolate@1.1.6": {
"licenses": "BSD-3-Clause",
"repository": "https://github.com/d3/d3-interpolate",
"publisher": "Mike Bostock",
"url": "http://bost.ocks.org/mike",
"path": "/Users/akhenry/Code/licenses/node_modules/d3-interpolate",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/d3-interpolate/LICENSE",
"licenseText": "Copyright 2010-2016 Mike Bostock\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the author nor the names of contributors may be used to\n endorse or promote products derived from this software without specific prior\n written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"copyright": "Copyright 2010-2016 Mike Bostock. All rights reserved."
},
"d3-scale@1.0.7": {
"licenses": "BSD-3-Clause",
"repository": "https://github.com/d3/d3-scale",
"publisher": "Mike Bostock",
"url": "http://bost.ocks.org/mike",
"path": "/Users/akhenry/Code/licenses/node_modules/d3-scale",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/d3-scale/LICENSE",
"licenseText": "Copyright 2010-2015 Mike Bostock\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the author nor the names of contributors may be used to\n endorse or promote products derived from this software without specific prior\n written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"copyright": "Copyright 2010-2015 Mike Bostock. All rights reserved."
},
"d3-selection@1.3.2": {
"licenses": "BSD-3-Clause",
"repository": "https://github.com/d3/d3-selection",
"publisher": "Mike Bostock",
"url": "https://bost.ocks.org/mike",
"path": "/Users/akhenry/Code/licenses/node_modules/d3-selection",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/d3-selection/LICENSE",
"licenseText": "Copyright (c) 2010-2018, Michael Bostock\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* The name Michael Bostock may not be used to endorse or promote products\n derived from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,\nINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\nBUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\nOF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\nEVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"copyright": "Copyright (c) 2010-2018, Michael Bostock. All rights reserved."
},
"d3-time-format@2.1.3": {
"licenses": "BSD-3-Clause",
"repository": "https://github.com/d3/d3-time-format",
"publisher": "Mike Bostock",
"url": "http://bost.ocks.org/mike",
"path": "/Users/akhenry/Code/licenses/node_modules/d3-time-format",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/d3-time-format/LICENSE",
"licenseText": "Copyright 2010-2017 Mike Bostock\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the author nor the names of contributors may be used to\n endorse or promote products derived from this software without specific prior\n written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"copyright": "Copyright 2010-2017 Mike Bostock. All rights reserved."
},
"d3-time@1.0.10": {
"licenses": "BSD-3-Clause",
"repository": "https://github.com/d3/d3-time",
"publisher": "Mike Bostock",
"url": "http://bost.ocks.org/mike",
"path": "/Users/akhenry/Code/licenses/node_modules/d3-time",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/d3-time/LICENSE",
"licenseText": "Copyright 2010-2016 Mike Bostock\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the author nor the names of contributors may be used to\n endorse or promote products derived from this software without specific prior\n written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.",
"copyright": "Copyright 2010-2016 Mike Bostock. All rights reserved."
},
"eventemitter3@1.2.0": {
"licenses": "MIT",
"repository": "https://github.com/primus/eventemitter3",
"publisher": "Arnout Kazemier",
"path": "/Users/akhenry/Code/licenses/node_modules/eventemitter3",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/eventemitter3/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Arnout Kazemier\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.",
"copyright": "Copyright (c) 2014 Arnout Kazemier"
},
"file-saver@1.3.8": {
"licenses": "MIT",
"repository": "https://github.com/eligrey/FileSaver.js",
"publisher": "Eli Grey",
"email": "me@eligrey.com",
"path": "/Users/akhenry/Code/licenses/node_modules/file-saver",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/file-saver/LICENSE.md",
"licenseText": "The MIT License\n\nCopyright © 2016 [Eli Grey][1].\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n [1]: http://eligrey.com",
"copyright": "Copyright © 2016 [Eli Grey][1]."
},
"html2canvas@1.0.0-alpha.12": {
"licenses": "MIT",
"repository": "https://github.com/niklasvh/html2canvas",
"publisher": "Niklas von Hertzen",
"email": "niklasvh@gmail.com",
"url": "https://hertzen.com",
"path": "/Users/akhenry/Code/licenses/node_modules/html2canvas",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/html2canvas/LICENSE",
"licenseText": "Copyright (c) 2012 Niklas von Hertzen\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.",
"copyright": "Copyright (c) 2012 Niklas von Hertzen"
},
"location-bar@3.0.1": {
"licenses": "BSD-2-Clause",
"repository": "https://github.com/KidkArolis/location-bar",
"publisher": "Karolis Narkevicius",
"path": "/Users/akhenry/Code/licenses/node_modules/location-bar",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/location-bar/README.md",
"licenseText": ""
},
"lodash@3.10.1": {
"licenses": "MIT",
"repository": "https://github.com/lodash/lodash",
"publisher": "John-David Dalton",
"email": "john.david.dalton@gmail.com",
"url": "http://allyoucanleet.com/",
"path": "/Users/akhenry/Code/licenses/node_modules/lodash",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/lodash/LICENSE",
"licenseText": "Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>\nBased on Underscore.js, copyright 2009-2015 Jeremy Ashkenas,\nDocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
"copyright": "Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/>. Based on Underscore.js, copyright 2009-2015 Jeremy Ashkenas,. DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>"
},
"moment-duration-format@2.2.2": {
"licenses": "MIT",
"repository": "https://github.com/jsmreese/moment-duration-format",
"path": "/Users/akhenry/Code/licenses/node_modules/moment-duration-format",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/moment-duration-format/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2018 John Madhavan-Reese\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
"copyright": "Copyright (c) 2018 John Madhavan-Reese"
},
"moment-timezone@0.5.23": {
"licenses": "MIT",
"repository": "https://github.com/moment/moment-timezone",
"publisher": "Tim Wood",
"email": "washwithcare@gmail.com",
"url": "http://timwoodcreates.com/",
"path": "/Users/akhenry/Code/licenses/node_modules/moment-timezone",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/moment-timezone/LICENSE",
"licenseText": "The MIT License (MIT)\r\n\r\nCopyright (c) JS Foundation and other contributors\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy of\r\nthis software and associated documentation files (the \"Software\"), to deal in\r\nthe Software without restriction, including without limitation the rights to\r\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r\nthe Software, and to permit persons to whom the Software is furnished to do so,\r\nsubject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in all\r\ncopies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
"copyright": "Copyright (c) JS Foundation and other contributors"
},
"moment@2.24.0": {
"licenses": "MIT",
"repository": "https://github.com/moment/moment",
"publisher": "Iskren Ivov Chernev",
"email": "iskren.chernev@gmail.com",
"url": "https://github.com/ichernev",
"path": "/Users/akhenry/Code/licenses/node_modules/moment",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/moment/LICENSE",
"licenseText": "Copyright (c) JS Foundation and other contributors\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use,\ncopy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\nWHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.",
"copyright": "Copyright (c) JS Foundation and other contributors"
},
"painterro@0.2.71": {
"licenses": "MIT",
"publisher": "Ivan Borshchov",
"path": "/Users/akhenry/Code/licenses/node_modules/painterro",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/painterro/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2017 Ivan Borshchov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.",
"copyright": "Copyright (c) 2017 Ivan Borshchov"
},
"printj@1.2.1": {
"licenses": "Apache-2.0",
"repository": "https://github.com/SheetJS/printj",
"publisher": "sheetjs",
"path": "/Users/akhenry/Code/licenses/node_modules/printj",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/printj/LICENSE",
"licenseText": "Copyright (C) 2016-present SheetJS\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.",
"copyright": "Copyright (C) 2016-present SheetJS"
},
"vue@2.5.6": {
"licenses": "MIT",
"repository": "https://github.com/vuejs/vue",
"publisher": "Evan You",
"path": "/Users/akhenry/Code/licenses/node_modules/vue",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/vue/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2013-present, Yuxi (Evan) You\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.",
"copyright": "Copyright (c) 2013-present, Yuxi (Evan) You"
},
"zepto@1.2.0": {
"licenses": "MIT",
"repository": "https://github.com/madrobby/zepto",
"path": "/Users/akhenry/Code/licenses/node_modules/zepto",
"licenseFile": "/Users/akhenry/Code/licenses/node_modules/zepto/README.md",
"licenseText": "Copyright (c) 2010-2018 Thomas Fuchs\nhttp://zeptojs.com/\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
}
}

View File

@ -26,7 +26,7 @@
</div>
<div class="c-ne__local-controls--hidden">
<button class="c-click-icon c-click-icon--major icon-trash"
<button class="c-icon-button c-icon-button--major icon-trash"
title="Delete this entry"
@click="deleteEntry">
</button>

View File

@ -0,0 +1,200 @@
/*****************************************************************************
* 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([
'uuid'
], function (
uuid
) {
function isTelemetry(domainObject) {
if (openmct.telemetry.isTelemetryObject(domainObject)
&& domainObject.type !== 'summary-widget'
&& domainObject.type !== 'example.imagery') {
return true;
} else {
return false;
}
}
function migrateDisplayLayout(domainObject, childObjects) {
const DEFAULT_GRID_SIZE = [32, 32];
let migratedObject = Object.assign({}, domainObject);
let panels = migratedObject.configuration.layout.panels;
let items = [];
Object.keys(panels).forEach(key => {
let panel = panels[key];
let domainObject = childObjects[key];
if (isTelemetry(domainObject)) {
items.push({
width: panel.dimensions[0],
height: panel.dimensions[1],
x: panel.position[0],
y: panel.position[1],
useGrid: true,
identifier: domainObject.identifier,
id: uuid(),
type: 'telemetry-view',
displayMode: 'all',
value: openmct.telemetry.getMetadata(domainObject).getDefaultDisplayValue(),
stroke: "transparent",
fill: "",
color: "",
size: "13px"
});
} else {
items.push({
width: panel.dimensions[0],
height: panel.dimensions[1],
x: panel.position[0],
y: panel.position[1],
useGrid: true,
identifier: domainObject.identifier,
id: uuid(),
type: 'subobject-view',
hasFrame: panel.hasFrame
});
}
});
migratedObject.configuration.items = items;
migratedObject.configuration.layoutGrid = migratedObject.layoutGrid || DEFAULT_GRID_SIZE;
delete migratedObject.layoutGrid;
delete migratedObject.configuration.layout;
return migratedObject;
}
function migrateFixedPositionConfiguration(elements, telemetryObjects) {
const DEFAULT_STROKE = "transparent";
const DEFAULT_SIZE = "13px";
const DEFAULT_COLOR = "";
const DEFAULT_FILL = "";
let items = [];
elements.forEach(element => {
let item = {
x: element.x,
y: element.y,
width: element.width,
height: element.height,
useGrid: element.useGrid,
id: uuid()
};
if (element.type === "fixed.telemetry") {
item.type = "telemetry-view";
item.stroke = element.stroke || DEFAULT_STROKE;
item.fill = element.fill || DEFAULT_FILL;
item.color = element.color || DEFAULT_COLOR;
item.size = element.size || DEFAULT_SIZE;
item.identifier = telemetryObjects[element.id].identifier;
item.displayMode = element.titled ? 'all' : 'value';
item.value = openmct.telemetry.getMetadata(telemetryObjects[element.id]).getDefaultDisplayValue();
} else if (element.type === 'fixed.box') {
item.type = "box-view";
item.stroke = element.stroke || DEFAULT_STROKE;
item.fill = element.fill || DEFAULT_FILL;
} else if (element.type === 'fixed.line') {
item.type = "line-view";
item.x2 = element.x2;
item.y2 = element.y2;
item.stroke = element.stroke || DEFAULT_STROKE;
delete item.height;
delete item.width;
} else if (element.type === 'fixed.text') {
item.type = "text-view";
item.text = element.text;
item.stroke = element.stroke || DEFAULT_STROKE;
item.fill = element.fill || DEFAULT_FILL;
item.color = element.color || DEFAULT_COLOR;
item.size = element.size || DEFAULT_SIZE;
} else if (element.type === 'fixed.image') {
item.type = "image-view";
item.url =element.url;
item.stroke = element.stroke || DEFAULT_STROKE;
}
items.push(item);
});
return items;
}
return [
{
check(domainObject) {
return domainObject.type === 'layout' && domainObject.configuration.layout;
},
migrate(domainObject) {
let childObjects = {};
let promises = Object.keys(domainObject.configuration.layout.panels).map(key => {
return openmct.objects.get(key)
.then(object => {
childObjects[key] = object;
});
});
return Promise.all(promises)
.then(function () {
return migrateDisplayLayout(domainObject, childObjects);
});
},
},
{
check(domainObject) {
return domainObject.type === 'telemetry.fixed' && domainObject.configuration['fixed-display'];
},
migrate(domainObject) {
const DEFAULT_GRID_SIZE = [64, 16];
let newLayoutObject = {
identifier: domainObject.identifier,
location: domainObject.location,
name: domainObject.name,
type: "layout"
};
let layoutType = openmct.types.get('layout');
layoutType.definition.initialize(newLayoutObject);
newLayoutObject.composition = domainObject.composition;
newLayoutObject.configuration.layoutGrid = domainObject.layoutGrid || DEFAULT_GRID_SIZE;
let elements = domainObject.configuration['fixed-display'].elements;
let telemetryObjects = {};
let promises = elements.map(element => {
if (element.id) {
return openmct.objects.get(element.id)
.then(object => {
telemetryObjects[element.id] = object;
});
}
});
return Promise.all(promises)
.then(function () {
newLayoutObject.configuration.items =
migrateFixedPositionConfiguration(elements, telemetryObjects);
return newLayoutObject;
});
}
}
];
});

View File

@ -19,40 +19,33 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
@mixin labelValidate($sym, $c) {
> .label {
@include glyphAfter($sym);
&:after {
color: $c;
margin-left: $interiorMargin;
}
import migrations from './Migrations.js'
export default function () {
function needsMigration(domainObject) {
return migrations.some(m => m.check(domainObject));
}
}
mct-form.validates {
.form-row.validates {
> .label {
padding-right: $reqSymbolM; // Keep room for validation element
&:after {
font-size: $reqSymbolFontSize;
}
}
&.invalid,
&.invalid.req { @include labelValidate($glyph-icon-x, $colorFormInvalid); }
&.valid,
&.valid.req { @include labelValidate($glyph-icon-check, $colorFormValid); }
&.req { @include labelValidate($glyph-icon-asterisk, $colorFormRequired); }
function migrateObject(domainObject) {
return migrations.filter(m => m.check(domainObject))[0]
.migrate(domainObject);
}
}
body.desktop .form-row.validates > .label {
&:after {
position: absolute;
right: $interiorMargin;
height: 100%;
line-height: 200%;
}
}
return function (openmct) {
let wrappedFunction = openmct.objects.get;
openmct.objects.get = function migrate(identifier) {
return wrappedFunction.apply(openmct.objects, [identifier])
.then(function (object) {
if (needsMigration(object)) {
migrateObject(object)
.then(newObject => {
openmct.objects.mutate(newObject, 'persisted', Date.now());
return newObject;
});
}
return object;
});
}
};
}

View File

@ -250,7 +250,8 @@ define([
{"has": "telemetry"}
],
"model": {
"composition": []
"composition": [],
"configuration": {}
},
"properties": [],
"priority": 890

View File

@ -136,7 +136,7 @@
<span class="t-object-alert t-alert-unsynced"
title="This plot is not currently displaying the latest data.
Reset Pan/zoom to return to view latest data."></span>
<div class="gl-plot-display-area">
<div class="gl-plot-display-area has-local-controls has-cursor-guides">
<mct-ticks axis="xAxis">
<div class="gl-plot-hash hash-v"
ng-repeat="tick in ticks track by tick.value"
@ -147,7 +147,6 @@
</div>
</mct-ticks>
<mct-ticks axis="yAxis">
<div class="gl-plot-hash hash-h"
ng-repeat="tick in ticks track by tick.value"
@ -155,7 +154,6 @@
</div>
</mct-ticks>
<mct-chart config="config"
series="series"
rectangles="rectangles"
@ -164,23 +162,37 @@
the-y-axis="yAxis">
</mct-chart>
<div class="h-local-controls h-local-controls-overlay-content"
ng-show="plotHistory.length">
<div class="l-btn-set">
<a class="s-button icon-arrow-left"
ng-click="plot.back()"
title="Restore previous pan/zoom">
</a>
<a class="s-button icon-reset"
ng-click="plot.clear()"
title="Reset pan/zoom">
</a>
<div class="gl-plot__local-controls h-local-controls h-local-controls--overlay-content c-local-controls--show-on-hover">
<div class="c-button-set c-button-set--strip-h">
<button class="c-button icon-minus"
ng-click="plot.zoom('out', 0.2)"
title="Zoom out">
</button>
<button class="c-button icon-plus"
ng-click="plot.zoom('in', 0.2)"
title="Zoom in">
</button>
</div>
<div class="c-button-set c-button-set--strip-h"
ng-disabled="!plotHistory.length">
<button class="c-button icon-arrow-left"
ng-click="plot.back()"
title="Restore previous pan/zoom">
</button>
<button class="c-button icon-reset"
ng-click="plot.clear()"
title="Reset pan/zoom">
</button>
</div>
</div>
<span class="t-wait-spinner loading" ng-show="plot.isRequestPending()">
</span>
<!--Cursor guides-->
<div class="c-cursor-guide--v js-cursor-guide--v"
ng-show="plot.cursorGuide">
</div>
<div class="c-cursor-guide--h js-cursor-guide--h"
ng-show="plot.cursorGuide">
</div>
</div>
<div class="gl-plot-axis-area gl-plot-x"

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information.
-->
<div ng-controller="PlotOptionsController">
<ul class="tree c-tree">
<ul class="c-tree">
<h2 title="Plot series display properties in this object">Plot Series</h2>
<li ng-repeat="series in config.series.models">
<div class="c-tree__item menus-to-left">

View File

@ -20,7 +20,7 @@
at runtime from the About dialog for additional information.
-->
<div ng-controller="PlotOptionsController">
<ul class="tree c-tree">
<ul class="c-tree">
<h2 title="Display properties for this object">Plot Series</h2>
<li ng-repeat="series in plotSeries"
ng-controller="PlotSeriesFormController"
@ -87,13 +87,15 @@
ng-style="{ background: series.get('color').asHexString() }">
</span>
</div>
<div class="c-palette--inline c-palette__items" ng-show="toggle.isActive()">
<div class="u-contents" ng-repeat="group in config.series.palette.groups()">
<div class="c-palette__item"
ng-repeat="color in group"
ng-class="{ 'selected': series.get('color').equalTo(color) }"
ng-style="{ background: color.asHexString() }"
ng-click="setColor(color)">
<div class="c-palette c-palette--color">
<div class="c-palette__items" ng-show="toggle.isActive()">
<div class="u-contents" ng-repeat="group in config.series.palette.groups()">
<div class="c-palette__item"
ng-repeat="color in group"
ng-class="{ 'selected': series.get('color').equalTo(color) }"
ng-style="{ background: color.asHexString() }"
ng-click="setColor(color)">
</div>
</div>
</div>
</div>

View File

@ -20,31 +20,34 @@
at runtime from the About dialog for additional information.
-->
<span ng-controller="PlotController as controller"
class="abs holder holder-plot has-control-bar"
ng-class="{
'loading': !!pending
}"
>
class="abs holder holder-plot has-control-bar">
<div class="l-control-bar" ng-show="!controller.hideExportButtons">
<span class="l-btn-set">
<a class="s-button t-export labeled icon-download"
<span class="c-button-set c-button-set--strip-h">
<button class="c-button icon-download"
ng-click="controller.exportPNG()"
title="Export This View's Data as PNG">
PNG
</a>
<a class="s-button t-export labeled"
<span class="c-button__label">PNG</span>
</button>
<button class="c-button"
ng-click="controller.exportJPG()"
title="Export This View's Data as JPG">
JPG
</a>
<span class="c-button__label">JPG</span>
</button>
</span>
<button class="c-button icon-crosshair"
ng-class="{ 'is-active': controller.cursorGuide }"
ng-click="controller.toggleCursorGuide($event)"
title="Toggle cursor guides">
</button>
</div>
<div class="l-view-section">
<div class="c-loading--overlay loading"
ng-show="!!pending"></div>
<mct-plot config="controller.config"
series="series"
the-y-axis="yAxis"
the-x-axis="xAxis">
</mct-plot>
</mct-plot>
</div>
</span>

View File

@ -20,26 +20,29 @@
at runtime from the About dialog for additional information.
-->
<span ng-controller="StackedPlotController as stackedPlot"
class="abs holder holder-plot has-control-bar t-plot-stacked"
ng-class="{
'loading': !!currentRequest.pending
}">
class="abs holder holder-plot has-control-bar t-plot-stacked">
<div class="l-control-bar" ng-show="!stackedPlot.hideExportButtons">
<span class="l-btn-set">
<a class="s-button t-export labeled icon-download"
<span class="c-button-set c-button-set--strip-h">
<button class="c-button icon-download"
ng-click="stackedPlot.exportPNG()"
title="Export This View's Data as PNG">
PNG
</a>
<a class="s-button t-export labeled"
<span class="c-button__label">PNG</span>
</button>
<button class="c-button"
ng-click="stackedPlot.exportJPG()"
title="Export This View's Data as JPG">
JPG
</a>
</span>
<span class="c-button__label">JPG</span>
</button>
</span>
<button class="c-button icon-crosshair"
ng-class="{ 'is-active': stackedPlot.cursorGuide }"
ng-click="stackedPlot.toggleCursorGuide($event)"
title="Toggle cursor guides">
</button>
</div>
<div class="l-view-section">
<div class="c-loading--overlay loading"
ng-show="!!currentRequest.pending"></div>
<div class="gl-plot child-frame"
ng-repeat="telemetryObject in telemetryObjects"
ng-class="{

View File

@ -101,6 +101,19 @@ define([
seriesConfig.identifier.namespace === identifier.namespace;
})[0];
},
/**
* Retrieve the persisted filters for a given identifier.
*/
getPersistedFilters: function (identifier) {
var domainObject = this.get('domainObject'),
keystring = this.openmct.objects.makeKeyString(identifier);
if (!domainObject.configuration || !domainObject.configuration.filters) {
return;
}
return domainObject.configuration.filters[keystring];
},
/**
* Update the domain object with the given value.
*/

View File

@ -84,6 +84,7 @@ define([
this.listenTo(this, 'change:xKey', this.onXKeyChange, this);
this.listenTo(this, 'change:yKey', this.onYKeyChange, this);
this.persistedConfig = options.persistedConfig;
this.filters = options.filters;
Model.apply(this, arguments);
this.onXKeyChange(this.get('xKey'));
@ -139,13 +140,16 @@ define([
* @returns {Promise}
*/
fetch: function (options) {
options = _.extend({}, {size: 1000, strategy: 'minmax'}, options || {});
options = _.extend({}, {size: 1000, strategy: 'minmax', filters: this.filters}, options || {});
if (!this.unsubscribe) {
this.unsubscribe = this.openmct
.telemetry
.subscribe(
this.domainObject,
this.add.bind(this)
this.add.bind(this),
{
filters: this.filters
}
);
}
@ -360,6 +364,19 @@ define([
}
}
},
/**
* Updates filters, clears the plot series, unsubscribes and resubscribes
* @public
*/
updateFiltersAndRefresh: function (updatedFilters) {
this.filters = updatedFilters;
this.reset();
if (this.unsubscribe) {
this.unsubscribe();
delete this.unsubscribe;
}
this.fetch();
}
});

View File

@ -66,6 +66,7 @@ define([
},
addTelemetryObject: function (domainObject, index) {
var seriesConfig = this.plot.getPersistedSeriesConfig(domainObject.identifier);
var filters = this.plot.getPersistedFilters(domainObject.identifier);
var plotObject = this.plot.get('domainObject');
if (!seriesConfig) {
@ -92,7 +93,8 @@ define([
collection: this,
openmct: this.openmct,
persistedConfig: this.plot
.getPersistedSeriesConfig(domainObject.identifier)
.getPersistedSeriesConfig(domainObject.identifier),
filters: filters
}));
},
removeTelemetryObject: function (identifier) {

View File

@ -78,6 +78,7 @@ define([
this.listenTo(this.$canvas, 'mousemove', this.trackMousePosition, this);
this.listenTo(this.$canvas, 'mouseleave', this.untrackMousePosition, this);
this.listenTo(this.$canvas, 'mousedown', this.onMouseDown, this);
this.listenTo(this.$canvas, 'wheel', this.wheelZoom, this);
this.watchForMarquee();
@ -92,6 +93,12 @@ define([
this.$scope.series = this.config.series.models;
this.$scope.legend = this.config.legend;
this.cursorGuideVertical = this.$element[0].querySelector('.js-cursor-guide--v');
this.cursorGuideHorizontal = this.$element[0].querySelector('.js-cursor-guide--h');
this.cursorGuide = false;
this.listenTo(this.$scope, 'cursorguide', this.toggleCursorGuide, this);
this.listenTo(this.$scope, '$destroy', this.destroy, this);
this.listenTo(this.$scope, 'plot:tickWidth', this.onTickWidthChange, this);
this.listenTo(this.$scope, 'plot:highlight:set', this.onPlotHighlightSet, this);
@ -143,6 +150,9 @@ define([
y: this.yScale.invert(this.positionOverElement.y)
};
if (this.cursorGuide) {
this.updateCrosshairs($event);
}
this.highlightValues(this.positionOverPlot.x);
this.updateMarquee();
this.updatePan();
@ -150,6 +160,11 @@ define([
$event.preventDefault();
};
MCTPlotController.prototype.updateCrosshairs = function ($event) {
this.cursorGuideVertical.style.left = ($event.clientX - this.chartElementBounds.x) + 'px';
this.cursorGuideHorizontal.style.top = ($event.clientY - this.chartElementBounds.y) + 'px';
};
MCTPlotController.prototype.trackChartElementBounds = function ($event) {
if ($event.target === this.$canvas[1]) {
this.chartElementBounds = $event.target.getBoundingClientRect();
@ -266,6 +281,102 @@ define([
this.marquee = undefined;
};
MCTPlotController.prototype.zoom = function (zoomDirection, zoomFactor) {
this.freeze();
this.trackHistory();
var currentXaxis = this.$scope.xAxis.get('displayRange'),
currentYaxis = this.$scope.yAxis.get('displayRange'),
xAxisDist= (currentXaxis.max - currentXaxis.min) * zoomFactor,
yAxisDist = (currentYaxis.max - currentYaxis.min) * zoomFactor;
if (zoomDirection === 'in') {
this.$scope.xAxis.set('displayRange', {
min: currentXaxis.min + xAxisDist,
max: currentXaxis.max - xAxisDist
});
this.$scope.yAxis.set('displayRange', {
min: currentYaxis.min + yAxisDist,
max: currentYaxis.max - yAxisDist
});
} else if (zoomDirection === 'out') {
this.$scope.xAxis.set('displayRange', {
min: currentXaxis.min - xAxisDist,
max: currentXaxis.max + xAxisDist
});
this.$scope.yAxis.set('displayRange', {
min: currentYaxis.min - yAxisDist,
max: currentYaxis.max + yAxisDist
});
}
this.$scope.$emit('user:viewport:change:end');
};
MCTPlotController.prototype.wheelZoom = function (event) {
event.preventDefault();
if (!this.positionOverPlot) {
return;
}
this.freeze();
window.clearTimeout(this.stillZooming);
let xDisplayRange = this.$scope.xAxis.get('displayRange'),
yDisplayRange = this.$scope.yAxis.get('displayRange'),
xAxisDist = (xDisplayRange.max - xDisplayRange.min),
yAxisDist = (yDisplayRange.max - yDisplayRange.min),
xDistMouseToMax = xDisplayRange.max - this.positionOverPlot.x,
xDistMouseToMin = this.positionOverPlot.x - xDisplayRange.min,
yDistMouseToMax = yDisplayRange.max - this.positionOverPlot.y,
yDistMouseToMin = this.positionOverPlot.y - yDisplayRange.min,
xAxisMaxDist = xDistMouseToMax / xAxisDist,
xAxisMinDist = xDistMouseToMin / xAxisDist,
yAxisMaxDist = yDistMouseToMax / yAxisDist,
yAxisMinDist = yDistMouseToMin / yAxisDist;
let plotHistoryStep;
if (!plotHistoryStep) {
plotHistoryStep = {
x: xDisplayRange,
y: yDisplayRange
};
}
if (event.wheelDelta < 0) {
this.$scope.xAxis.set('displayRange', {
min: xDisplayRange.min + ((xAxisDist * 0.01) * xAxisMinDist),
max: xDisplayRange.max - ((xAxisDist * 0.01) * xAxisMaxDist)
});
this.$scope.yAxis.set('displayRange', {
min: yDisplayRange.min + ((yAxisDist * 0.01) * yAxisMinDist),
max: yDisplayRange.max - ((yAxisDist * 0.01) * yAxisMaxDist)
});
} else if (event.wheelDelta >= 0) {
this.$scope.xAxis.set('displayRange', {
min: xDisplayRange.min - ((xAxisDist * 0.01) * xAxisMinDist),
max: xDisplayRange.max + ((xAxisDist * 0.01) * xAxisMaxDist)
});
this.$scope.yAxis.set('displayRange', {
min: yDisplayRange.min - ((yAxisDist * 0.01) * yAxisMinDist),
max: yDisplayRange.max + ((yAxisDist * 0.01) * yAxisMaxDist)
});
}
this.stillZooming = window.setTimeout(function () {
this.plotHistory.push(plotHistoryStep);
plotHistoryStep = undefined;
this.$scope.$emit('user:viewport:change:end');
}.bind(this), 250);
};
MCTPlotController.prototype.startPan = function ($event) {
this.trackMousePosition($event);
this.freeze();
@ -362,5 +473,9 @@ define([
this.stopListening();
};
MCTPlotController.prototype.toggleCursorGuide = function ($event) {
this.cursorGuide = !this.cursorGuide;
};
return MCTPlotController;
});

View File

@ -30,7 +30,7 @@ define(
],
function (
html2canvas,
saveAs
{ saveAs }
) {
/**

View File

@ -59,6 +59,7 @@ define([
this.openmct = openmct;
this.objectService = objectService;
this.exportImageService = exportImageService;
this.cursorGuide = false;
$scope.pending = 0;
@ -71,6 +72,14 @@ define([
this.config.series.forEach(this.addSeries, this);
this.followTimeConductor();
this.newStyleDomainObject = $scope.domainObject.useCapability('adapter');
this.filterObserver = this.openmct.objects.observe(
this.newStyleDomainObject,
'configuration.filters',
this.updateFiltersAndResubscribe.bind(this)
);
}
eventHelpers.extend(PlotController.prototype);
@ -154,6 +163,9 @@ define([
clearInterval(this.checkForSize);
delete this.checkForSize;
}
if (this.filterObserver) {
this.filterObserver();
}
};
PlotController.prototype.loadMoreData = function (range, purge) {
@ -244,6 +256,12 @@ define([
xRange.max === xDisplayRange.max);
};
PlotController.prototype.updateFiltersAndResubscribe = function (updatedFilters) {
this.config.series.forEach(function (series) {
series.updateFiltersAndRefresh(updatedFilters[series.keyString]);
});
};
/**
* Export view as JPG.
*/
@ -266,6 +284,11 @@ define([
}.bind(this));
};
PlotController.prototype.toggleCursorGuide = function ($event) {
this.cursorGuide = !this.cursorGuide;
this.$scope.$broadcast('cursorguide', $event);
};
return PlotController;
});

View File

@ -35,6 +35,8 @@ define([
this.$element = $element;
this.exportImageService = exportImageService;
this.$scope = $scope;
this.cursorGuide = false;
$scope.telemetryObjects = [];
@ -145,5 +147,10 @@ define([
}.bind(this));
};
StackedPlotController.prototype.toggleCursorGuide = function ($event) {
this.cursorGuide = !this.cursorGuide;
this.$scope.$broadcast('cursorguide', $event);
};
return StackedPlotController;
});

View File

@ -39,7 +39,9 @@ define([
'./folderView/plugin',
'./flexibleLayout/plugin',
'./tabs/plugin',
'./LADTable/plugin'
'./LADTable/plugin',
'./filters/plugin',
'./objectMigration/plugin'
], function (
_,
UTCTimeSystem,
@ -59,7 +61,9 @@ define([
FolderView,
FlexibleLayout,
Tabs,
LADTable
LADTable,
Filters,
ObjectMigration
) {
var bundleMap = {
LocalStorage: 'platform/persistence/local',
@ -74,26 +78,6 @@ define([
};
});
plugins.Snow = function () {
return function install(openmct) {
openmct.legacyExtension('constants', {
key: "THEME",
value: "snow"
});
import('snow' /* webpackChunkName: "theme-snow" */);
};
};
plugins.Espresso = function () {
return function install(openmct) {
openmct.legacyExtension('constants', {
key: "THEME",
value: "espresso"
});
import('espresso' /* webpackChunkName: "theme-espresso" */);
};
};
plugins.UTCTimeSystem = UTCTimeSystem;
plugins.ImportExport = ImportExport;
@ -174,6 +158,8 @@ define([
plugins.Tabs = Tabs;
plugins.FlexibleLayout = FlexibleLayout;
plugins.LADTable = LADTable;
plugins.Filters = Filters;
plugins.ObjectMigration = ObjectMigration.default;
return plugins;
});

View File

@ -0,0 +1,101 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2019, 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.
*****************************************************************************/
export default class RemoveAction {
constructor(openmct) {
this.name = 'Remove';
this.description = 'Remove this object from its containing object.';
this.cssClass = "icon-trash";
this.openmct = openmct;
}
invoke(objectPath) {
let object = objectPath[0];
let parent = objectPath[1];
this.showConfirmDialog(object).then(() => {
this.removeFromComposition(parent, object);
if (this.inNavigationPath(object)) {
this.navigateTo(objectPath.slice(1));
}
}).catch(() =>{});
}
showConfirmDialog(object) {
return new Promise((resolve, reject) => {
let dialog = this.openmct.overlays.dialog({
title: `Remove ${object.name}`,
iconClass: 'alert',
message: 'Warning! This action will remove this object. Are you sure you want to continue?',
buttons: [
{
label: 'OK',
callback: () => {
dialog.dismiss();
resolve();
}
},
{
label: 'Cancel',
callback: () => {
dialog.dismiss();
reject();
}
}
]
})
})
}
inNavigationPath(object) {
return this.openmct.router.path
.some(objectInPath => this.openmct.objects.areIdsEqual(objectInPath.identifier, object.identifier));
}
navigateTo(objectPath) {
let urlPath = objectPath.reverse()
.map(object => this.openmct.objects.makeKeyString(object.identifier))
.join("/");
window.location.href = '#/browse/' + urlPath;
}
removeFromComposition(parent, child) {
let composition = parent.composition.filter(id =>
!this.openmct.objects.areIdsEqual(id, child.identifier)
);
this.openmct.objects.mutate(parent, 'composition', composition);
}
appliesTo(objectPath) {
let object = objectPath[0];
let objectType = object && this.openmct.types.get(object.type);
let parent = objectPath[1];
let parentType = parent && this.openmct.types.get(parent.type);
return objectType.definition.creatable &&
parentType &&
parentType.definition.creatable &&
Array.isArray(parent.composition);
}
}

View File

@ -1,5 +1,5 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2018, United States Government
* Open MCT, Copyright (c) 2014-2019, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
@ -19,13 +19,10 @@
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/* Styles for the iframe EmbeddedPageController element */
import RemoveAction from "./RemoveAction";
.l-iframe {
iframe {
display: block;
height: 100%;
width: 100%;
border: none;
export default function () {
return function (openmct) {
openmct.contextMenu.registerAction(new RemoveAction(openmct));
}
}

View File

@ -61,7 +61,6 @@ define([
"key": "url",
"name": "URL",
"control": "textfield",
"pattern": "^(ftp|https?)\\:\\/\\/",
"required": false,
"cssClass": "l-input-lg"
},

View File

@ -1,4 +1,4 @@
<a class="e-control s-button s-menu-button menu-element">
<!--a class="e-control s-button s-menu-button menu-element">
<span class="l-click-area"></span>
<span class="t-swatch"></span>
<div class="menu l-palette">
@ -7,4 +7,15 @@
<span class="l-palette-item-label">None</span>
</div>
</div>
</a>
</a-->
<div class="c-ctrl-wrapper">
<button class="c-button--menu c-button--swatched js-button">
<div class="c-swatch t-swatch"></div>
</button>
<div class="c-menu c-palette">
<div class="c-palette__item-none">
<div class="c-palette__item"></div>
</div>
<div class="c-palette__items"></div>
</div>
</div>

View File

@ -1,4 +1,4 @@
<div class="e-control select">
<span>
<select>
</select>
</div>
</span>

View File

@ -1,21 +1,20 @@
<div>
<div class="l-compact-form has-local-controls l-widget-rule s-widget-rule">
<div class="widget-rule-header">
<span class="flex-elem l-widget-thumb-wrapper">
<span class="grippy-holder">
<span class="t-grippy grippy local-control local-controls-hidden"></span>
</span>
<span class="view-control expanded"></span>
<span class="t-widget-thumb widget-thumb">
<span class="widget-label">DEF</span>
</span>
</span>
<span class="flex-elem rule-title">Default Title</span>
<span class="flex-elem rule-description grows">Rule description goes here</span>
<span class="flex-elem local-control local-controls-hidden l-rule-action-buttons-wrapper">
<div class="c-sw-rule">
<div class="c-sw-rule__ui l-compact-form has-local-controls l-widget-rule s-widget-rule">
<div class="c-sw-rule__ui__header widget-rule-header">
<div class="c-sw-rule__grippy-wrapper">
<div class="c-sw-rule__grippy t-grippy local-control local-controls-hidden"></div>
</div>
<div class="c-disclosure-triangle c-disclosure-triangle--expanded is-enabled js-disclosure"></div>
<div class="t-widget-thumb widget-thumb c-sw c-sw--thumb">
<div class="c-sw__icon js-sw__icon"></div>
<div class="c-sw__label js-sw__label"></div>
</div>
<div class="flex-elem rule-title">Default Title</div>
<div class="flex-elem rule-description grows">Rule description goes here</div>
<div class="flex-elem local-control local-controls-hidden l-rule-action-buttons-wrapper">
<a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this rule"></a>
<a class="s-icon-button icon-trash t-delete" title="Delete this rule"></a>
</span>
</div>
</div>
<div class="widget-rule-content expanded">
<ul>
@ -28,7 +27,7 @@
<li class="connects-to-previous">
<label>Label:</label>
<span class="controls t-label-input">
<input class="e-control t-rule-label-input" type="text" />
<input class="t-rule-label-input" type="text" />
</span>
</li>
<li class="connects-to-previous">
@ -40,26 +39,25 @@
</li>
<li class="connects-to-previous">
<label>Style:</label>
<span class="controls t-style-input">
</span>
<span class="controls t-style-input"></span>
</li>
</ul>
<ul class="t-widget-rule-config">
<li>
<label>Trigger when</label>
<span class="controls">
<div class="e-control select">
<select class="t-trigger">
<option value="any">any condition is met</option>
<option value="all">all conditions are met</option>
</select>
</div>
<select class="t-trigger">
<option value="any">any condition is met</option>
<option value="all">all conditions are met</option>
</select>
</span>
</li>
<li>
<label></label>
<span class="controls">
<a class="e-control s-button labeled add-condition icon-plus">Add Condition</a>
<button class="c-button add-condition icon-plus">
<span class="c-button__label">Add Condition</span>
</button>
</span>
</li>
</ul>

View File

@ -8,7 +8,9 @@
</div>
<div class="t-test-data-config w-widget-test-data-items">
<div class="holder add-rule-button-wrapper align-right">
<a id="addRule" class="e-control s-button major labeled add-test-condition icon-plus">Add Test Value</a>
<button id="addRule" class="c-button c-button--major add-test-condition icon-plus">
<span class="c-button__label">Add Test Value</span>
</button>
</div>
</div>
</div>

View File

@ -1,22 +1,31 @@
<div class="w-summary-widget s-status-no-data">
<a id="widget" class="t-summary-widget l-summary-widget s-summary-widget labeled">
<span id="widgetLabel" class="label widget-label">Default Static Name</span>
<div class="c-sw-edit w-summary-widget s-status-no-data">
<a id="widget" class="t-summary-widget l-summary-widget s-summary-widget labeled c-sw">
<div id="widgetIcon" class="c-sw__icon js-sw__icon"></div>
<div id="widgetLabel" class="label widget-label c-sw__label js-sw__label">Default Static Name</div>
</a>
<div class="holder flex-elem t-message-inline l-message message-severity-alert t-message-widget-no-data">
<div class="c-summary-widget__message holder flex-elem t-message-inline c-message message-severity-alert t-message-widget-no-data">
<div class="w-message-contents l-message-body-only">
<div class="message-body">
You must add at least one telemetry object to edit this widget.
</div>
</div>
</div>
<div class="holder l-flex-col l-flex-accordion flex-elem grows widget-edit-holder expanded-widget-test-data expanded-widget-rules">
<div class="section-header"><span class="view-control t-view-control-test-data expanded"></span>Test Data Values</div>
<div class="widget-test-data flex-accordion-holder"></div>
<div class="section-header"><span class="view-control t-view-control-rules expanded"></span>Rules</div>
<div class="holder widget-rules-wrapper flex-elem expanded">
<div id="ruleArea" class="widget-rules"></div>
<div class="c-sw-edit__ui holder l-flex-accordion flex-elem grows widget-edit-holder expanded-widget-test-data expanded-widget-rules">
<div class="c-sw-edit__ui__header">
<span class="c-disclosure-triangle c-disclosure-triangle--expanded is-enabled t-view-control-test-data"></span>
<span class="c-sw-edit__ui__header-label">Test Data Values</span>
</div>
<div class="c-sw-edit__ui__test-data widget-test-data flex-accordion-holder"></div>
<div class="c-sw-edit__ui__header">
<span class="c-disclosure-triangle c-disclosure-triangle--expanded is-enabled t-view-control-rules"></span>
<span class="c-sw-edit__ui__header-label">Rules</span>
</div>
<div class="c-sw-editui__rules-wrapper holder widget-rules-wrapper flex-elem expanded">
<div id="ruleArea" class="c-sw-editui__rules widget-rules"></div>
<div class="holder add-rule-button-wrapper align-right">
<a id="addRule" class="s-button major labeled add-rule-button icon-plus">Add Rule</a>
<button id="addRule" class="c-button c-button--major add-rule-button icon-plus">
<span class="c-button__label">Add Rule</span>
</button>
</div>
</div>
</div>

View File

@ -32,6 +32,7 @@ define([
function Rule(ruleConfig, domainObject, openmct, conditionManager, widgetDnD, container) {
eventHelpers.extend(this);
var self = this;
const THUMB_ICON_CLASS = 'c-sw__icon js-sw__icon';
this.config = ruleConfig;
this.domainObject = domainObject;
@ -50,11 +51,12 @@ define([
this.duplicate = this.duplicate.bind(this);
this.thumbnail = $('.t-widget-thumb', this.domElement);
this.thumbnailLabel = $('.widget-label', this.domElement);
this.thumbnailIcon = $('.js-sw__icon', this.domElement);
this.thumbnailLabel = $('.c-sw__label', this.domElement);
this.title = $('.rule-title', this.domElement);
this.description = $('.rule-description', this.domElement);
this.trigger = $('.t-trigger', this.domElement);
this.toggleConfigButton = $('.view-control', this.domElement);
this.toggleConfigButton = $('.js-disclosure', this.domElement);
this.configArea = $('.widget-rule-content', this.domElement);
this.grippy = $('.t-grippy', this.domElement);
this.conditionArea = $('.t-widget-rule-config', this.domElement);
@ -79,7 +81,7 @@ define([
this.colorInputs = {
'background-color': new ColorPalette('icon-paint-bucket', container),
'border-color': new ColorPalette('icon-line-horz', container),
'color': new ColorPalette('icon-T', container)
'color': new ColorPalette('icon-font', container)
};
this.colorInputs.color.toggleNullOption();
@ -92,7 +94,7 @@ define([
function onIconInput(icon) {
self.config.icon = icon;
self.updateDomainObject('icon', icon);
self.thumbnailLabel.removeClass().addClass('label widget-label ' + icon);
self.thumbnailIcon.removeClass().addClass(THUMB_ICON_CLASS + ' ' + icon);
self.eventEmitter.emit('change');
}
@ -168,7 +170,7 @@ define([
*/
function toggleConfig() {
self.configArea.toggleClass('expanded');
self.toggleConfigButton.toggleClass('expanded');
self.toggleConfigButton.toggleClass('c-disclosure-triangle--expanded');
self.config.expanded = !self.config.expanded;
}
@ -179,7 +181,7 @@ define([
});
// Initialize thumbs when first loading
this.thumbnailLabel.removeClass().addClass('label widget-label ' + self.config.icon);
this.thumbnailIcon.removeClass().addClass(THUMB_ICON_CLASS + ' ' + self.config.icon);
this.thumbnailLabel.html(self.config.label);
Object.keys(this.colorInputs).forEach(function (inputKey) {
@ -227,7 +229,7 @@ define([
if (!this.config.expanded) {
this.configArea.removeClass('expanded');
this.toggleConfigButton.removeClass('expanded');
this.toggleConfigButton.removeClass('c-disclosure-triangle--expanded');
}
if (this.domainObject.configuration.ruleOrder.length === 2) {

View File

@ -22,8 +22,8 @@ define([
//default css configuration for new rules
var DEFAULT_PROPS = {
'color': '#ffffff',
'background-color': '#38761d',
'color': '#cccccc',
'background-color': '#666666',
'border-color': 'rgba(0,0,0,0)'
};
@ -74,15 +74,12 @@ define([
this.show = this.show.bind(this);
this.destroy = this.destroy.bind(this);
this.addRule = this.addRule.bind(this);
this.onEdit = this.onEdit.bind(this);
this.addHyperlink(domainObject.url, domainObject.openNewTab);
this.watchForChanges(openmct, domainObject);
var id = objectUtils.makeKeyString(this.domainObject.identifier),
self = this,
oldDomainObject,
statusCapability;
self = this;
/**
* Toggles the configuration area for test data in the view
@ -90,7 +87,7 @@ define([
*/
function toggleTestData() {
self.outerWrapper.toggleClass('expanded-widget-test-data');
self.toggleTestDataControl.toggleClass('expanded');
self.toggleTestDataControl.toggleClass('c-disclosure-triangle--expanded');
}
this.listenTo(this.toggleTestDataControl, 'click', toggleTestData);
@ -100,22 +97,12 @@ define([
*/
function toggleRules() {
self.outerWrapper.toggleClass('expanded-widget-rules');
self.toggleRulesControl.toggleClass('expanded');
self.toggleRulesControl.toggleClass('c-disclosure-triangle--expanded');
}
this.listenTo(this.toggleRulesControl, 'click', toggleRules);
openmct.$injector.get('objectService')
.getObjects([id])
.then(function (objs) {
oldDomainObject = objs[id];
statusCapability = oldDomainObject.getCapability('status');
self.editListenerUnsubscribe = statusCapability.listen(self.onEdit);
if (statusCapability.get('editing')) {
self.onEdit(['editing']);
} else {
self.onEdit([]);
}
});
.getObjects([id]);
}
/**
@ -172,7 +159,6 @@ define([
});
this.refreshRules();
this.updateWidget();
this.updateView();
this.listenTo(this.addRuleButton, 'click', this.addRule);
this.conditionManager.on('receiveTelemetry', this.executeRules, this);
@ -196,37 +182,6 @@ define([
this.stopListening();
};
/**
* A callback function for the Open MCT status capability listener. If the
* view representing the domain object is in edit mode, update the internal
* state and widget view accordingly.
* @param {string[]} status an array containing the domain object's current status
*/
SummaryWidget.prototype.onEdit = function (status) {
if (status && status.includes('editing')) {
this.editing = true;
} else {
this.editing = false;
}
this.updateView();
};
/**
* If this view is currently in edit mode, show all rule configuration interfaces.
* Otherwise, hide them.
*/
SummaryWidget.prototype.updateView = function () {
if (this.editing) {
this.ruleArea.show();
this.testDataArea.show();
this.addRuleButton.show();
} else {
this.ruleArea.hide();
this.testDataArea.hide();
this.addRuleButton.hide();
}
};
/**
* Update the view from the current rule configuration and order
*/
@ -260,11 +215,14 @@ define([
* Update the widget's appearance from the configuration of the active rule
*/
SummaryWidget.prototype.updateWidget = function () {
const WIDGET_CLASS = 'c-sw js-sw',
WIDGET_LABEL_CLASS = 'c-sw__label js-sw__label',
WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon';
var activeRule = this.rulesById[this.activeId];
this.applyStyle($('#widget', this.domElement), activeRule.getProperty('style'));
$('#widget', this.domElement).prop('title', activeRule.getProperty('message'));
$('#widgetLabel', this.domElement).html(activeRule.getProperty('label'));
$('#widgetLabel', this.domElement).removeClass().addClass('label widget-label c-summary-widget__label ' + activeRule.getProperty('icon'));
$('#widgetIcon', this.domElement).removeClass().addClass(WIDGET_ICON_CLASS + ' ' + activeRule.getProperty('icon'));
};
/**

View File

@ -36,11 +36,11 @@ function (
var domElement = $(this.palette.getDOM()),
self = this;
$('.s-menu-button', domElement).addClass('t-color-palette-menu-button');
$('.c-button--menu', domElement).addClass('c-button--swatched');
$('.t-swatch', domElement).addClass('color-swatch');
$('.l-palette', domElement).addClass('l-color-palette');
$('.c-palette', domElement).addClass('c-palette--color');
$('.s-palette-item', domElement).each(function () {
$('.c-palette__item', domElement).each(function () {
var elem = this;
$(elem).css('background-color', elem.dataset.item);
});

View File

@ -51,11 +51,11 @@ define([
var domElement = $(this.palette.getDOM()),
self = this;
$('.s-menu-button', domElement).addClass('t-icon-palette-menu-button');
$('.c-button--menu', domElement).addClass('c-button--swatched');
$('.t-swatch', domElement).addClass('icon-swatch');
$('.l-palette', domElement).addClass('l-icon-palette');
$('.c-palette', domElement).addClass('c-palette--icon');
$('.s-palette-item', domElement).each(function () {
$('.c-palette-item', domElement).each(function () {
var elem = this;
$(elem).addClass(elem.dataset.item);
});

View File

@ -30,33 +30,34 @@ define([
this.domElement = $(paletteTemplate);
this.itemElements = {
nullOption: $('.l-option-row .s-palette-item', this.domElement)
nullOption: $('.c-palette__item-none .c-palette__item', this.domElement)
};
this.eventEmitter = new EventEmitter();
this.supportedCallbacks = ['change'];
this.value = this.items[0];
this.nullOption = ' ';
this.button = $('.js-button', this.domElement);
this.menu = $('.c-menu', this.domElement);
this.hideMenu = this.hideMenu.bind(this);
self.domElement.addClass(this.cssClass);
self.button.addClass(this.cssClass);
self.setNullOption(this.nullOption);
$('.l-palette-row', self.domElement).after('<div class = "l-palette-row"> </div>');
self.items.forEach(function (item) {
var itemElement = $('<div class = "l-palette-item s-palette-item"' +
' data-item = ' + item + '> </div>');
$('.l-palette-row:last-of-type', self.domElement).append(itemElement);
var itemElement = $('<div class = "c-palette__item ' + item + '"' +
' data-item = ' + item + '></div>');
$('.c-palette__items', self.domElement).append(itemElement);
self.itemElements[item] = itemElement;
});
$('.menu', self.domElement).hide();
$('.c-menu', self.domElement).hide();
this.listenTo($(document), 'click', this.hideMenu);
this.listenTo($('.l-click-area', self.domElement), 'click', function (event) {
this.listenTo($('.js-button', self.domElement), 'click', function (event) {
event.stopPropagation();
$('.menu', self.container).hide();
$('.menu', self.domElement).show();
$('.c-menu', self.container).hide();
$('.c-menu', self.domElement).show();
});
/**
@ -69,10 +70,10 @@ define([
var elem = event.currentTarget,
item = elem.dataset.item;
self.set(item);
$('.menu', self.domElement).hide();
$('.c-menu', self.domElement).hide();
}
this.listenTo($('.s-palette-item', self.domElement), 'click', handleItemClick);
this.listenTo($('.c-palette__item', self.domElement), 'click', handleItemClick);
}
/**
@ -90,7 +91,7 @@ define([
};
Palette.prototype.hideMenu = function () {
$('.menu', this.domElement).hide();
$('.c-menu', this.domElement).hide();
};
/**
@ -139,8 +140,8 @@ define([
* Update the view assoicated with the currently selected item
*/
Palette.prototype.updateSelected = function (item) {
$('.s-palette-item', this.domElement).removeClass('selected');
this.itemElements[item].addClass('selected');
$('.c-palette__item', this.domElement).removeClass('is-selected');
this.itemElements[item].addClass('is-selected');
if (item === 'nullOption') {
$('.t-swatch', this.domElement).addClass('no-selection');
} else {
@ -162,7 +163,7 @@ define([
* Hides the 'no selection' option to be hidden in the view if it doesn't apply
*/
Palette.prototype.toggleNullOption = function () {
$('.l-option-row', this.domElement).toggle();
$('.c-palette__item-none', this.domElement).toggle();
};
return Palette;

View File

@ -3,6 +3,8 @@ define([
], function (
summaryWidgetTemplate
) {
const WIDGET_ICON_CLASS = 'c-sw__icon js-sw__icon';
function SummaryWidgetView(domainObject, openmct) {
this.openmct = openmct;
this.domainObject = domainObject;
@ -18,7 +20,7 @@ define([
this.widget.title = datum.message;
this.label.title = datum.message;
this.label.innerHTML = datum.ruleLabel;
this.label.className = 'label widget-label c-summary-widget__label ' + datum.icon;
this.icon.className = WIDGET_ICON_CLASS + ' ' + datum.icon;
};
SummaryWidgetView.prototype.render = function () {
@ -29,7 +31,8 @@ define([
this.container.innerHTML = summaryWidgetTemplate;
this.widget = this.container.querySelector('a');
this.label = this.container.querySelector('.widget-label');
this.icon = this.container.querySelector('#widgetIcon');
this.label = this.container.querySelector('.js-sw__label');
if (this.domainObject.url) {

View File

@ -1,5 +1,4 @@
<div class="w-summary-widget s-status-no-data c-widget-wrapper u-contents">
<a class="t-summary-widget c-button c-summary-widget u-links u-fills-container">
<span class="label widget-label c-summary-widget__label">Loading...</span>
</a>
</div>
<a class="t-summary-widget c-summary-widget js-sw u-links u-fills-container">
<div id="widgetIcon" class="c-sw__icon js-sw__icon"></div>
<div id="widgetLabel" class="c-sw__label js-sw__label">Loading...</div>
</a>

View File

@ -46,7 +46,7 @@ define([
view: function (selection) {
let component;
let domainObject = selection[0].context.item;
const tableConfiguration = new TelemetryTableConfiguration(domainObject, openmct);
let tableConfiguration = new TelemetryTableConfiguration(domainObject, openmct);
return {
show: function (element) {
component = new Vue({
@ -64,6 +64,7 @@ define([
destroy: function () {
component.$destroy();
component = undefined;
tableConfiguration = undefined;
}
}
},

View File

@ -53,13 +53,20 @@ define([
this.isTelemetryObject = this.isTelemetryObject.bind(this);
this.refreshData = this.refreshData.bind(this);
this.requestDataFor = this.requestDataFor.bind(this);
this.updateFilters = this.updateFilters.bind(this);
this.buildOptionsFromConfiguration = this.buildOptionsFromConfiguration.bind(this);
this.filterObserver = undefined;
this.createTableRowCollections();
openmct.time.on('bounds', this.refreshData);
openmct.time.on('timeSystem', this.refreshData);
}
initialize() {
if (this.domainObject.type === 'table') {
this.filterObserver = this.openmct.objects.observe(this.domainObject, 'configuration.filters', this.updateFilters);
this.loadComposition();
} else {
this.addTelemetryObject(this.domainObject);
@ -68,19 +75,24 @@ define([
createTableRowCollections() {
this.boundedRows = new BoundedTableRowCollection(this.openmct);
//By default, sort by current time system, ascending.
this.filteredRows = new FilteredTableRowCollection(this.boundedRows);
this.filteredRows.sortBy({
//Fetch any persisted default sort
let sortOptions = this.configuration.getConfiguration().sortOptions;
//If no persisted sort order, default to sorting by time system, ascending.
sortOptions = sortOptions || {
key: this.openmct.time.timeSystem().key,
direction: 'asc'
});
};
this.filteredRows.sortBy(sortOptions);
}
loadComposition() {
this.tableComposition = this.openmct.composition.get(this.domainObject);
if (this.tableComposition !== undefined) {
this.tableComposition.load().then((composition) => {
composition = composition.filter(this.isTelemetryObject);
this.configuration.addColumnsForAllObjects(composition);
@ -101,6 +113,15 @@ define([
this.emit('object-added', telemetryObject);
}
updateFilters() {
this.filteredRows.clear();
this.boundedRows.clear();
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
this.telemetryObjects.forEach(this.requestDataFor.bind(this));
this.telemetryObjects.forEach(this.subscribeTo.bind(this));
}
removeTelemetryObject(objectIdentifier) {
this.configuration.removeColumnsForObject(objectIdentifier, true);
let keyString = this.openmct.objects.makeKeyString(objectIdentifier);
@ -113,8 +134,8 @@ define([
requestDataFor(telemetryObject) {
this.incrementOutstandingRequests();
return this.openmct.telemetry.request(telemetryObject)
let requestOptions = this.buildOptionsFromConfiguration(telemetryObject);
return this.openmct.telemetry.request(telemetryObject, requestOptions)
.then(telemetryData => {
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
let columnMap = this.getColumnMapForObject(keyString);
@ -151,6 +172,7 @@ define([
if (!isTick) {
this.filteredRows.clear();
this.boundedRows.clear();
this.boundedRows.sortByTimeSystem(this.openmct.time.timeSystem());
this.telemetryObjects.forEach(this.requestDataFor);
}
}
@ -165,29 +187,53 @@ define([
}
subscribeTo(telemetryObject) {
let subscribeOptions = this.buildOptionsFromConfiguration(telemetryObject);
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier);
let columnMap = this.getColumnMapForObject(keyString);
let limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
this.subscriptions[keyString] = this.openmct.telemetry.subscribe(telemetryObject, (datum) => {
this.boundedRows.add(new TelemetryTableRow(datum, columnMap, keyString, limitEvaluator));
});
}, subscribeOptions);
}
isTelemetryObject(domainObject) {
return domainObject.hasOwnProperty('telemetry');
}
buildOptionsFromConfiguration(telemetryObject) {
let keyString = this.openmct.objects.makeKeyString(telemetryObject.identifier),
filters = this.domainObject.configuration &&
this.domainObject.configuration.filters &&
this.domainObject.configuration.filters[keyString];
return {filters} || {};
}
unsubscribe(keyString) {
this.subscriptions[keyString]();
delete this.subscriptions[keyString];
}
sortBy(sortOptions) {
this.filteredRows.sortBy(sortOptions);
if (this.openmct.editor.isEditing()) {
let configuration = this.configuration.getConfiguration();
configuration.sortOptions = sortOptions;
this.configuration.updateConfiguration(configuration);
}
}
destroy() {
this.boundedRows.destroy();
this.filteredRows.destroy();
Object.keys(this.subscriptions).forEach(this.unsubscribe, this);
this.openmct.time.off('bounds', this.refreshData);
this.openmct.time.on('timeSystem', this.refreshData);
if (this.filterObserver) {
this.filterObserver();
}
if (this.tableComposition !== undefined) {
this.tableComposition.off('add', this.addTelemetryObject);

View File

@ -49,7 +49,7 @@ define(function () {
getFormattedValue(telemetryDatum) {
let formattedValue = this.formatter.format(telemetryDatum);
if (typeof formattedValue !== 'string') {
if (formattedValue !== undefined && typeof formattedValue !== 'string') {
return formattedValue.toString();
} else {
return formattedValue;

View File

@ -44,7 +44,7 @@ define([], function () {
getRowLimitClass() {
if (!this.rowLimitClass) {
let limitEvaluation = this.limitEvaluator.evaluate(this.datum);
let limitEvaluation = this.limitEvaluator.evaluate(this.datum);
this.rowLimitClass = limitEvaluation && limitEvaluation.cssClass;
}
return this.rowLimitClass;

View File

@ -41,7 +41,6 @@ define(
this.bounds = this.bounds.bind(this)
this.sortByTimeSystem(openmct.time.timeSystem());
openmct.time.on('timeSystem', this.sortByTimeSystem);
this.lastBounds = openmct.time.bounds();
openmct.time.on('bounds', this.bounds);
@ -51,8 +50,8 @@ define(
// Insert into either in-bounds array, or the future buffer.
// Data in the future buffer will be re-evaluated for possible
// insertion on next bounds change
let beforeStartOfBounds = item.datum[this.sortOptions.key] < this.lastBounds.start;
let afterEndOfBounds = item.datum[this.sortOptions.key] > this.lastBounds.end;
let beforeStartOfBounds = this.parseTime(item.datum[this.sortOptions.key]) < this.lastBounds.start;
let afterEndOfBounds = this.parseTime(item.datum[this.sortOptions.key]) > this.lastBounds.end;
if (!afterEndOfBounds && !beforeStartOfBounds) {
return super.addOne(item);
@ -64,6 +63,12 @@ define(
sortByTimeSystem(timeSystem) {
this.sortBy({key: timeSystem.key, direction: 'asc'});
let formatter = this.openmct.telemetry.getValueFormatter({
key: timeSystem.key,
source: timeSystem.key,
format: timeSystem.timeFormat
});
this.parseTime = formatter.parse.bind(formatter);
this.futureBuffer.sortBy({key: timeSystem.key, direction: 'asc'});
}
@ -131,7 +136,6 @@ define(
}
destroy() {
this.openmct.time.off('timeSystem', this.sortByTimeSystem);
this.openmct.time.off('bounds', this.bounds);
}
}

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