Compare commits

..

15 Commits

Author SHA1 Message Date
akhenry
57abac96b2 Implement basic plot synchronization 2018-02-24 12:03:38 -08:00
Pete Richards
204f6be9d0 [Tests] Exclude plot from coverage requirement
Per discussions about backfilling tests with conversion away from
angular, removing plot plugin from coverage requirement so as to
allow build to complete.

https://github.com/nasa/openmct/pull/1557#issuecomment-364239059
2018-02-19 17:20:35 -08:00
Pete Richards
c988c6c43b install plot in openmct instead of mct type 2018-02-19 17:15:00 -08:00
Pete Richards
c143475f3e Add and update copyright notices 2018-02-19 17:02:27 -08:00
Pete Richards
b248f57daa [ExportImage] Restore 3669e776a9
Restore changes from 3669e776a9,
such that exported image color can be overridden if desired.
2018-02-19 16:46:12 -08:00
Pete Richards
f4e8c5ecf6 [Style] make code style compliant 2018-02-19 16:38:15 -08:00
Charles Hacskaylo
11ef7c0ccd [Frontend] Added missing flex declaration
Fixes #1557
- Fixes disappearing plot chart area when legend is hidden;
2018-02-19 16:38:15 -08:00
Charles Hacskaylo
9c125e0454 [Frontend] Fixed indiscriminate selector in Style Guide CSS
Fixes #1557
- Was fubaring palette example in Style Guide
2018-02-19 16:38:15 -08:00
Charles Hacskaylo
439a654f6d [Frontend] More fixes for Sum Widgets and FP
Fixes #1557
- Fixed Palettes in Summary Widgets and Fixed Position
- Renamed .l-inline-color-palette to .l-inline-palette
2018-02-19 16:38:15 -08:00
Charles Hacskaylo
9d32a3f1c3 [Frontend] Fixed inline palette in plots series config
Fixes #1557
- Brought back missing .inline-color-palette class
- Tweaks in _palette.scss

TODO: fix palettes in Summary Widgets
2018-02-19 16:38:15 -08:00
Pete Richards
705e25df8a correct pallette reference 2018-02-19 16:38:15 -08:00
Pete Richards
787d6887dc Add LAD support and state type to generators 2018-02-19 16:38:15 -08:00
Pete Richards
1d516240a8 Telemetry API Updates 2018-02-19 16:38:15 -08:00
Pete Richards
90ee0a309f New Plot
* Remove Old Plot Code
* Remove Telemetry Panel Type
* UTCFormat.parse: passthrough numbers
* TelemetryAPI: default request arguments
* TelemetryAPI: fix enum formatting

pass value instead of trying to create datum
Set max precision to avoid errors
Install plot by default
Make sure plots are always plots
Set default yKey on series
Use new time API
Update hints
proper id formats
Only redraw on change
Set x axis on bounds change

while MCTChart can handle inserting points in the middle of the
chart series, this is not performant with a large insert volume.

So, when merging historical requests with existing data, do a batch
sort and uniq check up front and then reset the data such that MCTChart
only has to do appends.  This is significantly faster than doing
large volumes of inserts.

[CSS] Plot Styles

Moved .view-control out of tree.scss and
generalized as a control prior to implementing
it as the expand/collapse control in plot legends
for #584 and #618.

Major layout changes to enable flex approach for
legend and plot display area; table styling for
expanded legend; legend on top, bottom, right
and left support added but still a bunch to do.

Style and markup normalization for legend elements.

Significant mods to layout in Inspector while
editing, using new .compact-form class;
mod to ul.tree li to allow .compact-form layout
class within tree scope;

Significant layout cleanup, mainly for stacked plots
and expanded legend;
Config options hide/show controls fully ported from
Pete's original version;

New 12px crosshair glyph added;
'hover-value-enabled' markup and class refinements;

Generalized .t-alert-unsynced, moved to _icons.scss;
Moved .t-object-alert to proper location in plot markup;
Added new .t-stacked-plot class;

Moved location of padding for .hover-value-enabled
elements

Minor refactor to use explicit legend-collapsed and -expanded CSS
classes, replacing ng-show statements;
Removed ng-style that was setting column widths based on char
count of column name;

lobal rename of `compact-form` to `inspector-config`;
Applied same to plot-options-browse.html for tree items;

Renamed `plot-legend-on-*` selector;
Fixed layout for plot-legend-hidden, involved
returning `plot-wrapper-axis-and-display-area` to
use position: absolute;

Fixes #584
Fixes #618
Fixes #1590

Fix for renamed view-control classes

Fixes #1795
Also removed .has-children from plot-options html files;

Don't try and recreate datums

Clear highlights when leaving plot

Simplify Plot Series Interface

Update Plot Options to match new Inspectors

Use edit context to support subobject editing
2018-02-19 16:38:15 -08:00
Pete Richards
72d05283da Remove Old Plot 2018-02-19 16:37:29 -08:00
95 changed files with 535 additions and 5224 deletions

View File

@@ -25,4 +25,4 @@
"html2canvas": "^0.4.1",
"moment-timezone": "^0.5.13"
}
}
}

View File

@@ -1,108 +0,0 @@
define([
'lodash'
], function (
_
) {
var METADATA_BY_TYPE = {
'generator': {
values: [
{
key: "name",
name: "Name"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "yesterday",
name: "Yesterday",
format: "utc",
hints: {
domain: 2
}
},
{
key: "sin",
name: "Sine",
hints: {
range: 1
}
},
{
key: "cos",
name: "Cosine",
hints: {
range: 2
}
}
]
},
'example.state-generator': {
values: [
{
key: "name",
name: "Name"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "state",
source: "value",
name: "State",
format: "enum",
enumerations: [
{
value: 0,
string: "OFF"
},
{
value: 1,
string: "ON"
}
],
hints: {
range: 1
}
},
{
key: "value",
name: "Value",
hints: {
range: 2
}
}
]
}
}
function GeneratorMetadataProvider() {
}
GeneratorMetadataProvider.prototype.appliesTo = function (domainObject) {
return METADATA_BY_TYPE.hasOwnProperty(domainObject.type);
};
GeneratorMetadataProvider.prototype.getMetadata = function (domainObject) {
return _.extend(
{},
domainObject.telemetry,
METADATA_BY_TYPE[domainObject.type]
);
};
return GeneratorMetadataProvider;
});

View File

@@ -24,13 +24,11 @@
define([
"./GeneratorProvider",
"./SinewaveLimitCapability",
"./StateGeneratorProvider",
"./GeneratorMetadataProvider"
"./StateGeneratorProvider"
], function (
GeneratorProvider,
SinewaveLimitCapability,
StateGeneratorProvider,
GeneratorMetadataProvider
StateGeneratorProvider
) {
var legacyExtensions = {
@@ -72,7 +70,47 @@ define([
],
initialize: function (object) {
object.telemetry = {
duration: 5
duration: 5,
values: [
{
key: "name",
name: "Name"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "state",
source: "value",
name: "State",
format: "enum",
enumerations: [
{
value: 0,
string: "OFF"
},
{
value: 1,
string: "ON"
}
],
hints: {
range: 1
}
},
{
key: "value",
name: "Value",
hints: {
range: 2
}
}
]
}
}
});
@@ -152,13 +190,48 @@ define([
amplitude: 1,
offset: 0,
dataRateInHz: 1,
phase: 0
phase: 0,
values: [
{
key: "name",
name: "Name"
},
{
key: "utc",
name: "Time",
format: "utc",
hints: {
domain: 1
}
},
{
key: "yesterday",
name: "Yesterday",
format: "utc",
hints: {
domain: 2
}
},
{
key: "sin",
name: "Sine",
hints: {
range: 1
}
},
{
key: "cos",
name: "Cosine",
hints: {
range: 2
}
}
]
};
}
});
openmct.telemetry.addProvider(new GeneratorProvider());
openmct.telemetry.addMetadataProvider(new GeneratorMetadataProvider());
};
});

View File

@@ -0,0 +1,146 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2017, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/*global define*/
define([
'legacyRegistry',
'../../platform/commonUI/browse/src/InspectorRegion',
'../../platform/commonUI/regions/src/Region'
], function (
legacyRegistry,
InspectorRegion,
Region
) {
"use strict";
/**
* Add a 'plot options' region part to the inspector region for the
* Telemetry Plot type only. {@link InspectorRegion} is a default region
* implementation that is added automatically to all types. In order to
* customize what appears in the inspector region, you can start from a
* blank slate by using Region, or customize the default inspector
* region by using {@link InspectorRegion}.
*/
var plotInspector = new InspectorRegion(),
/**
* Two region parts are defined here. One that appears only in browse
* mode, and one that appears only in edit mode. For not they both point
* to the same representation, but a different key could be used here to
* include a customized representation for edit mode.
*/
plotOptionsBrowseRegion = new Region({
name: "plot-options",
title: "Plot Options",
modes: ['browse'],
content: {
key: "plot-options-browse"
}
}),
plotOptionsEditRegion = new Region({
name: "plot-options",
title: "Plot Options",
modes: ['edit'],
content: {
key: "plot-options-browse"
}
});
/**
* Both parts are added, and policies of type 'region' will determine
* which is shown based on domain object state. A default policy is
* provided which will check the 'modes' attribute of the region part
* definition.
*/
plotInspector.addRegion(plotOptionsBrowseRegion);
plotInspector.addRegion(plotOptionsEditRegion);
legacyRegistry.register("example/plotType", {
"name": "Plot Type",
"description": "Example illustrating registration of a new object type",
"extensions": {
"types": [
{
"key": "plot",
"name": "Example Telemetry Plot",
"cssClass": "icon-telemetry-panel",
"description": "For development use. A plot for displaying telemetry.",
"priority": 10,
"delegates": [
"telemetry"
],
"features": "creation",
"contains": [
{
"has": "telemetry"
}
],
"model": {
"composition": []
},
"inspector": plotInspector,
"telemetry": {
"source": "generator",
"domains": [
{
"key": "time",
"name": "Time"
},
{
"key": "yesterday",
"name": "Yesterday"
},
{
"key": "delta",
"name": "Delta",
"format": "example.delta"
}
],
"ranges": [
{
"key": "sin",
"name": "Sine"
},
{
"key": "cos",
"name": "Cosine"
}
]
},
"properties": [
{
"name": "Period",
"control": "textfield",
"cssClass": "l-input-sm l-numeric",
"key": "period",
"required": true,
"property": [
"telemetry",
"period"
],
"pattern": "^\\d*(\\.\\d*)?$"
}
]
}
]
}
});
});

View File

@@ -68,7 +68,6 @@
]
}));
openmct.install(openmct.plugins.SummaryWidget());
openmct.install(openmct.plugins.Notebook());
openmct.time.clock('local', {start: -THIRTY_MINUTES, end: 0});
openmct.time.timeSystem('utc');
openmct.start();

View File

@@ -37,14 +37,14 @@ module.exports = function(config) {
{pattern: 'bower_components/**/*.js', included: false},
{pattern: 'node_modules/d3-*/**/*.js', included: false},
{pattern: 'node_modules/vue/**/*.js', included: false},
{pattern: 'src/**/*', included: false},
{pattern: 'node_modules/@cristian77/**/*.js', included: false},
{pattern: 'src/**/*.js', included: false},
{pattern: 'example/**/*.html', included: false},
{pattern: 'example/**/*.js', included: false},
{pattern: 'example/**/*.json', included: false},
{pattern: 'platform/**/*.js', included: false},
{pattern: 'warp/**/*.js', included: false},
{pattern: 'platform/**/*.html', included: false},
{pattern: 'src/**/*.html', included: false},
'test-main.js'
],

View File

@@ -49,8 +49,7 @@ requirejs.config({
"d3-format": "node_modules/d3-format/build/d3-format.min",
"d3-interpolate": "node_modules/d3-interpolate/build/d3-interpolate.min",
"d3-time": "node_modules/d3-time/build/d3-time.min",
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min",
"painterro": "node_modules/@cristian77/painterro/build/painterro.min"
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min"
},
"shim": {
"angular": {
@@ -68,9 +67,6 @@ requirejs.config({
"moment-duration-format": {
"deps": ["moment"]
},
"painterro": {
"exports": "Painterro"
},
"saveAs": {
"exports": "saveAs"
},

View File

@@ -1,9 +1,8 @@
{
"name": "openmct",
"version": "0.13.2-SNAPSHOT",
"version": "0.12.1-SNAPSHOT",
"description": "The Open MCT core platform",
"dependencies": {
"@cristian77/painterro": "^0.2.48",
"d3-array": "^1.0.2",
"d3-axis": "^1.0.4",
"d3-collection": "^1.0.2",
@@ -29,7 +28,7 @@
"gulp-jshint-html-reporter": "^0.1.3",
"gulp-rename": "^1.2.2",
"gulp-requirejs-optimize": "^0.3.1",
"gulp-sass": "^3.1.0",
"gulp-sass": "^2.2.0",
"gulp-sourcemaps": "^1.6.0",
"jasmine-core": "^2.3.0",
"jscs-html-reporter": "^0.1.0",

View File

@@ -38,7 +38,6 @@
ng-class="{ last:($index + 1) === contextualParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-click="parent.getCapability('action').perform('navigate')"
class="location-item">
</mct-representation>
</span>
@@ -50,7 +49,6 @@
ng-class="{ last:($index + 1) === primaryParents.length }">
<mct-representation key="'label'"
mct-object="parent"
ng-click="parent.getCapability('action').perform('navigate')"
class="location-item">
</mct-representation>
</span>

View File

@@ -88,15 +88,11 @@ define(
* @private
*/
ElementsController.prototype.refreshComposition = function (domainObject) {
var refreshTracker = {};
this.currentRefresh = refreshTracker;
var selectedObjectComposition = domainObject && domainObject.useCapability('composition');
if (selectedObjectComposition) {
selectedObjectComposition.then(function (composition) {
if (this.currentRefresh === refreshTracker) {
this.scope.composition = composition;
}
this.scope.composition = composition;
}.bind(this));
} else {
this.scope.composition = [];

View File

@@ -31,34 +31,11 @@ define(
mockSelection,
mockDomainObject,
mockMutationCapability,
mockCompositionCapability,
mockCompositionObjects,
mockComposition,
mockUnlisten,
selectable = [],
controller;
function mockPromise(value) {
return {
then: function (thenFunc) {
return mockPromise(thenFunc(value));
}
};
}
function createDomainObject() {
return {
useCapability: function () {
return mockCompositionCapability;
}
};
}
beforeEach(function () {
mockComposition = ["a", "b"];
mockCompositionObjects = mockComposition.map(createDomainObject);
mockCompositionCapability = mockPromise(mockCompositionObjects);
mockUnlisten = jasmine.createSpy('unlisten');
mockMutationCapability = jasmine.createSpyObj("mutationCapability", [
"listen"
@@ -68,7 +45,7 @@ define(
"getCapability",
"useCapability"
]);
mockDomainObject.useCapability.andReturn(mockCompositionCapability);
mockDomainObject.useCapability.andCallThrough();
mockDomainObject.getCapability.andReturn(mockMutationCapability);
mockScope = jasmine.createSpyObj("$scope", ['$on']);
@@ -88,7 +65,7 @@ define(
}
};
spyOn(ElementsController.prototype, 'refreshComposition').andCallThrough();
spyOn(ElementsController.prototype, 'refreshComposition');
controller = new ElementsController(mockScope, mockOpenMCT);
});
@@ -160,25 +137,6 @@ define(
expect(mockDomainObject.getCapability).not.toHaveBeenCalledWith('mutation');
});
it("checks concurrent changes to composition", function () {
var secondMockComposition = ["a", "b", "c"],
secondMockCompositionObjects = secondMockComposition.map(createDomainObject),
firstCompositionCallback,
secondCompositionCallback;
spyOn(mockCompositionCapability, "then").andCallThrough();
controller.refreshComposition(mockDomainObject);
controller.refreshComposition(mockDomainObject);
firstCompositionCallback = mockCompositionCapability.then.calls[0].args[0];
secondCompositionCallback = mockCompositionCapability.then.calls[1].args[0];
secondCompositionCallback(secondMockCompositionObjects);
firstCompositionCallback(mockCompositionObjects);
expect(mockScope.composition).toBe(secondMockCompositionObjects);
});
});
}
);

View File

@@ -33,14 +33,6 @@
}
}
[class*="icon-"].labeled {
// Moved from .s-button and generalized
&:before {
// Fend off label from icon when it's included
margin-right: $interiorMarginSm;
}
}
/************************** CHAR UNICODES */
$glyph-icon-alert-rect: '\e900';

View File

@@ -270,4 +270,37 @@
@extend .s-summary-widget;
@extend .l-summary-widget;
padding: $interiorMarginSm $interiorMargin;
}
// Hide and show elements in the rule-header on hover
.l-widget-rule,
.l-widget-test-data-item {
.grippy,
.l-rule-action-buttons-wrapper,
.l-condition-action-buttons-wrapper,
.l-widget-test-data-item-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 500ms);
opacity: 0;
}
&:hover {
.grippy,
.l-rule-action-buttons-wrapper,
.l-widget-test-data-item-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 0);
opacity: 1;
}
}
.l-rule-action-buttons-wrapper {
.t-delete {
margin-left: 10px;
}
}
.t-condition {
&:hover {
.l-condition-action-buttons-wrapper {
@include trans-prop-nice($props: opacity, $dur: 0);
opacity: 1;
}
}
}
}

View File

@@ -34,6 +34,11 @@ $pad: $interiorMargin * $baseRatio;
line-height: $btnStdH;
padding: 0 $pad;
&.labeled:before {
// Icon when it's included
margin-right: $interiorMarginSm;
}
&.lg {
font-size: 1rem;
}

View File

@@ -338,7 +338,7 @@ input[type="text"].s-input-inline,
@include btnSubtle($bg: $colorSelectBg);
@extend .icon-arrow-down; // Context arrow
display: inline-block;
line-height: 180%;
padding: 0 $interiorMargin;
overflow: hidden;
position: relative;
select {
@@ -349,8 +349,8 @@ input[type="text"].s-input-inline,
color: $colorSelectFg;
cursor: pointer;
border: none !important;
padding: 0 20px 0 $interiorMargin;
width: 100%;
padding: 4px 25px 2px 0px;
width: 130%;
option {
margin: $interiorMargin 0; // Firefox
}
@@ -359,7 +359,6 @@ input[type="text"].s-input-inline,
@include transform(translateY(-50%));
color: rgba($colorInvokeMenu, percentToDecimal($contrastInvokeMenuPercent));
display: block;
font-size: 0.8em;
pointer-events: none;
position: absolute;
right: $interiorMargin;

View File

@@ -77,14 +77,6 @@
position: relative;
}
.s-menu {
border-radius: $basicCr;
@include containerSubtle($colorMenuBg, $colorMenuFg);
@include boxShdw($shdwMenu);
@include txtShdw($shdwMenuText);
padding: $interiorMarginSm 0;
}
.menu {
border-radius: $basicCr;
@include containerSubtle($colorMenuBg, $colorMenuFg);

View File

@@ -131,18 +131,16 @@ body.mobile {
}
}
@include phonePortrait() {
body.phone {
.pane-tree-showing {
.pane.left.treeview {
width: $proporMenuOnly !important;
}
.pane.right.items {
left: 0 !important;
@include transform(translateX($proporMenuOnly));
.holder-object-and-inspector {
opacity: 0;
}
body.phone.portrait {
.pane-tree-showing {
.pane.left.treeview {
width: $proporMenuOnly !important;
}
.pane.right.items {
left: 0 !important;
@include transform(translateX($proporMenuOnly));
.holder-object-and-inspector {
opacity: 0;
}
}
}

View File

@@ -34,7 +34,7 @@
$iconEdgeM: 4px;
$iconD: $treeSearchInputBarH - ($iconEdgeM*2);
@extend .icon-magnify;
font-size: 0.8rem;
font-size: 0.8em;
position: relative;
.search-input {
@@ -60,7 +60,7 @@
position: relative;
width: 100%;
padding-left: $iconD + $interiorMargin !important;
padding-right: $iconD + $interiorMargin !important;
padding-right: ($iconD * 2) + ($interiorMargin * 2) !important;
// Make work for mct-control textfield
input {
@@ -82,7 +82,8 @@
}
.clear-input {
right: $interiorMargin;
// Hiding for now with addition of Cancel button
right: $iconD + $interiorMargin;
// Icon is visible only when there is text input
visibility: hidden;
@@ -97,25 +98,16 @@
}
}
&.search-filter-by-type {
.search-input {
padding-right: ($iconD * 2) + ($interiorMargin * 2) !important; // Allow room for menu-icon
}
.menu-icon {
// 'v' invoke menu icon for filtering by type
font-size: 0.8em;
padding-right: $iconEdgeM;
right: $iconEdgeM;
text-align: right;
&:hover {
color: pullForward($colorInputIcon, 10%);
}
}
.clear-input {
right: $iconD + $interiorMargin;
}
}
.menu-icon {
// 'v' invoke menu icon
font-size: 0.8em;
padding-right: $iconEdgeM;
right: $iconEdgeM;
text-align: right;
&:hover {
color: pullForward($colorInputIcon, 10%);
}
}
.search-menu-holder {
float: right;

View File

@@ -373,10 +373,10 @@ define(
*/
FixedController.prototype.updateView = function (telemetryObject, datum) {
var metadata = this.openmct.telemetry.getMetadata(telemetryObject);
var valueMetadata = this.chooseValueMetadataToDisplay(metadata);
var formattedTelemetryValue = this.getFormattedTelemetryValueForKey(valueMetadata, datum);
var telemetryKeyToDisplay = this.chooseTelemetryKeyToDisplay(metadata);
var formattedTelemetryValue = this.getFormattedTelemetryValueForKey(telemetryKeyToDisplay, datum, metadata);
var limitEvaluator = this.openmct.telemetry.limitEvaluator(telemetryObject);
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, valueMetadata);
var alarm = limitEvaluator && limitEvaluator.evaluate(datum, telemetryKeyToDisplay);
this.setDisplayedValue(
telemetryObject,
@@ -389,28 +389,29 @@ define(
/**
* @private
*/
FixedController.prototype.getFormattedTelemetryValueForKey = function (valueMetadata, datum) {
FixedController.prototype.getFormattedTelemetryValueForKey = function (telemetryKeyToDisplay, datum, metadata) {
var valueMetadata = metadata.value(telemetryKeyToDisplay);
var formatter = this.openmct.telemetry.getValueFormatter(valueMetadata);
return formatter.format(datum);
return formatter.format(datum[valueMetadata.key]);
};
/**
* @private
*/
FixedController.prototype.chooseValueMetadataToDisplay = function (metadata) {
FixedController.prototype.chooseTelemetryKeyToDisplay = function (metadata) {
// If there is a range value, show that preferentially
var valueMetadata = metadata.valuesForHints(['range'])[0];
var telemetryKeyToDisplay = metadata.valuesForHints(['range'])[0];
// If no range is defined, default to the highest priority non time-domain data.
if (valueMetadata === undefined) {
if (telemetryKeyToDisplay === undefined) {
var valuesOrderedByPriority = metadata.values();
valueMetadata = valuesOrderedByPriority.filter(function (values) {
return !(values.hints.domain);
telemetryKeyToDisplay = valuesOrderedByPriority.filter(function (valueMetadata) {
return !(valueMetadata.hints.domain);
})[0];
}
return valueMetadata;
return telemetryKeyToDisplay.source;
};
/**

View File

@@ -38,10 +38,6 @@ define([
' </div>' +
' </div>';
var NEW_NOTEBOOK_BUTTON_TEMPLATE = '<a class="s-button labeled icon-notebook new-notebook-entry" title="New Notebook Entry">' +
'<span class="title-label">New Notebook Entry</span>' +
'</a>';
/**
* MCT Trigger Modal is intended for use in only one location: inside the
* object-header to allow views in a layout to be popped out in a modal.
@@ -78,28 +74,9 @@ define([
closeButton,
doneButton,
blocker,
overlayContainer,
notebookButtonEl,
notebookButton,
actions = $scope.domainObject.getCapability('action'),
notebookAction = actions.getActions({'key': 'notebook-new-entry'});
if (notebookAction) {
if (notebookAction.length > 0) {
notebookButtonEl = document.createElement('div');
$(notebookButtonEl).addClass('notebook-button-container');
notebookButtonEl.innerHTML = NEW_NOTEBOOK_BUTTON_TEMPLATE;
notebookButton = frame.querySelector('.object-browse-bar .right');
notebookButton.prepend(notebookButtonEl);
// $(frame.querySelector('.object-holder')).addClass('container-notebook');
notebookButton.addEventListener('click', function () {
notebookAction[0].perform();
});
}
}
overlayContainer;
function openOverlay() {
// Remove frame classes from being applied in a non-frame context
$(frame).removeClass('frame frame-template');
overlay = document.createElement('div');
@@ -132,11 +109,7 @@ define([
overlay = undefined;
}
toggleOverlay = function (event) {
if (event) {
event.stopPropagation();
}
toggleOverlay = function () {
if (!isOpen) {
openOverlay();
isOpen = true;

View File

@@ -106,8 +106,8 @@ define(
'telemetryFormatter',
['format']
);
mockFormatter.format.andCallFake(function (valueMetadata) {
return "Formatted " + valueMetadata.value;
mockFormatter.format.andCallFake(function (value) {
return "Formatted " + value;
});
mockDomainObject = jasmine.createSpyObj(
@@ -697,7 +697,7 @@ define(
source: 'range'
}
]);
var key = controller.chooseValueMetadataToDisplay(mockMetadata).source;
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
expect(key).toEqual('range');
});
@@ -719,7 +719,7 @@ define(
}
}
]);
var key = controller.chooseValueMetadataToDisplay(mockMetadata).source;
var key = controller.chooseTelemetryKeyToDisplay(mockMetadata);
expect(key).toEqual('image');
});

View File

@@ -52,12 +52,6 @@ define([
beforeEach(function () {
$scope = jasmine.createSpyObj('$scope', ['$on']);
$scope.domainObject = { getCapability: function () {
return { getActions: function () {
}};
}};
$element = jasmine.createSpyObj('$element', [
'parent',
'remove',

View File

@@ -1,314 +0,0 @@
define([
"legacyRegistry",
"./src/controllers/NotebookController",
"./src/controllers/NewEntryController",
"./src/controllers/SelectSnapshotController",
"./src/controllers/LayoutNotebookController",
"./src/directives/MCTSnapshot",
"../layout/src/MCTTriggerModal",
"./src/directives/EntryDnd",
"./src/actions/ViewSnapshot",
"./src/actions/AnnotateSnapshot",
"./src/actions/RemoveEmbed",
"./src/actions/CreateSnapshot",
"./src/actions/RemoveSnapshot",
"./src/actions/NewEntryContextual",
"./src/capabilities/NotebookCapability",
"./src/policies/CompositionPolicy",
"./src/policies/ViewPolicy",
"text!./res/templates/layoutNotebook.html",
"text!./res/templates/notebook.html",
"text!./res/templates/entry.html",
"text!./res/templates/annotation.html",
"text!./res/templates/notifications.html",
"text!../layout/res/templates/frame.html",
"text!./res/templates/controls/embedControl.html",
"text!./res/templates/controls/snapSelect.html"
], function (
legacyRegistry,
NotebookController,
NewEntryController,
SelectSnapshotController,
LayoutNotebookController,
MCTSnapshot,
MCTModalNotebook,
MCTEntryDnd,
ViewSnapshotAction,
AnnotateSnapshotAction,
RemoveEmbedAction,
CreateSnapshotAction,
RemoveSnapshotAction,
newEntryAction,
NotebookCapability,
CompositionPolicy,
ViewPolicy,
layoutNotebookTemplate,
notebookTemplate,
entryTemplate,
annotationTemplate,
notificationsTemplate,
frameTemplate,
embedControlTemplate,
snapSelectTemplate
) {
legacyRegistry.register("platform/features/notebook", {
"name": "Notebook Plugin",
"description": "Create and save timestamped notes with embedded object snapshots.",
"extensions":
{
"types": [
{
"key": "notebook",
"name": "Notebook",
"cssClass": "icon-notebook",
"description": "Create and save timestamped notes with embedded object snapshots.",
"features": ["creation"],
"model": {
"entries": [],
"composition": [],
"entryTypes": []
}
}
],
"views": [
{
"key": "notebook.view",
"type": "notebook",
"cssClass": "icon-notebook",
"name": "notebook",
"template": notebookTemplate,
"editable": false,
"uses": [
"composition",
"action"
],
"gestures": [
"drop"
]
}
],
"controllers": [
{
"key": "NotebookController",
"implementation": NotebookController,
"depends": ["$scope",
"dialogService",
"popupService",
"agentService",
"objectService",
"navigationService",
"now",
"actionService",
"$timeout",
"$element",
"$sce"
]
},
{
"key": "NewEntryController",
"implementation": NewEntryController,
"depends": ["$scope",
"$rootScope"
]
},
{
"key": "selectSnapshotController",
"implementation": SelectSnapshotController,
"depends": ["$scope",
"$rootScope"
]
},
{
"key": "LayoutNotebookController",
"implementation": LayoutNotebookController,
"depends": ["$scope"]
}
],
"representations": [
{
"key": "draggedEntry",
"template": entryTemplate
},
{
"key": "frameLayoutNotebook",
"template": frameTemplate
}
],
"templates": [
{
"key": "annotate-snapshot",
"template": annotationTemplate
},
{
"key": "notificationTemplate",
"template": notificationsTemplate
}
],
"directives": [
{
"key": "mctSnapshot",
"implementation": MCTSnapshot,
"depends": [
"$rootScope",
"$document",
"exportImageService",
"dialogService",
"notificationService"
]
},
{
"key": "mctEntryDnd",
"implementation": MCTEntryDnd,
"depends": [
"$rootScope",
"$compile",
"dndService",
"typeService",
"notificationService"
]
},
{
"key": "mctModalNotebook",
"implementation": MCTModalNotebook,
"depends": [
"$document"
]
}
],
"actions": [
{
"key": "view-snapshot",
"implementation": ViewSnapshotAction,
"name": "View Snapshot",
"description": "View the large image in a modal",
"category": "embed",
"depends": [
"$compile"
]
},
{
"key": "annotate-snapshot",
"implementation": AnnotateSnapshotAction,
"name": "Annotate Snapshot",
"cssClass": "icon-pencil labeled",
"description": "Annotate embed's snapshot",
"category": "embed",
"depends": [
"dialogService",
"dndService",
"$rootScope"
]
},
{
"key": "remove-embed",
"implementation": RemoveEmbedAction,
"name": "Remove...",
"cssClass": "icon-trash labeled",
"description": "Remove this embed",
"category": [
"embed",
"embed-no-snap"
],
"depends": [
"dialogService"
]
},
{
"key": "remove-snapshot",
"implementation": RemoveSnapshotAction,
"name": "Remove Snapshot",
"cssClass": "icon-trash labeled",
"description": "Remove Snapshot of the embed",
"category": "embed",
"depends": [
"dialogService"
]
},
{
"key": "create-snapshot",
"implementation": CreateSnapshotAction,
"name": "Create Snapshot",
"description": "Create a snapshot for the embed",
"category": "embed-no-snap",
"priority": "preferred",
"depends": [
"$compile"
]
},
{
"key": "notebook-new-entry",
"implementation": newEntryAction,
"name": "New Notebook Entry",
"cssClass": "icon-notebook labeled",
"description": "Add a new entry",
"category": [
"contextual",
"view-control"
],
"depends": [
"$compile",
"$rootScope",
"dialogService",
"notificationService",
"linkService"
],
"priority": "preferred"
}
],
"licenses": [
{
"name": "painterro",
"version": "4.1.0",
"author": "Mike Bostock",
"description": "D3 (or D3.js) is a JavaScript library for visualizing data using web standards. D3 helps you bring data to life using SVG, Canvas and HTML. D3 combines powerful visualization and interaction techniques with a data-driven approach to DOM manipulation, giving you the full capabilities of modern browsers and the freedom to design the right visual interface for your data.",
"website": "https://d3js.org/",
"copyright": "Copyright 2010-2016 Mike Bostock",
"license": "BSD-3-Clause",
"link": "https://github.com/d3/d3/blob/master/LICENSE"
}
],
"capabilities": [
{
"key": "notebook",
"name": "Notebook Capability",
"description": "Provides a capability for looking for a notebook domain object",
"implementation": NotebookCapability,
"depends": [
"typeService"
]
}
],
"policies": [
{
"category": "composition",
"implementation": CompositionPolicy,
"message": "Objects of this type cannot contain objects of that type."
}
],
"controls": [
{
"key": "embed-control",
"template": embedControlTemplate
},
{
"key": "snapshot-select",
"template": snapSelectTemplate
}
],
"stylesheets": [
{
"stylesheetUrl": "css/notebook.css"
},
{
"stylesheetUrl": "css/notebook-espresso.css",
"theme": "espresso"
},
{
"stylesheetUrl": "css/notebook-snow.css",
"theme": "snow"
}
]
}
});
});

View File

@@ -1,236 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
.w-notebook {
font-size: 0.8rem;
overflow: hidden;
position: absolute;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
width: auto;
height: auto;
}
.l-notebook-drag-area {
padding: 10px;
font-style: italic;
cursor: pointer;
&:before { margin-right: 7px !important; }
.label {
@include ellipsize();
}
}
.frame {
.icon-notebook {
margin-right: 5px;
}
}
.overlay.l-dialog .title{
white-space: normal;
}
.w-notebook-entries {
//@include test($a: 0.1);
padding-right: $interiorMarginSm;
position: relative;
overflow-x: hidden;
overflow-y: scroll;
.t-entries-list {
}
}
.l-notebook-entry {
$p: $interiorMarginSm;
box-sizing: border-box;
margin-bottom: $p;
padding: $p $interiorMargin;
.s-notebook-entry-time,
.s-notebook-entry-text,
.notebook-entry-delete {
padding-top: $p;
padding-bottom: $p;
}
.s-notebook-entry-time {
border: 1px solid transparent; // Needed to maintain vertical alignment with s-notebook-entry-text
}
.l-notebook-entry-content{
.s-notebook-entry-text {
// Contenteditable div that holds text
min-height: 24px; // Needed in Firefox when field is blank
}
.entry-embeds{
flex-wrap: wrap;
}
.snap-thumb {
cursor: pointer;
}
}
}
.l-entry-embed {
$m: $interiorMarginSm;
position: relative;
margin: $m $m 0 0;
padding: $interiorMarginSm;
&.has-snapshot {
&:before {
position: absolute;
text-shadow: rgba(black, 0.7) 0 1px 5px;
z-index: 2;
}
}
.snap-thumb {
$d: 50px;
width: $d;
height: $d;
border-radius: 5px;
overflow: hidden;
img {
height: 100%;
width: 100%;
}
}
.embed-info {
margin-left: $interiorMargin;
.embed-title {
font-weight: bold;
}
}
}
.t-contents,
.snap-annotation {
// Todo: don't write this to t-contents, add a l- class
overflow: hidden;
img{
width: 100%;
}
}
.notebook-filters {
.select {
margin-left: $interiorMargin;
}
}
/********************************************* MOBILE */
@include phonePortrait() {
body.phone {
.w-notebook-entry-time-and-content {
flex-direction: column !important;
}
.s-notebook-entry-time,
.notebook-entry-delete {
padding-top: 0;
padding-bottom: 0;
}
}
}
/********************************************* SNAPSHOT VIEWING IN OVERLAY CONTEXT */
.t-snapshot {
.l-view-header {
.view-date {
float: right;
}
.view-snap-info {
float: left;
}
.s-button {
clear: both;
float: left;
margin-top: 10px;
font-size: 14px;
}
.view-info {
float: left;
.embed-icon {
display: inline-block;
}
.embed-title {
display: inline-block;
margin: 0 5px 0 10px;
}
.object-header {
display: inline-block;
}
}
}
}
.ptro-color-main{
top: 0;
z-index: 10;
position: relative;
height: auto;
min-height: 41px;
}
.ptro-wrapper{
bottom: 0;
top: 40px;
}
/********************************************* New Notebook Entry from Large View overlay */
.notebook-button-container {
//margin-right: $interiorMargin; // TODO: change to apply margin-left to view switcher button in Large View
}
/********************************************* NO IDEA WHAT THERE ARE APPLYING TO */
.context-available {
outline: none;
}
.menu-element.menu-view{
z-index: 999;
}
.overlay.l-dialog .abs.editor{
padding-right: 0;
}
.overlay.l-dialog .outer-holder.annotation-dialog{
width: 90%;
height: 90%;
}
.snap-annotation-wrapper{
padding-top: 40px;
}
.t-console {
// Temp console-like reporting element
max-height: 200px;
box-sizing: border-box;
padding: 5px;
}

View File

@@ -1,68 +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.
*****************************************************************************/
.l-notebook-drag-area {
border: 1px dashed rgba($colorKey, 0.7);
border-radius: $controlCr;
color: rgba($colorBodyFg, 0.7);
&:hover {
background: rgba($colorKey, 0.2);
color: $colorBodyFg;
}
&.drag-active{
border-color: $colorKey;
}
}
.s-notebook-entry {
background-color: rgba($colorBodyFg, 0.1);
border-radius: $basicCr;
&:hover {
background-color: rgba($colorBodyFg, 0.2);
}
.s-notebook-entry-time {
color: rgba($colorBodyFg, 0.5);
}
}
.l-entry-embed {
border-radius: $controlCr;
background-color: rgba($colorBodyFg, 0.1);
&.has-snapshot {
&:before {
color: $colorBodyFg;
}
}
}
.snapshot {
background: $colorBodyBg;
}
.snap-thumb {
border: 1px solid $colorInteriorBorder;
}

View File

@@ -1,32 +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.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "../../../../commonUI/general/res/sass/constants";
@import "../../../../commonUI/general/res/sass/mixins";
@import "../../../../commonUI/general/res/sass/glyphs";
@import "../../../../commonUI/themes/espresso/res/sass/constants";
@import "../../../../commonUI/themes/espresso/res/sass/mixins";
//@import "constants";
//@import "constants-espresso";
@import "notebook-thematic";

View File

@@ -1,32 +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.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "../../../../commonUI/general/res/sass/constants";
@import "../../../../commonUI/general/res/sass/mixins";
@import "../../../../commonUI/general/res/sass/glyphs";
@import "../../../../commonUI/themes/snow/res/sass/constants";
@import "../../../../commonUI/themes/snow/res/sass/mixins";
//@import "constants";
//@import "constants-snow";
@import "notebook-thematic";

View File

@@ -1,28 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2015, 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.
*****************************************************************************/
$output-bourbon-deprecation-warnings: false;
@import "bourbon";
@import "../../../../commonUI/general/res/sass/constants";
@import "../../../../commonUI/general/res/sass/mixins";
@import "../../../../commonUI/general/res/sass/mobile/constants";
@import "../../../../commonUI/general/res/sass/mobile/mixins";
@import "notebook-base";

View File

@@ -1,2 +0,0 @@
<div class="snap-annotation" id="snap-annotation" ng-init="ngModel.tracker()">
</div>

View File

@@ -1,51 +0,0 @@
<!--
Open MCT, Copyright (c) 2009-2016, United States Government
as represented by the Administrator of the National Aeronautics and Space
Administration. All rights reserved.
Open MCT is licensed under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
Open MCT includes source code licensed under additional open source
licenses. See the Open Source Licenses file (LICENSES.md) included with
this source code distribution or the Licensing information page available
at runtime from the About dialog for additional information.
-->
<!--
This element appears in the overlay dialog when initiating a new Notebook Entry from a view's Notebook button -->
<div class='form-control'>
<ng-form name="mctControl">
<div class='fields' ng-controller="NewEntryController">
<div class="l-flex-row new-notebook-entry-embed l-entry-embed {{cssClass}}"
ng-class="{ 'has-snapshot' : snapToggle }">
<div class="holder flex-elem snap-thumb"
ng-if="snapToggle">
<img ng-src="{{snapshot.src}}" alt="{{snapshot.modified}}">
</div>
<div class="holder flex-elem embed-info">
<div class="embed-title">{{objectName}}</div>
<div class="embed-date"
ng-if="snapToggle">{{snapshot.modified| date:'yyyy-MM-dd HH:mm:ss'}}</div>
</div>
<div class="holder flex-elem annotate-new"
ng-if="snapToggle">
<a class="s-button flex-elem icon-pencil "
title="Annotate this snapshot"
ng-click="annotateSnapshot()">
<span class="title-label">Annotate</span>
</a>
</div>
</div>
</div>
</ng-form>
</div>

View File

@@ -1,30 +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.
-->
<div class='form-control select' ng-controller="selectSnapshotController">
<select
ng-model="selectModel"
ng-options="opt.value as opt.name for opt in options"
ng-required="ngRequired"
name="mctControl">
<!-- <option value="" ng-show="!ngModel[field]">- Select One -</option> -->
</select>
</div>

View File

@@ -1,38 +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.
-->
<div class="frame snap-frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
<div class="abs object-browse-bar l-flex-row">
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<mct-representation
key="'switcher'"
ng-model="representation"
mct-object="domainObject">
</mct-representation>
</div>
</div>
<div class="abs object-holder" data-entry = "{{parameters.entry}}" data-embed = "{{parameters.embed}}" mct-snapshot ng-if="representation.selected.key">
<mct-representation
key="representation.selected.key"
mct-object="representation.selected.key && domainObject">
</mct-representation>
</div>
</div>

View File

@@ -1,54 +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.
-->
<div class="frame frame-template t-frame-inner abs t-object-type-{{ representation.selected.key }}">
<div class="abs object-browse-bar l-flex-row">
<div class="left flex-elem l-flex-row grows">
<mct-representation
key="'object-header-frame'"
mct-object="domainObject"
class="l-flex-row flex-elem object-header grows">
</mct-representation>
</div>
<div class="btn-bar right l-flex-row flex-elem flex-justify-end flex-fixed">
<a class="s-button icon-notebook"
title="New Notebook Entry"
ng-if="parameters"
ng-click="ngModel()">
</a>
<mct-representation
key="'switcher'"
ng-model="representation"
mct-object="domainObject">
</mct-representation>
<a class="s-button icon-expand t-btn-view-large"
title="View large"
mct-modal-notebook>
</a>
</div>
</div>
<div class="abs object-holder">
<mct-representation
key="representation.selected.key"
mct-object="representation.selected.key && domainObject">
</mct-representation>
</div>
</div>

View File

@@ -1,84 +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.
-->
<div class="abs l-layout"
ng-controller="LayoutController as controller"
ng-click="controller.clearSelection()">
<!-- Background grid -->
<div class="l-grid-holder" ng-click="controller.clearSelection()">
<div class="l-grid l-grid-x"
ng-if="!controller.getGridSize()[0] < 3"
ng-style="{ 'background-size': controller.getGridSize() [0] + 'px 100%' }"></div>
<div class="l-grid l-grid-y"
ng-if="!controller.getGridSize()[1] < 3"
ng-style="{ 'background-size': '100% ' + controller.getGridSize() [1] + 'px' }"></div>
</div>
<div class='abs frame t-frame-outer child-frame panel s-selectable s-moveable s-hover-border'
ng-class="{ 'no-frame': !controller.hasFrame(childObject), 's-selected':controller.selected(childObject) }"
ng-repeat="childObject in composition"
ng-click="controller.select($event, childObject.getId())"
ng-style="controller.getFrameStyle(childObject.getId())">
<div ng-controller="LayoutNotebookController as controller">
<mct-representation key="'frameLayoutNotebook'"
class="t-rep-frame holder contents abs"
parameters = "hasNotebookAction"
ng-model="newNotebook"
mct-object="childObject">
</mct-representation>
</div>
<!-- Drag handles -->
<span class="abs t-edit-handle-holder s-hover-border" ng-if="controller.selected(childObject)">
<span class="edit-handle edit-move"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [0,0])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-nw"
mct-drag-down="controller.startDrag(childObject.getId(), [1,1], [-1,-1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-ne"
mct-drag-down="controller.startDrag(childObject.getId(), [0,1], [1,-1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-sw"
mct-drag-down="controller.startDrag(childObject.getId(), [1,0], [-1,1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
<span class="edit-corner edit-resize-se"
mct-drag-down="controller.startDrag(childObject.getId(), [0,0], [1,1])"
mct-drag="controller.continueDrag(delta)"
mct-drag-up="controller.endDrag()">
</span>
</span>
</div>
</div>

View File

@@ -1,124 +0,0 @@
<div ng-controller="NotebookController as controller" class="mct-notebook w-notebook l-flex-col">
<div class="l-notebook-head holder l-flex-row flex-elem">
<div class="l-flex-row holder grows holder-search">
<div class="search-bar flex-elem l-flex-row grows"
ng-class="{ holder: !(entrySearch === '' || entrySearch === undefined) }">
<div class="holder flex-elem grows">
<input class="search-input"
type="text" tabindex="10000"
ng-model="entrySearch"
ng-keyup="controller.search()"/>
<a class="clear-icon clear-input icon-x-in-circle"
ng-class="{show: !(entrySearch === '' || entrySearch === undefined)}"
ng-click="entrySearch = ''; controller.search()"></a>
</div>
</div>
</div>
<div class="notebook-filters right l-flex-row flex-elem grows flex-justify-end">
<div class="select">
<select ng-model="showTime">
<option value="0" selected="selected">Show all</option>
<option value="1">Last hour</option>
<option value="8">Last 8 hours</option>
<option value="24">Last 24 hours</option>
</select>
</div>
<div class="select">
<select ng-model="sortEntries">
<option value="-createdOn" selected="selected">Newest first</option>
<option value="createdOn">Oldest first</option>
</select>
</div>
</div>
</div>
<!-- drag area -->
<div class="holder flex-elem l-flex-row icon-plus labeled l-notebook-drag-area" ng-click="newEntry($event)"
id="newEntry" mct-entry-dnd>
<span class="label">To start a new entry, click here or drag and drop any object</span>
</div>
<!-- entries -->
<div class="holder flex-elem grows w-notebook-entries t-entries-list" ng-mouseover="handleActive()">
<ul>
<li class="l-flex-row has-local-controls l-notebook-entry s-notebook-entry"
id="{{'entry_'+ entry.id}}"
ng-if="hoursFilter(showTime,entry.createdOn)"
ng-repeat="entry in model.entries | filter:entrySearch | orderBy: sortEntries track by $index"
ng-init="$last && finished(model.entries)"
mct-entry-dnd>
<div class="holder flex-elem l-flex-row grows w-notebook-entry-time-and-content">
<div class="holder flex-elem s-notebook-entry-time">
<span>{{entry.createdOn | date:'yyyy-MM-dd'}}</span>
<span>{{entry.createdOn | date:'HH:mm:ss'}}</span>
</div>
<div class="holder flex-elem l-flex-col grows l-notebook-entry-content">
<div contenteditable="true"
ng-blur="textBlur($event, entry.id)"
ng-focus="textFocus($event, entry.id)"
ng-model="entry.text"
placeholder="Enter text here"
class="flex-elem s-input-inline t-notebook-entry-input s-notebook-entry-text"
ng-bind-html="trustedHtml(entry.text)">
</div>
<!-- embeds -->
<div class="flex-elem entry-embeds l-flex-row">
<div class="l-flex-row l-entry-embed {{embed.cssClass}}"
ng-repeat="embed in entry.embeds track by $index"
ng-class="{ 'has-snapshot' : embed.snapshot }"
id="{{embed.id}}">
<div class="snap-thumb"
ng-if="embed.snapshot"
ng-click="viewSnapshot($event,embed.snapshot.src,embed.id,entry.createdOn,this,embed)">
<img ng-src="{{embed.snapshot.src}}" src="//:0" alt="{{embed.id}}">
</div>
<div class="embed-info l-flex-col">
<div class="embed-title object-header">
<a ng-click='navigate($event,embed.type)'>{{embed.name}}</a>
<a class='context-available' ng-click='openMenu($event,embed.type)'></a>
</div>
<div class="hide-menu" ng-show="false">
<div class="menu-element context-menu-wrapper mobile-disable-select">
<div class="menu context-menu">
<ul>
<li ng-repeat="menu in menuEmbed"
ng-click="menu.perform($event,embed.snapshot.src,embed.id,entry.createdOn,this,embed)"
title="{{menu.getMetadata().description}}"
class="{{menu.getMetadata().cssClass}}"
ng-if="embed.snapshot">
{{menu.getMetadata().name}}
</li>
<li ng-repeat="menu in menuEmbedNoSnap"
ng-click="menu.perform($event,embed.snapshot.src,embed.id,entry.createdOn,this)"
title="{{menu.getMetadata().description}}"
class="{{menu.getMetadata().cssClass}}"
ng-if="!embed.snapshot">
{{menu.getMetadata().name}}
</li>
<li ng-repeat="menu in embedActions"
ng-click="menu.perform()"
title="{{menu.name}}"
class="{{menu.cssClass}}">
{{menu.name}}
</li>
</ul>
</div>
</div>
</div>
<div class="embed-date"
ng-if="embed.snapshot">{{embed.id| date:'yyyy-MM-dd HH:mm:ss'}}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- delete entry -->
<div class="holder flex-elem local-control notebook-entry-delete">
<a class="s-icon-button icon-trash" title="Delete Entry" ng-click="deleteEntry($event)"></a>
</div>
</li>
</ul>
</div>
</div>

View File

@@ -1,8 +0,0 @@
<span class="status block">
<!-- DO NOT ADD SPACES BETWEEN THE SPANS - IT ADDS WHITE SPACE!! -->
<span class="status-indicator icon-bell"></span>
<span class="label">
Notifications
</span>
<span class="count"></span>
</span>

View File

@@ -1,111 +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.
*****************************************************************************/
/**
* Module defining viewSnapshot (Originally NewWindowAction). Created by vwoeltje on 11/18/14.
*/
define(
["painterro", "zepto"],
function (Painterro, $) {
var ANNOTATION_STRUCT = {
title: "Annotate Snapshot",
template: "annotate-snapshot",
options: [{
name: "OK",
key: "ok",
description: "save annotation"
},
{
name: "Cancel",
key: "cancel",
description: "cancel editing"
}]
};
function AnnotateSnapshot(dialogService,dndService,$rootScope,context) {
context = context || {};
// Choose the object to be opened into a new tab
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
this.dndService = dndService;
this.$rootScope = $rootScope;
}
AnnotateSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId,$scope) {
var DOMAIN_OBJECT = this.domainObject;
var ROOTSCOPE = this.$rootScope;
this.dialogService.getUserChoice(ANNOTATION_STRUCT)
.then(saveNotes);
var painterro;
var tracker = function () {
$(document.body).find('.l-dialog .outer-holder').addClass('annotation-dialog');
painterro = Painterro({
id: 'snap-annotation',
backgroundFillColor: '#eee',
hiddenTools: ['save', 'open', 'close','eraser'],
saveHandler: function (image, done) {
if (entryId && embedId) {
var elementPos = DOMAIN_OBJECT.model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = DOMAIN_OBJECT.model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
$scope.saveSnap(image.asBlob(), embedPos, elementPos);
}else {
ROOTSCOPE.snapshot = {'src': image.asDataURL('image/png'),
'modified': Date.now()};
}
done(true);
}
}).show(snapshot);
};
ANNOTATION_STRUCT.model = {'tracker': tracker};
function saveNotes(param) {
if (param === 'ok') {
painterro.save();
}else {
ROOTSCOPE.snapshot = "annotationCancelled";
}
}
};
return AnnotateSnapshot;
}
);

View File

@@ -1,65 +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 () {
var SNAPSHOT_TEMPLATE = '<mct-representation key="\'draggedEntry\'"' +
'parameters="{entry:entryId,embed:embedId}"' +
'class="t-rep-frame holder"' +
'mct-object="selObj">' +
'</mct-representation>';
function CreateSnapshot($compile,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.context = context;
this.$compile = $compile;
}
CreateSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId,$scope) {
var compile = this.$compile;
var model = this.domainObject.model;
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
var embedType = entryEmbeds[embedPos].type;
$scope.getDomainObj(embedType).then(function (resp) {
if (entryId >= 0 && embedId >= 0) {
$scope.selObj = resp[embedType];
$scope.entryId = elementPos;
$scope.embedId = embedPos;
compile(SNAPSHOT_TEMPLATE)($scope);
}
});
};
return CreateSnapshot;
}
);

View File

@@ -1,195 +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 () {
var SNAPSHOT_TEMPLATE = '<mct-representation key="\'draggedEntry\'"' +
'class="t-rep-frame holder"' +
'mct-object="selObj">' +
'</mct-representation>';
var NEW_TASK_FORM = {
name: "Create a Notebook Entry",
hint: "Please select one Notebook",
sections: [{
rows: [{
name: 'Entry',
key: 'entry',
control: 'textarea',
required: true,
"cssClass": "l-textarea-sm"
},
{
name: 'Embed Type',
key: 'withSnapshot',
control: 'snapshot-select',
"options": [
{
"name": "Link and Snapshot",
"value": true
},
{
"name": "Link only",
"value": false
}
]
},
{
name: 'Embed',
key: 'embedObject',
control: 'embed-control'
},
{
name: 'Save in Notebook',
key: 'saveNotebook',
control: 'locator',
validate: validateLocation
}]
}]
};
function NewEntryContextual($compile,$rootScope,dialogService,notificationService,linkService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
this.notificationService = notificationService;
this.linkService = linkService;
this.$rootScope = $rootScope;
this.$compile = $compile;
}
function validateLocation(newParentObj) {
return newParentObj.model.type === 'notebook';
}
NewEntryContextual.prototype.perform = function () {
var self = this;
var domainObj = this.domainObject;
var notification = this.notificationService;
var dialogService = this.dialogService;
var rootScope = this.$rootScope;
rootScope.newEntryText = '';
// Create the overlay element and add it to the document's body
this.$rootScope.selObj = domainObj;
this.$rootScope.selValue = "";
var newScope = rootScope.$new();
newScope.selObj = domainObj;
newScope.selValue = "";
this.$compile(SNAPSHOT_TEMPLATE)(newScope);
//newScope.$destroy();
this.$rootScope.$watch("snapshot", setSnapshot);
function setSnapshot(value) {
if (value === "annotationCancelled") {
rootScope.snapshot = rootScope.lastValue;
rootScope.lastValue = '';
}else if (value && value !== rootScope.lastValue) {
var overlayModel = {
title: NEW_TASK_FORM.name,
message: NEW_TASK_FORM.message,
structure: NEW_TASK_FORM,
value: {'entry': rootScope.newEntryText || ""}
};
rootScope.currentDialog = overlayModel;
dialogService.getDialogResponse(
"overlay-dialog",
overlayModel,
function () {
return overlayModel.value;
}
).then(addNewEntry);
rootScope.lastValue = value;
}
}
function addNewEntry(options) {
options.selectedModel = options.embedObject.getModel();
options.cssClass = options.embedObject.getCapability('type').typeDef.cssClass;
if (self.$rootScope.snapshot) {
options.snapshot = self.$rootScope.snapshot;
self.$rootScope.snapshot = undefined;
}else {
options.snapshot = undefined;
}
if (!options.withSnapshot) {
options.snapshot = '';
}
createSnap(options);
}
function createSnap(options) {
options.saveNotebook.useCapability('mutation', function (model) {
var entries = model.entries;
var lastEntry = entries[entries.length - 1];
var date = Date.now();
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
model.entries.push({
'id': date,
'createdOn': date,
'text': options.entry,
'embeds': [{'type': options.embedObject.getId(),
'id': '' + date,
'cssClass': options.cssClass,
'name': options.selectedModel.name,
'snapshot': options.snapshot
}]
});
}else {
model.entries[entries.length - 1] = {
'id': date,
'createdOn': date,
'text': options.entry,
'embeds': [{'type': options.embedObject.getId(),
'id': '' + date,
'cssClass': options.cssClass,
'name': options.selectedModel.name,
'snapshot': options.snapshot
}]
};
}
});
notification.info({
title: "Notebook Entry created"
});
}
};
NewEntryContextual.appliesTo = function (context) {
var domainObject = context.domainObject;
return domainObject && domainObject.hasCapability("notebook") &&
domainObject.getCapability("notebook").isNotebook();
};
return NewEntryContextual;
}
);

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 () {
function RemoveEmbed(dialogService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
}
RemoveEmbed.prototype.perform = function ($event,snapshot,embedId,entryId) {
var DOMAIN_OBJ = this.domainObject;
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Embed. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
remove();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
function remove() {
DOMAIN_OBJ.useCapability('mutation', function (model) {
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
model.entries[elementPos].embeds.splice(embedPos, 1);
});
}
};
return RemoveEmbed;
}
);

View File

@@ -1,74 +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 () {
function RemoveSnapshot(dialogService,context) {
context = context || {};
this.domainObject = context.selectedObject || context.domainObject;
this.dialogService = dialogService;
}
RemoveSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId) {
var DOMAIN_OBJ = this.domainObject;
var errorDialog = this.dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Snapshot. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
remove();
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
function remove() {
DOMAIN_OBJ.useCapability('mutation', function (model) {
var elementPos = model.entries.map(function (x) {
return x.createdOn;
}).indexOf(entryId);
var entryEmbeds = model.entries[elementPos].embeds;
var embedPos = entryEmbeds.map(function (x) {
return x.id;
}).indexOf(embedId);
model.entries[elementPos].embeds[embedPos].snapshot = "";
});
}
};
return RemoveSnapshot;
}
);

View File

@@ -1,160 +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.
*****************************************************************************/
/**
* Module defining ViewSnapshot
*/
define(
['zepto'],
function ($) {
var OVERLAY_TEMPLATE = '' +
' <div class="abs blocker"></div>' +
' <div class="abs outer-holder">' +
' <a class="close icon-x-in-circle"></a>' +
' <div class="abs inner-holder l-flex-col">' +
' <div class="t-contents flex-elem holder grows"></div>' +
' <div class="bottom-bar flex-elem holder">' +
' <a class="t-done s-button major">Done</a>' +
' </div>' +
' </div>' +
' </div>';
var toggleOverlay,
overlay,
closeButton,
doneButton,
blocker,
overlayContainer,
img,
annotateButton,
annotateImg;
function ViewSnapshot($compile,context) {
context = context || {};
this.$compile = $compile;
}
function openOverlay(url,header) {
overlay = document.createElement('div');
$(overlay).addClass('abs overlay l-large-view');
overlay.innerHTML = OVERLAY_TEMPLATE;
overlayContainer = overlay.querySelector('.t-contents');
closeButton = overlay.querySelector('a.close');
closeButton.addEventListener('click', toggleOverlay);
doneButton = overlay.querySelector('a.t-done');
doneButton.addEventListener('click', toggleOverlay);
blocker = overlay.querySelector('.abs.blocker');
blocker.addEventListener('click', toggleOverlay);
annotateButton = header.querySelector('a.icon-pencil');
annotateButton.addEventListener('click', annotateImg);
document.body.appendChild(overlay);
img = document.createElement('img');
img.src = url;
overlayContainer.appendChild(header);
overlayContainer.appendChild(img);
}
function closeOverlay() {
overlayContainer.removeChild(img);
document.body.removeChild(overlay);
closeButton.removeEventListener('click', toggleOverlay);
closeButton = undefined;
doneButton.removeEventListener('click', toggleOverlay);
doneButton = undefined;
blocker.removeEventListener('click', toggleOverlay);
blocker = undefined;
overlayContainer = undefined;
overlay = undefined;
img = undefined;
}
function headerTemplate() {
var template = '<div class="t-snapshot l-view-header">' +
'<div class="view-info">' +
'<div ng-class="cssClass" class="embed-icon"></div>' +
'<div class="embed-title">{{entryName}}</div>' +
'<div class="object-header">' +
'<a href="" class="context-available" ng-click="openMenu($event,embedType)""></a>' +
'</div>' +
'<div class="hide-menu" ng-show="false">' +
'<div class="menu-element menu-view context-menu-wrapper mobile-disable-select">' +
'<div class="menu context-menu">' +
'<ul>' +
'<li ng-repeat="menu in embedActions"' +
'ng-click="menu.perform()"' +
'title="{{menu.name}}"' +
'class="{{menu.cssClass}}">' +
'{{menu.name}}' +
'</li>' +
'</ul>' +
'</div>' +
'</div>' +
'</div>' +
'</div>' +
'<div class="view-date">' +
'<span class="icon-alert-rect" title="Snapshot">' +
'</span> ' +
'SNAPSHOT {{snapDate | date:\'yyyy-MM-dd HH:mm:ss\'}}' +
'</div>' +
'<a class="s-button icon-pencil" title="Annotate">' +
'<span class="title-label">Annotate</span>' +
'</a>' +
'</div>';
return template;
}
ViewSnapshot.prototype.perform = function ($event,snapshot,embedId,entryId,$scope,embed) {
var isOpen = false;
// Create the overlay element and add it to the document's body
$scope.cssClass = embed.cssClass;
$scope.embedType = embed.type;
$scope.entryName = embed.name;
$scope.snapDate = +embedId;
var element = this.$compile(headerTemplate())($scope);
var annotateAction = $scope.action.getActions({category: 'embed'})[1];
toggleOverlay = function () {
if (!isOpen) {
openOverlay(snapshot, element[0]);
isOpen = true;
} else {
closeOverlay();
isOpen = false;
}
};
annotateImg = function () {
closeOverlay();
annotateAction.perform($event, snapshot, embedId, entryId, $scope);
};
toggleOverlay();
};
return ViewSnapshot;
}
);

View File

@@ -1,50 +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 () {
/**
* The notebook capability allows a domain object to know whether the
* notebook plugin is present or not.
*
* @constructor
*/
function NotebookCapability(typeService, domainObject) {
this.domainObject = domainObject;
this.typeService = typeService;
return this;
}
/**
* Returns true if there is a notebook domain Object.
*
* @returns {Boolean}
*/
NotebookCapability.prototype.isNotebook = function () {
return this.typeService.getType('notebook');
};
return NotebookCapability;
}
);

View File

@@ -1,54 +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.
*****************************************************************************/
/**
* This bundle implements object types and associated views for
* display-building.
*/
define(
[],
function () {
/**
* The LayoutNotebookController is responsible for supporting the
* notebook feature creation on theLayout view.
**/
function LayoutNotebookController($scope) {
$scope.hasNotebookAction = undefined;
$scope.newNotebook = undefined;
var actions = $scope.domainObject.getCapability('action');
var notebookAction = actions.getActions({'key': 'notebook-new-entry'});
if (notebookAction.length > 0) {
$scope.hasNotebookAction = true;
$scope.newNotebook = function () {
notebookAction[0].perform();
};
}
}
return LayoutNotebookController;
}
);

View File

@@ -1,66 +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.
*****************************************************************************/
/**
* Module defining NewEntryController. */
define(
[],
function () {
function NewEntryController($scope,$rootScope) {
$scope.snapshot = undefined;
$scope.snapToggle = true;
$scope.entryText = '';
var annotateAction = $rootScope.selObj.getCapability('action').getActions(
{category: 'embed'})[1];
$scope.$parent.$parent.ngModel[$scope.$parent.$parent.field] = $rootScope.selObj;
$scope.objectName = $rootScope.selObj.getModel().name;
$scope.cssClass = $rootScope.selObj.getCapability('type').typeDef.cssClass;
$scope.annotateSnapshot = function ($event) {
if ($rootScope.currentDialog.value) {
$rootScope.newEntryText = $scope.$parent.$parent.ngModel.entry;
$rootScope.currentDialog.cancel();
annotateAction.perform($event, $rootScope.snapshot.src);
$rootScope.currentDialog = undefined;
}
};
function updateSnapshot(img) {
$scope.snapshot = img;
}
// Update set of actions whenever the action capability
// changes or becomes available.
$rootScope.$watch("snapshot", updateSnapshot);
$rootScope.$watch("selValue", toggleEmbed);
function toggleEmbed(value) {
$scope.snapToggle = value;
}
}
return NewEntryController;
}
);

View File

@@ -1,355 +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.
*****************************************************************************/
/*-- main controller file, here is the core functionality of the notebook plugin --*/
define(
['zepto'],
function ($) {
function NotebookController(
$scope,
dialogService,
popupService,
agentService,
objectService,
navigationService,
now,
actionService,
$timeout,
$element,
$sce
) {
$scope.entriesEl = $(document.body).find('.t-entries-list');
$scope.sortEntries = '-createdOn';
$scope.showTime = "0";
$scope.editEntry = false;
$scope.entrySearch = '';
$scope.entryTypes = [];
$scope.embedActions = [];
$scope.currentEntryValue = '';
/*--seconds in an hour--*/
var SECONDS_IN_AN_HOUR = 60 * 60 * 1000;
this.scope = $scope;
$scope.hoursFilter = function (hours,entryTime) {
if (+hours) {
return entryTime > (Date.now() - SECONDS_IN_AN_HOUR * (+hours));
}else {
return true;
}
};
$scope.scrollToTop = function () {
var entriesContainer = $scope.entriesEl.parent();
entriesContainer[0].scrollTop = 0;
};
$scope.newEntry = function ($event) {
$scope.scrollToTop();
var entries = $scope.domainObject.model.entries;
var lastEntry = entries[entries.length - 1];
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
$scope.domainObject.useCapability('mutation', function (model) {
var id = Date.now();
model.entries.push({'id': id, 'createdOn': id});
});
} else {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entries.length - 1].createdOn = Date.now();
});
}
$scope.entrySearch = '';
};
$scope.deleteEntry = function ($event) {
/* This is really brittle - change the markup and this doesn't work */
var delId = $event.currentTarget.parentElement.parentElement.id;
var errorDialog = dialogService.showBlockingMessage({
severity: "error",
title: "This action will permanently delete this Notebook entry. Do you want to continue?",
minimized: true, // want the notification to be minimized initially (don't show banner)
options: [{
label: "OK",
callback: function () {
errorDialog.dismiss();
var elementPos = $scope.domainObject.model.entries.map(function (x) {
return x.id;
}).indexOf(+delId.replace('entry_', ''));
if (elementPos !== -1) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries.splice(elementPos, 1);
});
} else {
window.console.log('delete error');
}
}
},{
label: "Cancel",
callback: function () {
errorDialog.dismiss();
}
}]
});
};
$scope.textFocus = function ($event, entryId) {
if ($event.currentTarget && $event.currentTarget.innerText) {
/*
On focus, if the currentTarget isn't blank, set the global currentEntryValue = the
content of the current focus. This will be used at blur to determine if the
current entry has been modified or not.
Not sure this is right, would think we'd always want to set curEntVal even if blank
*/
$scope.currentEntryValue = $event.currentTarget.innerText;
} else {
$event.target.innerText = '';
}
};
//On text blur(when focus is removed)
$scope.textBlur = function ($event, entryId) {
// entryId is the unique numeric based on the original createdOn
if ($event.target && $event.target.innerText !== "") {
var elementPos = $scope.domainObject.model.entries.map(function (x) {
return x.id;
}).indexOf(+(entryId));
// If the text of an entry has been changed, then update the text and the modifiedOn numeric
// Otherwise, don't do anything
if ($scope.currentEntryValue !== $event.target.innerText) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[elementPos].text = $event.target.innerText;
model.entries[elementPos].modified = Date.now();
});
}
}
};
$scope.finished = function (model) {
var lastEntry = model[model.length - 1];
if (!lastEntry.text) {
var newEntry = $scope.entriesEl.children().children()[0];
$(newEntry).find('.t-notebook-entry-input').focus();
}
};
$scope.handleActive = function () {
var newEntry = $scope.entriesEl.find('.active');
if (newEntry) {
newEntry.removeClass('active');
}
};
$scope.clearSearch = function () {
$scope.entrySearch = '';
};
$scope.viewSnapshot = function ($event,snapshot,embedId,entryId,$innerScope,domainObject) {
var viewAction = $scope.action.getActions({category: 'embed'})[0];
viewAction.perform($event, snapshot, embedId, entryId, $innerScope, domainObject);
};
$scope.parseText = function (text) {
if (text) {
return text.split(/\r\n|\r|\n/gi);
}
};
//parse text and add line breaks where needed
$scope.trustedHtml = function (text) {
if (text) {
return $sce.trustAsHtml(this.parseText(text).join('<br>'));
}
};
$scope.renderImage = function (img) {
return URL.createObjectURL(img);
};
$scope.getDomainObj = function (id) {
return objectService.getObjects([id]);
};
function refreshComp(change) {
if (change && change.length) {
change[0].getCapability('action').getActions({key: 'remove'})[0].perform();
}
}
$scope.actionToMenuOption = function (action) {
return {
key: action.getMetadata().key,
name: action.getMetadata().name,
cssClass: action.getMetadata().cssClass,
perform: action.perform
};
};
// Maintain all "conclude-editing" and "save" actions in the
// present context.
function updateActions() {
$scope.menuEmbed = $scope.action ?
$scope.action.getActions({category: 'embed'}) :
[];
$scope.menuEmbedNoSnap = $scope.action ?
$scope.action.getActions({category: 'embed-no-snap'}) :
[];
$scope.menuActions = $scope.action ?
$scope.action.getActions({key: 'window'}) :
[];
}
// Update set of actions whenever the action capability
// changes or becomes available.
$scope.$watch("action", updateActions);
$scope.navigate = function ($event,embedType) {
if ($event) {
$event.preventDefault();
}
$scope.getDomainObj(embedType).then(function (resp) {
navigationService.setNavigation(resp[embedType]);
});
};
$scope.saveSnap = function (url,embedPos,entryPos) {
var snapshot = false;
if (url) {
if (embedPos !== -1 && entryPos !== -1) {
var reader = new window.FileReader();
reader.readAsDataURL(url);
reader.onloadend = function () {
snapshot = reader.result;
$scope.domainObject.useCapability('mutation', function (model) {
if (model.entries[entryPos]) {
model.entries[entryPos].embeds[embedPos].snapshot = {
'src': snapshot,
'type': url.type,
'size': url.size,
'modified': Date.now()
};
model.entries[entryPos].embeds[embedPos].id = Date.now();
}
});
};
}
}else {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryPos].embeds[embedPos].snapshot = snapshot;
});
}
};
/*---popups menu embeds----*/
function getEmbedActions(embedType) {
if (!$scope.embedActions.length) {
$scope.getDomainObj(embedType).then(function (resp) {
$scope.embedActions = [];
$scope.embedActions.push($scope.actionToMenuOption(
$scope.action.getActions({key: 'window',selectedObject: resp[embedType]})[0]
));
$scope.embedActions.push({
key: 'navigate',
name: 'Go to Original',
cssClass: '',
perform: function () {
$scope.navigate('', embedType);
}
});
});
}
}
$scope.openMenu = function ($event,embedType) {
$event.preventDefault();
getEmbedActions(embedType);
var body = $(document).find('body'),
initiatingEvent = agentService.isMobile() ?
'touchstart' : 'mousedown',
dismissExistingMenu,
menu,
popup;
var container = $($event.currentTarget).parent().parent();
menu = container.find('.menu-element');
// Remove the context menu
function dismiss() {
container.find('.hide-menu').append(menu);
body.off("mousedown", dismiss);
dismissExistingMenu = undefined;
$scope.embedActions = [];
}
// Dismiss any menu which was already showing
if (dismissExistingMenu) {
dismissExistingMenu();
}
// ...and record the presence of this menu.
dismissExistingMenu = dismiss;
popup = popupService.display(menu, [$event.pageX,$event.pageY], {
marginX: 0,
marginY: -50
});
// Stop propagation so that clicks or touches on the menu do not close the menu
menu.on(initiatingEvent, function (event) {
event.stopPropagation();
$timeout(dismiss, 300);
});
// Dismiss the menu when body is clicked/touched elsewhere
// ('mousedown' because 'click' breaks left-click context menus)
// ('touchstart' because 'touch' breaks context menus up)
body.on(initiatingEvent, dismiss);
};
$scope.$watchCollection("composition", refreshComp);
$scope.$on('$destroy', function () {});
}
return NotebookController;
});

View File

@@ -1,44 +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.
*****************************************************************************/
/**
* Module defining SelectSnapshotController. */
define(
[],
function () {
function SelectSnapshotController($scope,$rootScope) {
$scope.selectModel = true;
function selectprint(value) {
$rootScope.selValue = value;
$scope.$parent.$parent.ngModel[$scope.$parent.$parent.field] = value;
}
$scope.$watch("selectModel", selectprint);
}
return SelectSnapshotController;
}
);

View File

@@ -1,140 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['zepto'], function ($) {
var SNAPSHOT_TEMPLATE = '<mct-representation key="\'draggedEntry\'"' +
'parameters="{entry:entryId,embed:embedId}"' +
'class="t-rep-frame holder"' +
'mct-object="selObj">' +
'</mct-representation>';
function EntryDnd($rootScope,$compile,dndService,typeService,notificationService) {
function link($scope, $element) {
function drop(e) {
var selectedObject = dndService.getData('mct-domain-object');
var selectedModel = selectedObject.getModel();
var cssClass = selectedObject.getCapability('type').typeDef.cssClass;
var entryId = -1;
var embedId = -1;
$scope.clearSearch();
if ($element[0].id === 'newEntry') {
entryId = $scope.domainObject.model.entries.length;
embedId = 0;
var lastEntry = $scope.domainObject.model.entries[entryId - 1];
if (lastEntry === undefined || lastEntry.text || lastEntry.embeds) {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries.push({'createdOn': +Date.now(),
'id': +Date.now(),
'embeds': [{'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
}]
});
});
}else {
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryId - 1] =
{'createdOn': +Date.now(),
'embeds': [{'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
}]
};
});
}
$scope.scrollToTop();
notificationService.info({
title: "Notebook Entry created"
});
}else {
entryId = $scope.domainObject.model.entries.map(function (x) {
return (x.id);
}).indexOf(Number($element[0].id.replace('entry_', '')));
if (!$scope.domainObject.model.entries[entryId].embeds) {
$scope.domainObject.model.entries[entryId].embeds = [];
}
$scope.domainObject.useCapability('mutation', function (model) {
model.entries[entryId].embeds.push({'type': selectedObject.getId(),
'id': '' + Date.now(),
'cssClass': cssClass,
'name': selectedModel.name,
'snapshot': ''
});
});
embedId = $scope.domainObject.model.entries[entryId].embeds.length - 1;
if (selectedObject) {
e.preventDefault();
}
}
if (entryId >= 0 && embedId >= 0) {
$scope.selObj = selectedObject;
$scope.entryId = entryId;
$scope.embedId = embedId;
$compile(SNAPSHOT_TEMPLATE)($scope);
}
if ($(e.currentTarget).hasClass('drag-active')) {
$(e.currentTarget).removeClass('drag-active');
}
}
function dragover(e) {
if (!$(e.currentTarget).hasClass('drag-active')) {
$(e.currentTarget).addClass('drag-active');
}
}
// Listen for the drop itself
$element.on('dragover', dragover);
$element.on('drop', drop);
$scope.$on('$destroy', function () {
$element.off('dragover', dragover);
$element.off('drop', drop);
});
}
return {
restrict: 'A',
link: link
};
}
return EntryDnd;
});

View File

@@ -1,166 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define([
'zepto'
], function (
$
) {
var OVERLAY_TEMPLATE = '' +
' <div class="abs blocker"></div>' +
' <div class="abs outer-holder">' +
' <a class="close icon-x-in-circle"></a>' +
' <div class="abs inner-holder l-flex-col">' +
' <div class="t-contents flex-elem holder grows"></div>' +
' <div class="bottom-bar flex-elem holder">' +
' <a class="t-done s-button major">Done</a>' +
' </div>' +
' </div>' +
' </div>';
var NEW_NOTEBOOK_BUTTON_TEMPLATE = '<a class="s-button labeled icon-notebook new-notebook-entry" title="New Notebook Entry">' +
'<span class="title-label">New Notebook Entry</span>' +
'</a>';
/**
* MCT Trigger Modal is intended for use in only one location: inside the
* object-header to allow views in a layout to be popped out in a modal.
* Users can close the modal and go back to normal, and everything generally
* just works fine.
*
* This code is sensitive to how our html is constructed-- particularly with
* how it locates the the container of an element in a layout. However, it
* should be able to handle slight relocations so long as it is always a
* descendent of a `.frame` element.
*/
function MCTModalNotebook($document) {
var document = $document[0];
function link($scope, $element) {
var frame = $element.parent();
for (var i = 0; i < 10; i++) {
if (frame.hasClass('frame')) {
break;
}
frame = frame.parent();
}
if (!frame.hasClass('frame')) {
$element.remove();
return;
}
frame = frame[0];
var layoutContainer = frame.parentElement,
isOpen = false,
toggleOverlay,
overlay,
closeButton,
doneButton,
notebookButton,
blocker,
overlayContainer,
notebookButtonEl;
function openOverlay() {
// Remove frame classes from being applied in a non-frame context
$(frame).removeClass('frame frame-template');
overlay = document.createElement('div');
$(overlay).addClass('abs overlay l-large-view');
overlay.innerHTML = OVERLAY_TEMPLATE;
overlayContainer = overlay.querySelector('.t-contents');
closeButton = overlay.querySelector('a.close');
closeButton.addEventListener('click', toggleOverlay);
doneButton = overlay.querySelector('a.t-done');
doneButton.addEventListener('click', toggleOverlay);
blocker = overlay.querySelector('.abs.blocker');
blocker.addEventListener('click', toggleOverlay);
document.body.appendChild(overlay);
layoutContainer.removeChild(frame);
overlayContainer.appendChild(frame);
//verify if there is a new notebook entry action
var actions = $scope.domainObject.getCapability('action');
var notebookAction = actions.getActions({'key': 'notebook-new-entry'});
if (notebookAction.length > 0) {
notebookButtonEl = document.createElement('div');
$(notebookButtonEl).addClass('notebook-button-container');
notebookButtonEl.innerHTML = NEW_NOTEBOOK_BUTTON_TEMPLATE;
notebookButton = frame.querySelector('.object-browse-bar .right');
notebookButton.prepend(notebookButtonEl);
// $(frame.querySelector('.object-holder')).addClass('container-notebook');
notebookButton.addEventListener('click', function () {
notebookAction[0].perform();
});
}
}
function closeOverlay() {
$(frame).addClass('frame frame-template');
overlayContainer.removeChild(frame);
layoutContainer.appendChild(frame);
document.body.removeChild(overlay);
closeButton.removeEventListener('click', toggleOverlay);
closeButton = undefined;
doneButton.removeEventListener('click', toggleOverlay);
doneButton = undefined;
blocker.removeEventListener('click', toggleOverlay);
blocker = undefined;
overlayContainer = undefined;
overlay = undefined;
if (notebookButton) {
notebookButton.removeChild(notebookButtonEl);
}
}
toggleOverlay = function (event) {
event.stopPropagation();
if (!isOpen) {
openOverlay();
isOpen = true;
} else {
closeOverlay();
isOpen = false;
}
};
$element.on('click', toggleOverlay);
$scope.$on('$destroy', function () {
$element.off('click', toggleOverlay);
});
}
return {
restrict: 'A',
link: link
};
}
return MCTModalNotebook;
});

View File

@@ -1,133 +0,0 @@
/*****************************************************************************
* Open MCT, Copyright (c) 2014-2016, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open MCT is licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* Open MCT includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
define(['zepto'], function ($) {
/**
*/
function MCTSnapshot($rootScope,$document,exportImageService,dialogService,notificationService) {
var document = $document[0];
function link($scope, $element,$attrs) {
var element = $element[0];
var layoutContainer = element.parentElement,
toggleOverlay,
makeImg,
saveImg,
snapshot = document.createElement('div');
function openOverlay() {
// Remove frame classes from being applied in a non-frame context
$(snapshot).addClass('abs overlay l-large-view snapshot');
snapshot.appendChild(element);
document.body.appendChild(snapshot);
}
function closeOverlay() {
if (snapshot) {
snapshot.removeChild(element);
layoutContainer.remove();
}
document.body.removeChild(snapshot);
snapshot = undefined;
$element.remove();
}
toggleOverlay = function () {
openOverlay();
makeImg(element);
};
makeImg = function (el) {
var scope = $scope;
var dialog = dialogService.showBlockingMessage({
title: "Saving...",
hint: "Taking Snapshot...",
unknownProgress: true,
severity: "info",
delay: true
});
window.setTimeout(function () {
window.EXPORT_IMAGE_TIMEOUT = 5000;
exportImageService.exportPNGtoSRC(el).then(function (img) {
if (img) {
if (dialog) {
dialog.dismiss();
}
if ($element[0].dataset.entry && $element[0].dataset.embed) {
saveImg(img, +$element[0].dataset.entry, +$element[0].dataset.embed);
closeOverlay();
} else {
var reader = new window.FileReader();
reader.readAsDataURL(img);
reader.onloadend = function () {
$($element[0]).attr("data-snapshot", reader.result);
$rootScope.snapshot = {'src': reader.result,
'type': img.type,
'size': img.size,
'modified': Date.now()
};
closeOverlay(false);
scope.$destroy();
};
}
} else {
dialog.dismiss();
}
}, function (error) {
if (dialog) {
dialog.dismiss();
}
closeOverlay();
});
}, 500);
window.EXPORT_IMAGE_TIMEOUT = 500;
};
saveImg = function (url,entryId,embedId) {
$scope.$parent.$parent.$parent.saveSnap(url, embedId, entryId);
};
if ($(document.body).find('.overlay.snapshot').length === 0) {
toggleOverlay();
}
$scope.$on('$destroy', function () {
$element.off('click', toggleOverlay);
$element.remove();
});
}
return {
restrict: 'A',
link: link
};
}
return MCTSnapshot;
});

View File

@@ -1,44 +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.
*****************************************************************************/
/**
* This bundle implements "containment" rules, which determine which objects
* can be contained within a notebook.
*/
define(
[],
function () {
function CompositionPolicy() {
}
CompositionPolicy.prototype.allow = function (parent, child) {
var parentDef = parent.getCapability('type').getName();
if (parentDef === 'Notebook' && child.getCapability('status').list().length) {
return false;
}
return true;
};
return CompositionPolicy;
}
);

View File

@@ -1,40 +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 () {
function ViewPolicy() {
}
ViewPolicy.prototype.allow = function (view, domainObject) {
// if (view.key === 'layout') {
// return false;
// }
return true;
};
return ViewPolicy;
}
);

View File

@@ -21,7 +21,7 @@
-->
<div class="angular-w l-flex-col flex-elem grows holder" ng-controller="SearchController as controller">
<div class="l-flex-col flex-elem grows holder holder-search" ng-controller="SearchMenuController as menuController">
<div class="search-bar search-filter-by-type flex-elem l-flex-row"
<div class="search-bar flex-elem l-flex-row"
ng-controller="ToggleController as toggle"
ng-class="{ holder: !(ngModel.input === '' || ngModel.input === undefined) }">
<div class="holder flex-elem grows">

View File

@@ -31,8 +31,7 @@ define([
'./policies/AdapterCompositionPolicy',
'./policies/AdaptedViewPolicy',
'./runs/AlternateCompositionInitializer',
'./runs/TimeSettingsURLHandler',
'./runs/TypeDeprecationChecker'
'./runs/TimeSettingsURLHandler'
], function (
legacyRegistry,
ActionDialogDecorator,
@@ -44,8 +43,7 @@ define([
AdapterCompositionPolicy,
AdaptedViewPolicy,
AlternateCompositionInitializer,
TimeSettingsURLHandler,
TypeDeprecationChecker
TimeSettingsURLHandler
) {
legacyRegistry.register('src/adapter', {
"extensions": {
@@ -109,10 +107,6 @@ define([
}
],
runs: [
{
implementation: TypeDeprecationChecker,
depends: ["types[]"]
},
{
implementation: AlternateCompositionInitializer,
depends: ["openmct"]

View File

@@ -1,47 +0,0 @@
/*****************************************************************************
* Open openmct, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open openmct 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 openmct includes source code licensed under additional open source
* licenses. See the Open Source Licenses file (LICENSES.md) included with
* this source code distribution or the Licensing information page available
* at runtime from the About dialog for additional information.
*****************************************************************************/
/* global console */
define([
], function (
) {
function checkForDeprecatedFunctionality(typeDef) {
if (typeDef.hasOwnProperty('telemetry')) {
console.warn(
'DEPRECATION WARNING: Telemetry data on type ' +
'registrations will be deprecated in a future version, ' +
'please convert to a custom telemetry metadata provider ' +
'for type: ' + typeDef.key
);
}
}
function TypeDeprecationChecker(types) {
types.forEach(checkForDeprecatedFunctionality);
}
return TypeDeprecationChecker;
});

View File

@@ -1,269 +0,0 @@
define([
'./CompositionAPI',
'./CompositionCollection'
], function (
CompositionAPI,
CompositionCollection
) {
describe('The Composition API', function () {
var publicAPI;
var compositionAPI;
var topicService;
var mutationTopic;
beforeEach(function () {
mutationTopic = jasmine.createSpyObj('mutationTopic', [
'listen'
]);
topicService = jasmine.createSpy('topicService');
topicService.andReturn(mutationTopic);
publicAPI = {};
publicAPI.objects = jasmine.createSpyObj('ObjectAPI', [
'get'
]);
publicAPI.objects.get.andCallFake(function (identifier) {
return Promise.resolve({identifier: identifier});
});
publicAPI.$injector = jasmine.createSpyObj('$injector', [
'get'
]);
publicAPI.$injector.get.andReturn(topicService);
compositionAPI = new CompositionAPI(publicAPI);
});
it('returns falsy if an object does not support composition', function () {
expect(compositionAPI.get({})).toBeFalsy();
});
describe('default composition', function () {
var domainObject;
var composition;
beforeEach(function () {
domainObject = {
name: 'test folder',
identifier: {
namespace: 'test',
key: '1'
},
composition: [
{
namespace: 'test',
key: 'a'
}
]
};
composition = compositionAPI.get(domainObject);
});
it('returns composition collection', function () {
expect(composition).toBeDefined();
expect(composition).toEqual(jasmine.any(CompositionCollection));
});
it('loads composition from domain object', function () {
var listener = jasmine.createSpy('addListener');
var loaded = false;
composition.on('add', listener);
composition.load()
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(listener.calls.length).toBe(1);
expect(listener).toHaveBeenCalledWith({
identifier: {namespace: 'test', key: 'a'}
});
});
});
// TODO: Implement add/removal in new default provider.
xit('synchronizes changes between instances', function () {
var otherComposition = compositionAPI.get(domainObject);
var addListener = jasmine.createSpy('addListener');
var removeListener = jasmine.createSpy('removeListener');
var otherAddListener = jasmine.createSpy('otherAddListener');
var otherRemoveListener = jasmine.createSpy('otherRemoveListener');
composition.on('add', addListener);
composition.on('remove', removeListener);
otherComposition.on('add', otherAddListener);
otherComposition.on('remove', otherRemoveListener);
var loaded = false;
Promise.all([composition.load(), otherComposition.load()])
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
expect(removeListener).not.toHaveBeenCalled();
expect(otherRemoveListener).not.toHaveBeenCalled();
var object = addListener.mostRecentCall.args[0];
composition.remove(object);
expect(removeListener).toHaveBeenCalled();
expect(otherRemoveListener).toHaveBeenCalled();
addListener.reset();
otherAddListener.reset();
composition.add(object);
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
removeListener.reset();
otherRemoveListener.reset();
otherComposition.remove(object);
expect(removeListener).toHaveBeenCalled();
expect(otherRemoveListener).toHaveBeenCalled();
addListener.reset();
otherAddListener.reset();
otherComposition.add(object);
expect(addListener).toHaveBeenCalled();
expect(otherAddListener).toHaveBeenCalled();
});
});
});
describe('static custom composition', function () {
var customProvider;
var domainObject;
var composition;
beforeEach(function () {
// A simple custom provider, returns the same composition for
// all objects of a given type.
customProvider = {
appliesTo: function (object) {
return object.type === 'custom-object-type';
},
load: function (object) {
return Promise.resolve([
{
namespace: 'custom',
key: 'thing'
}
]);
}
};
domainObject = {
identifier: {
namespace: 'test',
key: '1'
},
type: 'custom-object-type'
};
compositionAPI.addProvider(customProvider);
composition = compositionAPI.get(domainObject);
});
it('supports listening and loading', function () {
var listener = jasmine.createSpy('addListener');
var loaded = false;
composition.on('add', listener);
composition.load()
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(listener.calls.length).toBe(1);
expect(listener).toHaveBeenCalledWith({
identifier: {namespace: 'custom', key: 'thing'}
});
});
});
});
describe('dynamic custom composition', function () {
var customProvider;
var domainObject;
var composition;
beforeEach(function () {
// A dynamic provider, loads an empty composition and exposes
// listener functions.
customProvider = jasmine.createSpyObj('dynamicProvider', [
'appliesTo',
'load',
'on',
'off'
]);
customProvider.appliesTo.andReturn('true');
customProvider.load.andReturn(Promise.resolve([]));
domainObject = {
identifier: {
namespace: 'test',
key: '1'
},
type: 'custom-object-type'
};
compositionAPI.addProvider(customProvider);
composition = compositionAPI.get(domainObject);
});
it('supports listening and loading', function () {
var addListener = jasmine.createSpy('addListener');
var removeListener = jasmine.createSpy('removeListener');
var loaded = false;
composition.on('add', addListener);
composition.on('remove', removeListener);
expect(customProvider.on).toHaveBeenCalledWith(
domainObject,
'add',
jasmine.any(Function),
jasmine.any(CompositionCollection)
);
expect(customProvider.on).toHaveBeenCalledWith(
domainObject,
'remove',
jasmine.any(Function),
jasmine.any(CompositionCollection)
);
var add = customProvider.on.calls[0].args[2];
var remove = customProvider.on.calls[1].args[2];
composition.load()
.then(function () {
loaded = true;
});
waitsFor(function () {
return loaded;
});
runs(function () {
expect(addListener).not.toHaveBeenCalled();
expect(removeListener).not.toHaveBeenCalled();
add({namespace: 'custom', key: 'thing'});
});
waitsFor(function () {
return addListener.calls.length > 0;
});
runs(function () {
expect(addListener).toHaveBeenCalledWith({
identifier: {namespace: 'custom', key: 'thing'}
});
remove(addListener.mostRecentCall.args[0]);
});
waitsFor(function () {
return removeListener.calls.length > 0;
});
runs(function () {
expect(removeListener).toHaveBeenCalledWith({
identifier: {namespace: 'custom', key: 'thing'}
});
});
});
});
});
});

View File

@@ -1,128 +0,0 @@
/*****************************************************************************
* Open openmct, Copyright (c) 2014-2018, United States Government
* as represented by the Administrator of the National Aeronautics and Space
* Administration. All rights reserved.
*
* Open openmct 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 openmct 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([
'lodash'
], function (
_
) {
/**
* This is the default metadata provider; for any object with a "telemetry"
* property, this provider will return the value of that property as the
* telemetry metadata.
*
* This provider also implements legacy support for telemetry metadata
* defined on the type. Telemetry metadata definitions on type will be
* depreciated in the future.
*/
function DefaultMetadataProvider(openmct) {
this.openmct = openmct;
}
/**
* Applies to any domain object with a telemetry property, or whose type
* definition has a telemetry property.
*/
DefaultMetadataProvider.prototype.appliesTo = function (domainObject) {
return !!domainObject.telemetry || !!this.typeHasTelemetry(domainObject);
};
/**
* Retrieves valueMetadata from legacy metadata.
* @private
*/
function valueMetadatasFromOldFormat(metadata) {
var valueMetadatas = [];
valueMetadatas.push({
key: 'name',
name: 'Name'
});
metadata.domains.forEach(function (domain, index) {
var valueMetadata = _.clone(domain);
valueMetadata.hints = {
domain: index + 1
};
valueMetadatas.push(valueMetadata);
});
metadata.ranges.forEach(function (range, index) {
var valueMetadata = _.clone(range);
valueMetadata.hints = {
range: index,
priority: index + metadata.domains.length + 1
};
if (valueMetadata.type === 'enum') {
valueMetadata.key = 'enum';
valueMetadata.hints.y -= 10;
valueMetadata.hints.range -= 10;
valueMetadata.enumerations =
_.sortBy(valueMetadata.enumerations.map(function (e) {
return {
string: e.string,
value: +e.value
};
}), 'e.value');
valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
valueMetadata.max = _.max(valueMetadata.values);
valueMetadata.min = _.min(valueMetadata.values);
}
valueMetadatas.push(valueMetadata);
});
return valueMetadatas;
}
/**
* Returns telemetry metadata for a given domain object.
*/
DefaultMetadataProvider.prototype.getMetadata = function (domainObject) {
var metadata = domainObject.telemetry || {};
if (this.typeHasTelemetry(domainObject)) {
var typeMetadata = this.typeService.getType(domainObject.type).typeDef.telemetry;
_.extend(metadata, typeMetadata);
if (!metadata.values) {
metadata.values = valueMetadatasFromOldFormat(metadata);
}
}
return metadata;
};
/**
* @private
*/
DefaultMetadataProvider.prototype.typeHasTelemetry = function (domainObject) {
if (!this.typeService) {
this.typeService = this.openmct.$injector.get('typeService');
}
return !!this.typeService.getType(domainObject.type).typeDef.telemetry;
};
return DefaultMetadataProvider;
});

View File

@@ -1,9 +1,9 @@
/*****************************************************************************
* Open openmct, Copyright (c) 2014-2017, United States Government
* 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 openmct is licensed under the Apache License, Version 2.0 (the
* 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.
@@ -14,7 +14,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*
* Open openmct includes source code licensed under additional open source
* 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.
@@ -23,13 +23,11 @@
define([
'./TelemetryMetadataManager',
'./TelemetryValueFormatter',
'./DefaultMetadataProvider',
'../objects/object-utils',
'lodash'
], function (
TelemetryMetadataManager,
TelemetryValueFormatter,
DefaultMetadataProvider,
objectUtils,
_
) {
@@ -133,11 +131,10 @@ define([
* @augments module:openmct.TelemetryAPI~TelemetryProvider
* @memberof module:openmct
*/
function TelemetryAPI(openmct) {
this.openmct = openmct;
function TelemetryAPI(MCT) {
this.MCT = MCT;
this.requestProviders = [];
this.subscriptionProviders = [];
this.metadataProviders = [new DefaultMetadataProvider(this.openmct)];
this.metadataCache = new WeakMap();
this.formatMapCache = new WeakMap();
this.valueFormatterCache = new WeakMap();
@@ -204,13 +201,13 @@ define([
*/
TelemetryAPI.prototype.standardizeRequestOptions = function (options) {
if (!options.hasOwnProperty('start')) {
options.start = this.openmct.time.bounds().start;
options.start = this.MCT.time.bounds().start;
}
if (!options.hasOwnProperty('end')) {
options.end = this.openmct.time.bounds().end;
options.end = this.MCT.time.bounds().end;
}
if (!options.hasOwnProperty('domain')) {
options.domain = this.openmct.time.timeSystem().key;
options.domain = this.MCT.time.timeSystem().key;
}
};
@@ -303,34 +300,17 @@ define([
*/
TelemetryAPI.prototype.getMetadata = function (domainObject) {
if (!this.metadataCache.has(domainObject)) {
var metadataProvider = this.metadataProviders.filter(function (p) {
return p.appliesTo(domainObject);
})[0];
if (!metadataProvider) {
return;
if (!this.typeService) {
this.typeService = this.MCT.$injector.get('typeService');
}
var metadata = metadataProvider.getMetadata(domainObject);
this.metadataCache.set(
domainObject,
new TelemetryMetadataManager(metadata)
new TelemetryMetadataManager(domainObject, this.typeService)
);
}
return this.metadataCache.get(domainObject);
};
/**
* Register a telemetry metadata provider. This allows a developer to
* provide telemetry metadata for objects dynamically, instead of using
* the default provider.
*/
TelemetryAPI.prototype.addMetadataProvider = function (provider) {
this.metadataProviders.unshift(provider);
};
/**
* Return an array of valueMetadatas that are common to all supplied
* telemetry objects and match the requested hints.
@@ -363,7 +343,7 @@ define([
TelemetryAPI.prototype.getValueFormatter = function (valueMetadata) {
if (!this.valueFormatterCache.has(valueMetadata)) {
if (!this.formatService) {
this.formatService = this.openmct.$injector.get('formatService');
this.formatService = this.MCT.$injector.get('formatService');
}
this.valueFormatterCache.set(
valueMetadata,
@@ -395,7 +375,7 @@ define([
* @param {Format} format the
*/
TelemetryAPI.prototype.addFormat = function (format) {
this.openmct.legacyExtension('formats', {
this.MCT.legacyExtension('formats', {
key: format.key,
implementation: function () {
return format;

View File

@@ -27,6 +27,51 @@ define([
_
) {
function valueMetadatasFromOldFormat(metadata) {
var valueMetadatas = [];
valueMetadatas.push({
key: 'name',
name: 'Name'
});
metadata.domains.forEach(function (domain, index) {
var valueMetadata = _.clone(domain);
valueMetadata.hints = {
domain: index + 1
};
valueMetadatas.push(valueMetadata);
});
metadata.ranges.forEach(function (range, index) {
var valueMetadata = _.clone(range);
valueMetadata.hints = {
range: index,
priority: index + metadata.domains.length + 1
};
if (valueMetadata.type === 'enum') {
valueMetadata.key = 'enum';
valueMetadata.hints.y -= 10;
valueMetadata.hints.range -= 10;
valueMetadata.enumerations =
_.sortBy(valueMetadata.enumerations.map(function (e) {
return {
string: e.string,
value: +e.value
};
}), 'e.value');
valueMetadata.values = _.pluck(valueMetadata.enumerations, 'value');
valueMetadata.max = _.max(valueMetadata.values);
valueMetadata.min = _.min(valueMetadata.values);
}
valueMetadatas.push(valueMetadata);
});
return valueMetadatas;
}
function applyReasonableDefaults(valueMetadata, index) {
valueMetadata.source = valueMetadata.source || valueMetadata.key;
valueMetadata.hints = valueMetadata.hints || {};
@@ -74,14 +119,24 @@ define([
}
/**
* Utility class for handling and inspecting telemetry metadata. Applies
* reasonable defaults to simplify the task of providing metadata, while
* also providing methods for interrogating telemetry metadata.
* Utility class for handling telemetry metadata for a domain object.
* Wraps old format metadata to new format metadata.
* Provides methods for interrogating telemetry metadata.
*/
function TelemetryMetadataManager(metadata) {
this.metadata = metadata;
function TelemetryMetadataManager(domainObject, typeService) {
this.metadata = domainObject.telemetry || {};
this.valueMetadatas = this.metadata.values.map(applyReasonableDefaults);
if (this.metadata.values) {
this.valueMetadatas = this.metadata.values;
} else {
var typeMetadata = typeService
.getType(domainObject.type).typeDef.telemetry;
_.extend(this.metadata, typeMetadata);
this.valueMetadatas = valueMetadatasFromOldFormat(this.metadata);
}
this.valueMetadatas = this.valueMetadatas.map(applyReasonableDefaults);
}
/**

View File

@@ -38,6 +38,7 @@ define([
'../example/msl/bundle',
'../example/notifications/bundle',
'../example/persistence/bundle',
'../example/plotOptions/bundle',
'../example/policy/bundle',
'../example/profiling/bundle',
'../example/scratchpad/bundle',

View File

@@ -166,6 +166,11 @@
ng-show="plotHistory.length"
style="position: absolute; top: 8px; right: 8px;">
<a class="s-button icon-brackets"
ng-click="plot.syncConductor()"
title="Synchronize Time Conductor to plot bounds">
</a>
<a class="s-button icon-arrow-left"
ng-click="plot.back()"
title="Restore previous pan/zoom">

View File

@@ -75,6 +75,7 @@ function (
this.$scope.$watch('highlights', this.scheduleDraw);
this.$scope.$watch('rectangles', this.scheduleDraw);
this.config.series.forEach(this.onSeriesAdd, this);
window.chart = this;
}
eventHelpers.extend(MCTChartController.prototype);

View File

@@ -85,19 +85,6 @@ define([
this.listenTo(this, 'destroy', this.onDestroy, this);
},
/**
* Retrieve the persisted series config for a given identifier.
*/
getPersistedSeriesConfig: function (identifier) {
var domainObject = this.get('domainObject');
if (!domainObject.configuration || !domainObject.configuration.series) {
return;
}
return domainObject.configuration.series.filter(function (seriesConfig) {
return seriesConfig.identifier.key === identifier.key &&
seriesConfig.identifier.namespace === identifier.namespace;
})[0];
},
/**
* Update the domain object with the given value.
*/

View File

@@ -165,13 +165,10 @@ define([
return;
}
var valueMetadata = this.metadata.value(newKey);
var persistedConfig = this.get('persistedConfiguration');
if (!persistedConfig || !persistedConfig.interpolate) {
if (valueMetadata.format === 'enum') {
this.set('interpolate', 'stepAfter');
} else {
this.set('interpolate', 'linear');
}
if (valueMetadata.format === 'enum') {
this.set('interpolate', 'stepAfter');
} else {
this.set('interpolate', 'linear');
}
this.evaluate = function (datum) {
return this.limitEvaluator.evaluate(datum, valueMetadata);
@@ -236,6 +233,8 @@ define([
* @returns {Promise}
*/
load: function (options) {
this.resetOnAppend = true;
return this.fetch(options)
.then(function (res) {
this.emit('load');

View File

@@ -41,7 +41,6 @@ define([
this.palette = new color.ColorPalette();
this.listenTo(this, 'add', this.onSeriesAdd, this);
this.listenTo(this, 'remove', this.onSeriesRemove, this);
this.listenTo(this.plot, 'change:domainObject', this.trackPersistedConfig, this);
var domainObject = this.plot.get('domainObject');
if (domainObject.telemetry) {
@@ -50,14 +49,6 @@ define([
this.watchTelemetryContainer(domainObject);
}
},
trackPersistedConfig: function (domainObject) {
domainObject.configuration.series.forEach(function (seriesConfig) {
var series = this.byIdentifier(seriesConfig.identifier);
if (series) {
series.set('persistedConfiguration', seriesConfig);
}
}, this);
},
watchTelemetryContainer: function (domainObject) {
var composition = this.openmct.composition.get(domainObject);
this.listenTo(composition, 'add', this.addTelemetryObject, this);
@@ -84,9 +75,6 @@ define([
seriesConfig = JSON.parse(JSON.stringify(seriesConfig));
}
seriesConfig.persistedConfiguration =
this.plot.getPersistedSeriesConfig(domainObject.identifier);
this.add(new PlotSeries({
model: seriesConfig,
domainObject: domainObject,
@@ -137,19 +125,12 @@ define([
},
updateColorPalette: function (newColor, oldColor) {
this.palette.remove(newColor);
var seriesWithColor = this.filter(function (series) {
var seriesWithColor = this.series.filter(function (series) {
return series.get('color') === newColor;
})[0];
if (!seriesWithColor) {
this.palette.return(oldColor);
}
},
byIdentifier: function (identifier) {
return this.filter(function (series) {
var seriesIdentifier = series.get('identifier');
return seriesIdentifier.namespace === identifier.namespace &&
seriesIdentifier.key === identifier.key;
})[0];
}
});

View File

@@ -48,6 +48,7 @@ define([
this.configId = $scope.domainObject.getId();
this.setUpScope();
window.config = this;
}
eventHelpers.extend(PlotOptionsController.prototype);

View File

@@ -33,12 +33,13 @@ define([
* It supports pan and zoom, implements zoom history, and supports locating
* values near the cursor.
*/
function MCTPlotController($scope, $element, $window) {
function MCTPlotController($scope, $element, $window, openmct) {
this.$scope = $scope;
this.$scope.config = this.config;
this.$scope.plot = this;
this.$element = $element;
this.$window = $window;
this.openmct = openmct;
this.xScale = new LinearScale(this.config.xAxis.get('displayRange'));
this.yScale = new LinearScale(this.config.yAxis.get('displayRange'));
@@ -53,6 +54,7 @@ define([
this.listenTo(this.$scope, 'plot:clearHistory', this.clear, this);
this.initialize();
window.control = this;
}
MCTPlotController.$inject = ['$scope', '$element', '$window'];
@@ -331,6 +333,13 @@ define([
this.$scope.$emit('user:viewport:change:end');
};
MCTPlotController.prototype.syncConductor = function () {
var xDisplayRange = this.config.xAxis.get('displayRange');
this.openmct.time.stopClock();
this.openmct.time.bounds({start: xDisplayRange.min, end: xDisplayRange.max});
};
MCTPlotController.prototype.destroy = function () {
this.stopListening();
};

View File

@@ -33,7 +33,7 @@ define([
return {
restrict: "E",
template: PlotTemplate,
controller: MCTPlotController,
controller: ['$scope', '$element', '$window', 'openmct', MCTPlotController],
controllerAs: 'mctPlotController',
bindToController: {
config: "="

View File

@@ -117,6 +117,12 @@ define([
this.$scope = $scope;
this.$element = $element;
if (!window.ticks) {
window.ticks = [];
}
window.ticks.push(this);
this.tickCount = 4;
this.tickUpdate = false;
this.listenTo(this.axis, 'change:displayRange', this.updateTicks, this);

View File

@@ -141,17 +141,6 @@ define(
});
};
/**
* Takes a screenshot of a DOM node in PNG format.
* @param {node} element to be exported
* @param {string} filename the exported image
* @returns {promise}
*/
ExportImageService.prototype.exportPNGtoSRC = function (element) {
return this.renderElement(element, "png");
};
/**
* canvas.toBlob() not supported in IE < 10, Opera, and Safari. This polyfill
* implements the method in browsers that would not otherwise support it.

View File

@@ -71,6 +71,7 @@ define([
this.config.series.forEach(this.addSeries, this);
this.followTimeConductor();
window.plot = this;
}
eventHelpers.extend(PlotController.prototype);

View File

@@ -27,13 +27,11 @@ define([
'./autoflow/AutoflowTabularPlugin',
'./timeConductor/plugin',
'../../example/imagery/plugin',
'../../platform/features/notebook/bundle',
'../../platform/import-export/bundle',
'./summaryWidget/plugin',
'./URLIndicatorPlugin/URLIndicatorPlugin',
'./telemetryMean/plugin',
'./plot/plugin',
'./staticRootPlugin/plugin'
'./plot/plugin'
], function (
_,
UTCTimeSystem,
@@ -41,13 +39,11 @@ define([
AutoflowPlugin,
TimeConductorPlugin,
ExampleImagery,
Notebook,
ImportExport,
SummaryWidget,
URLIndicatorPlugin,
TelemetryMean,
PlotPlugin,
StaticRootPlugin
PlotPlugin
) {
var bundleMap = {
CouchDB: 'platform/persistence/couch',
@@ -55,7 +51,6 @@ define([
Espresso: 'platform/commonUI/themes/espresso',
LocalStorage: 'platform/persistence/local',
MyItems: 'platform/features/my-items',
Notebook: 'platform/features/notebook',
Snow: 'platform/commonUI/themes/snow'
};
@@ -71,8 +66,6 @@ define([
plugins.ImportExport = ImportExport;
plugins.StaticRootPlugin = StaticRootPlugin;
/**
* A tabular view showing the latest values of multiple telemetry points at
* once. Formatted so that labels and values are aligned.

View File

@@ -1,78 +0,0 @@
define([
'../../api/objects/object-utils'
], function (
objectUtils
) {
/**
* Transforms an import json blob into a object map that can be used to
* provide objects. Rewrites root identifier in import data with provided
* rootIdentifier, and rewrites all child object identifiers so that they
* exist in the same namespace as the rootIdentifier.
*/
function rewriteObjectIdentifiers(importData, rootIdentifier) {
var rootId = importData.rootId;
var objectString = JSON.stringify(importData.openmct);
Object.keys(importData.openmct).forEach(function (originalId, i) {
var newId;
if (originalId === rootId) {
newId = objectUtils.makeKeyString(rootIdentifier);
} else {
newId = objectUtils.makeKeyString({
namespace: rootIdentifier.namespace,
key: i
});
}
while (objectString.indexOf(originalId) !== -1) {
objectString = objectString.replace(
'"' + originalId + '"',
'"' + newId + '"'
);
}
});
return JSON.parse(objectString);
}
/**
* Convets all objects in an object make from old format objects to new
* format objects.
*/
function convertToNewObjects(oldObjectMap) {
return Object.keys(oldObjectMap)
.reduce(function (newObjectMap, key) {
newObjectMap[key] = objectUtils.toNewFormat(oldObjectMap[key], key);
return newObjectMap;
}, {});
}
/* Set the root location correctly for a top-level object */
function setRootLocation(objectMap, rootIdentifier) {
objectMap[objectUtils.makeKeyString(rootIdentifier)].location = 'ROOT';
return objectMap;
}
/**
* Takes importData (as provided by the ImportExport plugin) and exposes
* an object provider to fetch those objects.
*/
function StaticModelProvider(importData, rootIdentifier) {
var oldFormatObjectMap = rewriteObjectIdentifiers(importData, rootIdentifier);
var newFormatObjectMap = convertToNewObjects(oldFormatObjectMap);
this.objectMap = setRootLocation(newFormatObjectMap, rootIdentifier);
}
/**
* Standard "Get".
*/
StaticModelProvider.prototype.get = function (identifier) {
var keyString = objectUtils.makeKeyString(identifier);
if (this.objectMap[keyString]) {
return this.objectMap[keyString];
}
throw new Error(keyString + ' not found in import models.');
};
return StaticModelProvider;
});

View File

@@ -1,133 +0,0 @@
define([
'./StaticModelProvider',
'text!./static-provider-test.json'
], function (
StaticModelProvider,
testStaticDataText
) {
describe('StaticModelProvider', function () {
var staticProvider;
beforeEach(function () {
var staticData = JSON.parse(testStaticDataText);
staticProvider = new StaticModelProvider(staticData, {
namespace: 'my-import',
key: 'root'
});
});
describe('rootObject', function () {
var rootModel;
beforeEach(function () {
rootModel = staticProvider.get({
namespace: 'my-import',
key: 'root'
});
});
it('is located at top level', function () {
expect(rootModel.location).toBe('ROOT');
});
it('has new-format identifier', function () {
expect(rootModel.identifier).toEqual({
namespace: 'my-import',
key: 'root'
});
});
it('has new-format composition', function () {
expect(rootModel.composition).toContain({
namespace: 'my-import',
key: '1'
});
expect(rootModel.composition).toContain({
namespace: 'my-import',
key: '2'
});
});
});
describe('childObjects', function () {
var swg;
var layout;
var fixed;
beforeEach(function () {
swg = staticProvider.get({
namespace: 'my-import',
key: '1'
});
layout = staticProvider.get({
namespace: 'my-import',
key: '2'
});
fixed = staticProvider.get({
namespace: 'my-import',
key: '3'
});
});
it('match expected ordering', function () {
// this is a sanity check to make sure the identifiers map in
// the correct order.
expect(swg.type).toBe('generator');
expect(layout.type).toBe('layout');
expect(fixed.type).toBe('telemetry.fixed');
});
it('have new-style identifiers', function () {
expect(swg.identifier).toEqual({
namespace: 'my-import',
key: '1'
});
expect(layout.identifier).toEqual({
namespace: 'my-import',
key: '2'
});
expect(fixed.identifier).toEqual({
namespace: 'my-import',
key: '3'
});
});
it('have new-style composition', function () {
expect(layout.composition).toContain({
namespace: 'my-import',
key: '1'
});
expect(layout.composition).toContain({
namespace: 'my-import',
key: '3'
});
expect(fixed.composition).toContain({
namespace: 'my-import',
key: '1'
});
});
it('rewrites locations', function () {
expect(swg.location).toBe('my-import:root');
expect(layout.location).toBe('my-import:root');
expect(fixed.location).toBe('my-import:2');
});
it('rewrites matched identifiers in objects', function () {
expect(layout.configuration.layout.panels['my-import:1'])
.toBeDefined();
expect(layout.configuration.layout.panels['my-import:3'])
.toBeDefined();
expect(layout.configuration.layout.panels['483c00d4-bb1d-4b42-b29a-c58e06b322a0'])
.not.toBeDefined();
expect(layout.configuration.layout.panels['20273193-f069-49e9-b4f7-b97a87ed755d'])
.not.toBeDefined();
expect(fixed.configuration['fixed-display'].elements[0].id)
.toBe('my-import:1');
});
});
});
});

View File

@@ -1,51 +0,0 @@
define([
'./StaticModelProvider'
], function (
StaticModelProvider
) {
/**
* Static Root Plugin: takes an export file and exposes it as a new root
* object.
*/
function StaticRootPlugin(namespace, exportUrl) {
var rootIdentifier = {
namespace: namespace,
key: 'root'
};
var cachedProvider;
var loadProvider = function () {
return fetch(exportUrl)
.then(function (response) {
return response.json();
})
.then(function (importData) {
cachedProvider = new StaticModelProvider(importData, rootIdentifier);
return cachedProvider;
});
};
var getProvider = function () {
if (!cachedProvider) {
cachedProvider = loadProvider();
}
return Promise.resolve(cachedProvider);
};
return function install(openmct) {
openmct.objects.addRoot(rootIdentifier);
openmct.objects.addProvider(namespace, {
get: function (identifier) {
return getProvider().then(function (provider) {
return provider.get(identifier);
});
}
});
};
}
return StaticRootPlugin;
});

View File

@@ -1 +0,0 @@
{"openmct":{"a9122832-4b6e-43ea-8219-5359c14c5de8":{"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0","d2ac3ae4-0af2-49fe-81af-adac09936215"],"name":"import-provider-test","type":"folder","notes":"test data for import provider.","modified":1508522673278,"location":"mine","persisted":1508522673278},"483c00d4-bb1d-4b42-b29a-c58e06b322a0":{"telemetry":{"period":10,"amplitude":1,"offset":0,"dataRateInHz":1,"values":[{"key":"utc","name":"Time","format":"utc","hints":{"domain":1,"priority":0},"source":"utc"},{"key":"yesterday","name":"Yesterday","format":"utc","hints":{"domain":2,"priority":1},"source":"yesterday"},{"key":"sin","name":"Sine","hints":{"range":1,"priority":2},"source":"sin"},{"key":"cos","name":"Cosine","hints":{"range":2,"priority":3},"source":"cos"}]},"name":"SWG-10","type":"generator","modified":1508522652874,"location":"a9122832-4b6e-43ea-8219-5359c14c5de8","persisted":1508522652874},"d2ac3ae4-0af2-49fe-81af-adac09936215":{"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0","20273193-f069-49e9-b4f7-b97a87ed755d"],"name":"Layout","type":"layout","configuration":{"layout":{"panels":{"483c00d4-bb1d-4b42-b29a-c58e06b322a0":{"position":[0,0],"dimensions":[17,8]},"20273193-f069-49e9-b4f7-b97a87ed755d":{"position":[0,8],"dimensions":[17,1],"hasFrame":false}}}},"modified":1508522745580,"location":"a9122832-4b6e-43ea-8219-5359c14c5de8","persisted":1508522745580},"20273193-f069-49e9-b4f7-b97a87ed755d":{"layoutGrid":[64,16],"composition":["483c00d4-bb1d-4b42-b29a-c58e06b322a0"],"name":"FP Test","type":"telemetry.fixed","configuration":{"fixed-display":{"elements":[{"type":"fixed.telemetry","x":0,"y":0,"id":"483c00d4-bb1d-4b42-b29a-c58e06b322a0","stroke":"transparent","color":"","titled":true,"width":8,"height":2,"useGrid":true,"size":"24px"}]}},"modified":1508522717619,"location":"d2ac3ae4-0af2-49fe-81af-adac09936215","persisted":1508522717619}},"rootId":"a9122832-4b6e-43ea-8219-5359c14c5de8"}

View File

@@ -1,14 +1,4 @@
define([
'./SummaryWidgetsCompositionPolicy',
'./src/telemetry/SummaryWidgetMetadataProvider',
'./src/telemetry/SummaryWidgetTelemetryProvider',
'./src/views/SummaryWidgetViewProvider'
], function (
SummaryWidgetsCompositionPolicy,
SummaryWidgetMetadataProvider,
SummaryWidgetTelemetryProvider,
SummaryWidgetViewProvider
) {
define(['./src/SummaryWidget', './SummaryWidgetsCompositionPolicy'], function (SummaryWidget, SummaryWidgetsCompositionPolicy) {
function plugin() {
@@ -19,40 +9,8 @@ define([
cssClass: 'icon-summary-widget',
initialize: function (domainObject) {
domainObject.composition = [];
domainObject.configuration = {
ruleOrder: ['default'],
ruleConfigById: {
default: {
name: 'Default',
label: 'Unnamed Rule',
message: '',
id: 'default',
icon: ' ',
style: {
'color': '#ffffff',
'background-color': '#38761d',
'border-color': 'rgba(0,0,0,0)'
},
description: 'Default appearance for the widget',
conditions: [{
object: '',
key: '',
operation: '',
values: []
}],
jsCondition: '',
trigger: 'any',
expanded: 'true'
}
},
testDataConfig: [{
object: '',
key: '',
value: ''
}]
};
domainObject.configuration = {};
domainObject.openNewTab = 'thisTab';
domainObject.telemetry = {};
},
form: [
{
@@ -82,14 +40,26 @@ define([
]
};
function initViewProvider(openmct) {
return {
name: 'Widget View',
view: function (domainObject) {
return new SummaryWidget(domainObject, openmct);
},
canView: function (domainObject) {
return (domainObject.type === 'summary-widget');
},
editable: true,
key: 'summaryWidgets'
};
}
return function install(openmct) {
openmct.types.addType('summary-widget', widgetType);
openmct.objectViews.addProvider(initViewProvider(openmct));
openmct.legacyExtension('policies', {category: 'composition',
implementation: SummaryWidgetsCompositionPolicy, depends: ['openmct']
});
openmct.telemetry.addMetadataProvider(new SummaryWidgetMetadataProvider(openmct));
openmct.telemetry.addProvider(new SummaryWidgetTelemetryProvider(openmct));
openmct.objectViews.addProvider(new SummaryWidgetViewProvider(openmct));
};
}

View File

@@ -1,10 +1,10 @@
<li class="has-local-controls t-condition">
<li class="t-condition">
<label class="t-condition-context">when</label>
<span class="controls">
<span class="t-configuration"> </span>
<span class="t-value-inputs"> </span>
</span>
<span class="flex-elem local-control l-condition-action-buttons-wrapper">
<span class="flex-elem l-condition-action-buttons-wrapper">
<a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this condition"></a>
<a class="s-icon-button icon-trash t-delete" title="Delete this condition"></a>
</span>

View File

@@ -1,9 +1,9 @@
<div>
<div class="l-compact-form has-local-controls l-widget-rule s-widget-rule">
<div class="l-widget-rule s-widget-rule l-compact-form">
<div class="widget-rule-header">
<span class="flex-elem l-widget-thumb-wrapper">
<span class="grippy-holder">
<span class="t-grippy grippy local-control"></span>
<span class="t-grippy grippy"></span>
</span>
<span class="view-control expanded"></span>
<span class="t-widget-thumb widget-thumb">
@@ -12,7 +12,7 @@
</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 l-rule-action-buttons-wrapper">
<span class="flex-elem 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>

View File

@@ -1,4 +1,4 @@
<div class="t-test-data-item l-compact-form has-local-controls l-widget-test-data-item s-widget-test-data-item">
<div class="t-test-data-item l-compact-form l-widget-test-data-item s-widget-test-data-item">
<ul>
<li>
<label>Set </label>
@@ -7,7 +7,7 @@
<span class="equal-to hidden"> equal to </span>
<span class="t-value-inputs"></span>
</span>
<span class="flex-elem local-control l-widget-test-data-item-action-buttons-wrapper">
<span class="flex-elem l-widget-test-data-item-action-buttons-wrapper">
<a class="s-icon-button icon-duplicate t-duplicate" title="Duplicate this test value"></a>
<a class="s-icon-button icon-trash t-delete" title="Delete this test value"></a>
</span>

View File

@@ -123,22 +123,21 @@ define ([
* has completed and types have been parsed
*/
ConditionManager.prototype.parsePropertyTypes = function (object) {
var telemetryAPI = this.openmct.telemetry,
key,
type,
self = this;
this.telemetryTypesById[object.identifier.key] = {};
Object.values(this.telemetryMetadataById[object.identifier.key]).forEach(function (valueMetadata) {
var type;
if (valueMetadata.hints.hasOwnProperty('range')) {
type = 'number';
} else if (valueMetadata.hints.hasOwnProperty('domain')) {
type = 'number';
} else if (valueMetadata.key === 'name') {
type = 'string';
} else {
type = 'string';
}
this.telemetryTypesById[object.identifier.key][valueMetadata.key] = type;
this.addGlobalPropertyType(valueMetadata.key, type);
}, this);
self.telemetryTypesById[object.identifier.key] = {};
return telemetryAPI.request(object, {size: 1, strategy: 'latest'}).then(function (telemetry) {
Object.entries(telemetry[telemetry.length - 1]).forEach(function (telem) {
key = telem[0];
type = typeof telem[1];
self.telemetryTypesById[object.identifier.key][key] = type;
self.subscriptionCache[object.identifier.key][key] = telem[1];
self.addGlobalPropertyType(key, type);
});
});
};
/**
@@ -148,9 +147,23 @@ define ([
* and property types parsed
*/
ConditionManager.prototype.parseAllPropertyTypes = function () {
Object.values(this.compositionObjs).forEach(this.parsePropertyTypes, this);
this.metadataLoadComplete = true;
this.eventEmitter.emit('metadata');
var self = this,
index = 0,
objs = Object.values(self.compositionObjs),
promise = new Promise(function (resolve, reject) {
if (objs.length === 0) {
resolve();
}
objs.forEach(function (obj) {
self.parsePropertyTypes(obj).then(function () {
if (index === objs.length - 1) {
resolve();
}
index += 1;
});
});
});
return promise;
};
/**
@@ -199,12 +212,6 @@ define ([
self.subscriptions[objId] = telemetryAPI.subscribe(obj, function (datum) {
self.handleSubscriptionCallback(objId, datum);
}, {});
telemetryAPI.request(obj, {strategy: 'latest', size: 1})
.then(function (results) {
if (results && results.length) {
self.handleSubscriptionCallback(objId, results[results.length - 1]);
}
});
/**
* if this is the initial load, parsing property types will be postponed
@@ -246,9 +253,13 @@ define ([
* @private
*/
ConditionManager.prototype.onCompositionLoad = function () {
this.loadComplete = true;
this.eventEmitter.emit('load');
this.parseAllPropertyTypes();
var self = this;
self.loadComplete = true;
self.eventEmitter.emit('load');
self.parseAllPropertyTypes().then(function () {
self.metadataLoadComplete = true;
self.eventEmitter.emit('metadata');
});
};
/**

View File

@@ -1,42 +0,0 @@
define([
'./SummaryWidgetEvaluator',
'../../../../api/objects/object-utils'
], function (
SummaryWidgetEvaluator,
objectUtils
) {
function EvaluatorPool(openmct) {
this.openmct = openmct;
this.byObjectId = {};
this.byEvaluator = new WeakMap();
}
EvaluatorPool.prototype.get = function (domainObject) {
var objectId = objectUtils.makeKeyString(domainObject);
var poolEntry = this.byObjectId[objectId];
if (!poolEntry) {
poolEntry = {
leases: 0,
objectId: objectId,
evaluator: new SummaryWidgetEvaluator(domainObject, this.openmct)
};
this.byEvaluator.set(poolEntry.evaluator, poolEntry);
this.byObjectId[objectId] = poolEntry;
}
poolEntry.leases += 1;
return poolEntry.evaluator;
};
EvaluatorPool.prototype.release = function (evaluator) {
var poolEntry = this.byEvaluator.get(evaluator);
poolEntry.leases -= 1;
if (poolEntry.leases === 0) {
evaluator.destroy();
this.byEvaluator.delete(evaluator);
delete this.byObjectId[poolEntry.objectId];
}
};
return EvaluatorPool;
});

View File

@@ -1,58 +0,0 @@
define([
'./operations'
], function (
OPERATIONS
) {
function SummaryWidgetCondition(definition) {
this.object = definition.object;
this.key = definition.key;
this.values = definition.values;
if (!definition.operation) {
// TODO: better handling for default rule.
this.evaluate = function () {
return true;
};
} else {
this.comparator = OPERATIONS[definition.operation].operation;
}
}
SummaryWidgetCondition.prototype.evaluate = function (telemetryState) {
var stateKeys = Object.keys(telemetryState);
var state;
var result;
var i;
if (this.object === 'any') {
for (i = 0; i < stateKeys.length; i++) {
state = telemetryState[stateKeys[i]];
result = this.evaluateState(state);
if (result) {
return true;
}
}
return false;
} else if (this.object === 'all') {
for (i = 0; i < stateKeys.length; i++) {
state = telemetryState[stateKeys[i]];
result = this.evaluateState(state);
if (!result) {
return false;
}
}
return true;
} else {
return this.evaluateState(telemetryState[this.object]);
}
};
SummaryWidgetCondition.prototype.evaluateState = function (state) {
var testValues = [
state.formats[this.key].parse(state.lastDatum)
].concat(this.values);
return this.comparator(testValues);
};
return SummaryWidgetCondition;
});

View File

@@ -1,120 +0,0 @@
define([
'./SummaryWidgetCondition'
], function (
SummaryWidgetCondition
) {
describe('SummaryWidgetCondition', function () {
var condition;
var telemetryState;
beforeEach(function () {
// Format map intentionally uses different keys than those present
// in datum, which serves to verify conditions use format map to get
// data.
var formatMap = {
adjusted: {
parse: function (datum) {
return datum.value + 10;
}
},
raw: {
parse: function (datum) {
return datum.value;
}
}
};
telemetryState = {
objectId: {
formats: formatMap,
lastDatum: {
}
},
otherObjectId: {
formats: formatMap,
lastDatum: {
}
}
};
});
it('can evaluate if a single object matches', function () {
condition = new SummaryWidgetCondition({
object: 'objectId',
key: 'raw',
operation: 'greaterThan',
values: [
10
]
});
telemetryState.objectId.lastDatum.value = 5;
expect(condition.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 15;
expect(condition.evaluate(telemetryState)).toBe(true);
});
it('can evaluate if a single object matches (alternate keys)', function () {
condition = new SummaryWidgetCondition({
object: 'objectId',
key: 'adjusted',
operation: 'greaterThan',
values: [
10
]
});
telemetryState.objectId.lastDatum.value = -5;
expect(condition.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 5;
expect(condition.evaluate(telemetryState)).toBe(true);
});
it('can evaluate "if all objects match"', function () {
condition = new SummaryWidgetCondition({
object: 'all',
key: 'raw',
operation: 'greaterThan',
values: [
10
]
});
telemetryState.objectId.lastDatum.value = 0;
telemetryState.otherObjectId.lastDatum.value = 0;
expect(condition.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 0;
telemetryState.otherObjectId.lastDatum.value = 15;
expect(condition.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 15;
telemetryState.otherObjectId.lastDatum.value = 0;
expect(condition.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 15;
telemetryState.otherObjectId.lastDatum.value = 15;
expect(condition.evaluate(telemetryState)).toBe(true);
});
it('can evalute "if any object matches"', function () {
condition = new SummaryWidgetCondition({
object: 'any',
key: 'raw',
operation: 'greaterThan',
values: [
10
]
});
telemetryState.objectId.lastDatum.value = 0;
telemetryState.otherObjectId.lastDatum.value = 0;
expect(condition.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 0;
telemetryState.otherObjectId.lastDatum.value = 15;
expect(condition.evaluate(telemetryState)).toBe(true);
telemetryState.objectId.lastDatum.value = 15;
telemetryState.otherObjectId.lastDatum.value = 0;
expect(condition.evaluate(telemetryState)).toBe(true);
telemetryState.objectId.lastDatum.value = 15;
telemetryState.otherObjectId.lastDatum.value = 15;
expect(condition.evaluate(telemetryState)).toBe(true);
});
});
});

View File

@@ -1,239 +0,0 @@
define([
'./SummaryWidgetRule',
'../eventHelpers',
'../../../../api/objects/object-utils',
'lodash'
], function (
SummaryWidgetRule,
eventHelpers,
objectUtils,
_
) {
/**
* evaluates rules defined in a summary widget against either lad or
* realtime state.
*
* Does not handle mutation.
*/
function SummaryWidgetEvaluator(domainObject, openmct) {
this.openmct = openmct;
this.baseState = {};
this.updateRules(domainObject);
this.removeObserver = openmct.objects.observe(
domainObject,
'*',
this.updateRules.bind(this)
);
var composition = openmct.composition.get(domainObject);
this.listenTo(composition, 'add', this.addChild, this);
this.listenTo(composition, 'remove', this.removeChild, this);
this.loadPromise = composition.load();
}
eventHelpers.extend(SummaryWidgetEvaluator.prototype);
/**
* Subscribes to realtime telemetry for the given summary widget.
*/
SummaryWidgetEvaluator.prototype.subscribe = function (callback) {
var active = true;
var unsubscribes = [];
this.getBaseStateClone()
.then(function (realtimeStates) {
if (!active) {
return;
}
var updateCallback = function () {
var datum = this.evaluateState(
realtimeStates,
this.openmct.time.timeSystem().key
);
callback(datum);
}.bind(this);
unsubscribes = _.map(
realtimeStates,
this.subscribeToObjectState.bind(this, updateCallback)
);
}.bind(this));
return function () {
active = false;
unsubscribes.forEach(function (unsubscribe) {
unsubscribe();
});
};
};
/**
* Returns a promise for a telemetry datum obtained by evaluating the
* current lad data.
*/
SummaryWidgetEvaluator.prototype.requestLatest = function (options) {
return this.getBaseStateClone()
.then(function (ladState) {
var promises = Object.values(ladState)
.map(this.updateObjectStateFromLAD.bind(this, options));
return Promise.all(promises)
.then(function () {
return ladState;
});
}.bind(this))
.then(function (ladStates) {
return this.evaluateState(ladStates, options.domain);
}.bind(this));
};
SummaryWidgetEvaluator.prototype.updateRules = function (domainObject) {
this.rules = domainObject.configuration.ruleOrder.map(function (ruleId) {
return new SummaryWidgetRule(domainObject.configuration.ruleConfigById[ruleId]);
});
};
SummaryWidgetEvaluator.prototype.addChild = function (childObject) {
var childId = objectUtils.makeKeyString(childObject.identifier);
var metadata = this.openmct.telemetry.getMetadata(childObject);
var formats = this.openmct.telemetry.getFormatMap(metadata);
this.baseState[childId] = {
id: childId,
domainObject: childObject,
metadata: metadata,
formats: formats
};
};
SummaryWidgetEvaluator.prototype.removeChild = function (childObject) {
var childId = objectUtils.makeKeyString(childObject.identifier);
delete this.baseState[childId];
};
SummaryWidgetEvaluator.prototype.load = function () {
return this.loadPromise;
};
/**
* return a promise for a clone of the base state object.
*/
SummaryWidgetEvaluator.prototype.getBaseStateClone = function () {
return this.load()
.then(function () {
return _(this.baseState)
.values()
.map(_.clone)
.indexBy('id')
.value();
}.bind(this));
};
/**
* Subscribes to realtime updates for a given objectState, and invokes
* the supplied callback when objectState has been updated. Returns
* a function to unsubscribe.
* @private.
*/
SummaryWidgetEvaluator.prototype.subscribeToObjectState = function (callback, objectState) {
return this.openmct.telemetry.subscribe(
objectState.domainObject,
function (datum) {
objectState.lastDatum = datum;
objectState.timestamps = this.getTimestamps(objectState.id, datum);
callback();
}.bind(this)
);
};
/**
* Given an object state, will return a promise that is resolved when the
* object state has been updated from the LAD.
* @private.
*/
SummaryWidgetEvaluator.prototype.updateObjectStateFromLAD = function (options, objectState) {
options = _.extend({}, options, {
strategy: 'latest',
size: 1
});
return this.openmct
.telemetry
.request(
objectState.domainObject,
options
)
.then(function (results) {
objectState.lastDatum = results[results.length - 1];
objectState.timestamps = this.getTimestamps(
objectState.id,
objectState.lastDatum
);
}.bind(this));
};
/**
* Returns an object containing all domain values in a datum.
* @private.
*/
SummaryWidgetEvaluator.prototype.getTimestamps = function (childId, datum) {
var timestampedDatum = {};
this.openmct.time.getAllTimeSystems().forEach(function (timeSystem) {
timestampedDatum[timeSystem.key] =
this.baseState[childId].formats[timeSystem.key].parse(datum);
}, this);
return timestampedDatum;
};
/**
* Given a base datum(containing timestamps) and rule index, adds values
* from the matching rule.
*/
SummaryWidgetEvaluator.prototype.makeDatumFromRule = function (ruleIndex, baseDatum) {
var rule = this.rules[ruleIndex];
baseDatum.ruleLabel = rule.label;
baseDatum.ruleName = rule.name;
baseDatum.message = rule.message;
baseDatum.ruleIndex = ruleIndex;
baseDatum.backgroundColor = rule.style['background-color'];
baseDatum.textColor = rule.style.color;
baseDatum.borderColor = rule.style['border-color'];
baseDatum.icon = rule.icon;
return baseDatum;
};
/**
* evaluate a state object and return a summary widget telemetry datum.
* Will use the specified timestampKey to decide which timestamps to apply.
* @private.
*/
SummaryWidgetEvaluator.prototype.evaluateState = function (state, timestampKey) {
var latestTimestamp = _(state)
.map('timestamps')
.sortBy(timestampKey)
.first();
latestTimestamp = _.clone(latestTimestamp);
for (var i = this.rules.length - 1; i > 0; i--) {
if (this.rules[i].evaluate(state, false)) {
break;
}
}
return this.makeDatumFromRule(i, latestTimestamp);
};
SummaryWidgetEvaluator.prototype.destroy = function () {
this.stopListening();
this.removeObserver();
};
return SummaryWidgetEvaluator;
});

View File

@@ -1,97 +0,0 @@
define([
], function (
) {
function SummaryWidgetMetadataProvider(openmct) {
this.openmct = openmct;
}
SummaryWidgetMetadataProvider.prototype.appliesTo = function (domainObject) {
return domainObject.type === 'summary-widget';
};
SummaryWidgetMetadataProvider.prototype.getDomains = function (domainObject) {
return this.openmct.time.getAllTimeSystems().map(function (ts, i) {
return {
key: ts.key,
name: 'UTC',
format: ts.timeFormat,
hints: {
domain: i
}
};
});
};
SummaryWidgetMetadataProvider.prototype.getMetadata = function (domainObject) {
var ruleOrder = domainObject.configuration.ruleOrder || [];
var enumerations = ruleOrder
.filter(function (ruleId) {
return !!domainObject.configuration.ruleConfigById[ruleId];
})
.map(function (ruleId, ruleIndex) {
return {
string: domainObject.configuration.ruleConfigById[ruleId].label,
value: ruleIndex
};
});
var metadata = {
// Generally safe assumption is that we have one domain per timeSystem.
values: this.getDomains().concat([
{
name: 'state',
key: 'state',
source: 'ruleIndex',
format: 'enum',
enumerations: enumerations,
hints: {
range: 1
}
},
{
name: 'Rule Label',
key: 'ruleLabel',
format: 'string'
},
{
name: 'Rule Name',
key: 'ruleName',
format: 'string'
},
{
name: 'Message',
key: 'message',
format: 'string'
},
{
name: 'Background Color',
key: 'backgroundColor',
format: 'string'
},
{
name: 'Text Color',
key: 'textColor',
format: 'string'
},
{
name: 'Border Color',
key: 'borderColor',
format: 'string'
},
{
name: 'Display Icon',
key: 'icon',
format: 'string'
}
])
};
return metadata;
};
return SummaryWidgetMetadataProvider;
});

View File

@@ -1,51 +0,0 @@
define([
'./SummaryWidgetCondition'
], function (
SummaryWidgetCondition
) {
function SummaryWidgetRule(definition) {
this.name = definition.name;
this.label = definition.label;
this.id = definition.id;
this.icon = definition.icon;
this.style = definition.style;
this.message = definition.message;
this.description = definition.description;
this.conditions = definition.conditions.map(function (cDefinition) {
return new SummaryWidgetCondition(cDefinition);
});
this.trigger = definition.trigger;
}
/**
* Evaluate the given rule against a telemetryState and return true if it
* matches.
*/
SummaryWidgetRule.prototype.evaluate = function (telemetryState) {
var i;
var result;
if (this.trigger === 'all') {
for (i = 0; i < this.conditions.length; i++) {
result = this.conditions[i].evaluate(telemetryState);
if (!result) {
return false;
}
}
return true;
} else if (this.trigger === 'any') {
for (i = 0; i < this.conditions.length; i++) {
result = this.conditions[i].evaluate(telemetryState);
if (result) {
return true;
}
}
return false;
} else {
throw new Error('Invalid rule trigger: ' + this.trigger);
}
};
return SummaryWidgetRule;
});

View File

@@ -1,141 +0,0 @@
define([
'./SummaryWidgetRule'
], function (
SummaryWidgetRule
) {
describe('SummaryWidgetRule', function () {
var rule;
var telemetryState;
beforeEach(function () {
var formatMap = {
raw: {
parse: function (datum) {
return datum.value;
}
}
};
telemetryState = {
objectId: {
formats: formatMap,
lastDatum: {
}
},
otherObjectId: {
formats: formatMap,
lastDatum: {
}
}
};
});
it('allows single condition rules with any', function () {
rule = new SummaryWidgetRule({
trigger: 'any',
conditions: [{
object: 'objectId',
key: 'raw',
operation: 'greaterThan',
values: [
10
]
}]
});
telemetryState.objectId.lastDatum.value = 5;
expect(rule.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 15;
expect(rule.evaluate(telemetryState)).toBe(true);
});
it('allows single condition rules with all', function () {
rule = new SummaryWidgetRule({
trigger: 'all',
conditions: [{
object: 'objectId',
key: 'raw',
operation: 'greaterThan',
values: [
10
]
}]
});
telemetryState.objectId.lastDatum.value = 5;
expect(rule.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 15;
expect(rule.evaluate(telemetryState)).toBe(true);
});
it('can combine multiple conditions with all', function () {
rule = new SummaryWidgetRule({
trigger: 'all',
conditions: [{
object: 'objectId',
key: 'raw',
operation: 'greaterThan',
values: [
10
]
}, {
object: 'otherObjectId',
key: 'raw',
operation: 'greaterThan',
values: [
20
]
}]
});
telemetryState.objectId.lastDatum.value = 5;
telemetryState.otherObjectId.lastDatum.value = 5;
expect(rule.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 5;
telemetryState.otherObjectId.lastDatum.value = 25;
expect(rule.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 15;
telemetryState.otherObjectId.lastDatum.value = 5;
expect(rule.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 15;
telemetryState.otherObjectId.lastDatum.value = 25;
expect(rule.evaluate(telemetryState)).toBe(true);
});
it('can combine multiple conditions with any', function () {
rule = new SummaryWidgetRule({
trigger: 'any',
conditions: [{
object: 'objectId',
key: 'raw',
operation: 'greaterThan',
values: [
10
]
}, {
object: 'otherObjectId',
key: 'raw',
operation: 'greaterThan',
values: [
20
]
}]
});
telemetryState.objectId.lastDatum.value = 5;
telemetryState.otherObjectId.lastDatum.value = 5;
expect(rule.evaluate(telemetryState)).toBe(false);
telemetryState.objectId.lastDatum.value = 5;
telemetryState.otherObjectId.lastDatum.value = 25;
expect(rule.evaluate(telemetryState)).toBe(true);
telemetryState.objectId.lastDatum.value = 15;
telemetryState.otherObjectId.lastDatum.value = 5;
expect(rule.evaluate(telemetryState)).toBe(true);
telemetryState.objectId.lastDatum.value = 15;
telemetryState.otherObjectId.lastDatum.value = 25;
expect(rule.evaluate(telemetryState)).toBe(true);
});
});
});

View File

@@ -1,42 +0,0 @@
define([
'./EvaluatorPool'
], function (
EvaluatorPool
) {
function SummaryWidgetTelemetryProvider(openmct) {
this.pool = new EvaluatorPool(openmct);
}
SummaryWidgetTelemetryProvider.prototype.supportsRequest = function (domainObject, options) {
return domainObject.type === 'summary-widget';
};
SummaryWidgetTelemetryProvider.prototype.request = function (domainObject, options) {
if (options.strategy !== 'latest' && options.size !== 1) {
return Promise.resolve([]);
}
var evaluator = this.pool.get(domainObject);
return evaluator.requestLatest(options)
.then(function (latestDatum) {
this.pool.release(evaluator);
return [latestDatum];
}.bind(this));
};
SummaryWidgetTelemetryProvider.prototype.supportsSubscribe = function (domainObject) {
return domainObject.type === 'summary-widget';
};
SummaryWidgetTelemetryProvider.prototype.subscribe = function (domainObject, callback) {
var evaluator = this.pool.get(domainObject);
var unsubscribe = evaluator.subscribe(callback);
return function () {
this.pool.release(evaluator);
unsubscribe();
}.bind(this);
};
return SummaryWidgetTelemetryProvider;
});

View File

@@ -1,175 +0,0 @@
define([
], function (
) {
var OPERATIONS = {
equalTo: {
operation: function (input) {
return input[0] === input[1];
},
text: 'is equal to',
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' == ' + values[0];
}
},
notEqualTo: {
operation: function (input) {
return input[0] !== input[1];
},
text: 'is not equal to',
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' != ' + values[0];
}
},
greaterThan: {
operation: function (input) {
return input[0] > input[1];
},
text: 'is greater than',
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' > ' + values[0];
}
},
lessThan: {
operation: function (input) {
return input[0] < input[1];
},
text: 'is less than',
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' < ' + values[0];
}
},
greaterThanOrEq: {
operation: function (input) {
return input[0] >= input[1];
},
text: 'is greater than or equal to',
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' >= ' + values[0];
}
},
lessThanOrEq: {
operation: function (input) {
return input[0] <= input[1];
},
text: 'is less than or equal to',
appliesTo: ['number'],
inputCount: 1,
getDescription: function (values) {
return ' <= ' + values[0];
}
},
between: {
operation: function (input) {
return input[0] > input[1] && input[0] < input[2];
},
text: 'is between',
appliesTo: ['number'],
inputCount: 2,
getDescription: function (values) {
return ' between ' + values[0] + ' and ' + values[1];
}
},
notBetween: {
operation: function (input) {
return input[0] < input[1] || input[0] > input[2];
},
text: 'is not between',
appliesTo: ['number'],
inputCount: 2,
getDescription: function (values) {
return ' not between ' + values[0] + ' and ' + values[1];
}
},
textContains: {
operation: function (input) {
return input[0] && input[1] && input[0].includes(input[1]);
},
text: 'text contains',
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' contains ' + values[0];
}
},
textDoesNotContain: {
operation: function (input) {
return input[0] && input[1] && !input[0].includes(input[1]);
},
text: 'text does not contain',
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' does not contain ' + values[0];
}
},
textStartsWith: {
operation: function (input) {
return input[0].startsWith(input[1]);
},
text: 'text starts with',
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' starts with ' + values[0];
}
},
textEndsWith: {
operation: function (input) {
return input[0].endsWith(input[1]);
},
text: 'text ends with',
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' ends with ' + values[0];
}
},
textIsExactly: {
operation: function (input) {
return input[0] === input[1];
},
text: 'text is exactly',
appliesTo: ['string'],
inputCount: 1,
getDescription: function (values) {
return ' is exactly ' + values[0];
}
},
isUndefined: {
operation: function (input) {
return typeof input[0] === 'undefined';
},
text: 'is undefined',
appliesTo: ['string', 'number'],
inputCount: 0,
getDescription: function () {
return ' is undefined';
}
},
isDefined: {
operation: function (input) {
return typeof input[0] !== 'undefined';
},
text: 'is defined',
appliesTo: ['string', 'number'],
inputCount: 0,
getDescription: function () {
return ' is defined';
}
}
};
return OPERATIONS;
});

View File

@@ -1,82 +0,0 @@
define([
'text!./summary-widget.html'
], function (
summaryWidgetTemplate
) {
function SummaryWidgetView(domainObject, openmct) {
this.openmct = openmct;
this.domainObject = domainObject;
this.hasUpdated = false;
}
SummaryWidgetView.prototype.updateState = function (datum) {
this.hasUpdated = true;
this.widget.style.color = datum.textColor;
this.widget.style.backgroundColor = datum.backgroundColor;
this.widget.style.borderColor = datum.borderColor;
this.widget.title = datum.message;
this.label.innerHTML = datum.ruleLabel;
this.label.className = 'label widget-label ' + datum.icon;
};
SummaryWidgetView.prototype.render = function (domainObject) {
if (this.unsubscribe) {
this.unsubscribe();
}
this.hasUpdated = false;
this.container.innerHTML = summaryWidgetTemplate;
this.widget = this.container.querySelector('a');
this.label = this.container.querySelector('.widget-label');
if (domainObject.url) {
this.widget.setAttribute('href', domainObject.url);
} else {
this.widget.removeAttribute('href');
}
if (domainObject.openNewTab === 'newTab') {
this.widget.setAttribute('target', '_blank');
} else {
this.widget.removeAttribute('target');
}
var renderTracker = {};
this.renderTracker = renderTracker;
this.openmct.telemetry.request(this.domainObject, {
strategy: 'latest',
size: 1
}).then(function (results) {
if (this.hasUpdated || this.renderTracker !== renderTracker) {
return;
}
this.updateState(results[results.length - 1]);
}.bind(this));
this.unsubscribe = this.openmct
.telemetry
.subscribe(domainObject, this.updateState.bind(this));
};
SummaryWidgetView.prototype.show = function (container) {
this.container = container;
this.render(this.domainObject);
this.removeMutationListener = this.openmct.objects.observe(
this.domainObject,
'*',
this.render.bind(this)
);
};
SummaryWidgetView.prototype.destroy = function (container) {
this.unsubscribe();
this.removeMutationListener();
delete this.widget;
delete this.label;
delete this.openmct;
delete this.domainObject;
};
return SummaryWidgetView;
});

View File

@@ -1,42 +0,0 @@
define([
'../SummaryWidget',
'./SummaryWidgetView',
'../../../../api/objects/object-utils'
], function (
SummaryWidgetEditView,
SummaryWidgetView,
objectUtils
) {
/**
*
*/
function SummaryWidgetViewProvider(openmct) {
return {
key: 'summary-widget-viewer',
name: 'Widget View',
canView: function (domainObject) {
return domainObject.type === 'summary-widget';
},
view: function (domainObject) {
var statusService = openmct.$injector.get('statusService');
var objectId = objectUtils.makeKeyString(domainObject.identifier);
var statuses = statusService.listStatuses(objectId);
var isEditing = statuses.indexOf('editing') !== -1;
if (isEditing) {
return new SummaryWidgetEditView(domainObject, openmct);
} else {
return new SummaryWidgetView(domainObject, openmct);
}
},
editable: true,
priority: function (domainObject) {
return 1;
}
};
}
return SummaryWidgetViewProvider;
});

View File

@@ -1,5 +0,0 @@
<div class="w-summary-widget s-status-no-data">
<a class="t-summary-widget l-summary-widget s-summary-widget labeled">
<span class="label widget-label">Loading...</span>
</a>
</div>

View File

@@ -19,7 +19,6 @@ define(['../src/ConditionManager'], function (ConditionManager) {
removeCallbackSpy,
telemetryCallbackSpy,
metadataCallbackSpy,
telemetryRequests,
mockTelemetryValues,
mockTelemetryValues2,
mockConditionEvaluator;
@@ -62,43 +61,31 @@ define(['../src/ConditionManager'], function (ConditionManager) {
mockCompObject1: {
property1: {
key: 'property1',
name: 'Property 1',
format: 'string',
hints: {}
name: 'Property 1'
},
property2: {
key: 'property2',
name: 'Property 2',
hints: {
domain: 1
}
name: 'Property 2'
}
},
mockCompObject2: {
property3: {
key: 'property3',
name: 'Property 3',
format: 'string',
hints: {}
name: 'Property 3'
},
property4: {
key: 'property4',
name: 'Property 4',
hints: {
range: 1
}
name: 'Property 4'
}
},
mockCompObject3: {
property1: {
key: 'property1',
name: 'Property 1',
hints: {}
name: 'Property 1'
},
property2: {
key: 'property2',
name: 'Property 2',
hints: {}
name: 'Property 2'
}
}
};
@@ -173,20 +160,22 @@ define(['../src/ConditionManager'], function (ConditionManager) {
unregisterSpies[event]();
});
mockComposition.load.andCallFake(function () {
mockComposition.triggerCallback('add', mockCompObject1);
mockComposition.triggerCallback('add', mockCompObject2);
mockComposition.triggerCallback('load');
mockEventCallbacks.add(mockCompObject1);
mockEventCallbacks.add(mockCompObject2);
mockEventCallbacks.load();
});
mockComposition.triggerCallback.andCallFake(function (event, obj) {
mockComposition.triggerCallback.andCallFake(function (event) {
if (event === 'add') {
mockEventCallbacks.add(obj);
mockEventCallbacks.add(mockCompObject3);
} else if (event === 'remove') {
mockEventCallbacks.remove(obj.identifier);
mockEventCallbacks.remove({
key: 'mockCompObject2'
});
} else {
mockEventCallbacks[event]();
}
});
telemetryRequests = [];
mockTelemetryAPI = jasmine.createSpyObj('telemetryAPI', [
'request',
'canProvideTelemetry',
@@ -195,15 +184,9 @@ define(['../src/ConditionManager'], function (ConditionManager) {
'triggerTelemetryCallback'
]);
mockTelemetryAPI.request.andCallFake(function (obj) {
var req = {
object: obj
};
req.promise = new Promise(function (resolve, reject) {
req.resolve = resolve;
req.reject = reject;
return new Promise(function (resolve, reject) {
resolve(mockTelemetryValues[obj.identifer.key]);
});
telemetryRequests.push(req);
return req.promise;
});
mockTelemetryAPI.canProvideTelemetry.andReturn(true);
mockTelemetryAPI.getMetadata.andCallFake(function (obj) {
@@ -262,50 +245,41 @@ define(['../src/ConditionManager'], function (ConditionManager) {
var allKeys = {
property1: {
key: 'property1',
name: 'Property 1',
format: 'string',
hints: {}
name: 'Property 1'
},
property2: {
key: 'property2',
name: 'Property 2',
hints: {
domain: 1
}
name: 'Property 2'
},
property3: {
key: 'property3',
name: 'Property 3',
format: 'string',
hints: {}
name: 'Property 3'
},
property4: {
key: 'property4',
name: 'Property 4',
hints: {
range: 1
}
name: 'Property 4'
}
};
expect(conditionManager.getTelemetryMetadata('all')).toEqual(allKeys);
expect(conditionManager.getTelemetryMetadata('any')).toEqual(allKeys);
mockComposition.triggerCallback('add', mockCompObject3);
mockComposition.triggerCallback('add');
expect(conditionManager.getTelemetryMetadata('all')).toEqual(allKeys);
expect(conditionManager.getTelemetryMetadata('any')).toEqual(allKeys);
});
it('loads and gets telemetry property types', function () {
conditionManager.parseAllPropertyTypes();
expect(conditionManager.getTelemetryPropertyType('mockCompObject1', 'property1'))
.toEqual('string');
expect(conditionManager.getTelemetryPropertyType('mockCompObject2', 'property4'))
.toEqual('number');
expect(conditionManager.metadataLoadCompleted()).toEqual(true);
expect(metadataCallbackSpy).toHaveBeenCalled();
conditionManager.parseAllPropertyTypes().then(function () {
expect(conditionManager.getTelemetryPropertyType('mockCompObject1', 'property1'))
.toEqual('string');
expect(conditionManager.getTelemetryPropertyType('mockCompObject2', 'property4'))
.toEqual('number');
expect(conditionManager.metadataLoadComplete()).toEqual(true);
expect(metadataCallbackSpy).toHaveBeenCalled();
});
});
it('responds to a composition add event and invokes the appropriate handlers', function () {
mockComposition.triggerCallback('add', mockCompObject3);
mockComposition.triggerCallback('add');
expect(addCallbackSpy).toHaveBeenCalledWith(mockCompObject3);
expect(conditionManager.getComposition()).toEqual({
mockCompObject1: mockCompObject1,
@@ -315,7 +289,7 @@ define(['../src/ConditionManager'], function (ConditionManager) {
});
it('responds to a composition remove event and invokes the appropriate handlers', function () {
mockComposition.triggerCallback('remove', mockCompObject2);
mockComposition.triggerCallback('remove');
expect(removeCallbackSpy).toHaveBeenCalledWith({
key: 'mockCompObject2'
});
@@ -326,7 +300,7 @@ define(['../src/ConditionManager'], function (ConditionManager) {
});
it('unregisters telemetry subscriptions and composition listeners on destroy', function () {
mockComposition.triggerCallback('add', mockCompObject3);
mockComposition.triggerCallback('add');
conditionManager.destroy();
Object.values(unsubscribeSpies).forEach(function (spy) {
expect(spy).toHaveBeenCalled();
@@ -337,19 +311,7 @@ define(['../src/ConditionManager'], function (ConditionManager) {
});
it('populates its LAD cache with historial data on load, if available', function () {
expect(telemetryRequests.length).toBe(2);
expect(telemetryRequests[0].object).toBe(mockCompObject1);
expect(telemetryRequests[1].object).toBe(mockCompObject2);
expect(telemetryCallbackSpy).not.toHaveBeenCalled();
telemetryRequests[0].resolve([mockTelemetryValues.mockCompObject1]);
telemetryRequests[1].resolve([mockTelemetryValues.mockCompObject2]);
waitsFor(function () {
return telemetryCallbackSpy.calls.length === 2;
});
runs(function () {
conditionManager.parseAllPropertyTypes().then(function () {
expect(conditionManager.subscriptionCache.mockCompObject1.property1).toEqual('Its a string');
expect(conditionManager.subscriptionCache.mockCompObject2.property4).toEqual(66);
});
@@ -390,10 +352,12 @@ define(['../src/ConditionManager'], function (ConditionManager) {
});
it('gets the human-readable name of a telemetry field', function () {
expect(conditionManager.getTelemetryPropertyName('mockCompObject1', 'property1'))
.toEqual('Property 1');
expect(conditionManager.getTelemetryPropertyName('mockCompObject2', 'property4'))
.toEqual('Property 4');
conditionManager.parseAllPropertyTypes().then(function () {
expect(conditionManager.getTelemetryPropertyName('mockCompObject1', 'property1'))
.toEqual('Property 1');
expect(conditionManager.getTelemetryPropertyName('mockCompObject2', 'property4'))
.toEqual('Property 4');
});
});
it('gets its associated ConditionEvaluator', function () {

View File

@@ -75,8 +75,7 @@ requirejs.config({
"d3-format": "node_modules/d3-format/build/d3-format.min",
"d3-interpolate": "node_modules/d3-interpolate/build/d3-interpolate.min",
"d3-time": "node_modules/d3-time/build/d3-time.min",
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min",
"painterro": "node_modules/@cristian77/painterro/build/painterro.min"
"d3-time-format": "node_modules/d3-time-format/build/d3-time-format.min"
},
"shim": {
@@ -126,4 +125,4 @@ requirejs.config({
window.__karma__.start.apply(window.__karma__, args);
});
}
});
});